/*
  The shared configuration for tidal and riptide sibling tokens
  riptide.finance

  @nightg0at
  SPDX-License-Identifier: MIT
*/

// File: @openzeppelin/contracts/token/ERC20/IERC20.sol


pragma solidity >=0.6.0 <0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the amount of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves `amount` tokens from the caller's account to `recipient`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address recipient, uint256 amount) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `sender` to `recipient` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);

    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);
}

// File: @openzeppelin/contracts/introspection/IERC165.sol



pragma solidity >=0.6.0 <0.8.0;

/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

// File: @openzeppelin/contracts/token/ERC1155/IERC1155.sol



pragma solidity >=0.6.2 <0.8.0;


/**
 * @dev Required interface of an ERC1155 compliant contract, as defined in the
 * https://eips.ethereum.org/EIPS/eip-1155[EIP].
 *
 * _Available since v3.1._
 */
interface IERC1155 is IERC165 {
    /**
     * @dev Emitted when `value` tokens of token type `id` are transferred from `from` to `to` by `operator`.
     */
    event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value);

    /**
     * @dev Equivalent to multiple {TransferSingle} events, where `operator`, `from` and `to` are the same for all
     * transfers.
     */
    event TransferBatch(address indexed operator, address indexed from, address indexed to, uint256[] ids, uint256[] values);

    /**
     * @dev Emitted when `account` grants or revokes permission to `operator` to transfer their tokens, according to
     * `approved`.
     */
    event ApprovalForAll(address indexed account, address indexed operator, bool approved);

    /**
     * @dev Emitted when the URI for token type `id` changes to `value`, if it is a non-programmatic URI.
     *
     * If an {URI} event was emitted for `id`, the standard
     * https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[guarantees] that `value` will equal the value
     * returned by {IERC1155MetadataURI-uri}.
     */
    event URI(string value, uint256 indexed id);

    /**
     * @dev Returns the amount of tokens of token type `id` owned by `account`.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     */
    function balanceOf(address account, uint256 id) external view returns (uint256);

    /**
     * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {balanceOf}.
     *
     * Requirements:
     *
     * - `accounts` and `ids` must have the same length.
     */
    function balanceOfBatch(address[] calldata accounts, uint256[] calldata ids) external view returns (uint256[] memory);

    /**
     * @dev Grants or revokes permission to `operator` to transfer the caller's tokens, according to `approved`,
     *
     * Emits an {ApprovalForAll} event.
     *
     * Requirements:
     *
     * - `operator` cannot be the caller.
     */
    function setApprovalForAll(address operator, bool approved) external;

    /**
     * @dev Returns true if `operator` is approved to transfer ``account``'s tokens.
     *
     * See {setApprovalForAll}.
     */
    function isApprovedForAll(address account, address operator) external view returns (bool);

    /**
     * @dev Transfers `amount` tokens of token type `id` from `from` to `to`.
     *
     * Emits a {TransferSingle} event.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - If the caller is not `from`, it must be have been approved to spend ``from``'s tokens via {setApprovalForAll}.
     * - `from` must have a balance of tokens of type `id` of at least `amount`.
     * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
     * acceptance magic value.
     */
    function safeTransferFrom(address from, address to, uint256 id, uint256 amount, bytes calldata data) external;

    /**
     * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {safeTransferFrom}.
     *
     * Emits a {TransferBatch} event.
     *
     * Requirements:
     *
     * - `ids` and `amounts` must have the same length.
     * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
     * acceptance magic value.
     */
    function safeBatchTransferFrom(address from, address to, uint256[] calldata ids, uint256[] calldata amounts, bytes calldata data) external;
}

// File: @openzeppelin/contracts/introspection/IERC1820Registry.sol



pragma solidity >=0.6.0 <0.8.0;

/**
 * @dev Interface of the global ERC1820 Registry, as defined in the
 * https://eips.ethereum.org/EIPS/eip-1820[EIP]. Accounts may register
 * implementers for interfaces in this registry, as well as query support.
 *
 * Implementers may be shared by multiple accounts, and can also implement more
 * than a single interface for each account. Contracts can implement interfaces
 * for themselves, but externally-owned accounts (EOA) must delegate this to a
 * contract.
 *
 * {IERC165} interfaces can also be queried via the registry.
 *
 * For an in-depth explanation and source code analysis, see the EIP text.
 */
interface IERC1820Registry {
    /**
     * @dev Sets `newManager` as the manager for `account`. A manager of an
     * account is able to set interface implementers for it.
     *
     * By default, each account is its own manager. Passing a value of `0x0` in
     * `newManager` will reset the manager to this initial state.
     *
     * Emits a {ManagerChanged} event.
     *
     * Requirements:
     *
     * - the caller must be the current manager for `account`.
     */
    function setManager(address account, address newManager) external;

    /**
     * @dev Returns the manager for `account`.
     *
     * See {setManager}.
     */
    function getManager(address account) external view returns (address);

    /**
     * @dev Sets the `implementer` contract as ``account``'s implementer for
     * `interfaceHash`.
     *
     * `account` being the zero address is an alias for the caller's address.
     * The zero address can also be used in `implementer` to remove an old one.
     *
     * See {interfaceHash} to learn how these are created.
     *
     * Emits an {InterfaceImplementerSet} event.
     *
     * Requirements:
     *
     * - the caller must be the current manager for `account`.
     * - `interfaceHash` must not be an {IERC165} interface id (i.e. it must not
     * end in 28 zeroes).
     * - `implementer` must implement {IERC1820Implementer} and return true when
     * queried for support, unless `implementer` is the caller. See
     * {IERC1820Implementer-canImplementInterfaceForAddress}.
     */
    function setInterfaceImplementer(address account, bytes32 _interfaceHash, address implementer) external;

    /**
     * @dev Returns the implementer of `interfaceHash` for `account`. If no such
     * implementer is registered, returns the zero address.
     *
     * If `interfaceHash` is an {IERC165} interface id (i.e. it ends with 28
     * zeroes), `account` will be queried for support of it.
     *
     * `account` being the zero address is an alias for the caller's address.
     */
    function getInterfaceImplementer(address account, bytes32 _interfaceHash) external view returns (address);

    /**
     * @dev Returns the interface hash for an `interfaceName`, as defined in the
     * corresponding
     * https://eips.ethereum.org/EIPS/eip-1820#interface-name[section of the EIP].
     */
    function interfaceHash(string calldata interfaceName) external pure returns (bytes32);

    /**
     *  @notice Updates the cache with whether the contract implements an ERC165 interface or not.
     *  @param account Address of the contract for which to update the cache.
     *  @param interfaceId ERC165 interface for which to update the cache.
     */
    function updateERC165Cache(address account, bytes4 interfaceId) external;

    /**
     *  @notice Checks whether a contract implements an ERC165 interface or not.
     *  If the result is not cached a direct lookup on the contract address is performed.
     *  If the result is not cached or the cached value is out-of-date, the cache MUST be updated manually by calling
     *  {updateERC165Cache} with the contract address.
     *  @param account Address of the contract to check.
     *  @param interfaceId ERC165 interface to check.
     *  @return True if `account` implements `interfaceId`, false otherwise.
     */
    function implementsERC165Interface(address account, bytes4 interfaceId) external view returns (bool);

    /**
     *  @notice Checks whether a contract implements an ERC165 interface or not without using nor updating the cache.
     *  @param account Address of the contract to check.
     *  @param interfaceId ERC165 interface to check.
     *  @return True if `account` implements `interfaceId`, false otherwise.
     */
    function implementsERC165InterfaceNoCache(address account, bytes4 interfaceId) external view returns (bool);

    event InterfaceImplementerSet(address indexed account, bytes32 indexed interfaceHash, address indexed implementer);

    event ManagerChanged(address indexed account, address indexed newManager);
}

// File: @uniswap/v2-core/contracts/interfaces/IUniswapV2Pair.sol

pragma solidity >=0.5.0;

interface IUniswapV2Pair {
    event Approval(address indexed owner, address indexed spender, uint value);
    event Transfer(address indexed from, address indexed to, uint value);

    function name() external pure returns (string memory);
    function symbol() external pure returns (string memory);
    function decimals() external pure returns (uint8);
    function totalSupply() external view returns (uint);
    function balanceOf(address owner) external view returns (uint);
    function allowance(address owner, address spender) external view returns (uint);

    function approve(address spender, uint value) external returns (bool);
    function transfer(address to, uint value) external returns (bool);
    function transferFrom(address from, address to, uint value) external returns (bool);

    function DOMAIN_SEPARATOR() external view returns (bytes32);
    function PERMIT_TYPEHASH() external pure returns (bytes32);
    function nonces(address owner) external view returns (uint);

    function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external;

    event Mint(address indexed sender, uint amount0, uint amount1);
    event Burn(address indexed sender, uint amount0, uint amount1, address indexed to);
    event Swap(
        address indexed sender,
        uint amount0In,
        uint amount1In,
        uint amount0Out,
        uint amount1Out,
        address indexed to
    );
    event Sync(uint112 reserve0, uint112 reserve1);

    function MINIMUM_LIQUIDITY() external pure returns (uint);
    function factory() external view returns (address);
    function token0() external view returns (address);
    function token1() external view returns (address);
    function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);
    function price0CumulativeLast() external view returns (uint);
    function price1CumulativeLast() external view returns (uint);
    function kLast() external view returns (uint);

    function mint(address to) external returns (uint liquidity);
    function burn(address to) external returns (uint amount0, uint amount1);
    function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external;
    function skim(address to) external;
    function sync() external;

    function initialize(address, address) external;
}

// File: @openzeppelin/contracts/GSN/Context.sol



pragma solidity >=0.6.0 <0.8.0;

/*
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with GSN meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address payable) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes memory) {
        this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
        return msg.data;
    }
}

// File: @openzeppelin/contracts/access/Ownable.sol



pragma solidity >=0.6.0 <0.8.0;

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor () internal {
        address msgSender = _msgSender();
        _owner = msgSender;
        emit OwnershipTransferred(address(0), msgSender);
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        require(_owner == _msgSender(), "Ownable: caller is not the owner");
        _;
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions anymore. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby removing any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        emit OwnershipTransferred(_owner, address(0));
        _owner = address(0);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        emit OwnershipTransferred(_owner, newOwner);
        _owner = newOwner;
    }
}

// File: contracts/Whitelist.sol

/*
  Exemption whitelists and convenience methods for Tidal's punitive mechanisms

  @nightg0at
*/


pragma solidity 0.6.12;

contract Whitelist is Ownable {

  // protectors can be ERC20, ERC777 or ERC1155 tokens
  // ERC115 tokens have a different balanceOf() method, so we use the ERC1820 registry to identify the ERC1155 interface
  IERC1820Registry private erc1820Registry; // 0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24
  bytes4 private constant ERC1155_INTERFACE_ID = 0xd9b67a26;

  // used to identify if an address is likely to be a uniswap pair
  address private constant UNISWAP_FACTORY = 0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f;

  // tokens that offer the holder some kind of protection
  struct TokenAttributes {
    bool active;
    uint256 proportion; // proportion of incoming tokens used as burn amount (typically 1 or 0.5)
    uint256 floor; // the lowest the balance can be after a wipeout event
  }

  struct TokenID {
    address addr;
    uint256 id; // for IERC1155 tokens else 0
  }

  // addresses that have some kind of protection as if they are holding protective tokens
  struct AddressAttributes {
    bool active;
    uint256 proportion;
    uint256 floor;
  }

  // addresses that do not incur punitive burns or wipeouts as senders or receivers
  struct WhitelistAttributes {
    bool active;
    bool sendBurn;
    bool receiveBurn;
    bool sendWipeout;
    bool receiveWipeout;
  }

  uint256 public defaultProportion = 1e18;
  uint256 public defaultFloor = 0;

  constructor(
    IERC1820Registry _erc1820Registry
  ) public {
    erc1820Registry = _erc1820Registry;
    
    // premine our starting protectors. Surfboards and trident NFTs
    addProtector(0xf90AeeF57Ae8Bc85FE8d40a3f4a45042F4258c67, 0, 5e17, 0); // surfboard, 0.5x, 0 floor
    addProtector(0xd07dc4262BCDbf85190C01c996b4C06a461d2430, 78947, 1e18, 2496e14); // bronze trident , 1x, 0.2496 floor
    addProtector(0xd07dc4262BCDbf85190C01c996b4C06a461d2430, 78955, 1e18, 42e16); // silver trident, 1x, 0.42 floor
    addProtector(0xd07dc4262BCDbf85190C01c996b4C06a461d2430, 78963, 1e18, 69e16); // gold trident, 1x, 0.69 floor

  }

  mapping (address => AddressAttributes) public protectedAddress;
  TokenID[] public protectors;
  mapping (address => mapping (uint256 => TokenAttributes)) public protectorAttributes;
  mapping (address => WhitelistAttributes) public whitelist;
  

  function addProtector(address _token, uint256 _id, uint256 _proportion, uint256 _floor) public onlyOwner {
    uint256 id = isERC1155(_token) ? _id : 0;
    require(protectorAttributes[_token][id].active == false, "WIPEOUT::addProtector: Token already active");
    editProtector(_token, true, id, _proportion, _floor);
    protectors.push(TokenID(_token, id));
  }

  function editProtector(address _token, bool _active, uint256 _id, uint256 _proportion, uint256 _floor) public onlyOwner {
    protectorAttributes[_token][_id] = TokenAttributes(_active, _proportion, _floor);
  }

  function protectorLength() external view returns (uint256) {
    return protectors.length;
  }

  function getProtectorAttributes(address _addr, uint256 _id) external view returns (bool, uint256, uint256) {
    return (
      protectorAttributes[_addr][_id].active,
      protectorAttributes[_addr][_id].proportion,
      protectorAttributes[_addr][_id].floor
    );
  }


  function isERC1155(address _token) private view returns (bool) {
    return erc1820Registry.implementsERC165Interface(_token, ERC1155_INTERFACE_ID);
  }

  function hasProtector(address _addr, address _protector, uint256 _id) public view returns (bool) {
    bool has = false;
    if (isERC1155(_protector)) {
      if (IERC1155(_protector).balanceOf(_addr, _id) > 0) {
        has = true;
      }
    } else {
      if (IERC20(_protector).balanceOf(_addr) > 0) {
        has = true;
      }
    }
    return has;
  }

  function cumulativeProtectionOf(address _addr) external view returns (uint256, uint256) {
    uint256 proportion = defaultProportion;
    uint256 floor = defaultFloor;
    for (uint256 i=0; i<protectors.length; i++) {
      address protector = protectors[i].addr;
      uint256 id = protectors[i].id;
      if (hasProtector(_addr, protector, id)) {
        if (proportion > protectorAttributes[protector][id].proportion) {
          proportion = protectorAttributes[protector][id].proportion;
        }
        if (floor < protectorAttributes[protector][id].floor) {
          floor = protectorAttributes[protector][id].floor;
        }
      }
    }
    return (proportion, floor);
  }

  function setProtectedAddress(address _addr, bool _active, uint256 _proportion, uint256 _floor) public onlyOwner {
    require(_addr != address(0), "WIPEOUT::setProtector: zero address");
    protectedAddress[_addr] = AddressAttributes(_active, _proportion, _floor);
  }

  function getProtectedAddress(address _addr) external view returns (bool, uint256, uint256) {
    return (
      protectedAddress[_addr].active,
      protectedAddress[_addr].proportion,
      protectedAddress[_addr].floor
    );
  }

  function setWhitelist(
    address _whitelisted,
    bool _active,
    bool _sendBurn,
    bool _receiveBurn,
    bool _sendWipeout,
    bool _receiveWipeout
  ) public onlyOwner {
    require(_whitelisted != address(0), "WIPEOUT::setWhitelist: zero address");
    whitelist[_whitelisted] = WhitelistAttributes(_active, _sendBurn, _receiveBurn, _sendWipeout, _receiveWipeout);
  }

  function getWhitelist(address _addr) external view returns (bool, bool, bool, bool, bool) {
    return (
      whitelist[_addr].active,
      whitelist[_addr].sendBurn,
      whitelist[_addr].receiveBurn,
      whitelist[_addr].sendWipeout,
      whitelist[_addr].receiveWipeout
    );
  }

  // checks if the address is a deployed contract and if so,
  // checks if the factory() method is present and returns the uniswap factory address.
  // returns true if it is.
  // This is easy to spoof but the gains are low enough for this to be ok.
  function isUniswapTokenPair(address _addr) public view returns (bool) {
    uint32 size;
    assembly {
      size := extcodesize(_addr)
    }
    if (size == 0) {
      return false;
    } else {
      try IUniswapV2Pair(_addr).factory() returns (address _factory) {
        return _factory == UNISWAP_FACTORY ? true : false;
      } catch {
        return false;
      }
    }
  }

  function isUniswapTokenPairWith(address _pair, address _token) public view returns (bool) {
    return (IUniswapV2Pair(_pair).token0() == _token || IUniswapV2Pair(_pair).token1() == _token);
  }

  function willBurn(address _sender, address _recipient) public view returns (bool) {
    // returns true if everything is false
    return !(whitelist[_sender].sendBurn || whitelist[_recipient].receiveBurn);
  }

  function willWipeout(address _sender, address _recipient) public view returns (bool) {
    bool whitelisted = whitelist[_sender].sendWipeout || isUniswapTokenPair(_sender);
    whitelisted = whitelisted || whitelist[_recipient].receiveWipeout;
    // returns true if everything is false
    return !whitelisted;
  }

}

// File: contracts/interfaces/ITideToken.sol


pragma solidity 0.6.12;


interface ITideToken is IERC20 {
  function owner() external view returns (address);
  function mint(address _to, uint256 _amount) external;
  function setParent(address _newConfig) external;
  function wipeout(address _recipient, uint256 _amount) external;
}

// File: contracts/TideParent.sol

pragma solidity 0.6.12;

contract TideParent is Whitelist {

  address private _poseidon;
  address[2] public siblings; //0: tidal, 1: riptide

  uint256 private _burnRate = 69e15; //6.9%, 0.069
  uint256 private _transmuteRate = 42e14; //0.42%, 0.0042

  constructor(
    IERC1820Registry _erc1820Registry
  ) public Whitelist(_erc1820Registry) {}

  function setPoseidon(address _newPoseidon) public onlyOwner {
    if (whitelist[_poseidon].active) {
      setWhitelist(_poseidon, false, false, false, false, false);
    }
    if (protectedAddress[_poseidon].active) {
      setProtectedAddress(_poseidon, false, 0, 0);
    }
    setProtectedAddress(_newPoseidon, true, 5e17, 69e16);
    setWhitelist(_newPoseidon, true, true, false, true, false);
    _poseidon = _newPoseidon;
  }

  function setSibling(uint256 _index, address _token) public onlyOwner {
    require(_token != address(0), "TIDEPARENT::setToken: zero address");
    siblings[_index] = _token;
  }

  function setAddresses(address _siblingA, address _siblingB, address _newPoseidon) external onlyOwner {
    setSibling(0, _siblingA);
    setSibling(1, _siblingB);
    setPoseidon(_newPoseidon);
  }

  function setBurnRate(uint256 _newBurnRate) external onlyOwner {
    require(_newBurnRate <= 2e17, "TIDEPARENT:setBurnRate: 20% max");
    _burnRate = _newBurnRate;
  }

  function setTransmuteRate(uint256 _newTransmuteRate) external onlyOwner {
    require(_newTransmuteRate <= 1e17, "TIDEPARENT:setTransmuteRate: 10% max");
    _transmuteRate = _newTransmuteRate;
  }

  function setNewParent(address _newConfig) external onlyOwner {
    ITideToken(siblings[0]).setParent(_newConfig);
    ITideToken(siblings[1]).setParent(_newConfig);
  }

  function poseidon() public view returns (address) {
    return _poseidon;
  }

  function burnRate() public view returns (uint256) {
    return _burnRate;
  }

  function transmuteRate() public view returns (uint256) {
    return _transmuteRate;
  }

  function sibling(address _siblingCandidate) public view returns (address) {
    if (_siblingCandidate == siblings[0]) {
      return siblings[1];
    } else if (_siblingCandidate == siblings[1]) {
      return siblings[0];
    } else {
      return address(0);
    }
  }
}