More Info
Private Name Tags
ContractCreator
Latest 25 from a total of 381 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Remove Liquidity | 33432262 | 912 days ago | IN | 0 POL | 0.00240687 | ||||
Remove Liquidity | 33432252 | 912 days ago | IN | 0 POL | 0.00259377 | ||||
Remove Liquidity | 33432240 | 912 days ago | IN | 0 POL | 0.00240639 | ||||
Transfer | 29680109 | 1009 days ago | IN | 10.798 POL | 0.00063165 | ||||
Transfer | 28609728 | 1035 days ago | IN | 10 POL | 0.00098032 | ||||
Fulfill | 28266310 | 1044 days ago | IN | 0 POL | 0.00454499 | ||||
Prepare | 28264823 | 1044 days ago | IN | 0 POL | 0.02374863 | ||||
Fulfill | 28264513 | 1044 days ago | IN | 0 POL | 0.01078129 | ||||
Prepare | 28264082 | 1044 days ago | IN | 0 POL | 0.00763436 | ||||
Fulfill | 27359175 | 1067 days ago | IN | 0 POL | 0.00448974 | ||||
Prepare | 26370998 | 1092 days ago | IN | 0 POL | 0.00602449 | ||||
Prepare | 26370957 | 1092 days ago | IN | 0 POL | 0.00590882 | ||||
Prepare | 26220596 | 1096 days ago | IN | 0 POL | 0.00826045 | ||||
Prepare | 26219867 | 1096 days ago | IN | 0 POL | 0.00619211 | ||||
Prepare | 26219824 | 1096 days ago | IN | 0 POL | 0.00796149 | ||||
Fulfill | 26219337 | 1096 days ago | IN | 0 POL | 0.00669448 | ||||
Prepare | 26219330 | 1096 days ago | IN | 0 POL | 0.00900391 | ||||
Prepare | 26218378 | 1096 days ago | IN | 0 POL | 0.00544849 | ||||
Fulfill | 26218202 | 1096 days ago | IN | 0 POL | 0.00358185 | ||||
Fulfill | 26217810 | 1096 days ago | IN | 0 POL | 0.00355894 | ||||
Fulfill | 26217300 | 1096 days ago | IN | 0 POL | 0.00663315 | ||||
Fulfill | 26214782 | 1096 days ago | IN | 0 POL | 0.00584508 | ||||
Fulfill | 26213553 | 1096 days ago | IN | 0 POL | 0.00691134 | ||||
Prepare | 26199356 | 1097 days ago | IN | 0 POL | 0.00760434 | ||||
Prepare | 25834758 | 1107 days ago | IN | 0 POL | 0.00195448 |
Latest 25 internal transactions (View All)
Parent Transaction Hash | Block | From | To | |||
---|---|---|---|---|---|---|
31551224 | 960 days ago | 0.02849677 POL | ||||
31551224 | 960 days ago | 0.02849677 POL | ||||
31547927 | 960 days ago | 0.00675947 POL | ||||
31547926 | 960 days ago | 0.00675947 POL | ||||
31544534 | 960 days ago | 0.00566252 POL | ||||
31544534 | 960 days ago | 0.00566252 POL | ||||
31538199 | 960 days ago | 0.0061737 POL | ||||
31537606 | 960 days ago | 0.00662788 POL | ||||
31537606 | 960 days ago | 0.00662788 POL | ||||
31537415 | 960 days ago | 0.00544754 POL | ||||
31537415 | 960 days ago | 0.00544754 POL | ||||
31536460 | 960 days ago | 0.00549345 POL | ||||
31536460 | 960 days ago | 0.00549345 POL | ||||
31534538 | 960 days ago | 0.006622 POL | ||||
31534538 | 960 days ago | 0.006622 POL | ||||
31533736 | 960 days ago | 0.0090398 POL | ||||
31533736 | 960 days ago | 0.0090398 POL | ||||
31526521 | 960 days ago | 0.0058098 POL | ||||
31526521 | 960 days ago | 0.0058098 POL | ||||
31519949 | 961 days ago | 0.00780752 POL | ||||
31519949 | 961 days ago | 0.00780752 POL | ||||
31517534 | 961 days ago | 0.00608121 POL | ||||
31517534 | 961 days ago | 0.00608121 POL | ||||
31517527 | 961 days ago | 0.00689553 POL | ||||
31517527 | 961 days ago | 0.00689553 POL |
Loading...
Loading
Similar Match Source Code This contract matches the deployed Bytecode of the Source Code for Contract 0x95CE8B1c...9c3572827 The constructor portion of the code might be different and could alter the actual behaviour of the contract
Contract Name:
Router
Compiler Version
v0.8.4+commit.c7e474f2
Contract Source Code (Solidity)
/** *Submitted for verification at polygonscan.com on 2021-12-21 */ // Sources flattened with hardhat v2.6.8 https://hardhat.org // File contracts/PriceOracle.sol pragma solidity ^0.8.4; abstract contract PriceOracle { /// @notice Indicator that this is a PriceOracle contract (for inspection) bool public constant isPriceOracle = true; /** * @notice Get the price of a token * @param token The token to get the price of * @return The asset price mantissa (scaled by 1e18). * Zero means the price is unavailable. */ function getTokenPrice(address token) external virtual view returns (uint); } // File @openzeppelin/contracts/utils/math/[email protected] pragma solidity ^0.8.0; // CAUTION // This version of SafeMath should only be used with Solidity 0.8 or later, // because it relies on the compiler's built in overflow checks. /** * @dev Wrappers over Solidity's arithmetic operations. * * NOTE: `SafeMath` is no longer needed starting with Solidity 0.8. The compiler * now has built in overflow checking. */ library SafeMath { /** * @dev Returns the addition of two unsigned integers, with an overflow flag. * * _Available since v3.4._ */ function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { uint256 c = a + b; if (c < a) return (false, 0); return (true, c); } } /** * @dev Returns the substraction of two unsigned integers, with an overflow flag. * * _Available since v3.4._ */ function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { if (b > a) return (false, 0); return (true, a - b); } } /** * @dev Returns the multiplication of two unsigned integers, with an overflow flag. * * _Available since v3.4._ */ function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 if (a == 0) return (true, 0); uint256 c = a * b; if (c / a != b) return (false, 0); return (true, c); } } /** * @dev Returns the division of two unsigned integers, with a division by zero flag. * * _Available since v3.4._ */ function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { if (b == 0) return (false, 0); return (true, a / b); } } /** * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag. * * _Available since v3.4._ */ function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { if (b == 0) return (false, 0); return (true, a % b); } } /** * @dev Returns the addition of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `+` operator. * * Requirements: * * - Addition cannot overflow. */ function add(uint256 a, uint256 b) internal pure returns (uint256) { return a + b; } /** * @dev Returns the subtraction of two unsigned integers, reverting on * overflow (when the result is negative). * * Counterpart to Solidity's `-` operator. * * Requirements: * * - Subtraction cannot overflow. */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { return a - b; } /** * @dev Returns the multiplication of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `*` operator. * * Requirements: * * - Multiplication cannot overflow. */ function mul(uint256 a, uint256 b) internal pure returns (uint256) { return a * b; } /** * @dev Returns the integer division of two unsigned integers, reverting on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. * * Requirements: * * - The divisor cannot be zero. */ function div(uint256 a, uint256 b) internal pure returns (uint256) { return a / b; } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * reverting when dividing by zero. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function mod(uint256 a, uint256 b) internal pure returns (uint256) { return a % b; } /** * @dev Returns the subtraction of two unsigned integers, reverting with custom message on * overflow (when the result is negative). * * CAUTION: This function is deprecated because it requires allocating memory for the error * message unnecessarily. For custom revert reasons use {trySub}. * * Counterpart to Solidity's `-` operator. * * Requirements: * * - Subtraction cannot overflow. */ function sub( uint256 a, uint256 b, string memory errorMessage ) internal pure returns (uint256) { unchecked { require(b <= a, errorMessage); return a - b; } } /** * @dev Returns the integer division of two unsigned integers, reverting with custom message on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function div( uint256 a, uint256 b, string memory errorMessage ) internal pure returns (uint256) { unchecked { require(b > 0, errorMessage); return a / b; } } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * reverting with custom message when dividing by zero. * * CAUTION: This function is deprecated because it requires allocating memory for the error * message unnecessarily. For custom revert reasons use {tryMod}. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function mod( uint256 a, uint256 b, string memory errorMessage ) internal pure returns (uint256) { unchecked { require(b > 0, errorMessage); return a % b; } } } // File @openzeppelin/contracts/token/ERC20/[email protected] pragma solidity ^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/utils/[email protected] pragma solidity ^0.8.0; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize, which returns 0 for contracts in // construction, since the code is only stored at the end of the // constructor execution. uint256 size; assembly { size := extcodesize(account) } return size > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); (bool success, ) = recipient.call{value: amount}(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCall(target, data, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value ) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value, string memory errorMessage ) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); require(isContract(target), "Address: call to non-contract"); (bool success, bytes memory returndata) = target.call{value: value}(data); return _verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall( address target, bytes memory data, string memory errorMessage ) internal view returns (bytes memory) { require(isContract(target), "Address: static call to non-contract"); (bool success, bytes memory returndata) = target.staticcall(data); return _verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall(target, data, "Address: low-level delegate call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { require(isContract(target), "Address: delegate call to non-contract"); (bool success, bytes memory returndata) = target.delegatecall(data); return _verifyCallResult(success, returndata, errorMessage); } function _verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) private pure returns (bytes memory) { if (success) { return returndata; } else { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } } // File @openzeppelin/contracts/token/ERC20/utils/[email protected] pragma solidity ^0.8.0; /** * @title SafeERC20 * @dev Wrappers around ERC20 operations that throw on failure (when the token * contract returns false). Tokens that return no value (and instead revert or * throw on failure) are also supported, non-reverting calls are assumed to be * successful. * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ library SafeERC20 { using Address for address; function safeTransfer( IERC20 token, address to, uint256 value ) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); } function safeTransferFrom( IERC20 token, address from, address to, uint256 value ) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); } /** * @dev Deprecated. This function has issues similar to the ones found in * {IERC20-approve}, and its usage is discouraged. * * Whenever possible, use {safeIncreaseAllowance} and * {safeDecreaseAllowance} instead. */ function safeApprove( IERC20 token, address spender, uint256 value ) internal { // safeApprove should only be called when setting an initial allowance, // or when resetting it to zero. To increase and decrease it, use // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' require( (value == 0) || (token.allowance(address(this), spender) == 0), "SafeERC20: approve from non-zero to non-zero allowance" ); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); } function safeIncreaseAllowance( IERC20 token, address spender, uint256 value ) internal { uint256 newAllowance = token.allowance(address(this), spender) + value; _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } function safeDecreaseAllowance( IERC20 token, address spender, uint256 value ) internal { unchecked { uint256 oldAllowance = token.allowance(address(this), spender); require(oldAllowance >= value, "SafeERC20: decreased allowance below zero"); uint256 newAllowance = oldAllowance - value; _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function _callOptionalReturn(IERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that // the target address contains contract code and also asserts for success in the low-level call. bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed"); if (returndata.length > 0) { // Return data is optional require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } } } // File contracts/interfaces/IERC20Extended.sol pragma solidity ^0.8.4; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20Extended { /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the token decimals. */ function decimals() external view returns (uint8); /** * @dev Returns the token symbol. */ function symbol() external view returns (string memory); /** * @dev Returns the token name. */ function name() external view returns (string memory); /** * @dev Returns the bep token owner. */ function getOwner() external view returns (address); /** * @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 contracts/ConnextPriceOracle.sol pragma solidity ^0.8.4; interface AggregatorV3Interface { function decimals() external view returns (uint8); function description() external view returns (string memory); function version() external view returns (uint256); // getRoundData and latestRoundData should both raise "No data present" // if they do not have data to report, instead of returning unset values // which could be misinterpreted as actual reported values. function getRoundData(uint80 _roundId) external view returns ( uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound ); function latestRoundData() external view returns ( uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound ); } contract ConnextPriceOracle is PriceOracle { using SafeMath for uint256; using SafeERC20 for IERC20Extended; address public admin; address public wrapped; /// @notice Chainlink Aggregators mapping(address => AggregatorV3Interface) public aggregators; struct PriceInfo { address token; // Address of token contract, TOKEN address baseToken; // Address of base token contract, BASETOKEN address lpToken; // Address of TOKEN-BASETOKEN pair contract bool active; // Active status of price record 0 } mapping(address => PriceInfo) public priceRecords; mapping(address => uint256) public assetPrices; event NewAdmin(address oldAdmin, address newAdmin); event PriceRecordUpdated(address token, address baseToken, address lpToken, bool _active); event DirectPriceUpdated(address token, uint256 oldPrice, uint256 newPrice); event AggregatorUpdated(address tokenAddress, address source); constructor(address _wrapped) { wrapped = _wrapped; admin = msg.sender; } function getTokenPrice(address _tokenAddress) public view override returns (uint256) { address tokenAddress = _tokenAddress; if (_tokenAddress == address(0)) { tokenAddress = wrapped; } uint256 tokenPrice = assetPrices[tokenAddress]; if (tokenPrice == 0) { tokenPrice = getPriceFromOracle(tokenAddress); } if (tokenPrice == 0) { tokenPrice = getPriceFromDex(tokenAddress); } return tokenPrice; } function getPriceFromDex(address _tokenAddress) public view returns (uint256) { PriceInfo storage priceInfo = priceRecords[_tokenAddress]; if (priceInfo.active) { uint256 rawTokenAmount = IERC20Extended(priceInfo.token).balanceOf(priceInfo.lpToken); uint256 tokenDecimalDelta = 18-uint256(IERC20Extended(priceInfo.token).decimals()); uint256 tokenAmount = rawTokenAmount.mul(10**tokenDecimalDelta); uint256 rawBaseTokenAmount = IERC20Extended(priceInfo.baseToken).balanceOf(priceInfo.lpToken); uint256 baseTokenDecimalDelta = 18-uint256(IERC20Extended(priceInfo.baseToken).decimals()); uint256 baseTokenAmount = rawBaseTokenAmount.mul(10**baseTokenDecimalDelta); uint256 baseTokenPrice = getPriceFromOracle(priceInfo.baseToken); uint256 tokenPrice = baseTokenPrice.mul(baseTokenAmount).div(tokenAmount); return tokenPrice; } else { return 0; } } function getPriceFromOracle(address _tokenAddress) public view returns (uint256) { uint256 chainLinkPrice = getPriceFromChainlink(_tokenAddress); return chainLinkPrice; } function getPriceFromChainlink(address _tokenAddress) public view returns (uint256) { AggregatorV3Interface aggregator = aggregators[_tokenAddress]; if (address(aggregator) != address(0)) { ( , int answer, , , ) = aggregator.latestRoundData(); // It's fine for price to be 0. We have two price feeds. if (answer == 0) { return 0; } // Extend the decimals to 1e18. uint retVal = uint(answer); uint price = retVal.mul(10**(18 - uint(aggregator.decimals()))); return price; } return 0; } function setDexPriceInfo(address _token, address _baseToken, address _lpToken, bool _active) external { require(msg.sender == admin, "only admin can set DEX price"); PriceInfo storage priceInfo = priceRecords[_token]; uint256 baseTokenPrice = getPriceFromOracle(_baseToken); require(baseTokenPrice > 0, "invalid base token"); priceInfo.token = _token; priceInfo.baseToken = _baseToken; priceInfo.lpToken = _lpToken; priceInfo.active = _active; emit PriceRecordUpdated(_token, _baseToken, _lpToken, _active); } function setDirectPrice(address _token, uint256 _price) external { require(msg.sender == admin, "only admin can set direct price"); emit DirectPriceUpdated(_token, assetPrices[_token], _price); assetPrices[_token] = _price; } function setAdmin(address newAdmin) external { require(msg.sender == admin, "only admin can set new admin"); address oldAdmin = admin; admin = newAdmin; emit NewAdmin(oldAdmin, newAdmin); } function setAggregators(address[] calldata tokenAddresses, address[] calldata sources) external { require(msg.sender == admin, "only the admin may set the aggregators"); for (uint i = 0; i < tokenAddresses.length; i++) { aggregators[tokenAddresses[i]] = AggregatorV3Interface(sources[i]); emit AggregatorUpdated(tokenAddresses[i], sources[i]); } } function compareStrings(string memory a, string memory b) internal pure returns (bool) { return (keccak256(abi.encodePacked((a))) == keccak256(abi.encodePacked((b)))); } } // File contracts/interfaces/IFulfillInterpreter.sol pragma solidity 0.8.4; interface IFulfillInterpreter { event Executed( bytes32 indexed transactionId, address payable callTo, address assetId, address payable fallbackAddress, uint256 amount, bytes callData, bytes returnData, bool success, bool isContract ); function getTransactionManager() external returns (address); function execute( bytes32 transactionId, address payable callTo, address assetId, address payable fallbackAddress, uint256 amount, bytes calldata callData ) external payable returns (bool success, bool isContract, bytes memory returnData); } // File contracts/lib/LibAsset.sol pragma solidity 0.8.4; /** * @title LibAsset * @author Connext <[email protected]> * @notice This library contains helpers for dealing with onchain transfers * of assets, including accounting for the native asset `assetId` * conventions and any noncompliant ERC20 transfers */ library LibAsset { /** * @dev All native assets use the empty address for their asset id * by convention */ address constant NATIVE_ASSETID = address(0); /** * @notice Determines whether the given assetId is the native asset * @param assetId The asset identifier to evaluate * @return Boolean indicating if the asset is the native asset */ function isNativeAsset(address assetId) internal pure returns (bool) { return assetId == NATIVE_ASSETID; } /** * @notice Gets the balance of the inheriting contract for the given asset * @param assetId The asset identifier to get the balance of * @return Balance held by contracts using this library */ function getOwnBalance(address assetId) internal view returns (uint256) { return isNativeAsset(assetId) ? address(this).balance : IERC20(assetId).balanceOf(address(this)); } /** * @notice Transfers ether from the inheriting contract to a given * recipient * @param recipient Address to send ether to * @param amount Amount to send to given recipient */ function transferNativeAsset(address payable recipient, uint256 amount) internal { Address.sendValue(recipient, amount); } /** * @notice Transfers tokens from the inheriting contract to a given * recipient * @param assetId Token address to transfer * @param recipient Address to send ether to * @param amount Amount to send to given recipient */ function transferERC20( address assetId, address recipient, uint256 amount ) internal { SafeERC20.safeTransfer(IERC20(assetId), recipient, amount); } /** * @notice Transfers tokens from a sender to a given recipient * @param assetId Token address to transfer * @param from Address of sender/owner * @param to Address of recipient/spender * @param amount Amount to transfer from owner to spender */ function transferFromERC20( address assetId, address from, address to, uint256 amount ) internal { SafeERC20.safeTransferFrom(IERC20(assetId), from, to, amount); } /** * @notice Increases the allowance of a token to a spender * @param assetId Token address of asset to increase allowance of * @param spender Account whos allowance is increased * @param amount Amount to increase allowance by */ function increaseERC20Allowance( address assetId, address spender, uint256 amount ) internal { require(!isNativeAsset(assetId), "#IA:034"); SafeERC20.safeIncreaseAllowance(IERC20(assetId), spender, amount); } /** * @notice Decreases the allowance of a token to a spender * @param assetId Token address of asset to decrease allowance of * @param spender Account whos allowance is decreased * @param amount Amount to decrease allowance by */ function decreaseERC20Allowance( address assetId, address spender, uint256 amount ) internal { require(!isNativeAsset(assetId), "#DA:034"); SafeERC20.safeDecreaseAllowance(IERC20(assetId), spender, amount); } /** * @notice Wrapper function to transfer a given asset (native or erc20) to * some recipient. Should handle all non-compliant return value * tokens as well by using the SafeERC20 contract by open zeppelin. * @param assetId Asset id for transfer (address(0) for native asset, * token address for erc20s) * @param recipient Address to send asset to * @param amount Amount to send to given recipient */ function transferAsset( address assetId, address payable recipient, uint256 amount ) internal { isNativeAsset(assetId) ? transferNativeAsset(recipient, amount) : transferERC20(assetId, recipient, amount); } } // File @openzeppelin/contracts/utils/[email protected] pragma solidity ^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 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) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } } // File @openzeppelin/contracts/access/[email protected] pragma solidity ^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() { _setOwner(_msgSender()); } /** * @dev Returns the address of the current owner. */ function owner() public view virtual 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 { _setOwner(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"); _setOwner(newOwner); } function _setOwner(address newOwner) private { address oldOwner = _owner; _owner = newOwner; emit OwnershipTransferred(oldOwner, newOwner); } } // File @openzeppelin/contracts/security/[email protected] pragma solidity ^0.8.0; /** * @dev Contract module that helps prevent reentrant calls to a function. * * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier * available, which can be applied to functions to make sure there are no nested * (reentrant) calls to them. * * Note that because there is a single `nonReentrant` guard, functions marked as * `nonReentrant` may not call one another. This can be worked around by making * those functions `private`, and then adding `external` `nonReentrant` entry * points to them. * * TIP: If you would like to learn more about reentrancy and alternative ways * to protect against it, check out our blog post * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul]. */ abstract contract ReentrancyGuard { // Booleans are more expensive than uint256 or any type that takes up a full // word because each write operation emits an extra SLOAD to first read the // slot's contents, replace the bits taken up by the boolean, and then write // back. This is the compiler's defense against contract upgrades and // pointer aliasing, and it cannot be disabled. // The values being non-zero value makes deployment a bit more expensive, // but in exchange the refund on every call to nonReentrant will be lower in // amount. Since refunds are capped to a percentage of the total // transaction's gas, it is best to keep them low in cases like this one, to // increase the likelihood of the full refund coming into effect. uint256 private constant _NOT_ENTERED = 1; uint256 private constant _ENTERED = 2; uint256 private _status; constructor() { _status = _NOT_ENTERED; } /** * @dev Prevents a contract from calling itself, directly or indirectly. * Calling a `nonReentrant` function from another `nonReentrant` * function is not supported. It is possible to prevent this from happening * by making the `nonReentrant` function external, and make it call a * `private` function that does the actual work. */ modifier nonReentrant() { // On the first call to nonReentrant, _notEntered will be true require(_status != _ENTERED, "ReentrancyGuard: reentrant call"); // Any calls to nonReentrant after this point will fail _status = _ENTERED; _; // By storing the original value once again, a refund is triggered (see // https://eips.ethereum.org/EIPS/eip-2200) _status = _NOT_ENTERED; } } // File contracts/interpreters/FulfillInterpreter.sol pragma solidity 0.8.4; /** * @title FulfillInterpreter * @author Connext <[email protected]> * @notice This library contains an `execute` function that is callabale by * an associated TransactionManager contract. This is used to execute * arbitrary calldata on a receiving chain. */ contract FulfillInterpreter is ReentrancyGuard, IFulfillInterpreter { address private immutable _transactionManager; constructor(address transactionManager) { _transactionManager = transactionManager; } /** * @notice Errors if the sender is not the transaction manager */ modifier onlyTransactionManager { require(msg.sender == _transactionManager, "#OTM:027"); _; } /** * @notice Returns the transaction manager address (only address that can * call the `execute` function) * @return The address of the associated transaction manager */ function getTransactionManager() override external view returns (address) { return _transactionManager; } /** * @notice Executes some arbitrary call data on a given address. The * call data executes can be payable, and will have `amount` sent * along with the function (or approved to the contract). If the * call fails, rather than reverting, funds are sent directly to * some provided fallbaack address * @param transactionId Unique identifier of transaction id that necessitated * calldata execution * @param callTo The address to execute the calldata on * @param assetId The assetId of the funds to approve to the contract or * send along with the call * @param fallbackAddress The address to send funds to if the `call` fails * @param amount The amount to approve or send with the call * @param callData The data to execute */ function execute( bytes32 transactionId, address payable callTo, address assetId, address payable fallbackAddress, uint256 amount, bytes calldata callData ) override external payable onlyTransactionManager returns (bool, bool, bytes memory) { // If it is not ether, approve the callTo // We approve here rather than transfer since many external contracts // simply require an approval, and it is unclear if they can handle // funds transferred directly to them (i.e. Uniswap) bool isNative = LibAsset.isNativeAsset(assetId); if (!isNative) { LibAsset.increaseERC20Allowance(assetId, callTo, amount); } // Check if the callTo is a contract bool success; bytes memory returnData; bool isContract = Address.isContract(callTo); if (isContract) { // Try to execute the callData // the low level call will return `false` if its execution reverts (success, returnData) = callTo.call{value: isNative ? amount : 0}(callData); } // Handle failure cases if (!success) { // If it fails, transfer to fallback LibAsset.transferAsset(assetId, fallbackAddress, amount); // Decrease allowance if (!isNative) { LibAsset.decreaseERC20Allowance(assetId, callTo, amount); } } // Emit event emit Executed( transactionId, callTo, assetId, fallbackAddress, amount, callData, returnData, success, isContract ); return (success, isContract, returnData); } } // File contracts/interfaces/ITransactionManager.sol pragma solidity 0.8.4; interface ITransactionManager { // Structs // Holds all data that is constant between sending and // receiving chains. The hash of this is what gets signed // to ensure the signature can be used on both chains. struct InvariantTransactionData { address receivingChainTxManagerAddress; address user; address router; address initiator; // msg.sender of sending side address sendingAssetId; address receivingAssetId; address sendingChainFallback; // funds sent here on cancel address receivingAddress; address callTo; uint256 sendingChainId; uint256 receivingChainId; bytes32 callDataHash; // hashed to prevent free option bytes32 transactionId; } // Holds all data that varies between sending and receiving // chains. The hash of this is stored onchain to ensure the // information passed in is valid. struct VariantTransactionData { uint256 amount; uint256 expiry; uint256 preparedBlockNumber; } // All Transaction data, constant and variable struct TransactionData { address receivingChainTxManagerAddress; address user; address router; address initiator; // msg.sender of sending side address sendingAssetId; address receivingAssetId; address sendingChainFallback; address receivingAddress; address callTo; bytes32 callDataHash; bytes32 transactionId; uint256 sendingChainId; uint256 receivingChainId; uint256 amount; uint256 expiry; uint256 preparedBlockNumber; // Needed for removal of active blocks on fulfill/cancel } // The structure of the signed data for fulfill struct SignedFulfillData { bytes32 transactionId; uint256 relayerFee; string functionIdentifier; // "fulfill" or "cancel" uint256 receivingChainId; // For domain separation address receivingChainTxManagerAddress; // For domain separation } // The structure of the signed data for cancellation struct SignedCancelData { bytes32 transactionId; string functionIdentifier; uint256 receivingChainId; address receivingChainTxManagerAddress; // For domain separation } /** * Arguments for calling prepare() * @param invariantData The data for a crosschain transaction that will * not change between sending and receiving chains. * The hash of this data is used as the key to store * the inforamtion that does change between chains * (amount,expiry,preparedBlock) for verification * @param amount The amount of the transaction on this chain * @param expiry The block.timestamp when the transaction will no longer be * fulfillable and is freely cancellable on this chain * @param encryptedCallData The calldata to be executed when the tx is * fulfilled. Used in the function to allow the user * to reconstruct the tx from events. Hash is stored * onchain to prevent shenanigans. * @param encodedBid The encoded bid that was accepted by the user for this * crosschain transfer. It is supplied as a param to the * function but is only used in event emission * @param bidSignature The signature of the bidder on the encoded bid for * this transaction. Only used within the function for * event emission. The validity of the bid and * bidSignature are enforced offchain * @param encodedMeta The meta for the function */ struct PrepareArgs { InvariantTransactionData invariantData; uint256 amount; uint256 expiry; bytes encryptedCallData; bytes encodedBid; bytes bidSignature; bytes encodedMeta; } /** * @param txData All of the data (invariant and variant) for a crosschain * transaction. The variant data provided is checked against * what was stored when the `prepare` function was called. * @param relayerFee The fee that should go to the relayer when they are * calling the function on the receiving chain for the user * @param signature The users signature on the transaction id + fee that * can be used by the router to unlock the transaction on * the sending chain * @param callData The calldata to be sent to and executed by the * `FulfillHelper` * @param encodedMeta The meta for the function */ struct FulfillArgs { TransactionData txData; uint256 relayerFee; bytes signature; bytes callData; bytes encodedMeta; } /** * Arguments for calling cancel() * @param txData All of the data (invariant and variant) for a crosschain * transaction. The variant data provided is checked against * what was stored when the `prepare` function was called. * @param signature The user's signature that allows a transaction to be * cancelled by a relayer * @param encodedMeta The meta for the function */ struct CancelArgs { TransactionData txData; bytes signature; bytes encodedMeta; } // Adding/removing asset events event RouterAdded(address indexed addedRouter, address indexed caller); event RouterRemoved(address indexed removedRouter, address indexed caller); // Adding/removing router events event AssetAdded(address indexed addedAssetId, address indexed caller); event AssetRemoved(address indexed removedAssetId, address indexed caller); // Liquidity events event LiquidityAdded(address indexed router, address indexed assetId, uint256 amount, address caller); event LiquidityRemoved(address indexed router, address indexed assetId, uint256 amount, address recipient); // Transaction events event TransactionPrepared( address indexed user, address indexed router, bytes32 indexed transactionId, TransactionData txData, address caller, PrepareArgs args ); event TransactionFulfilled( address indexed user, address indexed router, bytes32 indexed transactionId, FulfillArgs args, bool success, bool isContract, bytes returnData, address caller ); event TransactionCancelled( address indexed user, address indexed router, bytes32 indexed transactionId, CancelArgs args, address caller ); // Getters function getChainId() external view returns (uint256); function getStoredChainId() external view returns (uint256); // Owner only methods function addRouter(address router) external; function removeRouter(address router) external; function addAssetId(address assetId) external; function removeAssetId(address assetId) external; // Router only methods function addLiquidityFor(uint256 amount, address assetId, address router) external payable; function addLiquidity(uint256 amount, address assetId) external payable; function removeLiquidity( uint256 amount, address assetId, address payable recipient ) external; // Methods for crosschain transfers // called in the following order (in happy case) // 1. prepare by user on sending chain // 2. prepare by router on receiving chain // 3. fulfill by user on receiving chain // 4. fulfill by router on sending chain function prepare( PrepareArgs calldata args ) external payable returns (TransactionData memory); function fulfill( FulfillArgs calldata args ) external returns (TransactionData memory); function cancel(CancelArgs calldata args) external returns (TransactionData memory); } // File @openzeppelin/contracts/utils/cryptography/[email protected] pragma solidity ^0.8.0; /** * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations. * * These functions can be used to verify that a message was signed by the holder * of the private keys of a given address. */ library ECDSA { /** * @dev Returns the address that signed a hashed message (`hash`) with * `signature`. This address can then be used for verification purposes. * * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures: * this function rejects them by requiring the `s` value to be in the lower * half order, and the `v` value to be either 27 or 28. * * IMPORTANT: `hash` _must_ be the result of a hash operation for the * verification to be secure: it is possible to craft signatures that * recover to arbitrary addresses for non-hashed data. A safe way to ensure * this is by receiving a hash of the original message (which may otherwise * be too long), and then calling {toEthSignedMessageHash} on it. * * Documentation for signature generation: * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js] * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers] */ function recover(bytes32 hash, bytes memory signature) internal pure returns (address) { // Check the signature length // - case 65: r,s,v signature (standard) // - case 64: r,vs signature (cf https://eips.ethereum.org/EIPS/eip-2098) _Available since v4.1._ if (signature.length == 65) { bytes32 r; bytes32 s; uint8 v; // ecrecover takes the signature parameters, and the only way to get them // currently is to use assembly. assembly { r := mload(add(signature, 0x20)) s := mload(add(signature, 0x40)) v := byte(0, mload(add(signature, 0x60))) } return recover(hash, v, r, s); } else if (signature.length == 64) { bytes32 r; bytes32 vs; // ecrecover takes the signature parameters, and the only way to get them // currently is to use assembly. assembly { r := mload(add(signature, 0x20)) vs := mload(add(signature, 0x40)) } return recover(hash, r, vs); } else { revert("ECDSA: invalid signature length"); } } /** * @dev Overload of {ECDSA-recover} that receives the `r` and `vs` short-signature fields separately. * * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures] * * _Available since v4.2._ */ function recover( bytes32 hash, bytes32 r, bytes32 vs ) internal pure returns (address) { bytes32 s; uint8 v; assembly { s := and(vs, 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) v := add(shr(255, vs), 27) } return recover(hash, v, r, s); } /** * @dev Overload of {ECDSA-recover} that receives the `v`, `r` and `s` signature fields separately. */ function recover( bytes32 hash, uint8 v, bytes32 r, bytes32 s ) internal pure returns (address) { // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines // the valid range for s in (281): 0 < s < secp256k1n ÷ 2 + 1, and for v in (282): v ∈ {27, 28}. Most // signatures from current libraries generate a unique signature with an s-value in the lower half order. // // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept // these malleable signatures as well. require( uint256(s) <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0, "ECDSA: invalid signature 's' value" ); require(v == 27 || v == 28, "ECDSA: invalid signature 'v' value"); // If the signature is valid (and not malleable), return the signer address address signer = ecrecover(hash, v, r, s); require(signer != address(0), "ECDSA: invalid signature"); return signer; } /** * @dev Returns an Ethereum Signed Message, created from a `hash`. This * produces hash corresponding to the one signed with the * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] * JSON-RPC method as part of EIP-191. * * See {recover}. */ function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) { // 32 is the length in bytes of hash, // enforced by the type signature above return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash)); } /** * @dev Returns an Ethereum Signed Typed Data, created from a * `domainSeparator` and a `structHash`. This produces hash corresponding * to the one signed with the * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`] * JSON-RPC method as part of EIP-712. * * See {recover}. */ function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) { return keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); } } // File contracts/Router.sol pragma solidity ^0.8.4; contract Router is Ownable { address public immutable routerFactory; ITransactionManager public transactionManager; uint256 private chainId; address public recipient; address public routerSigner; struct SignedPrepareData { ITransactionManager.PrepareArgs args; address routerRelayerFeeAsset; uint256 routerRelayerFee; uint256 chainId; // For domain separation } struct SignedFulfillData { ITransactionManager.FulfillArgs args; address routerRelayerFeeAsset; uint256 routerRelayerFee; uint256 chainId; // For domain separation } struct SignedCancelData { ITransactionManager.CancelArgs args; address routerRelayerFeeAsset; uint256 routerRelayerFee; uint256 chainId; // For domain separation } struct SignedRemoveLiquidityData { uint256 amount; address assetId; address routerRelayerFeeAsset; uint256 routerRelayerFee; uint256 chainId; // For domain separation } event RelayerFeeAdded(address assetId, uint256 amount, address caller); event RelayerFeeRemoved(address assetId, uint256 amount, address caller); event RemoveLiquidity( uint256 amount, address assetId, address routerRelayerFeeAsset, uint256 routerRelayerFee, address caller ); event Prepare( ITransactionManager.InvariantTransactionData invariantData, address routerRelayerFeeAsset, uint256 routerRelayerFee, address caller ); event Fulfill( ITransactionManager.TransactionData txData, address routerRelayerFeeAsset, uint256 routerRelayerFee, address caller ); event Cancel( ITransactionManager.TransactionData txData, address routerRelayerFeeAsset, uint256 routerRelayerFee, address caller ); constructor(address _routerFactory) { routerFactory = _routerFactory; } // Prevents from calling methods other than routerFactory contract modifier onlyViaFactory() { require(msg.sender == routerFactory, "ONLY_VIA_FACTORY"); _; } function init( address _transactionManager, uint256 _chainId, address _routerSigner, address _recipient, address _owner ) external onlyViaFactory { transactionManager = ITransactionManager(_transactionManager); chainId = _chainId; routerSigner = _routerSigner; recipient = _recipient; transferOwnership(_owner); } function setRecipient(address _recipient) external onlyOwner { recipient = _recipient; } function setSigner(address _routerSigner) external onlyOwner { routerSigner = _routerSigner; } function addRelayerFee(uint256 amount, address assetId) external payable { // Sanity check: nonzero amounts require(amount > 0, "#RC_ARF:002"); // Transfer funds to contract // Validate correct amounts are transferred if (LibAsset.isNativeAsset(assetId)) { require(msg.value == amount, "#RC_ARF:005"); } else { require(msg.value == 0, "#RC_ARF:006"); LibAsset.transferFromERC20(assetId, msg.sender, address(this), amount); } // Emit event emit RelayerFeeAdded(assetId, amount, msg.sender); } function removeRelayerFee(uint256 amount, address assetId) external onlyOwner { // Sanity check: nonzero amounts require(amount > 0, "#RC_RRF:002"); // Transfer funds from contract LibAsset.transferAsset(assetId, payable(recipient), amount); // Emit event emit RelayerFeeRemoved(assetId, amount, msg.sender); } function removeLiquidity( uint256 amount, address assetId, address routerRelayerFeeAsset, uint256 routerRelayerFee, bytes calldata signature ) external { if (msg.sender != routerSigner) { SignedRemoveLiquidityData memory payload = SignedRemoveLiquidityData({ amount: amount, assetId: assetId, routerRelayerFeeAsset: routerRelayerFeeAsset, routerRelayerFee: routerRelayerFee, chainId: chainId }); address recovered = recoverSignature(abi.encode(payload), signature); require(recovered == routerSigner, "#RC_RL:040"); // Send the relayer the fee if (routerRelayerFee > 0) { LibAsset.transferAsset(routerRelayerFeeAsset, payable(msg.sender), routerRelayerFee); } } emit RemoveLiquidity(amount, assetId, routerRelayerFeeAsset, routerRelayerFee, msg.sender); return transactionManager.removeLiquidity(amount, assetId, payable(recipient)); } function prepare( ITransactionManager.PrepareArgs calldata args, address routerRelayerFeeAsset, uint256 routerRelayerFee, bytes calldata signature ) external payable returns (ITransactionManager.TransactionData memory) { if (msg.sender != routerSigner) { SignedPrepareData memory payload = SignedPrepareData({ args: args, routerRelayerFeeAsset: routerRelayerFeeAsset, routerRelayerFee: routerRelayerFee, chainId: chainId }); address recovered = recoverSignature(abi.encode(payload), signature); require(recovered == routerSigner, "#RC_P:040"); // Send the relayer the fee if (routerRelayerFee > 0) { LibAsset.transferAsset(routerRelayerFeeAsset, payable(msg.sender), routerRelayerFee); } } emit Prepare(args.invariantData, routerRelayerFeeAsset, routerRelayerFee, msg.sender); return transactionManager.prepare(args); } function fulfill( ITransactionManager.FulfillArgs calldata args, address routerRelayerFeeAsset, uint256 routerRelayerFee, bytes calldata signature ) external returns (ITransactionManager.TransactionData memory) { if (msg.sender != routerSigner) { SignedFulfillData memory payload = SignedFulfillData({ args: args, routerRelayerFeeAsset: routerRelayerFeeAsset, routerRelayerFee: routerRelayerFee, chainId: chainId }); address recovered = recoverSignature(abi.encode(payload), signature); require(recovered == routerSigner, "#RC_F:040"); // Send the relayer the fee if (routerRelayerFee > 0) { LibAsset.transferAsset(routerRelayerFeeAsset, payable(msg.sender), routerRelayerFee); } } emit Fulfill(args.txData, routerRelayerFeeAsset, routerRelayerFee, msg.sender); return transactionManager.fulfill(args); } function cancel( ITransactionManager.CancelArgs calldata args, address routerRelayerFeeAsset, uint256 routerRelayerFee, bytes calldata signature ) external returns (ITransactionManager.TransactionData memory) { if (msg.sender != routerSigner) { SignedCancelData memory payload = SignedCancelData({ args: args, routerRelayerFeeAsset: routerRelayerFeeAsset, routerRelayerFee: routerRelayerFee, chainId: chainId }); address recovered = recoverSignature(abi.encode(payload), signature); require(recovered == routerSigner, "#RC_C:040"); // Send the relayer the fee if (routerRelayerFee > 0) { LibAsset.transferAsset(routerRelayerFeeAsset, payable(msg.sender), routerRelayerFee); } } emit Cancel(args.txData, routerRelayerFeeAsset, routerRelayerFee, msg.sender); return transactionManager.cancel(args); } /** * @notice Holds the logic to recover the routerSigner from an encoded payload. * Will hash and convert to an eth signed message. * @param encodedPayload The payload that was signed * @param signature The signature you are recovering the routerSigner from */ function recoverSignature(bytes memory encodedPayload, bytes calldata signature) internal pure returns (address) { // Recover return ECDSA.recover(ECDSA.toEthSignedMessageHash(keccak256(encodedPayload)), signature); } receive() external payable {} } // File @openzeppelin/contracts/utils/[email protected] pragma solidity ^0.8.0; /** * @dev Helper to make usage of the `CREATE2` EVM opcode easier and safer. * `CREATE2` can be used to compute in advance the address where a smart * contract will be deployed, which allows for interesting new mechanisms known * as 'counterfactual interactions'. * * See the https://eips.ethereum.org/EIPS/eip-1014#motivation[EIP] for more * information. */ library Create2 { /** * @dev Deploys a contract using `CREATE2`. The address where the contract * will be deployed can be known in advance via {computeAddress}. * * The bytecode for a contract can be obtained from Solidity with * `type(contractName).creationCode`. * * Requirements: * * - `bytecode` must not be empty. * - `salt` must have not been used for `bytecode` already. * - the factory must have a balance of at least `amount`. * - if `amount` is non-zero, `bytecode` must have a `payable` constructor. */ function deploy( uint256 amount, bytes32 salt, bytes memory bytecode ) internal returns (address) { address addr; require(address(this).balance >= amount, "Create2: insufficient balance"); require(bytecode.length != 0, "Create2: bytecode length is zero"); assembly { addr := create2(amount, add(bytecode, 0x20), mload(bytecode), salt) } require(addr != address(0), "Create2: Failed on deploy"); return addr; } /** * @dev Returns the address where a contract will be stored if deployed via {deploy}. Any change in the * `bytecodeHash` or `salt` will result in a new destination address. */ function computeAddress(bytes32 salt, bytes32 bytecodeHash) internal view returns (address) { return computeAddress(salt, bytecodeHash, address(this)); } /** * @dev Returns the address where a contract will be stored if deployed via {deploy} from a contract located at * `deployer`. If `deployer` is this contract's address, returns the same value as {computeAddress}. */ function computeAddress( bytes32 salt, bytes32 bytecodeHash, address deployer ) internal pure returns (address) { bytes32 _data = keccak256(abi.encodePacked(bytes1(0xff), deployer, salt, bytecodeHash)); return address(uint160(uint256(_data))); } } // File contracts/interfaces/IRouterFactory.sol pragma solidity ^0.8.4; interface IRouterFactory { event RouterCreated(address router, address routerSigner, address recipient, address transactionManager); function getRouterAddress(address routerSigner) external view returns (address); function createRouter(address router, address recipient) external returns (address); } // File contracts/RouterFactory.sol pragma solidity ^0.8.4; contract RouterFactory is IRouterFactory, Ownable { /** * @dev The stored chain id of the contract, may be passed in to avoid any * evm issues */ uint256 private chainId; /** * @dev The transaction Manager contract */ ITransactionManager public transactionManager; /** * @dev Mapping of routerSigner to created Router contract address */ mapping(address => address) public routerAddresses; constructor(address _owner) { transferOwnership(_owner); } function init(address _transactionManager) external onlyOwner { require(address(_transactionManager) != address(0), "#RF_I:042"); transactionManager = ITransactionManager(_transactionManager); chainId = ITransactionManager(_transactionManager).getChainId(); } /** * @notice Allows us to create new router contract * @param routerSigner address router signer * @param recipient address recipient */ function createRouter(address routerSigner, address recipient) external override returns (address) { require(address(transactionManager) != address(0), "#RF_CR:042"); require(routerSigner != address(0), "#RF_CR:041"); require(recipient != address(0), "#RF_CR:007"); address payable router = payable(Create2.deploy(0, generateSalt(routerSigner), getBytecode())); Router(router).init(address(transactionManager), chainId, routerSigner, recipient, msg.sender); routerAddresses[routerSigner] = router; emit RouterCreated(router, routerSigner, recipient, address(transactionManager)); return router; } /** * @notice Allows us to get the address for a new router contract created via `createRouter` * @param routerSigner address router signer */ function getRouterAddress(address routerSigner) external view override returns (address) { return Create2.computeAddress(generateSalt(routerSigner), keccak256(getBytecode())); } //////////////////////////////////////// // Internal Methods function getBytecode() internal view returns (bytes memory) { bytes memory bytecode = type(Router).creationCode; return abi.encodePacked(bytecode, abi.encode(address(this))); } function generateSalt(address routerSigner) internal pure returns (bytes32) { return keccak256(abi.encodePacked(routerSigner)); } } // File contracts/test/Counter.sol pragma solidity 0.8.4; contract Counter { bool public shouldRevert; uint256 public count = 0; constructor() { shouldRevert = false; } function setShouldRevert(bool value) public { shouldRevert = value; } function increment() public { require(!shouldRevert, "increment: shouldRevert is true"); count += 1; } function incrementAndSend(address assetId, address recipient, uint256 amount) public payable { if (LibAsset.isNativeAsset(assetId)) { require(msg.value == amount, "incrementAndSend: INVALID_ETH_AMOUNT"); } else { require(msg.value == 0, "incrementAndSend: ETH_WITH_ERC"); LibAsset.transferFromERC20(assetId, msg.sender, address(this), amount); } increment(); LibAsset.transferAsset(assetId, payable(recipient), amount); } } // File contracts/interfaces/IERC20Minimal.sol pragma solidity >=0.5.0; /// @title Minimal ERC20 interface for Uniswap /// @notice Contains a subset of the full ERC20 interface that is used in Uniswap V3 interface IERC20Minimal { /// @notice Returns the balance of a token /// @param account The account for which to look up the number of tokens it has, i.e. its balance /// @return The number of tokens held by the account function balanceOf(address account) external view returns (uint256); /// @notice Transfers the amount of token from the `msg.sender` to the recipient /// @param recipient The account that will receive the amount transferred /// @param amount The number of tokens to send from the sender to the recipient /// @return Returns true for a successful transfer, false for an unsuccessful transfer function transfer(address recipient, uint256 amount) external returns (bool); /// @notice Returns the current allowance given to a spender by an owner /// @param owner The account of the token owner /// @param spender The account of the token spender /// @return The current allowance granted by `owner` to `spender` function allowance(address owner, address spender) external view returns (uint256); /// @notice Sets the allowance of a spender from the `msg.sender` to the value `amount` /// @param spender The account which will be allowed to spend a given amount of the owners tokens /// @param amount The amount of tokens allowed to be used by `spender` /// @return Returns true for a successful approval, false for unsuccessful function approve(address spender, uint256 amount) external returns (bool); /// @notice Transfers `amount` tokens from `sender` to `recipient` up to the allowance given to the `msg.sender` /// @param sender The account from which the transfer will be initiated /// @param recipient The recipient of the transfer /// @param amount The amount of the transfer /// @return Returns true for a successful transfer, false for unsuccessful function transferFrom( address sender, address recipient, uint256 amount ) external returns (bool); /// @notice Event emitted when tokens are transferred from one address to another, either via `#transfer` or `#transferFrom`. /// @param from The account from which the tokens were sent, i.e. the balance decreased /// @param to The account to which the tokens were sent, i.e. the balance increased /// @param value The amount of tokens that were transferred event Transfer(address indexed from, address indexed to, uint256 value); /// @notice Event emitted when the approval amount for the spender of a given owner's tokens changes. /// @param owner The account that approved spending of its tokens /// @param spender The account for which the spending allowance was modified /// @param value The new allowance from the owner to the spender event Approval(address indexed owner, address indexed spender, uint256 value); } // File @openzeppelin/contracts/token/ERC20/extensions/[email protected] pragma solidity ^0.8.0; /** * @dev Interface for the optional metadata functions from the ERC20 standard. * * _Available since v4.1._ */ interface IERC20Metadata is IERC20 { /** * @dev Returns the name of the token. */ function name() external view returns (string memory); /** * @dev Returns the symbol of the token. */ function symbol() external view returns (string memory); /** * @dev Returns the decimals places of the token. */ function decimals() external view returns (uint8); } // File @openzeppelin/contracts/token/ERC20/[email protected] pragma solidity ^0.8.0; /** * @dev Implementation of the {IERC20} interface. * * This implementation is agnostic to the way tokens are created. This means * that a supply mechanism has to be added in a derived contract using {_mint}. * For a generic mechanism see {ERC20PresetMinterPauser}. * * TIP: For a detailed writeup see our guide * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How * to implement supply mechanisms]. * * We have followed general OpenZeppelin guidelines: functions revert instead * of returning `false` on failure. This behavior is nonetheless conventional * and does not conflict with the expectations of ERC20 applications. * * Additionally, an {Approval} event is emitted on calls to {transferFrom}. * This allows applications to reconstruct the allowance for all accounts just * by listening to said events. Other implementations of the EIP may not emit * these events, as it isn't required by the specification. * * Finally, the non-standard {decreaseAllowance} and {increaseAllowance} * functions have been added to mitigate the well-known issues around setting * allowances. See {IERC20-approve}. */ contract ERC20 is Context, IERC20, IERC20Metadata { mapping(address => uint256) private _balances; mapping(address => mapping(address => uint256)) private _allowances; uint256 private _totalSupply; string private _name; string private _symbol; /** * @dev Sets the values for {name} and {symbol}. * * The default value of {decimals} is 18. To select a different value for * {decimals} you should overload it. * * All two of these values are immutable: they can only be set once during * construction. */ constructor(string memory name_, string memory symbol_) { _name = name_; _symbol = symbol_; } /** * @dev Returns the name of the token. */ function name() public view virtual override returns (string memory) { return _name; } /** * @dev Returns the symbol of the token, usually a shorter version of the * name. */ function symbol() public view virtual override returns (string memory) { return _symbol; } /** * @dev Returns the number of decimals used to get its user representation. * For example, if `decimals` equals `2`, a balance of `505` tokens should * be displayed to a user as `5,05` (`505 / 10 ** 2`). * * Tokens usually opt for a value of 18, imitating the relationship between * Ether and Wei. This is the value {ERC20} uses, unless this function is * overridden; * * NOTE: This information is only used for _display_ purposes: it in * no way affects any of the arithmetic of the contract, including * {IERC20-balanceOf} and {IERC20-transfer}. */ function decimals() public view virtual override returns (uint8) { return 18; } /** * @dev See {IERC20-totalSupply}. */ function totalSupply() public view virtual override returns (uint256) { return _totalSupply; } /** * @dev See {IERC20-balanceOf}. */ function balanceOf(address account) public view virtual override returns (uint256) { return _balances[account]; } /** * @dev See {IERC20-transfer}. * * Requirements: * * - `recipient` cannot be the zero address. * - the caller must have a balance of at least `amount`. */ function transfer(address recipient, uint256 amount) public virtual override returns (bool) { _transfer(_msgSender(), recipient, amount); return true; } /** * @dev See {IERC20-allowance}. */ function allowance(address owner, address spender) public view virtual override returns (uint256) { return _allowances[owner][spender]; } /** * @dev See {IERC20-approve}. * * Requirements: * * - `spender` cannot be the zero address. */ function approve(address spender, uint256 amount) public virtual override returns (bool) { _approve(_msgSender(), spender, amount); return true; } /** * @dev See {IERC20-transferFrom}. * * Emits an {Approval} event indicating the updated allowance. This is not * required by the EIP. See the note at the beginning of {ERC20}. * * Requirements: * * - `sender` and `recipient` cannot be the zero address. * - `sender` must have a balance of at least `amount`. * - the caller must have allowance for ``sender``'s tokens of at least * `amount`. */ function transferFrom( address sender, address recipient, uint256 amount ) public virtual override returns (bool) { _transfer(sender, recipient, amount); uint256 currentAllowance = _allowances[sender][_msgSender()]; require(currentAllowance >= amount, "ERC20: transfer amount exceeds allowance"); unchecked { _approve(sender, _msgSender(), currentAllowance - amount); } return true; } /** * @dev Atomically increases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. */ function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { _approve(_msgSender(), spender, _allowances[_msgSender()][spender] + addedValue); return true; } /** * @dev Atomically decreases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. * - `spender` must have allowance for the caller of at least * `subtractedValue`. */ function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { uint256 currentAllowance = _allowances[_msgSender()][spender]; require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero"); unchecked { _approve(_msgSender(), spender, currentAllowance - subtractedValue); } return true; } /** * @dev Moves `amount` of tokens from `sender` to `recipient`. * * This internal function is equivalent to {transfer}, and can be used to * e.g. implement automatic token fees, slashing mechanisms, etc. * * Emits a {Transfer} event. * * Requirements: * * - `sender` cannot be the zero address. * - `recipient` cannot be the zero address. * - `sender` must have a balance of at least `amount`. */ function _transfer( address sender, address recipient, uint256 amount ) internal virtual { require(sender != address(0), "ERC20: transfer from the zero address"); require(recipient != address(0), "ERC20: transfer to the zero address"); _beforeTokenTransfer(sender, recipient, amount); uint256 senderBalance = _balances[sender]; require(senderBalance >= amount, "ERC20: transfer amount exceeds balance"); unchecked { _balances[sender] = senderBalance - amount; } _balances[recipient] += amount; emit Transfer(sender, recipient, amount); _afterTokenTransfer(sender, recipient, amount); } /** @dev Creates `amount` tokens and assigns them to `account`, increasing * the total supply. * * Emits a {Transfer} event with `from` set to the zero address. * * Requirements: * * - `account` cannot be the zero address. */ function _mint(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: mint to the zero address"); _beforeTokenTransfer(address(0), account, amount); _totalSupply += amount; _balances[account] += amount; emit Transfer(address(0), account, amount); _afterTokenTransfer(address(0), account, amount); } /** * @dev Destroys `amount` tokens from `account`, reducing the * total supply. * * Emits a {Transfer} event with `to` set to the zero address. * * Requirements: * * - `account` cannot be the zero address. * - `account` must have at least `amount` tokens. */ function _burn(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: burn from the zero address"); _beforeTokenTransfer(account, address(0), amount); uint256 accountBalance = _balances[account]; require(accountBalance >= amount, "ERC20: burn amount exceeds balance"); unchecked { _balances[account] = accountBalance - amount; } _totalSupply -= amount; emit Transfer(account, address(0), amount); _afterTokenTransfer(account, address(0), amount); } /** * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens. * * This internal function is equivalent to `approve`, and can be used to * e.g. set automatic allowances for certain subsystems, etc. * * Emits an {Approval} event. * * Requirements: * * - `owner` cannot be the zero address. * - `spender` cannot be the zero address. */ function _approve( address owner, address spender, uint256 amount ) internal virtual { require(owner != address(0), "ERC20: approve from the zero address"); require(spender != address(0), "ERC20: approve to the zero address"); _allowances[owner][spender] = amount; emit Approval(owner, spender, amount); } /** * @dev Hook that is called before any transfer of tokens. This includes * minting and burning. * * Calling conditions: * * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens * will be transferred to `to`. * - when `from` is zero, `amount` tokens will be minted for `to`. * - when `to` is zero, `amount` of ``from``'s tokens will be burned. * - `from` and `to` are never both zero. * * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. */ function _beforeTokenTransfer( address from, address to, uint256 amount ) internal virtual {} /** * @dev Hook that is called after any transfer of tokens. This includes * minting and burning. * * Calling conditions: * * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens * has been transferred to `to`. * - when `from` is zero, `amount` tokens have been minted for `to`. * - when `to` is zero, `amount` of ``from``'s tokens have been burned. * - `from` and `to` are never both zero. * * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. */ function _afterTokenTransfer( address from, address to, uint256 amount ) internal virtual {} } // File contracts/test/FeeERC20.sol pragma solidity 0.8.4; /* This token is ONLY useful for testing * Anybody can mint as many tokens as they like * Anybody can burn anyone else's tokens */ contract FeeERC20 is ERC20 { uint256 public fee = 1; constructor() ERC20("Fee Token", "FEERC20") { _mint(msg.sender, 1000000 ether); } function setFee(uint256 _fee) external { fee = _fee; } function mint(address account, uint256 amount) external { _mint(account, amount); } function burn(address account, uint256 amount) external { _burn(account, amount); } function transfer(address account, uint256 amount) public override returns (bool) { uint256 toTransfer = amount - fee; _transfer(msg.sender, account, toTransfer); return true; } function transferFrom(address sender, address recipient, uint256 amount) public override returns (bool) { uint256 toTransfer = amount - fee; _burn(sender, fee); _transfer(sender, recipient, toTransfer); return true; } } // File contracts/test/LibAssetTest.sol pragma solidity 0.8.4; /// @title LibAssetTest /// @author Connext /// @notice Used to easily test the internal methods of /// LibAsset.sol by aliasing them to public /// methods. contract LibAssetTest { constructor() {} receive() external payable {} function isNativeAsset(address assetId) public pure returns (bool) { return LibAsset.isNativeAsset(assetId); } function getOwnBalance(address assetId) public view returns (uint256) { return LibAsset.getOwnBalance(assetId); } function transferNativeAsset(address payable recipient, uint256 amount) public { LibAsset.transferNativeAsset(recipient, amount); } function increaseERC20Allowance(address assetId, address spender, uint256 amount) public { LibAsset.increaseERC20Allowance(assetId, spender, amount); } function decreaseERC20Allowance(address assetId, address spender, uint256 amount) public { LibAsset.decreaseERC20Allowance(assetId, spender, amount); } function transferERC20( address assetId, address recipient, uint256 amount ) public { LibAsset.transferERC20(assetId, recipient, amount); } // This function is a wrapper for transfers of Ether or ERC20 tokens, // both standard-compliant ones as well as tokens that exhibit the // missing-return-value bug. function transferAsset( address assetId, address payable recipient, uint256 amount ) public { LibAsset.transferAsset(assetId, recipient, amount); } } // File contracts/test/RevertableERC20.sol pragma solidity 0.8.4; /* This token is ONLY useful for testing * Anybody can mint as many tokens as they like * Anybody can burn anyone else's tokens */ contract RevertableERC20 is ERC20 { bool public shouldRevert = false; constructor() ERC20("Revertable Token", "RVRT") { _mint(msg.sender, 1000000 ether); } function mint(address account, uint256 amount) external { require(!shouldRevert, "mint: SHOULD_REVERT"); _mint(account, amount); } function burn(address account, uint256 amount) external { require(!shouldRevert, "burn: SHOULD_REVERT"); _burn(account, amount); } function transfer(address account, uint256 amount) public override returns (bool) { require(!shouldRevert, "transfer: SHOULD_REVERT"); _transfer(msg.sender, account, amount); return true; } function setShouldRevert(bool _shouldRevert) external { shouldRevert = _shouldRevert; } } // File contracts/test/TestERC20.sol pragma solidity 0.8.4; /* This token is ONLY useful for testing * Anybody can mint as many tokens as they like * Anybody can burn anyone else's tokens */ contract TestERC20 is ERC20 { constructor() ERC20("Test Token", "TEST") { _mint(msg.sender, 1000000 ether); } function mint(address account, uint256 amount) external { _mint(account, amount); } function burn(address account, uint256 amount) external { _burn(account, amount); } } // File contracts/ProposedOwnable.sol pragma solidity 0.8.4; /** * @title ProposedOwnable * @notice 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 via a two step process: * 1. Call `proposeOwner` * 2. Wait out the delay period * 3. Call `acceptOwner` * * @dev 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. * * @dev The majority of this code was taken from the openzeppelin Ownable * contract * */ abstract contract ProposedOwnable { address private _owner; address private _proposed; uint256 private _proposedOwnershipTimestamp; bool private _routerOwnershipRenounced; uint256 private _routerOwnershipTimestamp; bool private _assetOwnershipRenounced; uint256 private _assetOwnershipTimestamp; uint256 private constant _delay = 7 days; event RouterOwnershipRenunciationProposed(uint256 timestamp); event RouterOwnershipRenounced(bool renounced); event AssetOwnershipRenunciationProposed(uint256 timestamp); event AssetOwnershipRenounced(bool renounced); event OwnershipProposed(address indexed proposedOwner); event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @notice Initializes the contract setting the deployer as the initial * owner. */ constructor() { _setOwner(msg.sender); } /** * @notice Returns the address of the current owner. */ function owner() public view virtual returns (address) { return _owner; } /** * @notice Returns the address of the proposed owner. */ function proposed() public view virtual returns (address) { return _proposed; } /** * @notice Returns the address of the proposed owner. */ function proposedTimestamp() public view virtual returns (uint256) { return _proposedOwnershipTimestamp; } /** * @notice Returns the timestamp when router ownership was last proposed to be renounced */ function routerOwnershipTimestamp() public view virtual returns (uint256) { return _routerOwnershipTimestamp; } /** * @notice Returns the timestamp when asset ownership was last proposed to be renounced */ function assetOwnershipTimestamp() public view virtual returns (uint256) { return _assetOwnershipTimestamp; } /** * @notice Returns the delay period before a new owner can be accepted. */ function delay() public view virtual returns (uint256) { return _delay; } /** * @notice Throws if called by any account other than the owner. */ modifier onlyOwner() { require(_owner == msg.sender, "#OO:029"); _; } /** * @notice Throws if called by any account other than the proposed owner. */ modifier onlyProposed() { require(_proposed == msg.sender, "#OP:035"); _; } /** * @notice Indicates if the ownership of the router whitelist has * been renounced */ function isRouterOwnershipRenounced() public view returns (bool) { return _owner == address(0) || _routerOwnershipRenounced; } /** * @notice Indicates if the ownership of the router whitelist has * been renounced */ function proposeRouterOwnershipRenunciation() public virtual onlyOwner { // Use contract as source of truth // Will fail if all ownership is renounced by modifier require(!_routerOwnershipRenounced, "#PROR:038"); // Begin delay, emit event _setRouterOwnershipTimestamp(); } /** * @notice Indicates if the ownership of the asset whitelist has * been renounced */ function renounceRouterOwnership() public virtual onlyOwner { // Contract as sournce of truth // Will fail if all ownership is renounced by modifier require(!_routerOwnershipRenounced, "#RRO:038"); // Ensure there has been a proposal cycle started require(_routerOwnershipTimestamp > 0, "#RRO:037"); // Delay has elapsed require((block.timestamp - _routerOwnershipTimestamp) > _delay, "#RRO:030"); // Set renounced, emit event, reset timestamp to 0 _setRouterOwnership(true); } /** * @notice Indicates if the ownership of the asset whitelist has * been renounced */ function isAssetOwnershipRenounced() public view returns (bool) { return _owner == address(0) || _assetOwnershipRenounced; } /** * @notice Indicates if the ownership of the asset whitelist has * been renounced */ function proposeAssetOwnershipRenunciation() public virtual onlyOwner { // Contract as sournce of truth // Will fail if all ownership is renounced by modifier require(!_assetOwnershipRenounced, "#PAOR:038"); // Start cycle, emit event _setAssetOwnershipTimestamp(); } /** * @notice Indicates if the ownership of the asset whitelist has * been renounced */ function renounceAssetOwnership() public virtual onlyOwner { // Contract as sournce of truth // Will fail if all ownership is renounced by modifier require(!_assetOwnershipRenounced, "#RAO:038"); // Ensure there has been a proposal cycle started require(_assetOwnershipTimestamp > 0, "#RAO:037"); // Ensure delay has elapsed require((block.timestamp - _assetOwnershipTimestamp) > _delay, "#RAO:030"); // Set ownership, reset timestamp, emit event _setAssetOwnership(true); } /** * @notice Indicates if the ownership has been renounced() by * checking if current owner is address(0) */ function renounced() public view returns (bool) { return _owner == address(0); } /** * @notice Sets the timestamp for an owner to be proposed, and sets the * newly proposed owner as step 1 in a 2-step process */ function proposeNewOwner(address newlyProposed) public virtual onlyOwner { // Contract as source of truth require(_proposed != newlyProposed || newlyProposed == address(0), "#PNO:036"); // Sanity check: reasonable proposal require(_owner != newlyProposed, "#PNO:038"); _setProposed(newlyProposed); } /** * @notice Renounces ownership of the contract after a delay */ function renounceOwnership() public virtual onlyOwner { // Ensure there has been a proposal cycle started require(_proposedOwnershipTimestamp > 0, "#RO:037"); // Ensure delay has elapsed require((block.timestamp - _proposedOwnershipTimestamp) > _delay, "#RO:030"); // Require proposed is set to 0 require(_proposed == address(0), "#RO:036"); // Emit event, set new owner, reset timestamp _setOwner(_proposed); } /** * @notice Transfers ownership of the contract to a new account (`newOwner`). * Can only be called by the current owner. */ function acceptProposedOwner() public virtual onlyProposed { // Contract as source of truth require(_owner != _proposed, "#APO:038"); // NOTE: no need to check if _proposedOwnershipTimestamp > 0 because // the only time this would happen is if the _proposed was never // set (will fail from modifier) or if the owner == _proposed (checked // above) // Ensure delay has elapsed require((block.timestamp - _proposedOwnershipTimestamp) > _delay, "#APO:030"); // Emit event, set new owner, reset timestamp _setOwner(_proposed); } ////// INTERNAL ////// function _setRouterOwnershipTimestamp() private { _routerOwnershipTimestamp = block.timestamp; emit RouterOwnershipRenunciationProposed(_routerOwnershipTimestamp); } function _setRouterOwnership(bool value) private { _routerOwnershipRenounced = value; _routerOwnershipTimestamp = 0; emit RouterOwnershipRenounced(value); } function _setAssetOwnershipTimestamp() private { _assetOwnershipTimestamp = block.timestamp; emit AssetOwnershipRenunciationProposed(_assetOwnershipTimestamp); } function _setAssetOwnership(bool value) private { _assetOwnershipRenounced = value; _assetOwnershipTimestamp = 0; emit AssetOwnershipRenounced(value); } function _setOwner(address newOwner) private { address oldOwner = _owner; _owner = newOwner; _proposedOwnershipTimestamp = 0; emit OwnershipTransferred(oldOwner, newOwner); } function _setProposed(address newlyProposed) private { _proposedOwnershipTimestamp = block.timestamp; _proposed = newlyProposed; emit OwnershipProposed(_proposed); } } // File contracts/TransactionManager.sol pragma solidity 0.8.4; /** * * @title TransactionManager * @author Connext <[email protected]> * @notice This contract holds the logic to facilitate crosschain transactions. * Transactions go through three phases in the happy case: * * 1. Route Auction (offchain): User broadcasts to our network * signalling their desired route. Routers respond with sealed bids * containing commitments to fulfilling the transaction within a * certain time and price range. * * 2. Prepare: Once the auction is completed, the transaction can be * prepared. The user submits a transaction to `TransactionManager` * contract on sender-side chain containing router's signed bid. This * transaction locks up the users funds on the sending chain. Upon * detecting an event containing their signed bid from the chain, * router submits the same transaction to `TransactionManager` on the * receiver-side chain, and locks up a corresponding amount of * liquidity. The amount locked on the receiving chain is `sending * amount - auction fee` so the router is incentivized to complete the * transaction. * * 3. Fulfill: Upon detecting the `TransactionPrepared` event on the * receiver-side chain, the user signs a message and sends it to a * relayer, who will earn a fee for submission. The relayer (which may * be the router) then submits the message to the `TransactionManager` * to complete their transaction on receiver-side chain and claim the * funds locked by the router. A relayer is used here to allow users * to submit transactions with arbitrary calldata on the receiving * chain without needing gas to do so. The router then submits the * same signed message and completes transaction on sender-side, * unlocking the original `amount`. * * If a transaction is not fulfilled within a fixed timeout, it * reverts and can be reclaimed by the party that called `prepare` on * each chain (initiator). Additionally, transactions can be cancelled * unilaterally by the person owed funds on that chain (router for * sending chain, user for receiving chain) prior to expiry. */ contract TransactionManager is ReentrancyGuard, ProposedOwnable, ITransactionManager { /** * @dev Mapping of router to balance specific to asset */ mapping(address => mapping(address => uint256)) public routerBalances; /** * @dev Mapping of allowed router addresses. Must be added to both * sending and receiving chains when forwarding a transfer. */ mapping(address => bool) public approvedRouters; /** * @dev Mapping of allowed assetIds on same chain as contract */ mapping(address => bool) public approvedAssets; /** * @dev Mapping of hash of `InvariantTransactionData` to the hash * of the `VariantTransactionData` */ mapping(bytes32 => bytes32) public variantTransactionData; /** * @dev The stored chain id of the contract, may be passed in to avoid any * evm issues */ uint256 private immutable chainId; /** * @dev Minimum timeout (will be the lowest on the receiving chain) */ uint256 public constant MIN_TIMEOUT = 1 days; // 24 hours /** * @dev Maximum timeout (will be the highest on the sending chain) */ uint256 public constant MAX_TIMEOUT = 30 days; // 720 hours /** * @dev The external contract that will execute crosschain * calldata */ IFulfillInterpreter public immutable interpreter; constructor(uint256 _chainId) { chainId = _chainId; interpreter = new FulfillInterpreter(address(this)); } /** * @notice Gets the chainId for this contract. If not specified during init * will use the block.chainId */ function getChainId() public view override returns (uint256 _chainId) { // Hold in memory to reduce sload calls uint256 chain = chainId; if (chain == 0) { // If not provided, pull from block chain = block.chainid; } return chain; } /** * @notice Allows us to get the chainId that this contract has stored */ function getStoredChainId() external view override returns (uint256) { return chainId; } /** * @notice Used to add routers that can transact crosschain * @param router Router address to add */ function addRouter(address router) external override onlyOwner { // Sanity check: not empty require(router != address(0), "#AR:001"); // Sanity check: needs approval require(approvedRouters[router] == false, "#AR:032"); // Update mapping approvedRouters[router] = true; // Emit event emit RouterAdded(router, msg.sender); } /** * @notice Used to remove routers that can transact crosschain * @param router Router address to remove */ function removeRouter(address router) external override onlyOwner { // Sanity check: not empty require(router != address(0), "#RR:001"); // Sanity check: needs removal require(approvedRouters[router] == true, "#RR:033"); // Update mapping approvedRouters[router] = false; // Emit event emit RouterRemoved(router, msg.sender); } /** * @notice Used to add assets on same chain as contract that can * be transferred. * @param assetId AssetId to add */ function addAssetId(address assetId) external override onlyOwner { // Sanity check: needs approval require(approvedAssets[assetId] == false, "#AA:032"); // Update mapping approvedAssets[assetId] = true; // Emit event emit AssetAdded(assetId, msg.sender); } /** * @notice Used to remove assets on same chain as contract that can * be transferred. * @param assetId AssetId to remove */ function removeAssetId(address assetId) external override onlyOwner { // Sanity check: already approval require(approvedAssets[assetId] == true, "#RA:033"); // Update mapping approvedAssets[assetId] = false; // Emit event emit AssetRemoved(assetId, msg.sender); } /** * @notice This is used by anyone to increase a router's available * liquidity for a given asset. * @param amount The amount of liquidity to add for the router * @param assetId The address (or `address(0)` if native asset) of the * asset you're adding liquidity for * @param router The router you are adding liquidity on behalf of */ function addLiquidityFor(uint256 amount, address assetId, address router) external payable override nonReentrant { _addLiquidityForRouter(amount, assetId, router); } /** * @notice This is used by any router to increase their available * liquidity for a given asset. * @param amount The amount of liquidity to add for the router * @param assetId The address (or `address(0)` if native asset) of the * asset you're adding liquidity for */ function addLiquidity(uint256 amount, address assetId) external payable override nonReentrant { _addLiquidityForRouter(amount, assetId, msg.sender); } /** * @notice This is used by any router to decrease their available * liquidity for a given asset. * @param amount The amount of liquidity to remove for the router * @param assetId The address (or `address(0)` if native asset) of the * asset you're removing liquidity for * @param recipient The address that will receive the liquidity being removed */ function removeLiquidity( uint256 amount, address assetId, address payable recipient ) external override nonReentrant { // Sanity check: recipient is sensible require(recipient != address(0), "#RL:007"); // Sanity check: nonzero amounts require(amount > 0, "#RL:002"); uint256 routerBalance = routerBalances[msg.sender][assetId]; // Sanity check: amount can be deducted for the router require(routerBalance >= amount, "#RL:008"); // Update router balances unchecked { routerBalances[msg.sender][assetId] = routerBalance - amount; } // Transfer from contract to specified recipient LibAsset.transferAsset(assetId, recipient, amount); // Emit event emit LiquidityRemoved(msg.sender, assetId, amount, recipient); } /** * @notice This function creates a crosschain transaction. When called on * the sending chain, the user is expected to lock up funds. When * called on the receiving chain, the router deducts the transfer * amount from the available liquidity. The majority of the * information about a given transfer does not change between chains, * with three notable exceptions: `amount`, `expiry`, and * `preparedBlock`. The `amount` and `expiry` are decremented * between sending and receiving chains to provide an incentive for * the router to complete the transaction and time for the router to * fulfill the transaction on the sending chain after the unlocking * signature is revealed, respectively. * @param args TODO */ function prepare( PrepareArgs calldata args ) external payable override nonReentrant returns (TransactionData memory) { // Sanity check: user is sensible require(args.invariantData.user != address(0), "#P:009"); // Sanity check: router is sensible require(args.invariantData.router != address(0), "#P:001"); // Router is approved *on both chains* require(isRouterOwnershipRenounced() || approvedRouters[args.invariantData.router], "#P:003"); // Sanity check: sendingChainFallback is sensible require(args.invariantData.sendingChainFallback != address(0), "#P:010"); // Sanity check: valid fallback require(args.invariantData.receivingAddress != address(0), "#P:026"); // Make sure the chains are different require(args.invariantData.sendingChainId != args.invariantData.receivingChainId, "#P:011"); // Make sure the chains are relevant uint256 _chainId = getChainId(); require(args.invariantData.sendingChainId == _chainId || args.invariantData.receivingChainId == _chainId, "#P:012"); { // Expiry scope // Make sure the expiry is greater than min uint256 buffer = args.expiry - block.timestamp; require(buffer >= MIN_TIMEOUT, "#P:013"); // Make sure the expiry is lower than max require(buffer <= MAX_TIMEOUT, "#P:014"); } // Make sure the hash is not a duplicate bytes32 digest = keccak256(abi.encode(args.invariantData)); require(variantTransactionData[digest] == bytes32(0), "#P:015"); // NOTE: the `encodedBid` and `bidSignature` are simply passed through // to the contract emitted event to ensure the availability of // this information. Their validity is asserted offchain, and // is out of scope of this contract. They are used as inputs so // in the event of a router or user crash, they may recover the // correct bid information without requiring an offchain store. // Amount actually used (if fee-on-transfer will be different than // supplied) uint256 amount = args.amount; // First determine if this is sender side or receiver side if (args.invariantData.sendingChainId == _chainId) { // Check the sender is correct require(msg.sender == args.invariantData.initiator, "#P:039"); // Sanity check: amount is sensible // Only check on sending chain to enforce router fees. Transactions could // be 0-valued on receiving chain if it is just a value-less call to some // `IFulfillHelper` require(args.amount > 0, "#P:002"); // Assets are approved // NOTE: Cannot check this on receiving chain because of differing // chain contexts require(isAssetOwnershipRenounced() || approvedAssets[args.invariantData.sendingAssetId], "#P:004"); // This is sender side prepare. The user is beginning the process of // submitting an onchain tx after accepting some bid. They should // lock their funds in the contract for the router to claim after // they have revealed their signature on the receiving chain via // submitting a corresponding `fulfill` tx // Validate correct amounts on msg and transfer from user to // contract amount = transferAssetToContract( args.invariantData.sendingAssetId, args.amount ); // Store the transaction variants. This happens after transferring to // account for fee on transfer tokens variantTransactionData[digest] = hashVariantTransactionData( amount, args.expiry, block.number ); } else { // This is receiver side prepare. The router has proposed a bid on the // transfer which the user has accepted. They can now lock up their // own liquidity on th receiving chain, which the user can unlock by // calling `fulfill`. When creating the `amount` and `expiry` on the // receiving chain, the router should have decremented both. The // expiry should be decremented to ensure the router has time to // complete the sender-side transaction after the user completes the // receiver-side transactoin. The amount should be decremented to act as // a fee to incentivize the router to complete the transaction properly. // Check that the callTo is a contract // NOTE: This cannot happen on the sending chain (different chain // contexts), so a user could mistakenly create a transfer that must be // cancelled if this is incorrect require(args.invariantData.callTo == address(0) || Address.isContract(args.invariantData.callTo), "#P:031"); // Check that the asset is approved // NOTE: This cannot happen on both chains because of differing chain // contexts. May be possible for user to create transaction that is not // prepare-able on the receiver chain. require(isAssetOwnershipRenounced() || approvedAssets[args.invariantData.receivingAssetId], "#P:004"); // Check that the caller is the router require(msg.sender == args.invariantData.router, "#P:016"); // Check that the router isnt accidentally locking funds in the contract require(msg.value == 0, "#P:017"); // Check that router has liquidity uint256 balance = routerBalances[args.invariantData.router][args.invariantData.receivingAssetId]; require(balance >= amount, "#P:018"); // Store the transaction variants variantTransactionData[digest] = hashVariantTransactionData( amount, args.expiry, block.number ); // Decrement the router liquidity // using unchecked because underflow protected against with require unchecked { routerBalances[args.invariantData.router][args.invariantData.receivingAssetId] = balance - amount; } } // Emit event TransactionData memory txData = TransactionData({ receivingChainTxManagerAddress: args.invariantData.receivingChainTxManagerAddress, user: args.invariantData.user, router: args.invariantData.router, initiator: args.invariantData.initiator, sendingAssetId: args.invariantData.sendingAssetId, receivingAssetId: args.invariantData.receivingAssetId, sendingChainFallback: args.invariantData.sendingChainFallback, callTo: args.invariantData.callTo, receivingAddress: args.invariantData.receivingAddress, callDataHash: args.invariantData.callDataHash, transactionId: args.invariantData.transactionId, sendingChainId: args.invariantData.sendingChainId, receivingChainId: args.invariantData.receivingChainId, amount: amount, expiry: args.expiry, preparedBlockNumber: block.number }); emit TransactionPrepared( txData.user, txData.router, txData.transactionId, txData, msg.sender, args ); return txData; } /** * @notice This function completes a crosschain transaction. When called on * the receiving chain, the user reveals their signature on the * transactionId and is sent the amount corresponding to the number * of shares the router locked when calling `prepare`. The router * then uses this signature to unlock the corresponding funds on the * receiving chain, which are then added back to their available * liquidity. The user includes a relayer fee since it is not * assumed they will have gas on the receiving chain. This function * *must* be called before the transaction expiry has elapsed. * @param args TODO */ function fulfill( FulfillArgs calldata args ) external override nonReentrant returns (TransactionData memory) { // Get the hash of the invariant tx data. This hash is the same // between sending and receiving chains. The variant data is stored // in the contract when `prepare` is called within the mapping. { // scope: validation and effects bytes32 digest = hashInvariantTransactionData(args.txData); // Make sure that the variant data matches what was stored require(variantTransactionData[digest] == hashVariantTransactionData( args.txData.amount, args.txData.expiry, args.txData.preparedBlockNumber ), "#F:019"); // Make sure the expiry has not elapsed require(args.txData.expiry >= block.timestamp, "#F:020"); // Make sure the transaction wasn't already completed require(args.txData.preparedBlockNumber > 0, "#F:021"); // Check provided callData matches stored hash require(keccak256(args.callData) == args.txData.callDataHash, "#F:024"); // To prevent `fulfill` / `cancel` from being called multiple times, the // preparedBlockNumber is set to 0 before being hashed. The value of the // mapping is explicitly *not* zeroed out so users who come online without // a store can tell the difference between a transaction that has not been // prepared, and a transaction that was already completed on the receiver // chain. variantTransactionData[digest] = hashVariantTransactionData( args.txData.amount, args.txData.expiry, 0 ); } // Declare these variables for the event emission. Are only assigned // IFF there is an external call on the receiving chain bool success; bool isContract; bytes memory returnData; uint256 _chainId = getChainId(); if (args.txData.sendingChainId == _chainId) { // The router is completing the transaction, they should get the // amount that the user deposited credited to their liquidity // reserves. // Make sure that the user is not accidentally fulfilling the transaction // on the sending chain require(msg.sender == args.txData.router, "#F:016"); // Validate the user has signed require( recoverFulfillSignature( args.txData.transactionId, args.relayerFee, args.txData.receivingChainId, args.txData.receivingChainTxManagerAddress, args.signature ) == args.txData.user, "#F:022" ); // Complete tx to router for original sending amount routerBalances[args.txData.router][args.txData.sendingAssetId] += args.txData.amount; } else { // Validate the user has signed, using domain of contract require( recoverFulfillSignature( args.txData.transactionId, args.relayerFee, _chainId, address(this), args.signature ) == args.txData.user, "#F:022" ); // Sanity check: fee <= amount. Allow `=` in case of only // wanting to execute 0-value crosschain tx, so only providing // the fee amount require(args.relayerFee <= args.txData.amount, "#F:023"); (success, isContract, returnData) = _receivingChainFulfill( args.txData, args.relayerFee, args.callData ); } // Emit event emit TransactionFulfilled( args.txData.user, args.txData.router, args.txData.transactionId, args, success, isContract, returnData, msg.sender ); return args.txData; } /** * @notice Any crosschain transaction can be cancelled after it has been * created to prevent indefinite lock up of funds. After the * transaction has expired, anyone can cancel it. Before the * expiry, only the recipient of the funds on the given chain is * able to cancel. On the sending chain, this means only the router * is able to cancel before the expiry, while only the user can * prematurely cancel on the receiving chain. * @param args TODO */ function cancel(CancelArgs calldata args) external override nonReentrant returns (TransactionData memory) { // Make sure params match against stored data // Also checks that there is an active transfer here // Also checks that sender or receiver chainID is this chainId (bc we checked it previously) // Get the hash of the invariant tx data. This hash is the same // between sending and receiving chains. The variant data is stored // in the contract when `prepare` is called within the mapping. bytes32 digest = hashInvariantTransactionData(args.txData); // Verify the variant data is correct require(variantTransactionData[digest] == hashVariantTransactionData(args.txData.amount, args.txData.expiry, args.txData.preparedBlockNumber), "#C:019"); // Make sure the transaction wasn't already completed require(args.txData.preparedBlockNumber > 0, "#C:021"); // To prevent `fulfill` / `cancel` from being called multiple times, the // preparedBlockNumber is set to 0 before being hashed. The value of the // mapping is explicitly *not* zeroed out so users who come online without // a store can tell the difference between a transaction that has not been // prepared, and a transaction that was already completed on the receiver // chain. variantTransactionData[digest] = hashVariantTransactionData(args.txData.amount, args.txData.expiry, 0); // Get chainId for gas uint256 _chainId = getChainId(); // Return the appropriate locked funds if (args.txData.sendingChainId == _chainId) { // Sender side, funds must be returned to the user if (args.txData.expiry >= block.timestamp) { // Timeout has not expired and tx may only be cancelled by router // NOTE: no need to validate the signature here, since you are requiring // the router must be the sender when the cancellation is during the // fulfill-able window require(msg.sender == args.txData.router, "#C:025"); } // Return users locked funds // NOTE: no need to check if amount > 0 because cant be prepared on // sending chain with 0 value LibAsset.transferAsset( args.txData.sendingAssetId, payable(args.txData.sendingChainFallback), args.txData.amount ); } else { // Receiver side, router liquidity is returned if (args.txData.expiry >= block.timestamp) { // Timeout has not expired and tx may only be cancelled by user // Validate signature require(msg.sender == args.txData.user || recoverCancelSignature(args.txData.transactionId, _chainId, address(this), args.signature) == args.txData.user, "#C:022"); // NOTE: there is no incentive here for relayers to submit this on // behalf of the user (i.e. fee not respected) because the user has not // locked funds on this contract. However, if the user reveals their // cancel signature to the router, they are incentivized to submit it // to unlock their own funds } // Return liquidity to router routerBalances[args.txData.router][args.txData.receivingAssetId] += args.txData.amount; } // Emit event emit TransactionCancelled( args.txData.user, args.txData.router, args.txData.transactionId, args, msg.sender ); // Return return args.txData; } ////////////////////////// /// Private functions /// ////////////////////////// /** * @notice Contains the logic to verify + increment a given routers liquidity * @param amount The amount of liquidity to add for the router * @param assetId The address (or `address(0)` if native asset) of the * asset you're adding liquidity for * @param router The router you are adding liquidity on behalf of */ function _addLiquidityForRouter( uint256 amount, address assetId, address router ) internal { // Sanity check: router is sensible require(router != address(0), "#AL:001"); // Sanity check: nonzero amounts require(amount > 0, "#AL:002"); // Router is approved require(isRouterOwnershipRenounced() || approvedRouters[router], "#AL:003"); // Asset is approved require(isAssetOwnershipRenounced() || approvedAssets[assetId], "#AL:004"); // Transfer funds to contract amount = transferAssetToContract(assetId, amount); // Update the router balances. Happens after pulling funds to account for // the fee on transfer tokens routerBalances[router][assetId] += amount; // Emit event emit LiquidityAdded(router, assetId, amount, msg.sender); } /** * @notice Handles transferring funds from msg.sender to the * transaction manager contract. Used in prepare, addLiquidity * @param assetId The address to transfer * @param specifiedAmount The specified amount to transfer. May not be the * actual amount transferred (i.e. fee on transfer * tokens) */ function transferAssetToContract(address assetId, uint256 specifiedAmount) internal returns (uint256) { uint256 trueAmount = specifiedAmount; // Validate correct amounts are transferred if (LibAsset.isNativeAsset(assetId)) { require(msg.value == specifiedAmount, "#TA:005"); } else { uint256 starting = LibAsset.getOwnBalance(assetId); require(msg.value == 0, "#TA:006"); LibAsset.transferFromERC20(assetId, msg.sender, address(this), specifiedAmount); // Calculate the *actual* amount that was sent here trueAmount = LibAsset.getOwnBalance(assetId) - starting; } return trueAmount; } /// @notice Recovers the signer from the signature provided by the user /// @param transactionId Transaction identifier of tx being recovered /// @param signature The signature you are recovering the signer from function recoverCancelSignature( bytes32 transactionId, uint256 receivingChainId, address receivingChainTxManagerAddress, bytes calldata signature ) internal pure returns (address) { // Create the signed payload SignedCancelData memory payload = SignedCancelData({ transactionId: transactionId, functionIdentifier: "cancel", receivingChainId: receivingChainId, receivingChainTxManagerAddress: receivingChainTxManagerAddress }); // Recover return recoverSignature(abi.encode(payload), signature); } /** * @notice Recovers the signer from the signature provided by the user * @param transactionId Transaction identifier of tx being recovered * @param relayerFee The fee paid to the relayer for submitting the * tx on behalf of the user. * @param signature The signature you are recovering the signer from */ function recoverFulfillSignature( bytes32 transactionId, uint256 relayerFee, uint256 receivingChainId, address receivingChainTxManagerAddress, bytes calldata signature ) internal pure returns (address) { // Create the signed payload SignedFulfillData memory payload = SignedFulfillData({ transactionId: transactionId, relayerFee: relayerFee, functionIdentifier: "fulfill", receivingChainId: receivingChainId, receivingChainTxManagerAddress: receivingChainTxManagerAddress }); // Recover return recoverSignature(abi.encode(payload), signature); } /** * @notice Holds the logic to recover the signer from an encoded payload. * Will hash and convert to an eth signed message. * @param encodedPayload The payload that was signed * @param signature The signature you are recovering the signer from */ function recoverSignature(bytes memory encodedPayload, bytes calldata signature) internal pure returns (address) { // Recover return ECDSA.recover( ECDSA.toEthSignedMessageHash(keccak256(encodedPayload)), signature ); } /** * @notice Returns the hash of only the invariant portions of a given * crosschain transaction * @param txData TransactionData to hash */ function hashInvariantTransactionData(TransactionData calldata txData) internal pure returns (bytes32) { InvariantTransactionData memory invariant = InvariantTransactionData({ receivingChainTxManagerAddress: txData.receivingChainTxManagerAddress, user: txData.user, router: txData.router, initiator: txData.initiator, sendingAssetId: txData.sendingAssetId, receivingAssetId: txData.receivingAssetId, sendingChainFallback: txData.sendingChainFallback, callTo: txData.callTo, receivingAddress: txData.receivingAddress, sendingChainId: txData.sendingChainId, receivingChainId: txData.receivingChainId, callDataHash: txData.callDataHash, transactionId: txData.transactionId }); return keccak256(abi.encode(invariant)); } /** * @notice Returns the hash of only the variant portions of a given * crosschain transaction * @param amount amount to hash * @param expiry expiry to hash * @param preparedBlockNumber preparedBlockNumber to hash * @return Hash of the variant data * */ function hashVariantTransactionData(uint256 amount, uint256 expiry, uint256 preparedBlockNumber) internal pure returns (bytes32) { VariantTransactionData memory variant = VariantTransactionData({ amount: amount, expiry: expiry, preparedBlockNumber: preparedBlockNumber }); return keccak256(abi.encode(variant)); } /** * @notice Handles the receiving-chain fulfillment. This function should * pay the relayer and either send funds to the specified address * or execute the calldata. Will return a tuple of boolean,bytes * indicating the success and return data of the external call. * @dev Separated from fulfill function to avoid stack too deep errors * * @param txData The TransactionData that needs to be fulfilled * @param relayerFee The fee to be paid to the relayer for submission * @param callData The data to be executed on the receiving chain * * @return Tuple representing (success, returnData) of the external call */ function _receivingChainFulfill( TransactionData calldata txData, uint256 relayerFee, bytes calldata callData ) internal returns (bool, bool, bytes memory) { // The user is completing the transaction, they should get the // amount that the router deposited less fees for relayer. // Get the amount to send uint256 toSend; unchecked { toSend = txData.amount - relayerFee; } // Send the relayer the fee if (relayerFee > 0) { LibAsset.transferAsset(txData.receivingAssetId, payable(msg.sender), relayerFee); } // Handle receiver chain external calls if needed if (txData.callTo == address(0)) { // No external calls, send directly to receiving address if (toSend > 0) { LibAsset.transferAsset(txData.receivingAssetId, payable(txData.receivingAddress), toSend); } return (false, false, new bytes(0)); } else { // Handle external calls with a fallback to the receiving // address in case the call fails so the funds dont remain // locked. bool isNativeAsset = LibAsset.isNativeAsset(txData.receivingAssetId); // First, transfer the funds to the helper if needed if (!isNativeAsset && toSend > 0) { LibAsset.transferERC20(txData.receivingAssetId, address(interpreter), toSend); } // Next, call `execute` on the helper. Helpers should internally // track funds to make sure no one user is able to take all funds // for tx, and handle the case of reversions return interpreter.execute{ value: isNativeAsset ? toSend : 0}( txData.transactionId, payable(txData.callTo), txData.receivingAssetId, payable(txData.receivingAddress), toSend, callData ); } } } // File contracts/interfaces/IPriceOracle.sol pragma solidity ^0.8.4; interface IPriceOracle { /** * @notice Get the price of a token * @param token The token to get the price of * @return The asset price mantissa (scaled by 1e18). * Zero means the price is unavailable. */ function getTokenPrice(address token) external view returns (uint256); } // File contracts/Multicall.sol pragma solidity ^0.8.4; pragma experimental ABIEncoderV2; /// @title Multicall - Aggregate results from multiple read-only function calls contract Multicall { struct Call { address target; bytes callData; } function aggregate(Call[] memory calls) public returns (uint256 blockNumber, bytes[] memory returnData) { blockNumber = block.number; returnData = new bytes[](calls.length); for (uint256 i = 0; i < calls.length; i++) { (bool success, bytes memory ret) = calls[i].target.call(calls[i].callData); require(success); returnData[i] = ret; } } } // File contracts/test/TestAggregator.sol pragma solidity 0.8.4; /* * This aggregator is ONLY useful for testing */ contract TestAggregator { uint8 public decimals = 18; string public description = "Chainlink Test Aggregator"; uint256 public version = 1; // getRoundData and latestRoundData should both raise "No data present" // if they do not have data to report, instead of returning unset values // which could be misinterpreted as actual reported values. function getRoundData(uint80 _roundId) external view returns ( uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound ){ return (_roundId, 1e18, 0, block.timestamp, 1e18); } function latestRoundData() external view returns ( uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound ) { return (1, 1e18, 0, block.timestamp, 1e18); } }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"_routerFactory","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"components":[{"internalType":"address","name":"receivingChainTxManagerAddress","type":"address"},{"internalType":"address","name":"user","type":"address"},{"internalType":"address","name":"router","type":"address"},{"internalType":"address","name":"initiator","type":"address"},{"internalType":"address","name":"sendingAssetId","type":"address"},{"internalType":"address","name":"receivingAssetId","type":"address"},{"internalType":"address","name":"sendingChainFallback","type":"address"},{"internalType":"address","name":"receivingAddress","type":"address"},{"internalType":"address","name":"callTo","type":"address"},{"internalType":"bytes32","name":"callDataHash","type":"bytes32"},{"internalType":"bytes32","name":"transactionId","type":"bytes32"},{"internalType":"uint256","name":"sendingChainId","type":"uint256"},{"internalType":"uint256","name":"receivingChainId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"expiry","type":"uint256"},{"internalType":"uint256","name":"preparedBlockNumber","type":"uint256"}],"indexed":false,"internalType":"struct ITransactionManager.TransactionData","name":"txData","type":"tuple"},{"indexed":false,"internalType":"address","name":"routerRelayerFeeAsset","type":"address"},{"indexed":false,"internalType":"uint256","name":"routerRelayerFee","type":"uint256"},{"indexed":false,"internalType":"address","name":"caller","type":"address"}],"name":"Cancel","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"address","name":"receivingChainTxManagerAddress","type":"address"},{"internalType":"address","name":"user","type":"address"},{"internalType":"address","name":"router","type":"address"},{"internalType":"address","name":"initiator","type":"address"},{"internalType":"address","name":"sendingAssetId","type":"address"},{"internalType":"address","name":"receivingAssetId","type":"address"},{"internalType":"address","name":"sendingChainFallback","type":"address"},{"internalType":"address","name":"receivingAddress","type":"address"},{"internalType":"address","name":"callTo","type":"address"},{"internalType":"bytes32","name":"callDataHash","type":"bytes32"},{"internalType":"bytes32","name":"transactionId","type":"bytes32"},{"internalType":"uint256","name":"sendingChainId","type":"uint256"},{"internalType":"uint256","name":"receivingChainId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"expiry","type":"uint256"},{"internalType":"uint256","name":"preparedBlockNumber","type":"uint256"}],"indexed":false,"internalType":"struct ITransactionManager.TransactionData","name":"txData","type":"tuple"},{"indexed":false,"internalType":"address","name":"routerRelayerFeeAsset","type":"address"},{"indexed":false,"internalType":"uint256","name":"routerRelayerFee","type":"uint256"},{"indexed":false,"internalType":"address","name":"caller","type":"address"}],"name":"Fulfill","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"address","name":"receivingChainTxManagerAddress","type":"address"},{"internalType":"address","name":"user","type":"address"},{"internalType":"address","name":"router","type":"address"},{"internalType":"address","name":"initiator","type":"address"},{"internalType":"address","name":"sendingAssetId","type":"address"},{"internalType":"address","name":"receivingAssetId","type":"address"},{"internalType":"address","name":"sendingChainFallback","type":"address"},{"internalType":"address","name":"receivingAddress","type":"address"},{"internalType":"address","name":"callTo","type":"address"},{"internalType":"uint256","name":"sendingChainId","type":"uint256"},{"internalType":"uint256","name":"receivingChainId","type":"uint256"},{"internalType":"bytes32","name":"callDataHash","type":"bytes32"},{"internalType":"bytes32","name":"transactionId","type":"bytes32"}],"indexed":false,"internalType":"struct ITransactionManager.InvariantTransactionData","name":"invariantData","type":"tuple"},{"indexed":false,"internalType":"address","name":"routerRelayerFeeAsset","type":"address"},{"indexed":false,"internalType":"uint256","name":"routerRelayerFee","type":"uint256"},{"indexed":false,"internalType":"address","name":"caller","type":"address"}],"name":"Prepare","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"assetId","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"address","name":"caller","type":"address"}],"name":"RelayerFeeAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"assetId","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"address","name":"caller","type":"address"}],"name":"RelayerFeeRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"address","name":"assetId","type":"address"},{"indexed":false,"internalType":"address","name":"routerRelayerFeeAsset","type":"address"},{"indexed":false,"internalType":"uint256","name":"routerRelayerFee","type":"uint256"},{"indexed":false,"internalType":"address","name":"caller","type":"address"}],"name":"RemoveLiquidity","type":"event"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"assetId","type":"address"}],"name":"addRelayerFee","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"components":[{"internalType":"address","name":"receivingChainTxManagerAddress","type":"address"},{"internalType":"address","name":"user","type":"address"},{"internalType":"address","name":"router","type":"address"},{"internalType":"address","name":"initiator","type":"address"},{"internalType":"address","name":"sendingAssetId","type":"address"},{"internalType":"address","name":"receivingAssetId","type":"address"},{"internalType":"address","name":"sendingChainFallback","type":"address"},{"internalType":"address","name":"receivingAddress","type":"address"},{"internalType":"address","name":"callTo","type":"address"},{"internalType":"bytes32","name":"callDataHash","type":"bytes32"},{"internalType":"bytes32","name":"transactionId","type":"bytes32"},{"internalType":"uint256","name":"sendingChainId","type":"uint256"},{"internalType":"uint256","name":"receivingChainId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"expiry","type":"uint256"},{"internalType":"uint256","name":"preparedBlockNumber","type":"uint256"}],"internalType":"struct ITransactionManager.TransactionData","name":"txData","type":"tuple"},{"internalType":"bytes","name":"signature","type":"bytes"},{"internalType":"bytes","name":"encodedMeta","type":"bytes"}],"internalType":"struct ITransactionManager.CancelArgs","name":"args","type":"tuple"},{"internalType":"address","name":"routerRelayerFeeAsset","type":"address"},{"internalType":"uint256","name":"routerRelayerFee","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"cancel","outputs":[{"components":[{"internalType":"address","name":"receivingChainTxManagerAddress","type":"address"},{"internalType":"address","name":"user","type":"address"},{"internalType":"address","name":"router","type":"address"},{"internalType":"address","name":"initiator","type":"address"},{"internalType":"address","name":"sendingAssetId","type":"address"},{"internalType":"address","name":"receivingAssetId","type":"address"},{"internalType":"address","name":"sendingChainFallback","type":"address"},{"internalType":"address","name":"receivingAddress","type":"address"},{"internalType":"address","name":"callTo","type":"address"},{"internalType":"bytes32","name":"callDataHash","type":"bytes32"},{"internalType":"bytes32","name":"transactionId","type":"bytes32"},{"internalType":"uint256","name":"sendingChainId","type":"uint256"},{"internalType":"uint256","name":"receivingChainId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"expiry","type":"uint256"},{"internalType":"uint256","name":"preparedBlockNumber","type":"uint256"}],"internalType":"struct ITransactionManager.TransactionData","name":"","type":"tuple"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"components":[{"internalType":"address","name":"receivingChainTxManagerAddress","type":"address"},{"internalType":"address","name":"user","type":"address"},{"internalType":"address","name":"router","type":"address"},{"internalType":"address","name":"initiator","type":"address"},{"internalType":"address","name":"sendingAssetId","type":"address"},{"internalType":"address","name":"receivingAssetId","type":"address"},{"internalType":"address","name":"sendingChainFallback","type":"address"},{"internalType":"address","name":"receivingAddress","type":"address"},{"internalType":"address","name":"callTo","type":"address"},{"internalType":"bytes32","name":"callDataHash","type":"bytes32"},{"internalType":"bytes32","name":"transactionId","type":"bytes32"},{"internalType":"uint256","name":"sendingChainId","type":"uint256"},{"internalType":"uint256","name":"receivingChainId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"expiry","type":"uint256"},{"internalType":"uint256","name":"preparedBlockNumber","type":"uint256"}],"internalType":"struct ITransactionManager.TransactionData","name":"txData","type":"tuple"},{"internalType":"uint256","name":"relayerFee","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"},{"internalType":"bytes","name":"callData","type":"bytes"},{"internalType":"bytes","name":"encodedMeta","type":"bytes"}],"internalType":"struct ITransactionManager.FulfillArgs","name":"args","type":"tuple"},{"internalType":"address","name":"routerRelayerFeeAsset","type":"address"},{"internalType":"uint256","name":"routerRelayerFee","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"fulfill","outputs":[{"components":[{"internalType":"address","name":"receivingChainTxManagerAddress","type":"address"},{"internalType":"address","name":"user","type":"address"},{"internalType":"address","name":"router","type":"address"},{"internalType":"address","name":"initiator","type":"address"},{"internalType":"address","name":"sendingAssetId","type":"address"},{"internalType":"address","name":"receivingAssetId","type":"address"},{"internalType":"address","name":"sendingChainFallback","type":"address"},{"internalType":"address","name":"receivingAddress","type":"address"},{"internalType":"address","name":"callTo","type":"address"},{"internalType":"bytes32","name":"callDataHash","type":"bytes32"},{"internalType":"bytes32","name":"transactionId","type":"bytes32"},{"internalType":"uint256","name":"sendingChainId","type":"uint256"},{"internalType":"uint256","name":"receivingChainId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"expiry","type":"uint256"},{"internalType":"uint256","name":"preparedBlockNumber","type":"uint256"}],"internalType":"struct ITransactionManager.TransactionData","name":"","type":"tuple"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_transactionManager","type":"address"},{"internalType":"uint256","name":"_chainId","type":"uint256"},{"internalType":"address","name":"_routerSigner","type":"address"},{"internalType":"address","name":"_recipient","type":"address"},{"internalType":"address","name":"_owner","type":"address"}],"name":"init","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"components":[{"internalType":"address","name":"receivingChainTxManagerAddress","type":"address"},{"internalType":"address","name":"user","type":"address"},{"internalType":"address","name":"router","type":"address"},{"internalType":"address","name":"initiator","type":"address"},{"internalType":"address","name":"sendingAssetId","type":"address"},{"internalType":"address","name":"receivingAssetId","type":"address"},{"internalType":"address","name":"sendingChainFallback","type":"address"},{"internalType":"address","name":"receivingAddress","type":"address"},{"internalType":"address","name":"callTo","type":"address"},{"internalType":"uint256","name":"sendingChainId","type":"uint256"},{"internalType":"uint256","name":"receivingChainId","type":"uint256"},{"internalType":"bytes32","name":"callDataHash","type":"bytes32"},{"internalType":"bytes32","name":"transactionId","type":"bytes32"}],"internalType":"struct ITransactionManager.InvariantTransactionData","name":"invariantData","type":"tuple"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"expiry","type":"uint256"},{"internalType":"bytes","name":"encryptedCallData","type":"bytes"},{"internalType":"bytes","name":"encodedBid","type":"bytes"},{"internalType":"bytes","name":"bidSignature","type":"bytes"},{"internalType":"bytes","name":"encodedMeta","type":"bytes"}],"internalType":"struct ITransactionManager.PrepareArgs","name":"args","type":"tuple"},{"internalType":"address","name":"routerRelayerFeeAsset","type":"address"},{"internalType":"uint256","name":"routerRelayerFee","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"prepare","outputs":[{"components":[{"internalType":"address","name":"receivingChainTxManagerAddress","type":"address"},{"internalType":"address","name":"user","type":"address"},{"internalType":"address","name":"router","type":"address"},{"internalType":"address","name":"initiator","type":"address"},{"internalType":"address","name":"sendingAssetId","type":"address"},{"internalType":"address","name":"receivingAssetId","type":"address"},{"internalType":"address","name":"sendingChainFallback","type":"address"},{"internalType":"address","name":"receivingAddress","type":"address"},{"internalType":"address","name":"callTo","type":"address"},{"internalType":"bytes32","name":"callDataHash","type":"bytes32"},{"internalType":"bytes32","name":"transactionId","type":"bytes32"},{"internalType":"uint256","name":"sendingChainId","type":"uint256"},{"internalType":"uint256","name":"receivingChainId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"expiry","type":"uint256"},{"internalType":"uint256","name":"preparedBlockNumber","type":"uint256"}],"internalType":"struct ITransactionManager.TransactionData","name":"","type":"tuple"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"recipient","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"assetId","type":"address"},{"internalType":"address","name":"routerRelayerFeeAsset","type":"address"},{"internalType":"uint256","name":"routerRelayerFee","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"removeLiquidity","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"assetId","type":"address"}],"name":"removeRelayerFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"routerFactory","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"routerSigner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_recipient","type":"address"}],"name":"setRecipient","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_routerSigner","type":"address"}],"name":"setSigner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"transactionManager","outputs":[{"internalType":"contract ITransactionManager","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]
Deployed Bytecode
0x6080604052600436106100f75760003560e01c80636e2054a91161008a578063ce97653911610059578063ce976539146102b6578063d42030ed146102c9578063f2fde38b146102e9578063fc6bee131461030957600080fd5b80636e2054a914610236578063715018a61461026357806382977466146102785780638da5cb5b1461029857600080fd5b80634ba51437116100c65780634ba51437146101a25780634f64cfc5146101d657806366d003ac146101f65780636c19e7831461021657600080fd5b80633411dbdc146101035780633b716452146101405780633bbed4a01461016057806344bd37651461018257600080fd5b366100fe57005b600080fd5b34801561010f57600080fd5b50600454610123906001600160a01b031681565b6040516001600160a01b0390911681526020015b60405180910390f35b34801561014c57600080fd5b50600154610123906001600160a01b031681565b34801561016c57600080fd5b5061018061017b3660046117e5565b61031c565b005b34801561018e57600080fd5b5061018061019d366004611801565b610371565b3480156101ae57600080fd5b506101237f00000000000000000000000073a37b3eb030cc3f9739ca5c16b7e6802f29412281565b3480156101e257600080fd5b506101806101f1366004611a9b565b61042e565b34801561020257600080fd5b50600354610123906001600160a01b031681565b34801561022257600080fd5b506101806102313660046117e5565b6104ff565b34801561024257600080fd5b50610256610251366004611915565b61054b565b604051610137919061248f565b34801561026f57600080fd5b506101806106f5565b34801561028457600080fd5b50610180610293366004611aca565b61072b565b3480156102a457600080fd5b506000546001600160a01b0316610123565b6102566102c4366004611956565b610918565b3480156102d557600080fd5b506102566102e4366004611888565b610a4f565b3480156102f557600080fd5b506101806103043660046117e5565b610b86565b610180610317366004611a9b565b610c21565b6000546001600160a01b0316331461034f5760405162461bcd60e51b815260040161034690612042565b60405180910390fd5b600380546001600160a01b0319166001600160a01b0392909216919091179055565b336001600160a01b037f00000000000000000000000073a37b3eb030cc3f9739ca5c16b7e6802f29412216146103dc5760405162461bcd60e51b815260206004820152601060248201526f4f4e4c595f5649415f464143544f525960801b6044820152606401610346565b600180546001600160a01b038088166001600160a01b031992831617909255600286905560048054868416908316179055600380549285169290911691909117905561042781610b86565b5050505050565b6000546001600160a01b031633146104585760405162461bcd60e51b815260040161034690612042565b600082116104965760405162461bcd60e51b815260206004820152600b60248201526a11a921afa929231d18181960a91b6044820152606401610346565b6003546104ae9082906001600160a01b031684610d40565b604080516001600160a01b03831681526020810184905233918101919091527f5d760a2d1cc0892ddaea1748093916f51d345b37724db0f69b41574a92adc06f906060015b60405180910390a15050565b6000546001600160a01b031633146105295760405162461bcd60e51b815260040161034690612042565b600480546001600160a01b0319166001600160a01b0392909216919091179055565b610553611491565b6004546001600160a01b0316331461062a57600060405180608001604052808861057c90612609565b8152602001876001600160a01b03168152602001868152602001600254815250905060006105ca826040516020016105b491906122e7565b6040516020818303038152906040528686610d69565b6004549091506001600160a01b038083169116146106165760405162461bcd60e51b815260206004820152600960248201526802352435f463a3034360bc1b6044820152606401610346565b851561062757610627873388610d40565b50505b6040517fbd58fe74fd3111b8d37f5a35a025b9cec45d1f01f329eb4e1b86a1428d5d333790610660908890889088903390612455565b60405180910390a16001546040516301362a3560e71b81526001600160a01b0390911690639b151a80906106989089906004016120da565b61020060405180830381600087803b1580156106b357600080fd5b505af11580156106c7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106eb9190611997565b9695505050505050565b6000546001600160a01b0316331461071f5760405162461bcd60e51b815260040161034690612042565b6107296000610e12565b565b6004546001600160a01b0316331461084a5760006040518060a00160405280888152602001876001600160a01b03168152602001866001600160a01b03168152602001858152602001600254815250905060006107e9826040516020016107d39190815181526020808301516001600160a01b039081169183019190915260408084015190911690820152606080830151908201526080918201519181019190915260a00190565b6040516020818303038152906040528585610d69565b6004549091506001600160a01b038083169116146108365760405162461bcd60e51b815260206004820152600a60248201526902352435f524c3a3034360b41b6044820152606401610346565b841561084757610847863387610d40565b50505b604080518781526001600160a01b0387811660208301528616818301526060810185905233608082015290517f964a82560b2c071ffde7bc11cc169cfbef31770b4457ff7fa561d5e2bfb515439181900360a00190a1600154600354604051633cc6af3160e21b8152600481018990526001600160a01b038881166024830152918216604482015291169063f31abcc490606401600060405180830381600087803b1580156108f857600080fd5b505af115801561090c573d6000803e3d6000fd5b50505050505050505050565b610920611491565b6004546001600160a01b031633146109e1576000604051806080016040528088610949906126b7565b8152602001876001600160a01b0316815260200186815260200160025481525090506000610981826040516020016105b491906123a6565b6004549091506001600160a01b038083169116146109cd5760405162461bcd60e51b815260206004820152600960248201526802352435f503a3034360bc1b6044820152606401610346565b85156109de576109de873388610d40565b50505b6040517f583ad731037599752e477b0d462b10b6067dab73e63227960e20403a8ee7f7f090610a17908890889088903390612176565b60405180910390a1600154604051636ca2c9b960e11b81526001600160a01b039091169063d9459372906106989089906004016121b0565b610a57611491565b6004546001600160a01b03163314610b18576000604051806080016040528088610a8090612572565b8152602001876001600160a01b0316815260200186815260200160025481525090506000610ab8826040516020016105b4919061225b565b6004549091506001600160a01b03808316911614610b045760405162461bcd60e51b815260206004820152600960248201526802352435f433a3034360bc1b6044820152606401610346565b8515610b1557610b15873388610d40565b50505b6040517fdb26fb99c342114246dbc5580bb4d02519f9250cacf10aafc127b24f755d4e3f90610b4e908890889088903390612455565b60405180910390a1600154604051635f48d15d60e11b81526001600160a01b039091169063be91a2ba90610698908990600401612077565b6000546001600160a01b03163314610bb05760405162461bcd60e51b815260040161034690612042565b6001600160a01b038116610c155760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401610346565b610c1e81610e12565b50565b60008211610c5f5760405162461bcd60e51b815260206004820152600b60248201526a11a921afa0a9231d18181960a91b6044820152606401610346565b6001600160a01b038116610caf57813414610caa5760405162461bcd60e51b815260206004820152600b60248201526a2352435f4152463a30303560a81b6044820152606401610346565b610cf7565b3415610ceb5760405162461bcd60e51b815260206004820152600b60248201526a11a921afa0a9231d18181b60a91b6044820152606401610346565b610cf781333085610e62565b604080516001600160a01b03831681526020810184905233918101919091527f1104e763408245681528382e9b9fcd4d8f1b4bce2e83f5ce2be8d1a5ec8323a0906060016104f3565b6001600160a01b03831615610d5f57610d5a838383610e74565b505050565b610d5a8282610e7f565b6000610e08610dcc85805190602001206040517f19457468657265756d205369676e6564204d6573736167653a0a3332000000006020820152603c8101829052600090605c01604051602081830303815290604052805190602001209050919050565b84848080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250610e8d92505050565b90505b9392505050565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b610e6e84848484610f37565b50505050565b610d5a838383610fa2565b610e898282610fd2565b5050565b6000815160411415610ec15760208201516040830151606084015160001a610eb7868285856110eb565b9350505050610f31565b815160401415610ee95760208201516040830151610ee0858383611294565b92505050610f31565b60405162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e677468006044820152606401610346565b92915050565b6040516001600160a01b0380851660248301528316604482015260648101829052610e6e9085906323b872dd60e01b906084015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b0319909316929092179091526112b4565b6040516001600160a01b038316602482015260448101829052610d5a90849063a9059cbb60e01b90606401610f6b565b804710156110225760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a20696e73756666696369656e742062616c616e63650000006044820152606401610346565b6000826001600160a01b03168260405160006040518083038185875af1925050503d806000811461106f576040519150601f19603f3d011682016040523d82523d6000602084013e611074565b606091505b5050905080610d5a5760405162461bcd60e51b815260206004820152603a60248201527f416464726573733a20756e61626c6520746f2073656e642076616c75652c207260448201527f6563697069656e74206d617920686176652072657665727465640000000000006064820152608401610346565b60007f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a08211156111685760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c604482015261756560f01b6064820152608401610346565b8360ff16601b148061117d57508360ff16601c145b6111d45760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202776272076616c604482015261756560f01b6064820152608401610346565b6040805160008082526020820180845288905260ff871692820192909252606081018590526080810184905260019060a0016020604051602081039080840390855afa158015611228573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b03811661128b5760405162461bcd60e51b815260206004820152601860248201527f45434453413a20696e76616c6964207369676e617475726500000000000000006044820152606401610346565b95945050505050565b60006001600160ff1b03821660ff83901c601b016106eb868287856110eb565b6000611309826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166113869092919063ffffffff16565b805190915015610d5a57808060200190518101906113279190611868565b610d5a5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610346565b6060610e08848460008585843b6113df5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610346565b600080866001600160a01b031685876040516113fb9190612013565b60006040518083038185875af1925050503d8060008114611438576040519150601f19603f3d011682016040523d82523d6000602084013e61143d565b606091505b509150915061144d828286611458565b979650505050505050565b60608315611467575081610e0b565b8251156114775782518084602001fd5b8160405162461bcd60e51b8152600401610346919061202f565b6040805161020081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e08101829052610100810182905261012081018290526101408101829052610160810182905261018081018290526101a081018290526101c081018290526101e081019190915290565b8035611520816127d7565b919050565b8051611520816127d7565b60008083601f840112611541578182fd5b5081356001600160401b03811115611557578182fd5b60208301915083602082850101111561156f57600080fd5b9250929050565b600082601f830112611586578081fd5b81356001600160401b03808211156115a0576115a06127c1565b604051601f8301601f19908116603f011681019082821181831017156115c8576115c86127c1565b816040528381528660208588010111156115e0578485fd5b8360208701602083013792830160200193909352509392505050565b60006101a0828403121561160e578081fd5b61161661249e565b905061162182611515565b815261162f60208301611515565b602082015261164060408301611515565b604082015261165160608301611515565b606082015261166260808301611515565b608082015261167360a08301611515565b60a082015261168460c08301611515565b60c082015261169560e08301611515565b60e08201526101006116a8818401611515565b908201526101208281013590820152610140808301359082015261016080830135908201526101809182013591810191909152919050565b600061020082840312156116f2578081fd5b6116fa6124c7565b905061170582611515565b815261171360208301611515565b602082015261172460408301611515565b604082015261173560608301611515565b606082015261174660808301611515565b608082015261175760a08301611515565b60a082015261176860c08301611515565b60c082015261177960e08301611515565b60e082015261010061178c818401611515565b9082015261012082810135908201526101408083013590820152610160808301359082015261018080830135908201526101a080830135908201526101c080830135908201526101e09182013591810191909152919050565b6000602082840312156117f6578081fd5b8135610e0b816127d7565b600080600080600060a08688031215611818578081fd5b8535611823816127d7565b945060208601359350604086013561183a816127d7565b9250606086013561184a816127d7565b9150608086013561185a816127d7565b809150509295509295909350565b600060208284031215611879578081fd5b81518015158114610e0b578182fd5b60008060008060006080868803121561189f578283fd5b85356001600160401b03808211156118b5578485fd5b90870190610240828a0312156118c9578485fd5b9095506020870135906118db826127d7565b90945060408701359350606087013590808211156118f7578283fd5b5061190488828901611530565b969995985093965092949392505050565b60008060008060006080868803121561192c578283fd5b85356001600160401b0380821115611942578485fd5b90870190610280828a0312156118c9578485fd5b60008060008060006080868803121561196d578283fd5b85356001600160401b0380821115611983578485fd5b90870190610260828a0312156118c9578485fd5b600061020082840312156119a9578081fd5b6119b16124c7565b6119ba83611525565b81526119c860208401611525565b60208201526119d960408401611525565b60408201526119ea60608401611525565b60608201526119fb60808401611525565b6080820152611a0c60a08401611525565b60a0820152611a1d60c08401611525565b60c0820152611a2e60e08401611525565b60e0820152610100611a41818501611525565b9082015261012083810151908201526101408084015190820152610160808401519082015261018080840151908201526101a080840151908201526101c080840151908201526101e0928301519281019290925250919050565b60008060408385031215611aad578182fd5b823591506020830135611abf816127d7565b809150509250929050565b60008060008060008060a08789031215611ae2578384fd5b863595506020870135611af4816127d7565b94506040870135611b04816127d7565b93506060870135925060808701356001600160401b03811115611b25578182fd5b611b3189828a01611530565b979a9699509497509295939492505050565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b60008151808452611b84816020860160208601612795565b601f01601f19169290920160200192915050565b611bb282611ba583611515565b6001600160a01b03169052565b611bbe60208201611515565b6001600160a01b03166020830152611bd860408201611515565b6001600160a01b03166040830152611bf260608201611515565b6001600160a01b03166060830152611c0c60808201611515565b6001600160a01b03166080830152611c2660a08201611515565b6001600160a01b031660a0830152611c4060c08201611515565b6001600160a01b031660c0830152611c5a60e08201611515565b6001600160a01b031660e0830152610100611c76828201611515565b6001600160a01b03169083015261012081810135908301526101408082013590830152610160808201359083015261018090810135910152565b80516001600160a01b031682526020810151611cd760208401826001600160a01b03169052565b506040810151611cf260408401826001600160a01b03169052565b506060810151611d0d60608401826001600160a01b03169052565b506080810151611d2860808401826001600160a01b03169052565b5060a0810151611d4360a08401826001600160a01b03169052565b5060c0810151611d5e60c08401826001600160a01b03169052565b5060e0810151611d7960e08401826001600160a01b03169052565b50610100818101516001600160a01b03169083015261012080820151908301526101408082015190830152610160808201519083015261018090810151910152565b611dc882611ba583611515565b611dd460208201611515565b6001600160a01b03166020830152611dee60408201611515565b6001600160a01b03166040830152611e0860608201611515565b6001600160a01b03166060830152611e2260808201611515565b6001600160a01b03166080830152611e3c60a08201611515565b6001600160a01b031660a0830152611e5660c08201611515565b6001600160a01b031660c0830152611e7060e08201611515565b6001600160a01b031660e0830152610100611e8c828201611515565b6001600160a01b03169083015261012081810135908301526101408082013590830152610160808201359083015261018080820135908301526101a080820135908301526101c080820135908301526101e090810135910152565b80516001600160a01b031682526020810151611f0e60208401826001600160a01b03169052565b506040810151611f2960408401826001600160a01b03169052565b506060810151611f4460608401826001600160a01b03169052565b506080810151611f5f60808401826001600160a01b03169052565b5060a0810151611f7a60a08401826001600160a01b03169052565b5060c0810151611f9560c08401826001600160a01b03169052565b5060e0810151611fb060e08401826001600160a01b03169052565b50610100818101516001600160a01b03169083015261012080820151908301526101408082015190830152610160808201519083015261018080820151908301526101a080820151908301526101c080820151908301526101e090810151910152565b60008251612025818460208701612795565b9190910192915050565b602081526000610e0b6020830184611b6c565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b602081526120886020820183611dbb565b600061209861020084018461252e565b61024061022081818701526120b261026087018486611b43565b93506120c08188018861252e565b878603601f1901848901529350905061144d848483611b43565b602081526120eb6020820183611dbb565b6000610220610200840135818401526121068185018561252e565b915061028061024081818701526121226102a087018585611b43565b93506121308188018861252e565b93509050601f1961026081888703018189015261214e868685611b43565b955061215c818a018a61252e565b95509250508087860301838801525061144d848483611b43565b61020081016121858287611b98565b6001600160a01b039485166101a08301526101c082019390935292166101e090920191909152919050565b602081526121c16020820183611b98565b60006101c06101a0840135818401526101e081850135818501526121e78186018661252e565b92509050610260610200818187015261220561028087018585611b43565b93506122138188018861252e565b93509050601f19610220818887030181890152612231868685611b43565b955061223f818a018a61252e565b955092505061024081888703018189015261214e868685611b43565b60208152600082516080602084015261227860a084018251611ee7565b60208101516102406102a08501526122946102e0850182611b6c565b905060408201519150609f19848203016102c08501526122b48183611b6c565b91505060018060a01b03602085015116604084015260408401516060840152606084015160808401528091505092915050565b60208152600082516080602084015261230460a084018251611ee7565b60208101516102a084015260408101516102806102c085015261232b610320850182611b6c565b90506060820151609f1980868403016102e087015261234a8383611b6c565b9250608084015193508086840301610300870152505061236a8183611b6c565b915050602084015161238760408501826001600160a01b03169052565b5060408401516060840152606084015160808401528091505092915050565b6020815260008251608060208401526123c360a084018251611cb0565b6020810151610240840152604081015161026081818601526060830151915080610280860152506123f8610300850182611b6c565b90506080820151609f1980868403016102a08701526124178383611b6c565b925060a0840151915080868403016102c08701526124358383611b6c565b925060c0840151935080868403016102e0870152505061236a8183611b6c565b61026081016124648287611dbb565b6001600160a01b03948516610200830152610220820193909352921661024090920191909152919050565b6102008101610f318284611ee7565b6040516101a081016001600160401b03811182821017156124c1576124c16127c1565b60405290565b60405161020081016001600160401b03811182821017156124c1576124c16127c1565b60405160a081016001600160401b03811182821017156124c1576124c16127c1565b60405160e081016001600160401b03811182821017156124c1576124c16127c1565b6000808335601e19843603018112612544578283fd5b83016020810192503590506001600160401b0381111561256357600080fd5b80360383131561156f57600080fd5b60006102408236031215612584578081fd5b604051606081016001600160401b0382821081831117156125a7576125a76127c1565b816040526125b536866116e0565b83526102008501359150808211156125cb578384fd5b6125d736838701611576565b60208401526102208501359150808211156125f0578384fd5b506125fd36828601611576565b60408301525092915050565b6000610280823603121561261b578081fd5b6126236124ea565b61262d36846116e0565b815261020083013560208201526102208301356001600160401b0380821115612654578384fd5b61266036838701611576565b6040840152610240850135915080821115612679578384fd5b61268536838701611576565b606084015261026085013591508082111561269e578384fd5b506126ab36828601611576565b60808301525092915050565b600061026082360312156126c9578081fd5b6126d161250c565b6126db36846115fc565b81526101a083013560208201526101c083013560408201526101e08301356001600160401b038082111561270d578384fd5b61271936838701611576565b6060840152610200850135915080821115612732578384fd5b61273e36838701611576565b6080840152610220850135915080821115612757578384fd5b61276336838701611576565b60a084015261024085013591508082111561277c578384fd5b5061278936828601611576565b60c08301525092915050565b60005b838110156127b0578181015183820152602001612798565b83811115610e6e5750506000910152565b634e487b7160e01b600052604160045260246000fd5b6001600160a01b0381168114610c1e57600080fdfea264697066735822122067312d6472fee91db6d3c198cbe2daf6612c356fe9ecf639d436483a7bbe479d64736f6c63430008040033
Deployed Bytecode Sourcemap
60308:8026:0:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;60498:27;;;;;;;;;;-1:-1:-1;60498:27:0;;;;-1:-1:-1;;;;;60498:27:0;;;;;;-1:-1:-1;;;;;18939:32:1;;;;18921:51;;18909:2;18894:18;60498:27:0;;;;;;;;60385:45;;;;;;;;;;-1:-1:-1;60385:45:0;;;;-1:-1:-1;;;;;60385:45:0;;;62766:96;;;;;;;;;;-1:-1:-1;62766:96:0;;;;;:::i;:::-;;:::i;:::-;;62391:369;;;;;;;;;;-1:-1:-1;62391:369:0;;;;;:::i;:::-;;:::i;60340:38::-;;;;;;;;;;;;;;;63546:347;;;;;;;;;;-1:-1:-1;63546:347:0;;;;;:::i;:::-;;:::i;60467:24::-;;;;;;;;;;-1:-1:-1;60467:24:0;;;;-1:-1:-1;;;;;60467:24:0;;;62868:102;;;;;;;;;;-1:-1:-1;62868:102:0;;;;;:::i;:::-;;:::i;65871:948::-;;;;;;;;;;-1:-1:-1;65871:948:0;;;;;:::i;:::-;;:::i;:::-;;;;;;;:::i;39220:94::-;;;;;;;;;;;;;:::i;63899:995::-;;;;;;;;;;-1:-1:-1;63899:995:0;;;;;:::i;:::-;;:::i;38569:87::-;;;;;;;;;;-1:-1:-1;38615:7:0;38642:6;-1:-1:-1;;;;;38642:6:0;38569:87;;64900:965;;;;;;:::i;:::-;;:::i;66825:942::-;;;;;;;;;;-1:-1:-1;66825:942:0;;;;;:::i;:::-;;:::i;39469:192::-;;;;;;;;;;-1:-1:-1;39469:192:0;;;;;:::i;:::-;;:::i;62976:564::-;;;;;;:::i;:::-;;:::i;62766:96::-;38615:7;38642:6;-1:-1:-1;;;;;38642:6:0;37433:10;38789:23;38781:68;;;;-1:-1:-1;;;38781:68:0;;;;;;;:::i;:::-;;;;;;;;;62834:9:::1;:22:::0;;-1:-1:-1;;;;;;62834:22:0::1;-1:-1:-1::0;;;;;62834:22:0;;;::::1;::::0;;;::::1;::::0;;62766:96::o;62391:369::-;62337:13;-1:-1:-1;;;;;62323:27:0;:10;:27;62315:56;;;;-1:-1:-1;;;62315:56:0;;22544:2:1;62315:56:0;;;22526:21:1;22583:2;22563:18;;;22556:30;-1:-1:-1;;;22602:18:1;;;22595:46;22658:18;;62315:56:0;22516:166:1;62315:56:0;62572:18:::1;:61:::0;;-1:-1:-1;;;;;62572:61:0;;::::1;-1:-1:-1::0;;;;;;62572:61:0;;::::1;;::::0;;;62640:7:::1;:18:::0;;;62665:12:::1;:28:::0;;;;::::1;::::0;;::::1;;::::0;;62700:9:::1;:22:::0;;;;::::1;::::0;;;::::1;::::0;;;::::1;::::0;;62729:25:::1;62747:6:::0;62729:17:::1;:25::i;:::-;62391:369:::0;;;;;:::o;63546:347::-;38615:7;38642:6;-1:-1:-1;;;;;38642:6:0;37433:10;38789:23;38781:68;;;;-1:-1:-1;;;38781:68:0;;;;;;;:::i;:::-;63686:1:::1;63677:6;:10;63669:34;;;::::0;-1:-1:-1;;;63669:34:0;;26241:2:1;63669:34:0::1;::::0;::::1;26223:21:1::0;26280:2;26260:18;;;26253:30;-1:-1:-1;;;26299:18:1;;;26292:41;26350:18;;63669:34:0::1;26213:161:1::0;63669:34:0::1;63789:9;::::0;63749:59:::1;::::0;63772:7;;-1:-1:-1;;;;;63789:9:0::1;63801:6:::0;63749:22:::1;:59::i;:::-;63841:46;::::0;;-1:-1:-1;;;;;19900:15:1;;19882:34;;19947:2;19932:18;;19925:34;;;63876:10:0::1;19975:18:1::0;;;19968:43;;;;63841:46:0::1;::::0;19832:2:1;19817:18;63841:46:0::1;;;;;;;;63546:347:::0;;:::o;62868:102::-;38615:7;38642:6;-1:-1:-1;;;;;38642:6:0;37433:10;38789:23;38781:68;;;;-1:-1:-1;;;38781:68:0;;;;;;;:::i;:::-;62936:12:::1;:28:::0;;-1:-1:-1;;;;;;62936:28:0::1;-1:-1:-1::0;;;;;62936:28:0;;;::::1;::::0;;;::::1;::::0;;62868:102::o;65871:948::-;66061:42;;:::i;:::-;66130:12;;-1:-1:-1;;;;;66130:12:0;66116:10;:26;66112:571;;66153:32;66188:176;;;;;;;;66223:4;66188:176;;;:::i;:::-;;;;;66261:21;-1:-1:-1;;;;;66188:176:0;;;;;66311:16;66188:176;;;;66347:7;;66188:176;;;66153:211;;66375:17;66395:48;66423:7;66412:19;;;;;;;;:::i;:::-;;;;;;;;;;;;;66433:9;;66395:16;:48::i;:::-;66473:12;;66375:68;;-1:-1:-1;;;;;;66460:25:0;;;66473:12;;66460:25;66452:47;;;;-1:-1:-1;;;66452:47:0;;24824:2:1;66452:47:0;;;24806:21:1;24863:1;24843:18;;;24836:29;-1:-1:-1;;;24881:18:1;;;24874:39;24930:18;;66452:47:0;24796:158:1;66452:47:0;66549:20;;66545:131;;66582:84;66605:21;66636:10;66649:16;66582:22;:84::i;:::-;66112:571;;;66694:73;;;;;;66702:4;;66715:21;;66738:16;;66756:10;;66694:73;:::i;:::-;;;;;;;;66781:18;;:32;;-1:-1:-1;;;66781:32:0;;-1:-1:-1;;;;;66781:18:0;;;;:26;;:32;;66808:4;;66781:32;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;66774:39;65871:948;-1:-1:-1;;;;;;65871:948:0:o;39220:94::-;38615:7;38642:6;-1:-1:-1;;;;;38642:6:0;37433:10;38789:23;38781:68;;;;-1:-1:-1;;;38781:68:0;;;;;;;:::i;:::-;39285:21:::1;39303:1;39285:9;:21::i;:::-;39220:94::o:0;63899:995::-;64104:12;;-1:-1:-1;;;;;64104:12:0;64090:10;:26;64086:619;;64127:40;64170:215;;;;;;;;64215:6;64170:215;;;;64241:7;-1:-1:-1;;;;;64170:215:0;;;;;64282:21;-1:-1:-1;;;;;64170:215:0;;;;;64332:16;64170:215;;;;64368:7;;64170:215;;;64127:258;;64396:17;64416:48;64444:7;64433:19;;;;;;;36560:13:1;;36542:32;;36621:4;36609:17;;;36603:24;-1:-1:-1;;;;;36703:21:1;;;36681:20;;;36674:51;;;;36785:4;36773:17;;;36767:24;36763:33;;;36741:20;;;36734:63;36853:4;36841:17;;;36835:24;36813:20;;;36806:54;36916:4;36904:17;;;36898:24;36876:20;;;36869:54;;;;36529:3;36514:19;;36496:433;64433:19:0;;;;;;;;;;;;;64454:9;;64416:16;:48::i;:::-;64494:12;;64396:68;;-1:-1:-1;;;;;;64481:25:0;;;64494:12;;64481:25;64473:48;;;;-1:-1:-1;;;64473:48:0;;27282:2:1;64473:48:0;;;27264:21:1;27321:2;27301:18;;;27294:30;-1:-1:-1;;;27340:18:1;;;27333:40;27390:18;;64473:48:0;27254:160:1;64473:48:0;64571:20;;64567:131;;64604:84;64627:21;64658:10;64671:16;64604:22;:84::i;:::-;64086:619;;;64718:85;;;38424:25:1;;;-1:-1:-1;;;;;38523:15:1;;;38518:2;38503:18;;38496:43;38575:15;;38555:18;;;38548:43;38622:2;38607:18;;38600:34;;;64792:10:0;38665:3:1;38650:19;;38643:44;64718:85:0;;;;;;;-1:-1:-1;64718:85:0;;;64817:18;;64877:9;;64817:71;;-1:-1:-1;;;64817:71:0;;;;;37987:25:1;;;-1:-1:-1;;;;;38086:15:1;;;38066:18;;;38059:43;64877:9:0;;;38118:18:1;;;38111:43;64817:18:0;;;:34;;37960:18:1;;64817:71:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;63899:995;;;;;;:::o;64900:965::-;65098:42;;:::i;:::-;65167:12;;-1:-1:-1;;;;;65167:12:0;65153:10;:26;65149:571;;65190:32;65225:176;;;;;;;;65260:4;65225:176;;;:::i;:::-;;;;;65298:21;-1:-1:-1;;;;;65225:176:0;;;;;65348:16;65225:176;;;;65384:7;;65225:176;;;65190:211;;65412:17;65432:48;65460:7;65449:19;;;;;;;;:::i;65432:48::-;65510:12;;65412:68;;-1:-1:-1;;;;;;65497:25:0;;;65510:12;;65497:25;65489:47;;;;-1:-1:-1;;;65489:47:0;;25161:2:1;65489:47:0;;;25143:21:1;25200:1;25180:18;;;25173:29;-1:-1:-1;;;25218:18:1;;;25211:39;25267:18;;65489:47:0;25133:158:1;65489:47:0;65586:20;;65582:131;;65619:84;65642:21;65673:10;65686:16;65619:22;:84::i;:::-;65149:571;;;65733:80;;;;;;65741:4;;65761:21;;65784:16;;65802:10;;65733:80;:::i;:::-;;;;;;;;65827:18;;:32;;-1:-1:-1;;;65827:32:0;;-1:-1:-1;;;;;65827:18:0;;;;:26;;:32;;65854:4;;65827:32;;;:::i;66825:942::-;67013:42;;:::i;:::-;67082:12;;-1:-1:-1;;;;;67082:12:0;67068:10;:26;67064:569;;67105:31;67139:175;;;;;;;;67173:4;67139:175;;;:::i;:::-;;;;;67211:21;-1:-1:-1;;;;;67139:175:0;;;;;67261:16;67139:175;;;;67297:7;;67139:175;;;67105:209;;67325:17;67345:48;67373:7;67362:19;;;;;;;;:::i;67345:48::-;67423:12;;67325:68;;-1:-1:-1;;;;;;67410:25:0;;;67423:12;;67410:25;67402:47;;;;-1:-1:-1;;;67402:47:0;;22207:2:1;67402:47:0;;;22189:21:1;22246:1;22226:18;;;22219:29;-1:-1:-1;;;22264:18:1;;;22257:39;22313:18;;67402:47:0;22179:158:1;67402:47:0;67499:20;;67495:131;;67532:84;67555:21;67586:10;67599:16;67532:22;:84::i;:::-;67064:569;;;67644:72;;;;;;67651:4;;67664:21;;67687:16;;67705:10;;67644:72;:::i;:::-;;;;;;;;67730:18;;:31;;-1:-1:-1;;;67730:31:0;;-1:-1:-1;;;;;67730:18:0;;;;:25;;:31;;67756:4;;67730:31;;;:::i;39469:192::-;38615:7;38642:6;-1:-1:-1;;;;;38642:6:0;37433:10;38789:23;38781:68;;;;-1:-1:-1;;;38781:68:0;;;;;;;:::i;:::-;-1:-1:-1;;;;;39558:22:0;::::1;39550:73;;;::::0;-1:-1:-1;;;39550:73:0;;21800:2:1;39550:73:0::1;::::0;::::1;21782:21:1::0;21839:2;21819:18;;;21812:30;21878:34;21858:18;;;21851:62;-1:-1:-1;;;21929:18:1;;;21922:36;21975:19;;39550:73:0::1;21772:228:1::0;39550:73:0::1;39634:19;39644:8;39634:9;:19::i;:::-;39469:192:::0;:::o;62976:564::-;63111:1;63102:6;:10;63094:34;;;;-1:-1:-1;;;63094:34:0;;25498:2:1;63094:34:0;;;25480:21:1;25537:2;25517:18;;;25510:30;-1:-1:-1;;;25556:18:1;;;25549:41;25607:18;;63094:34:0;25470:161:1;63094:34:0;-1:-1:-1;;;;;33291:25:0;;63221:237;;63288:6;63275:9;:19;63267:43;;;;-1:-1:-1;;;63267:43:0;;24484:2:1;63267:43:0;;;24466:21:1;24523:2;24503:18;;;24496:30;-1:-1:-1;;;24542:18:1;;;24535:41;24593:18;;63267:43:0;24456:161:1;63267:43:0;63221:237;;;63341:9;:14;63333:38;;;;-1:-1:-1;;;63333:38:0;;26942:2:1;63333:38:0;;;26924:21:1;26981:2;26961:18;;;26954:30;-1:-1:-1;;;27000:18:1;;;26993:41;27051:18;;63333:38:0;26914:161:1;63333:38:0;63380:70;63407:7;63416:10;63436:4;63443:6;63380:26;:70::i;:::-;63490:44;;;-1:-1:-1;;;;;19900:15:1;;19882:34;;19947:2;19932:18;;19925:34;;;63523:10:0;19975:18:1;;;19968:43;;;;63490:44:0;;19832:2:1;19817:18;63490:44:0;19799:218:1;36464:252:0;-1:-1:-1;;;;;33291:25:0;;;36589:121;;36669:41;36683:7;36692:9;36703:6;36669:13;:41::i;:::-;36464:252;;;:::o;36589:121::-;36621:38;36641:9;36652:6;36621:19;:38::i;68066:230::-;68170:7;68209:81;68223:55;68262:14;68252:25;;;;;;59621:58;;18422:66:1;59621:58:0;;;18410:79:1;18505:12;;;18498:28;;;59488:7:0;;18542:12:1;;59621:58:0;;;;;;;;;;;;59611:69;;;;;;59604:76;;59419:269;;;;68223:55;68280:9;;68209:81;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;68209:13:0;;-1:-1:-1;;;68209:81:0:i;:::-;68202:88;;68066:230;;;;;;:::o;39669:173::-;39725:16;39744:6;;-1:-1:-1;;;;;;39761:17:0;;-1:-1:-1;;;;;39761:17:0;;;;;;;;39794:40;;39744:6;;;39761:17;;39744:6;;39794:40;;;39669:173;;:::o;34814:194::-;34941:61;34975:7;34985:4;34991:2;34995:6;34941:26;:61::i;:::-;34814:194;;;;:::o;34357:181::-;34474:58;34504:7;34514:9;34525:6;34474:22;:58::i;33958:140::-;34056:36;34074:9;34085:6;34056:17;:36::i;:::-;33958:140;;:::o;55575:1270::-;55653:7;55873:9;:16;55893:2;55873:22;55869:969;;;56169:4;56154:20;;56148:27;56219:4;56204:20;;56198:27;56277:4;56262:20;;56256:27;55912:9;56248:36;56320:22;56328:4;56248:36;56148:27;56198;56320:7;:22::i;:::-;56313:29;;;;;;;55869:969;56364:9;:16;56384:2;56364:22;56360:478;;;56639:4;56624:20;;56618:27;56690:4;56675:20;;56669:27;56732:20;56740:4;56618:27;56669;56732:7;:20::i;:::-;56725:27;;;;;;56360:478;56785:41;;-1:-1:-1;;;56785:41:0;;21440:2:1;56785:41:0;;;21422:21:1;21479:2;21459:18;;;21452:30;21518:33;21498:18;;;21491:61;21569:18;;56785:41:0;21412:181:1;56360:478:0;55575:1270;;;;:::o;19141:248::-;19312:68;;-1:-1:-1;;;;;19241:15:1;;;19312:68:0;;;19223:34:1;19293:15;;19273:18;;;19266:43;19325:18;;;19318:34;;;19285:96:0;;19305:5;;-1:-1:-1;;;;19158:18:1;;19312:68:0;;;;-1:-1:-1;;19312:68:0;;;;;;;;;;;;;;-1:-1:-1;;;;;19312:68:0;-1:-1:-1;;;;;;19312:68:0;;;;;;;;;;;19285:19;:96::i;18922:211::-;19066:58;;-1:-1:-1;;;;;19555:32:1;;19066:58:0;;;19537:51:1;19604:18;;;19597:34;;;19039:86:0;;19059:5;;-1:-1:-1;;;;19510:18:1;;19066:58:0;19492:145:1;12497:317:0;12612:6;12587:21;:31;;12579:73;;;;-1:-1:-1;;;12579:73:0;;23719:2:1;12579:73:0;;;23701:21:1;23758:2;23738:18;;;23731:30;23797:31;23777:18;;;23770:59;23846:18;;12579:73:0;23691:179:1;12579:73:0;12684:33;;12666:12;;-1:-1:-1;;;;;12684:14:0;;;12706:6;;12666:12;12684:33;12666:12;12684:33;12706:6;12684:14;:33;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;12665:52;;;12736:7;12728:78;;;;-1:-1:-1;;;12728:78:0;;22889:2:1;12728:78:0;;;22871:21:1;22928:2;22908:18;;;22901:30;22967:34;22947:18;;;22940:62;23038:28;23018:18;;;23011:56;23084:19;;12728:78:0;22861:248:1;57607:1512:0;57735:7;58674:66;58660:80;;;58638:164;;;;-1:-1:-1;;;58638:164:0;;23316:2:1;58638:164:0;;;23298:21:1;23355:2;23335:18;;;23328:30;23394:34;23374:18;;;23367:62;-1:-1:-1;;;23445:18:1;;;23438:32;23487:19;;58638:164:0;23288:224:1;58638:164:0;58821:1;:7;;58826:2;58821:7;:18;;;;58832:1;:7;;58837:2;58832:7;58821:18;58813:65;;;;-1:-1:-1;;;58813:65:0;;25838:2:1;58813:65:0;;;25820:21:1;25877:2;25857:18;;;25850:30;25916:34;25896:18;;;25889:62;-1:-1:-1;;;25967:18:1;;;25960:32;26009:19;;58813:65:0;25810:224:1;58813:65:0;58993:24;;;58976:14;58993:24;;;;;;;;;20249:25:1;;;20322:4;20310:17;;20290:18;;;20283:45;;;;20344:18;;;20337:34;;;20387:18;;;20380:34;;;58993:24:0;;20221:19:1;;58993:24:0;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;58993:24:0;;-1:-1:-1;;58993:24:0;;;-1:-1:-1;;;;;;;59036:20:0;;59028:57;;;;-1:-1:-1;;;59028:57:0;;21087:2:1;59028:57:0;;;21069:21:1;21126:2;21106:18;;;21099:30;21165:26;21145:18;;;21138:54;21209:18;;59028:57:0;21059:174:1;59028:57:0;59105:6;57607:1512;-1:-1:-1;;;;;57607:1512:0:o;57105:371::-;57216:7;-1:-1:-1;;;;;57303:75:0;;57415:2;-1:-1:-1;57401:12:0;;;57397:21;57446:22;57454:4;57397:21;57463:1;57303:75;57446:7;:22::i;21495:716::-;21945:69;;;;;;;;;;;;;;;;;;21919:23;;21945:69;;-1:-1:-1;;;;;21945:27:0;;;21973:4;;21945:27;:69::i;:::-;22029:17;;21919:95;;-1:-1:-1;22029:21:0;22025:179;;22126:10;22115:30;;;;;;;;;;;;:::i;:::-;22107:85;;;;-1:-1:-1;;;22107:85:0;;27979:2:1;22107:85:0;;;27961:21:1;28018:2;27998:18;;;27991:30;28057:34;28037:18;;;28030:62;-1:-1:-1;;;28108:18:1;;;28101:40;28158:19;;22107:85:0;27951:232:1;13981:229:0;14118:12;14150:52;14172:6;14180:4;14186:1;14189:12;14118;11498:20;;15388:60;;;;-1:-1:-1;;;15388:60:0;;27621:2:1;15388:60:0;;;27603:21:1;27660:2;27640:18;;;27633:30;27699:31;27679:18;;;27672:59;27748:18;;15388:60:0;27593:179:1;15388:60:0;15462:12;15476:23;15503:6;-1:-1:-1;;;;;15503:11:0;15522:5;15529:4;15503:31;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;15461:73;;;;15552:52;15570:7;15579:10;15591:12;15552:17;:52::i;:::-;15545:59;15101:511;-1:-1:-1;;;;;;;15101:511:0:o;17570:712::-;17720:12;17749:7;17745:530;;;-1:-1:-1;17780:10:0;17773:17;;17745:530;17894:17;;:21;17890:374;;18092:10;18086:17;18153:15;18140:10;18136:2;18132:19;18125:44;18040:148;18228:20;;-1:-1:-1;;;18228:20:0;;;;18235:12;;18228:20;;;:::i;-1:-1:-1:-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;14:134:1:-;82:20;;111:31;82:20;111:31;:::i;:::-;63:85;;;:::o;153:138::-;232:13;;254:31;232:13;254:31;:::i;296:375::-;347:8;357:6;411:3;404:4;396:6;392:17;388:27;378:2;;436:8;426;419:26;378:2;-1:-1:-1;466:20:1;;-1:-1:-1;;;;;498:30:1;;495:2;;;548:8;538;531:26;495:2;592:4;584:6;580:17;568:29;;644:3;637:4;628:6;620;616:19;612:30;609:39;606:2;;;661:1;658;651:12;606:2;368:303;;;;;:::o;676:738::-;718:5;771:3;764:4;756:6;752:17;748:27;738:2;;793:5;786;779:20;738:2;820:20;;-1:-1:-1;;;;;889:10:1;;;886:2;;;902:18;;:::i;:::-;977:2;971:9;945:2;1031:13;;-1:-1:-1;;1027:22:1;;;1051:2;1023:31;1019:40;1007:53;;;1075:18;;;1095:22;;;1072:46;1069:2;;;1121:18;;:::i;:::-;1161:10;1157:2;1150:22;1196:2;1188:6;1181:18;1242:3;1235:4;1230:2;1222:6;1218:15;1214:26;1211:35;1208:2;;;1263:5;1256;1249:20;1208:2;1331;1324:4;1316:6;1312:17;1305:4;1297:6;1293:17;1280:54;1354:15;;;1371:4;1350:26;1343:41;;;;-1:-1:-1;1358:6:1;728:686;-1:-1:-1;;;728:686:1:o;1419:1191::-;1490:5;1538:6;1526:9;1521:3;1517:19;1513:32;1510:2;;;1562:5;1555;1548:20;1510:2;1588:17;;:::i;:::-;1579:26;;1628:29;1647:9;1628:29;:::i;:::-;1621:5;1614:44;1690:38;1724:2;1713:9;1709:18;1690:38;:::i;:::-;1685:2;1678:5;1674:14;1667:62;1761:38;1795:2;1784:9;1780:18;1761:38;:::i;:::-;1756:2;1749:5;1745:14;1738:62;1832:38;1866:2;1855:9;1851:18;1832:38;:::i;:::-;1827:2;1820:5;1816:14;1809:62;1904:39;1938:3;1927:9;1923:19;1904:39;:::i;:::-;1898:3;1891:5;1887:15;1880:64;1977:39;2011:3;2000:9;1996:19;1977:39;:::i;:::-;1971:3;1964:5;1960:15;1953:64;2050:39;2084:3;2073:9;2069:19;2050:39;:::i;:::-;2044:3;2037:5;2033:15;2026:64;2123:39;2157:3;2146:9;2142:19;2123:39;:::i;:::-;2117:3;2110:5;2106:15;2099:64;2182:3;2217:38;2251:2;2240:9;2236:18;2217:38;:::i;:::-;2201:14;;;2194:62;2275:3;2323:18;;;2310:32;2294:14;;;2287:56;2362:3;2410:18;;;2397:32;2381:14;;;2374:56;2449:3;2497:18;;;2484:32;2468:14;;;2461:56;2536:3;2584:18;;;2571:32;2555:14;;;2548:56;;;;2205:5;1500:1110;-1:-1:-1;1500:1110:1:o;2615:1448::-;2677:5;2725:6;2713:9;2708:3;2704:19;2700:32;2697:2;;;2749:5;2742;2735:20;2697:2;2775:22;;:::i;:::-;2766:31;;2820:29;2839:9;2820:29;:::i;:::-;2813:5;2806:44;2882:38;2916:2;2905:9;2901:18;2882:38;:::i;:::-;2877:2;2870:5;2866:14;2859:62;2953:38;2987:2;2976:9;2972:18;2953:38;:::i;:::-;2948:2;2941:5;2937:14;2930:62;3024:38;3058:2;3047:9;3043:18;3024:38;:::i;:::-;3019:2;3012:5;3008:14;3001:62;3096:39;3130:3;3119:9;3115:19;3096:39;:::i;:::-;3090:3;3083:5;3079:15;3072:64;3169:39;3203:3;3192:9;3188:19;3169:39;:::i;:::-;3163:3;3156:5;3152:15;3145:64;3242:39;3276:3;3265:9;3261:19;3242:39;:::i;:::-;3236:3;3229:5;3225:15;3218:64;3315:39;3349:3;3338:9;3334:19;3315:39;:::i;:::-;3309:3;3302:5;3298:15;3291:64;3374:3;3409:38;3443:2;3432:9;3428:18;3409:38;:::i;:::-;3393:14;;;3386:62;3467:3;3515:18;;;3502:32;3486:14;;;3479:56;3554:3;3602:18;;;3589:32;3573:14;;;3566:56;3641:3;3689:18;;;3676:32;3660:14;;;3653:56;3728:3;3776:18;;;3763:32;3747:14;;;3740:56;3815:3;3863:18;;;3850:32;3834:14;;;3827:56;3902:3;3950:18;;;3937:32;3921:14;;;3914:56;3989:3;4037:18;;;4024:32;4008:14;;;4001:56;;;;3397:5;2687:1376;-1:-1:-1;2687:1376:1:o;4068:257::-;4127:6;4180:2;4168:9;4159:7;4155:23;4151:32;4148:2;;;4201:6;4193;4186:22;4148:2;4245:9;4232:23;4264:31;4289:5;4264:31;:::i;4330:750::-;4425:6;4433;4441;4449;4457;4510:3;4498:9;4489:7;4485:23;4481:33;4478:2;;;4532:6;4524;4517:22;4478:2;4576:9;4563:23;4595:31;4620:5;4595:31;:::i;:::-;4645:5;-1:-1:-1;4697:2:1;4682:18;;4669:32;;-1:-1:-1;4753:2:1;4738:18;;4725:32;4766:33;4725:32;4766:33;:::i;:::-;4818:7;-1:-1:-1;4877:2:1;4862:18;;4849:32;4890:33;4849:32;4890:33;:::i;:::-;4942:7;-1:-1:-1;5001:3:1;4986:19;;4973:33;5015;4973;5015;:::i;:::-;5067:7;5057:17;;;4468:612;;;;;;;;:::o;5085:297::-;5152:6;5205:2;5193:9;5184:7;5180:23;5176:32;5173:2;;;5226:6;5218;5211:22;5173:2;5263:9;5257:16;5316:5;5309:13;5302:21;5295:5;5292:32;5282:2;;5343:6;5335;5328:22;5387:942;5514:6;5522;5530;5538;5546;5599:3;5587:9;5578:7;5574:23;5570:33;5567:2;;;5621:6;5613;5606:22;5567:2;5653:23;;-1:-1:-1;;;;;5725:14:1;;;5722:2;;;5757:6;5749;5742:22;5722:2;5785:22;;;;5841:3;5823:16;;;5819:26;5816:2;;;5863:6;5855;5848:22;5816:2;5891;;-1:-1:-1;5943:2:1;5928:18;;5915:32;;5956:31;5915:32;5956:31;:::i;:::-;6006:5;;-1:-1:-1;6058:2:1;6043:18;;6030:32;;-1:-1:-1;6115:2:1;6100:18;;6087:32;;6131:16;;;6128:2;;;6165:6;6157;6150:22;6128:2;;6209:60;6261:7;6250:8;6239:9;6235:24;6209:60;:::i;:::-;5557:772;;;;-1:-1:-1;5557:772:1;;-1:-1:-1;6288:8:1;;6183:86;5557:772;-1:-1:-1;;;5557:772:1:o;6334:943::-;6462:6;6470;6478;6486;6494;6547:3;6535:9;6526:7;6522:23;6518:33;6515:2;;;6569:6;6561;6554:22;6515:2;6601:23;;-1:-1:-1;;;;;6673:14:1;;;6670:2;;;6705:6;6697;6690:22;6670:2;6733:22;;;;6789:3;6771:16;;;6767:26;6764:2;;;6811:6;6803;6796:22;7282:943;7410:6;7418;7426;7434;7442;7495:3;7483:9;7474:7;7470:23;7466:33;7463:2;;;7517:6;7509;7502:22;7463:2;7549:23;;-1:-1:-1;;;;;7621:14:1;;;7618:2;;;7653:6;7645;7638:22;7618:2;7681:22;;;;7737:3;7719:16;;;7715:26;7712:2;;;7759:6;7751;7744:22;8230:1571;8333:6;8386:3;8374:9;8365:7;8361:23;8357:33;8354:2;;;8408:6;8400;8393:22;8354:2;8439:22;;:::i;:::-;8484:40;8514:9;8484:40;:::i;:::-;8477:5;8470:55;8557:49;8602:2;8591:9;8587:18;8557:49;:::i;:::-;8552:2;8545:5;8541:14;8534:73;8639:49;8684:2;8673:9;8669:18;8639:49;:::i;:::-;8634:2;8627:5;8623:14;8616:73;8721:49;8766:2;8755:9;8751:18;8721:49;:::i;:::-;8716:2;8709:5;8705:14;8698:73;8804:50;8849:3;8838:9;8834:19;8804:50;:::i;:::-;8798:3;8791:5;8787:15;8780:75;8888:50;8933:3;8922:9;8918:19;8888:50;:::i;:::-;8882:3;8875:5;8871:15;8864:75;8972:50;9017:3;9006:9;9002:19;8972:50;:::i;:::-;8966:3;8959:5;8955:15;8948:75;9056:50;9101:3;9090:9;9086:19;9056:50;:::i;:::-;9050:3;9043:5;9039:15;9032:75;9126:3;9161:49;9206:2;9195:9;9191:18;9161:49;:::i;:::-;9145:14;;;9138:73;9230:3;9271:18;;;9265:25;9249:14;;;9242:49;9310:3;9351:18;;;9345:25;9329:14;;;9322:49;9390:3;9431:18;;;9425:25;9409:14;;;9402:49;9470:3;9511:18;;;9505:25;9489:14;;;9482:49;9550:3;9591:18;;;9585:25;9569:14;;;9562:49;9630:3;9671:18;;;9665:25;9649:14;;;9642:49;9710:3;9751:18;;;9745:25;9729:14;;;9722:49;;;;-1:-1:-1;9149:5:1;8344:1457;-1:-1:-1;8344:1457:1:o;9806:325::-;9874:6;9882;9935:2;9923:9;9914:7;9910:23;9906:32;9903:2;;;9956:6;9948;9941:22;9903:2;9997:9;9984:23;9974:33;;10057:2;10046:9;10042:18;10029:32;10070:31;10095:5;10070:31;:::i;:::-;10120:5;10110:15;;;9893:238;;;;;:::o;10136:843::-;10242:6;10250;10258;10266;10274;10282;10335:3;10323:9;10314:7;10310:23;10306:33;10303:2;;;10357:6;10349;10342:22;10303:2;10398:9;10385:23;10375:33;;10458:2;10447:9;10443:18;10430:32;10471:31;10496:5;10471:31;:::i;:::-;10521:5;-1:-1:-1;10578:2:1;10563:18;;10550:32;10591:33;10550:32;10591:33;:::i;:::-;10643:7;-1:-1:-1;10697:2:1;10682:18;;10669:32;;-1:-1:-1;10752:3:1;10737:19;;10724:33;-1:-1:-1;;;;;10769:30:1;;10766:2;;;10817:6;10809;10802:22;10766:2;10861:58;10911:7;10902:6;10891:9;10887:22;10861:58;:::i;:::-;10293:686;;;;-1:-1:-1;10293:686:1;;-1:-1:-1;10293:686:1;;10938:8;;10293:686;-1:-1:-1;;;10293:686:1:o;11101:268::-;11189:6;11184:3;11177:19;11241:6;11234:5;11227:4;11222:3;11218:14;11205:43;-1:-1:-1;11159:3:1;11268:16;;;11286:4;11264:27;;;11257:40;;;;11351:2;11330:15;;;-1:-1:-1;;11326:29:1;11317:39;;;11313:50;;11167:202::o;11374:257::-;11415:3;11453:5;11447:12;11480:6;11475:3;11468:19;11496:63;11552:6;11545:4;11540:3;11536:14;11529:4;11522:5;11518:16;11496:63;:::i;:::-;11613:2;11592:15;-1:-1:-1;;11588:29:1;11579:39;;;;11620:4;11575:50;;11423:208;-1:-1:-1;;11423:208:1:o;11636:1468::-;11723:58;11777:3;11750:25;11769:5;11750:25;:::i;:::-;-1:-1:-1;;;;;11058:31:1;11046:44;;11036:60;11723:58;11805:36;11835:4;11828:5;11824:16;11805:36;:::i;:::-;-1:-1:-1;;;;;11058:31:1;11895:4;11886:14;;11046:44;11925:36;11955:4;11944:16;;11925:36;:::i;:::-;-1:-1:-1;;;;;11058:31:1;12015:4;12006:14;;11046:44;12045:36;12075:4;12064:16;;12045:36;:::i;:::-;-1:-1:-1;;;;;11058:31:1;12135:4;12126:14;;11046:44;12165:36;12195:4;12184:16;;12165:36;:::i;:::-;-1:-1:-1;;;;;11058:31:1;12255:4;12246:14;;11046:44;12285:36;-1:-1:-1;12304:16:1;;12285:36;:::i;:::-;-1:-1:-1;;;;;11058:31:1;-1:-1:-1;12366:14:1;;11046:44;12405:36;12435:4;12424:16;;12405:36;:::i;:::-;-1:-1:-1;;;;;11058:31:1;12495:4;12486:14;;11046:44;12525:36;12555:4;12544:16;;12525:36;:::i;:::-;-1:-1:-1;;;;;11058:31:1;12615:4;12606:14;;11046:44;12640:6;12670:34;12689:14;;;12670:34;:::i;:::-;-1:-1:-1;;;;;11058:31:1;12749:12;;;11046:44;12781:6;12830:14;;;12817:28;12803:12;;;12796:50;12865:6;12914:14;;;12901:28;12887:12;;;12880:50;12949:6;12998:14;;;12985:28;12971:12;;;12964:50;13033:6;13082:14;;;13069:28;13055:12;;13048:50;11713:1391::o;14536:1711::-;14614:58;14668:3;14641:25;14660:5;14641:25;:::i;14614:58::-;14696:36;14726:4;14719:5;14715:16;14696:36;:::i;:::-;-1:-1:-1;;;;;11058:31:1;14786:4;14777:14;;11046:44;14816:36;14846:4;14835:16;;14816:36;:::i;:::-;-1:-1:-1;;;;;11058:31:1;14906:4;14897:14;;11046:44;14936:36;14966:4;14955:16;;14936:36;:::i;:::-;-1:-1:-1;;;;;11058:31:1;15026:4;15017:14;;11046:44;15056:36;15086:4;15075:16;;15056:36;:::i;:::-;-1:-1:-1;;;;;11058:31:1;15146:4;15137:14;;11046:44;15176:36;-1:-1:-1;15195:16:1;;15176:36;:::i;:::-;-1:-1:-1;;;;;11058:31:1;-1:-1:-1;15257:14:1;;11046:44;15296:36;15326:4;15315:16;;15296:36;:::i;:::-;-1:-1:-1;;;;;11058:31:1;15386:4;15377:14;;11046:44;15416:36;15446:4;15435:16;;15416:36;:::i;:::-;-1:-1:-1;;;;;11058:31:1;15506:4;15497:14;;11046:44;15531:6;15561:34;15580:14;;;15561:34;:::i;:::-;-1:-1:-1;;;;;11058:31:1;15640:12;;;11046:44;15672:6;15721:14;;;15708:28;15694:12;;;15687:50;15756:6;15805:14;;;15792:28;15778:12;;;15771:50;15840:6;15889:14;;;15876:28;15862:12;;;15855:50;15924:6;15973:14;;;15960:28;15946:12;;;15939:50;16008:6;16057:14;;;16044:28;16030:12;;;16023:50;16092:6;16141:14;;;16128:28;16114:12;;;16107:50;16176:6;16225:14;;;16212:28;16198:12;;16191:50;14604:1643::o;16252:1644::-;16348:12;;-1:-1:-1;;;;;11058:31:1;;;11046:44;;16412:4;16401:16;;;16395:23;11058:31;;16468:14;;;11046:44;16531:4;16520:16;;;16514:23;11058:31;;16589:14;;;11046:44;16652:4;16641:16;;;16635:23;11058:31;;16710:14;;;11046:44;16773:4;16762:16;;;16756:23;11058:31;;16831:14;;;11046:44;16894:4;16883:16;;;16877:23;11058:31;;16952:14;;;11046:44;17015:4;17004:16;;;16998:23;11058:31;;17073:14;;;11046:44;17136:4;17125:16;;;17119:23;11058:31;;17194:14;;;11046:44;17228:6;17271:14;;;17265:21;11058:31;;;17338:12;;;11046:44;17370:6;17412:14;;;17406:21;17392:12;;;17385:43;17447:6;17489:14;;;17483:21;17469:12;;;17462:43;17524:6;17566:14;;;17560:21;17546:12;;;17539:43;17601:6;17643:14;;;17637:21;17623:12;;;17616:43;17678:6;17720:14;;;17714:21;17700:12;;;17693:43;17755:6;17797:14;;;17791:21;17777:12;;;17770:43;17832:6;17874:14;;;17868:21;17854:12;;17847:43;16311:1585::o;17901:274::-;18030:3;18068:6;18062:13;18084:53;18130:6;18125:3;18118:4;18110:6;18106:17;18084:53;:::i;:::-;18153:16;;;;;18038:137;-1:-1:-1;;18038:137:1:o;20661:219::-;20810:2;20799:9;20792:21;20773:4;20830:44;20870:2;20859:9;20855:18;20847:6;20830:44;:::i;26379:356::-;26581:2;26563:21;;;26600:18;;;26593:30;26659:34;26654:2;26639:18;;26632:62;26726:2;26711:18;;26553:182::o;28188:817::-;28375:2;28364:9;28357:21;28387:70;28453:2;28442:9;28438:18;28430:6;28387:70;:::i;:::-;28338:4;28500:59;28551:6;28543;28539:19;28531:6;28500:59;:::i;:::-;28578:6;28603:3;28642:2;28637;28626:9;28622:18;28615:30;28668:74;28737:3;28726:9;28722:19;28708:12;28694;28668:74;:::i;:::-;28654:88;;28789:55;28840:2;28832:6;28828:15;28820:6;28789:55;:::i;:::-;-1:-1:-1;;28884:22:1;;;28880:36;28860:18;;;28853:64;28751:93;-1:-1:-1;28751:93:1;-1:-1:-1;28934:65:1;28884:22;28751:93;;28934:65;:::i;29010:1208::-;29199:2;29188:9;29181:21;29211:70;29277:2;29266:9;29262:18;29254:6;29211:70;:::i;:::-;29162:4;29300:3;29364:6;29356;29352:19;29339:33;29334:2;29323:9;29319:18;29312:61;29416:55;29467:2;29459:6;29455:15;29447:6;29416:55;:::i;:::-;29382:89;;29490:6;29515:3;29554:2;29549;29538:9;29534:18;29527:30;29580:74;29649:3;29638:9;29634:19;29620:12;29606;29580:74;:::i;:::-;29566:88;;29701:55;29752:2;29744:6;29740:15;29732:6;29701:55;:::i;:::-;29663:93;;;;29779:2;29775:7;29801:3;29868:2;29856:9;29848:6;29844:22;29840:31;29835:2;29824:9;29820:18;29813:59;29895:65;29953:6;29937:14;29921;29895:65;:::i;:::-;29881:79;;30007:55;30058:2;30050:6;30046:15;30038:6;30007:55;:::i;:::-;29969:93;;;;;30126:2;30114:9;30106:6;30102:22;30098:31;30093:2;30082:9;30078:18;30071:59;;30147:65;30205:6;30189:14;30173;30147:65;:::i;30223:581::-;30527:3;30512:19;;30540:70;30516:9;30592:6;30540:70;:::i;:::-;-1:-1:-1;;;;;30685:15:1;;;30679:3;30664:19;;30657:44;30732:3;30717:19;;30710:35;;;;30782:15;;30776:3;30761:19;;;30754:44;;;;30710:35;30494:310;-1:-1:-1;30494:310:1:o;30809:1585::-;30998:2;30987:9;30980:21;31010:79;31085:2;31074:9;31070:18;31062:6;31010:79;:::i;:::-;30961:4;31108:3;31172:6;31164;31160:19;31147:33;31142:2;31131:9;31127:18;31120:61;31200:3;31264:2;31256:6;31252:15;31239:29;31234:2;31223:9;31219:18;31212:57;31312:55;31363:2;31355:6;31351:15;31343:6;31312:55;:::i;:::-;31278:89;;;;31386:6;31411:3;31450:2;31445;31434:9;31430:18;31423:30;31476:74;31545:3;31534:9;31530:19;31516:12;31502;31476:74;:::i;:::-;31462:88;;31597:55;31648:2;31640:6;31636:15;31628:6;31597:55;:::i;:::-;31559:93;;;;31675:2;31671:7;31697:3;31764:2;31752:9;31744:6;31740:22;31736:31;31731:2;31720:9;31716:18;31709:59;31791:65;31849:6;31833:14;31817;31791:65;:::i;:::-;31777:79;;31903:55;31954:2;31946:6;31942:15;31934:6;31903:55;:::i;:::-;31865:93;;;;;31977:3;32044:2;32032:9;32024:6;32020:22;32016:31;32011:2;32000:9;31996:18;31989:59;32071:65;32129:6;32113:14;32097;32071:65;:::i;32399:977::-;32596:2;32585:9;32578:21;32559:4;32634:6;32628:13;32677:4;32672:2;32661:9;32657:18;32650:32;32691:75;32761:3;32750:9;32746:19;32731:12;32725:19;32691:75;:::i;:::-;32821:2;32807:12;32803:21;32797:28;32862:6;32856:3;32845:9;32841:19;32834:35;32892:53;32940:3;32929:9;32925:19;32909:14;32892:53;:::i;:::-;32878:67;;33000:4;32986:12;32982:23;32976:30;32954:52;;33075:3;33071:8;33059:9;33051:6;33047:22;33043:37;33037:3;33026:9;33022:19;33015:66;33101:40;33134:6;33118:14;33101:40;:::i;:::-;33090:51;;;33224:1;33220;33215:3;33211:11;33207:19;33201:2;33193:6;33189:15;33183:22;33179:48;33172:4;33161:9;33157:20;33150:78;33282:4;33274:6;33270:17;33264:24;33259:2;33248:9;33244:18;33237:52;33345:2;33337:6;33333:15;33327:22;33320:4;33309:9;33305:20;33298:52;33367:3;33359:11;;;32568:808;;;;:::o;33381:1304::-;33580:2;33569:9;33562:21;33543:4;33618:6;33612:13;33661:4;33656:2;33645:9;33641:18;33634:32;33675:75;33745:3;33734:9;33730:19;33715:12;33709:19;33675:75;:::i;:::-;33811:2;33797:12;33793:21;33787:28;33781:3;33770:9;33766:19;33759:57;33871:4;33857:12;33853:23;33847:30;33914:6;33908:3;33897:9;33893:19;33886:35;33944:53;33992:3;33981:9;33977:19;33961:14;33944:53;:::i;:::-;33930:67;;34052:4;34038:12;34034:23;34028:30;34081:3;34077:8;34150:2;34138:9;34130:6;34126:22;34122:31;34116:3;34105:9;34101:19;34094:60;34177:40;34210:6;34194:14;34177:40;:::i;:::-;34163:54;;34272:4;34258:12;34254:23;34248:30;34226:52;;34343:2;34331:9;34323:6;34319:22;34315:31;34309:3;34298:9;34294:19;34287:60;;;34367:40;34400:6;34384:14;34367:40;:::i;:::-;34456:2;34444:15;;34438:22;-1:-1:-1;;;;;11058:31:1;;34527:4;34512:20;;11046:44;34356:51;;-1:-1:-1;34438:22:1;-1:-1:-1;34469:64:1;34589:4;34581:6;34577:17;34571:24;34564:4;34553:9;34549:20;34542:54;34652:4;34644:6;34640:17;34634:24;34627:4;34616:9;34612:20;34605:54;34676:3;34668:11;;;33552:1133;;;;:::o;34690:1614::-;34889:2;34878:9;34871:21;34852:4;34927:6;34921:13;34970:4;34965:2;34954:9;34950:18;34943:32;34984:84;35063:3;35052:9;35048:19;35033:12;35027:19;13214:12;;-1:-1:-1;;;;;11058:31:1;;;11046:44;;13278:4;13267:16;;;13261:23;11058:31;;13334:14;;;11046:44;13397:4;13386:16;;;13380:23;11058:31;;13455:14;;;11046:44;13518:4;13507:16;;;13501:23;11058:31;;13576:14;;;11046:44;13639:4;13628:16;;;13622:23;11058:31;;13697:14;;;11046:44;13760:4;13749:16;;;13743:23;11058:31;;13818:14;;;11046:44;13881:4;13870:16;;;13864:23;11058:31;;13939:14;;;11046:44;14002:4;13991:16;;;13985:23;11058:31;;14060:14;;;11046:44;14094:6;14137:14;;;14131:21;11058:31;;;14204:12;;;11046:44;14236:6;14278:14;;;14272:21;14258:12;;;14251:43;14313:6;14355:14;;;14349:21;14335:12;;;14328:43;14390:6;14432:14;;;14426:21;14412:12;;;14405:43;14467:6;14509:14;;;14503:21;14489:12;;14482:43;13177:1354;34984:84;35129:2;35115:12;35111:21;35105:28;35099:3;35088:9;35084:19;35077:57;35177:4;35163:12;35159:23;35153:30;35202:6;35244:2;35239;35228:9;35224:18;35217:30;35302:4;35288:12;35284:23;35278:30;35256:52;;35345:2;35339:3;35328:9;35324:19;35317:31;;35371:53;35419:3;35408:9;35404:19;35388:14;35371:53;:::i;:::-;35357:67;;35479:4;35465:12;35461:23;35455:30;35508:3;35504:8;35577:2;35565:9;35557:6;35553:22;35549:31;35543:3;35532:9;35528:19;35521:60;35604:40;35637:6;35621:14;35604:40;:::i;:::-;35590:54;;35699:3;35685:12;35681:22;35675:29;35653:51;;35769:2;35757:9;35749:6;35745:22;35741:31;35735:3;35724:9;35720:19;35713:60;35796:40;35829:6;35813:14;35796:40;:::i;:::-;35782:54;;35891:4;35877:12;35873:23;35867:30;35845:52;;35962:2;35950:9;35942:6;35938:22;35934:31;35928:3;35917:9;35913:19;35906:60;;;35986:40;36019:6;36003:14;35986:40;:::i;36934:554::-;37220:3;37205:19;;37233:61;37209:9;37276:6;37233:61;:::i;:::-;-1:-1:-1;;;;;37369:15:1;;;37363:3;37348:19;;37341:44;37416:3;37401:19;;37394:35;;;;37466:15;;37460:3;37445:19;;;37438:44;;;;37394:35;37187:301;-1:-1:-1;37187:301:1:o;37493:271::-;37693:3;37678:19;;37706:52;37682:9;37740:6;37706:52;:::i;38698:250::-;38765:2;38759:9;38807:6;38795:19;;38865:22;;;-1:-1:-1;;;;;38829:34:1;;38826:62;38823:2;;;38891:18;;:::i;:::-;38927:2;38920:22;38739:209;:::o;38953:255::-;39025:2;39019:9;39067:6;39055:19;;39125:22;;;-1:-1:-1;;;;;39089:34:1;;39086:62;39083:2;;;39151:18;;:::i;39470:253::-;39542:2;39536:9;39584:4;39572:17;;39640:22;;;-1:-1:-1;;;;;39604:34:1;;39601:62;39598:2;;;39666:18;;:::i;39728:253::-;39800:2;39794:9;39842:4;39830:17;;39898:22;;;-1:-1:-1;;;;;39862:34:1;;39859:62;39856:2;;;39924:18;;:::i;39986:511::-;40044:5;40051:6;40111:3;40098:17;40197:2;40193:7;40182:8;40166:14;40162:29;40158:43;40138:18;40134:68;40124:2;;40220:5;40213;40206:20;40124:2;40252:33;;40356:4;40343:18;;;-1:-1:-1;40304:21:1;;-1:-1:-1;;;;;;40373:30:1;;40370:2;;;40416:1;40413;40406:12;40370:2;40466:6;40450:14;40446:27;40436:8;40432:42;40429:2;;;40487:1;40484;40477:12;40502:949;40610:9;40669:6;40661:5;40645:14;40641:26;40637:39;40634:2;;;40697:9;40686;40679:28;40634:2;40738;40732:9;40780:4;40768:17;;-1:-1:-1;;;;;40837:18:1;;;40857:22;;;40834:46;40831:2;;;40883:18;;:::i;:::-;40923:10;40919:2;40912:22;40958:56;40999:14;40992:5;40958:56;:::i;:::-;40950:6;40943:72;41062:3;41055:5;41051:15;41038:29;41024:43;;41090:2;41082:6;41079:14;41076:2;;;41114:9;41103;41096:28;41076:2;41161:52;41198:14;41189:6;41182:5;41178:18;41161:52;:::i;:::-;41154:4;41146:6;41142:17;41135:79;41263:3;41256:5;41252:15;41239:29;41223:45;;41293:2;41283:8;41280:16;41277:2;;;41317:9;41306;41299:28;41277:2;;41362:54;41401:14;41390:8;41383:5;41379:20;41362:54;:::i;:::-;41357:2;41345:15;;41338:79;-1:-1:-1;41349:6:1;40624:827;-1:-1:-1;;40624:827:1:o;41456:1087::-;41566:9;41625:6;41617:5;41601:14;41597:26;41593:39;41590:2;;;41653:9;41642;41635:28;41590:2;41689:22;;:::i;:::-;41736:56;41777:14;41770:5;41736:56;:::i;:::-;41720:73;;41853:3;41842:15;;41829:29;41822:4;41809:18;;41802:57;41906:3;41895:15;;41882:29;-1:-1:-1;;;;;41960:14:1;;;41957:2;;;41995:9;41984;41977:28;41957:2;42043:52;42080:14;42071:6;42064:5;42060:18;42043:52;:::i;:::-;42036:4;42027:7;42023:18;42016:80;42145:3;42138:5;42134:15;42121:29;42105:45;;42175:2;42165:8;42162:16;42159:2;;;42199:9;42188;42181:28;42159:2;42247:54;42286:14;42275:8;42268:5;42264:20;42247:54;:::i;:::-;42240:4;42231:7;42227:18;42220:82;42351:3;42344:5;42340:15;42327:29;42311:45;;42381:2;42371:8;42368:16;42365:2;;;42405:9;42394;42387:28;42365:2;;42453:54;42492:14;42481:8;42474:5;42470:20;42453:54;:::i;:::-;42446:4;42433:18;;42426:82;-1:-1:-1;42437:7:1;41580:963;-1:-1:-1;;41580:963:1:o;42548:1368::-;42658:9;42717:6;42709:5;42693:14;42689:26;42685:39;42682:2;;;42745:9;42734;42727:28;42682:2;42781:22;;:::i;:::-;42828:65;42878:14;42871:5;42828:65;:::i;:::-;42812:82;;42954:3;42943:15;;42930:29;42923:4;42910:18;;42903:57;43020:3;43009:15;;42996:29;42989:4;42976:18;;42969:57;43073:3;43062:15;;43049:29;-1:-1:-1;;;;;43127:14:1;;;43124:2;;;43162:9;43151;43144:28;43124:2;43210:52;43247:14;43238:6;43231:5;43227:18;43210:52;:::i;:::-;43203:4;43194:7;43190:18;43183:80;43312:3;43305:5;43301:15;43288:29;43272:45;;43342:2;43332:8;43329:16;43326:2;;;43366:9;43355;43348:28;43326:2;43414:54;43453:14;43442:8;43435:5;43431:20;43414:54;:::i;:::-;43407:4;43398:7;43394:18;43387:82;43518:3;43511:5;43507:15;43494:29;43478:45;;43548:2;43538:8;43535:16;43532:2;;;43572:9;43561;43554:28;43532:2;43620:54;43659:14;43648:8;43641:5;43637:20;43620:54;:::i;:::-;43613:4;43604:7;43600:18;43593:82;43724:3;43717:5;43713:15;43700:29;43684:45;;43754:2;43744:8;43741:16;43738:2;;;43778:9;43767;43760:28;43738:2;;43826:54;43865:14;43854:8;43847:5;43843:20;43826:54;:::i;:::-;43819:4;43806:18;;43799:82;-1:-1:-1;43810:7:1;42672:1244;-1:-1:-1;;42672:1244:1:o;43921:258::-;43993:1;44003:113;44017:6;44014:1;44011:13;44003:113;;;44093:11;;;44087:18;44074:11;;;44067:39;44039:2;44032:10;44003:113;;;44134:6;44131:1;44128:13;44125:2;;;-1:-1:-1;;44169:1:1;44151:16;;44144:27;43974:205::o;44184:127::-;44245:10;44240:3;44236:20;44233:1;44226:31;44276:4;44273:1;44266:15;44300:4;44297:1;44290:15;44316:131;-1:-1:-1;;;;;44391:31:1;;44381:42;;44371:2;;44437:1;44434;44427:12
Swarm Source
ipfs://67312d6472fee91db6d3c198cbe2daf6612c356fe9ecf639d436483a7bbe479d
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.