More Info
Private Name Tags
ContractCreator
Latest 25 internal transactions (View All)
Advanced mode:
Parent Transaction Hash | Block | From | To | Value | ||
---|---|---|---|---|---|---|
20219977 | 46 secs ago | 0.00166388 ETH | ||||
20219977 | 46 secs ago | 0.00166388 ETH | ||||
20219600 | 1 hr ago | 0.0056033 ETH | ||||
20219600 | 1 hr ago | 0.0056033 ETH | ||||
20218998 | 3 hrs ago | 0.00093059 ETH | ||||
20218998 | 3 hrs ago | 0.00093059 ETH | ||||
20218488 | 5 hrs ago | 0.00139314 ETH | ||||
20218488 | 5 hrs ago | 0.00139314 ETH | ||||
20217702 | 7 hrs ago | 0.00332453 ETH | ||||
20217702 | 7 hrs ago | 0.00332453 ETH | ||||
20217358 | 8 hrs ago | 0.01694381 ETH | ||||
20217358 | 8 hrs ago | 0.01694381 ETH | ||||
20216830 | 10 hrs ago | 0.00120407 ETH | ||||
20216830 | 10 hrs ago | 0.00120407 ETH | ||||
20216493 | 11 hrs ago | 0.00322598 ETH | ||||
20216493 | 11 hrs ago | 0.00322598 ETH | ||||
20215982 | 13 hrs ago | 0.00672351 ETH | ||||
20215982 | 13 hrs ago | 0.00672351 ETH | ||||
20215881 | 13 hrs ago | 0.00082549 ETH | ||||
20215881 | 13 hrs ago | 0.00082549 ETH | ||||
20215809 | 14 hrs ago | 0.00654613 ETH | ||||
20215809 | 14 hrs ago | 0.00654613 ETH | ||||
20215739 | 14 hrs ago | 0.00330535 ETH | ||||
20215739 | 14 hrs ago | 0.00330535 ETH | ||||
20215603 | 14 hrs ago | 0.00247829 ETH |
Loading...
Loading
Contract Name:
RangoStargateMiddleware
Compiler Version
v0.8.16+commit.07a7930e
Optimization Enabled:
Yes with 10000 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity 0.8.16; import "../../libraries/LibInterchain.sol"; import "../../utils/ReentrancyGuard.sol"; import "../base/RangoBaseInterchainMiddleware.sol"; /// @title The middleware contract that handles Rango's receive messages from stargate. /// @author George /// @dev Note that this is not a facet and should be deployed separately. contract RangoStargateMiddleware is ReentrancyGuard, IRango, IStargateReceiver, RangoBaseInterchainMiddleware { /// @dev keccak256("exchange.rango.middleware.stargate") bytes32 internal constant STARGATE_MIDDLEWARE_NAMESPACE = hex"8f95700cb6d0d3fbe23970b0fed4ae8d3a19af1ff9db49b72f280b34bdf7bad8"; struct RangoStargateMiddlewareStorage { address stargateRouter; } function initStargateMiddleware( address _owner, address _stargateRouter, address _weth ) external onlyOwner { initBaseMiddleware(_owner, address(0), _weth); updateStargateRouterAddressInternal(_stargateRouter); } /// Events /// @notice Emits when the Stargate address is updated /// @param oldAddress The previous address /// @param newAddress The new address event StargateRouterAddressUpdated(address oldAddress, address newAddress); /// External Functions /// @notice Updates the address of stargateRouter /// @param newAddress The new address of owner function updateStargateRouter(address newAddress) external onlyOwner { updateStargateRouterAddressInternal(newAddress); } // @param _chainId The remote chainId sending the tokens // @param _srcAddress The remote Bridge address // @param _nonce The message ordering nonce // @param _token The token contract on the local chain // @param amountLD The qty of local _token contract tokens // @param _payload The bytes containing the _tokenOut, _deadline, _amountOutMin, _toAddr function sgReceive( uint16, bytes memory, uint256, address _token, uint256 amountLD, bytes memory payload ) external payable override nonReentrant { require(msg.sender == getRangoStargateMiddlewareStorage().stargateRouter, "sgReceive function can only be called by Stargate router"); Interchain.RangoInterChainMessage memory m = abi.decode((payload), (Interchain.RangoInterChainMessage)); (address receivedToken, uint dstAmount, IRango.CrossChainOperationStatus status) = LibInterchain.handleDestinationMessage(_token, amountLD, m); emit RangoBridgeCompleted( m.requestId, receivedToken, m.originalSender, m.recipient, dstAmount, status, m.dAppTag ); } /// Private and Internal function updateStargateRouterAddressInternal(address newAddress) private { require(newAddress != address(0), "Invalid StargateRouter"); RangoStargateMiddlewareStorage storage s = getRangoStargateMiddlewareStorage(); address oldAddress = s.stargateRouter; s.stargateRouter = newAddress; emit StargateRouterAddressUpdated(oldAddress, newAddress); } /// @dev fetch local storage function getRangoStargateMiddlewareStorage() private pure returns (RangoStargateMiddlewareStorage storage s) { bytes32 namespace = STARGATE_MIDDLEWARE_NAMESPACE; // solhint-disable-next-line no-inline-assembly assembly { s.slot := namespace } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-IERC20Permit.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612]. * * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't * need to send a transaction, and thus is not required to hold Ether at all. */ interface IERC20Permit { /** * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens, * given ``owner``'s signed approval. * * IMPORTANT: The same issues {IERC20-approve} has related to transaction * ordering also apply here. * * Emits an {Approval} event. * * Requirements: * * - `spender` cannot be the zero address. * - `deadline` must be a timestamp in the future. * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner` * over the EIP712-formatted function arguments. * - the signature must use ``owner``'s current nonce (see {nonces}). * * For more information on the signature format, see the * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP * section]. */ function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external; /** * @dev Returns the current nonce for `owner`. This value must be * included whenever a signature is generated for {permit}. * * Every successful call to {permit} increases ``owner``'s nonce by one. This * prevents a signature from being used multiple times. */ function nonces(address owner) external view returns (uint256); /** * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}. */ // solhint-disable-next-line func-name-mixedcase function DOMAIN_SEPARATOR() external view returns (bytes32); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @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); /** * @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 `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, 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 `from` to `to` 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 from, address to, uint256 amount ) external returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.7.0) (token/ERC20/utils/SafeERC20.sol) pragma solidity ^0.8.0; import "../IERC20.sol"; import "../extensions/draft-IERC20Permit.sol"; import "../../../utils/Address.sol"; /** * @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)); } } function safePermit( IERC20Permit token, address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) internal { uint256 nonceBefore = token.nonces(owner); token.permit(owner, spender, value, deadline, v, r, s); uint256 nonceAfter = token.nonces(owner); require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed"); } /** * @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"); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol) pragma solidity ^0.8.1; /** * @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 * ==== * * [IMPORTANT] * ==== * You shouldn't rely on `isContract` to protect against flash loan attacks! * * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract * constructor. * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize/address.code.length, which returns 0 // for contracts in construction, since the code is only stored at the end // of the constructor execution. return account.code.length > 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); } /** * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason using the provided one. * * _Available since v4.3._ */ function verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) internal 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 /// @solidity memory-safe-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity 0.8.16; import "../../libraries/LibDiamond.sol"; import "../../libraries/LibInterchain.sol"; // @title The base contract to be used as a parent of middleware classes // @author George // @dev Note that this is not a facet and should be extended and deployed separately. contract RangoBaseInterchainMiddleware { /// @dev keccak256("exchange.rango.middleware.base") bytes32 internal constant BASE_MIDDLEWARE_CONTRACT_NAMESPACE = hex"ad914d4300c64e1902ca499875cd8a76ae717047bcfaa9e806ff7ea4f6911268"; struct BaseInterchainMiddlewareStorage { address rangoDiamond; address owner; } struct whitelistRequest { address contractAddress; bytes4[] methodIds; } constructor(){updateOwnerInternal(tx.origin);} function initBaseMiddleware( address _owner, address _rangoDiamond, address _weth ) public onlyOwner { require(_owner != address(0)); updateOwnerInternal(_owner); updateRangoDiamondInternal(_rangoDiamond); LibSwapper.setWeth(_weth); } /// Events /// @notice Emits when the rango diamond address is updated /// @param oldAddress The previous address /// @param newAddress The new address event RangoDiamondAddressUpdated(address oldAddress, address newAddress); /// @notice Emits when the weth address is updated /// @param oldAddress The previous address /// @param newAddress The new address event WethAddressUpdated(address oldAddress, address newAddress); /// @notice Emits when the owner is updated /// @param previousOwner The previous owner /// @param newOwner The new owner event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /// @notice Notifies that admin manually refunded some money /// @param _token The address of refunded token, 0x000..00 address for native token /// @param _amount The amount that is refunded event Refunded(address _token, uint _amount); /// @notice Notifies that a new contract is whitelisted /// @param _address The address of the contract event ContractWhitelisted(address _address); /// @notice Notifies that a new contract is whitelisted /// @param contractAddress The address of the contract /// @param methods The method signatures that are whitelisted for a contractAddress event ContractAndMethodsWhitelisted(address contractAddress, bytes4[] methods); /// @notice Notifies that a new contract is blacklisted /// @param _address The address of the contract event ContractBlacklisted(address _address); /// @notice Notifies that a contract is blacklisted and the given methods are removed /// @param contractAddress The address of the contract /// @param methods The method signatures that are blacklisted for the given contractAddress event ContractAndMethodsBlacklisted(address contractAddress, bytes4[] methods); /// @notice Notifies that a new contract is whitelisted /// @param _dapp The address of the contract event MessagingDAppWhitelisted(address _dapp); /// @notice Notifies that a new contract is blacklisted /// @param _dapp The address of the contract event MessagingDAppBlacklisted(address _dapp); /// @notice used to limit access only to owner modifier onlyOwner() { require(msg.sender == getBaseInterchainMiddlewareStorage().owner, "should be called only by owner"); _; } /// @notice used to limit access only to rango diamond modifier onlyDiamond() { require(msg.sender == getBaseInterchainMiddlewareStorage().rangoDiamond, "should be called only from diamond"); _; } /// @notice Enables the contract to receive native ETH token from other contracts including WETH contract receive() external payable {} /// Administration & Control /// @notice Updates the address of rango diamond contract /// @param newAddress The new address of diamond contract function updateRangoDiamondAddress(address newAddress) external onlyOwner { updateRangoDiamondInternal(newAddress); } /// @notice Updates the address of weth contract /// @param newAddress The new address of weth contract function updateWethAddress(address newAddress) external onlyOwner { LibSwapper.setWeth(newAddress); } /// @notice Updates the address of owner /// @param newAddress The new address of owner function updateOwner(address newAddress) external onlyOwner { updateOwnerInternal(newAddress); } /// @notice Transfers an ERC20 token from this contract to msg.sender /// @dev This endpoint is to return money to a user if we didn't handle failure correctly and the money is still in the contract /// @dev Currently the money goes to admin and they should manually transfer it to a wallet later /// @param _tokenAddress The address of ERC20 token to be transferred /// @param _amount The amount of money that should be transfered function refund(address _tokenAddress, uint256 _amount) external onlyOwner { IERC20 ercToken = IERC20(_tokenAddress); uint balance = ercToken.balanceOf(address(this)); require(balance >= _amount, 'Insufficient balance'); SafeERC20.safeTransfer(IERC20(_tokenAddress), msg.sender, _amount); emit Refunded(_tokenAddress, _amount); } /// @notice Transfers the native token from this contract to msg.sender /// @dev This endpoint is to return money to a user if we didn't handle failure correctly and the money is still in the contract /// @dev Currently the money goes to admin and they should manually transfer it to a wallet later /// @param _amount The amount of native token that should be transferred function refundNative(uint256 _amount) external onlyOwner { uint balance = address(this).balance; require(balance >= _amount, 'Insufficient balance'); (bool sent,) = msg.sender.call{value : _amount}(""); require(sent, "failed to send native"); emit Refunded(LibSwapper.ETH, _amount); } /// @notice Adds a list of contracts to the whitelisted DEXes that can be called /// @param req The requests for whitelisting contracts and methods function addWhitelistContractMiddleWare(whitelistRequest[] calldata req) external onlyOwner { for (uint i = 0; i < req.length; i++) { LibSwapper.addMethodWhitelists(req[i].contractAddress, req[i].methodIds); emit ContractAndMethodsWhitelisted(req[i].contractAddress, req[i].methodIds); emit ContractWhitelisted(req[i].contractAddress); } } /// @notice Removes a contract from the whitelisted DEXes /// @param contractAddress The address of the DEX or dApp function removeWhitelistMiddleWare(address contractAddress) external onlyOwner { LibSwapper.removeWhitelist(contractAddress); emit ContractBlacklisted(contractAddress); } /// @notice Removes a contract and given method ids /// @param contractAddress The address of the contract /// @param methodIds The methods to be removed alongside the given contract function removeContractAndMethodIdsFromWhitelist( address contractAddress, bytes4[] calldata methodIds ) external onlyOwner { LibSwapper.removeWhitelist(contractAddress); emit ContractBlacklisted(contractAddress); for (uint i = 0; i < methodIds.length; i++) { LibSwapper.removeMethodWhitelist(contractAddress, methodIds[i]); } if (methodIds.length > 0) { emit ContractAndMethodsBlacklisted(contractAddress, methodIds); } } /// @notice Adds a list of contracts to the whitelisted messaging dApps that can be called /// @param _dapps The addresses of dApps function addMessagingDAppsMiddleWare(address[] calldata _dapps) external onlyOwner { address dapp; for (uint i = 0; i < _dapps.length; i++) { dapp = _dapps[i]; LibInterchain.addMessagingDApp(dapp); emit MessagingDAppWhitelisted(dapp); } } /// @notice Removes a contract from dApps that can be called /// @param _dapp The address of dApp function removeMessagingDAppContractMiddleWare(address _dapp) external onlyOwner { LibInterchain.removeMessagingDApp(_dapp); emit MessagingDAppBlacklisted(_dapp); } /// Internal and Private functions function updateRangoDiamondInternal(address newAddress) private { BaseInterchainMiddlewareStorage storage s = getBaseInterchainMiddlewareStorage(); address oldAddress = s.rangoDiamond; s.rangoDiamond = newAddress; emit RangoDiamondAddressUpdated(oldAddress, newAddress); } function updateOwnerInternal(address newAddress) private { BaseInterchainMiddlewareStorage storage s = getBaseInterchainMiddlewareStorage(); address oldAddress = s.owner; s.owner = newAddress; emit OwnershipTransferred(oldAddress, newAddress); } /// @dev fetch local storage function getBaseInterchainMiddlewareStorage() private pure returns (BaseInterchainMiddlewareStorage storage s) { bytes32 namespace = BASE_MIDDLEWARE_CONTRACT_NAMESPACE; // solhint-disable-next-line no-inline-assembly assembly { s.slot := namespace } } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.16; interface IDiamondCut { enum FacetCutAction { Add, Replace, Remove } // Add=0, Replace=1, Remove=2 struct FacetCut { address facetAddress; FacetCutAction action; bytes4[] functionSelectors; } /// @notice Add/replace/remove any number of functions and optionally execute /// a function with delegatecall /// @param _diamondCut Contains the facet addresses and function selectors /// @param _init The address of the contract or facet to execute _calldata /// @param _calldata A function call, including function selector and arguments /// _calldata is executed with delegatecall on _init function diamondCut( FacetCut[] calldata _diamondCut, address _init, bytes calldata _calldata ) external; event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata); }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity 0.8.16; /// @title An interface to interchain message types /// @author Uchiha Sasuke interface Interchain { enum ActionType { NO_ACTION, UNI_V2, UNI_V3, CALL } enum CallSubActionType { WRAP, UNWRAP, NO_ACTION } struct RangoInterChainMessage { address requestId; uint64 dstChainId; // @dev bridgeRealOutput is only used to disambiguate receipt of WETH and ETH and SHOULD NOT be used anywhere else! address bridgeRealOutput; address toToken; address originalSender; address recipient; ActionType actionType; bytes action; CallSubActionType postAction; uint16 dAppTag; // Extra message bytes dAppMessage; address dAppSourceContract; address dAppDestContract; } struct UniswapV2Action { address dexAddress; uint amountOutMin; address[] path; uint deadline; } struct UniswapV3ActionExactInputSingleParams { address dexAddress; address tokenIn; address tokenOut; uint24 fee; uint256 deadline; uint256 amountOutMinimum; uint160 sqrtPriceLimitX96; } /// @notice The requested call data which is computed off-chain and passed to the contract /// @param target The dex contract address that should be called /// @param callData The required data field that should be give to the dex contract to perform swap struct CallAction { address tokenIn; address spender; CallSubActionType preAction; address payable target; bytes callData; } }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity 0.8.16; interface IRango { struct RangoBridgeRequest { address requestId; address token; uint amount; uint platformFee; uint affiliateFee; address payable affiliatorAddress; uint destinationExecutorFee; uint16 dAppTag; } enum BridgeType {Across, CBridge, Hop, Hyphen, Multichain, Stargate, Synapse, Thorchain, Symbiosis, Axelar, Voyager, Poly, OptimismBridge, ArbitrumBridge, Wormhole, AllBridge} /// @notice Status of cross-chain swap /// @param Succeeded The whole process is success and end-user received the desired token in the destination /// @param RefundInSource Bridge was out of liquidity and middle asset (ex: USDC) is returned to user on source chain /// @param RefundInDestination Our handler on dest chain this.executeMessageWithTransfer failed and we send middle asset (ex: USDC) to user on destination chain /// @param SwapFailedInDestination Everything was ok, but the final DEX on destination failed (ex: Market price change and slippage) enum CrossChainOperationStatus { Succeeded, RefundInSource, RefundInDestination, SwapFailedInDestination } event RangoBridgeInitiated( address indexed requestId, address bridgeToken, uint256 bridgeAmount, address receiver, uint destinationChainId, bool hasInterchainMessage, bool hasDestinationSwap, uint8 indexed bridgeId, uint16 indexed dAppTag ); event RangoBridgeCompleted( address indexed requestId, address indexed token, address indexed originalSender, address receiver, uint amount, CrossChainOperationStatus status, uint16 dAppTag ); }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity 0.8.16; interface IRangoMessageReceiver { enum ProcessStatus { SUCCESS, REFUND_IN_SOURCE, REFUND_IN_DESTINATION } function handleRangoMessage( address token, uint amount, ProcessStatus status, bytes memory message ) external; }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity 0.8.16; import "./IStargateRouter.sol"; import "./Interchain.sol"; import "./IRango.sol"; import "../libraries/LibSwapper.sol"; /// @title An interface to interact with RangoStargateFacet /// @author Uchiha Sasuke interface IRangoStargate { enum StargateBridgeType {TRANSFER, TRANSFER_WITH_MESSAGE} struct StargateRequest { StargateBridgeType bridgeType; uint16 dstChainId; uint256 srcPoolId; uint256 dstPoolId; address payable srcGasRefundAddress; uint256 minAmountLD; uint256 dstGasForCall; uint256 dstNativeAmount; bytes dstNativeAddr; bytes to; uint stgFee; bytes imMessage; } function stargateSwapAndBridge( LibSwapper.SwapRequest memory request, LibSwapper.Call[] calldata calls, IRangoStargate.StargateRequest memory stargateRequest ) external payable; function stargateBridge( IRangoStargate.StargateRequest memory stargateRequest, IRango.RangoBridgeRequest memory bridgeRequest ) external payable; }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity 0.8.16; interface IStargateReceiver { function sgReceive( uint16 chainId, bytes memory srcAddress, uint256 nonce, address token, uint256 amountLD, bytes memory payload ) payable external; }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity 0.8.16; interface IStargateRouter { struct lzTxObj { uint256 dstGasForCall; uint256 dstNativeAmount; bytes dstNativeAddr; } function swap( uint16 _dstChainId, uint256 _srcPoolId, uint256 _dstPoolId, address payable _refundAddress, uint256 _amountLD, uint256 _minAmountLD, lzTxObj memory _lzTxParams, bytes calldata _to, bytes calldata _payload ) external payable; function swapETH( uint16 _dstChainId, address payable _refundAddress, bytes calldata _toAddress, uint256 _amountLD, uint256 _minAmountLD ) external payable; function quoteLayerZeroFee( uint16 _dstChainId, uint8 _functionType, bytes calldata _toAddress, bytes calldata _transferAndCallPayload, lzTxObj memory _lzTxParams ) external view returns (uint256, uint256); }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity 0.8.16; /// @dev based on swap router of uniswap v2 https://docs.uniswap.org/protocol/V2/reference/smart-contracts/router-02#swapexactethfortokens interface IUniswapV2 { function swapExactETHForTokens( uint amountOutMin, address[] calldata path, address to, uint deadline ) external payable returns (uint[] memory amounts); // For pangolin and trader joe function swapExactAVAXForTokens( uint amountOutMin, address[] calldata path, address to, uint deadline ) external payable returns (uint[] memory amounts); function swapTokensForExactTokens( uint256 amountOut, uint256 amountInMax, address[] calldata path, address to, uint256 deadline ) external returns (uint256[] memory amounts); function swapExactTokensForTokens( uint256 amountIn, uint256 amountOutMin, address[] calldata path, address to, uint256 deadline ) external returns (uint256[] memory amounts); }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity 0.8.16; /// @dev based on IswapRouter of UniswapV3 https://docs.uniswap.org/protocol/reference/periphery/interfaces/ISwapRouter interface IUniswapV3 { struct ExactInputSingleParams { address tokenIn; address tokenOut; uint24 fee; address recipient; uint256 deadline; uint256 amountIn; uint256 amountOutMinimum; uint160 sqrtPriceLimitX96; } function exactInputSingle(ExactInputSingleParams calldata params) external payable returns (uint256 amountOut); }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity 0.8.16; interface IWETH { function deposit() external payable; function withdraw(uint256) external; }
// SPDX-License-Identifier: MIT pragma solidity 0.8.16; import { IDiamondCut } from "../interfaces/IDiamondCut.sol"; /// Implementation of EIP-2535 Diamond Standard /// https://eips.ethereum.org/EIPS/eip-2535 library LibDiamond { /// @dev keccak256("diamond.standard.diamond.storage"); bytes32 internal constant DIAMOND_STORAGE_POSITION = hex"c8fcad8db84d3cc18b4c41d551ea0ee66dd599cde068d998e57d5e09332c131c"; // Diamond specific errors error IncorrectFacetCutAction(); error NoSelectorsInFacet(); error FunctionAlreadyExists(); error FacetAddressIsZero(); error FacetAddressIsNotZero(); error FacetContainsNoCode(); error FunctionDoesNotExist(); error FunctionIsImmutable(); error InitZeroButCalldataNotEmpty(); error CalldataEmptyButInitNotZero(); error InitReverted(); // ---------------- struct FacetAddressAndPosition { address facetAddress; uint96 functionSelectorPosition; // position in facetFunctionSelectors.functionSelectors array } struct FacetFunctionSelectors { bytes4[] functionSelectors; uint256 facetAddressPosition; // position of facetAddress in facetAddresses array } struct DiamondStorage { // maps function selector to the facet address and // the position of the selector in the facetFunctionSelectors.selectors array mapping(bytes4 => FacetAddressAndPosition) selectorToFacetAndPosition; // maps facet addresses to function selectors mapping(address => FacetFunctionSelectors) facetFunctionSelectors; // facet addresses address[] facetAddresses; // Used to query if a contract implements an interface. // Used to implement ERC-165. mapping(bytes4 => bool) supportedInterfaces; // owner of the contract address contractOwner; } function diamondStorage() internal pure returns (DiamondStorage storage ds) { bytes32 position = DIAMOND_STORAGE_POSITION; // solhint-disable-next-line no-inline-assembly assembly { ds.slot := position } } event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); function setContractOwner(address _newOwner) internal { DiamondStorage storage ds = diamondStorage(); address previousOwner = ds.contractOwner; ds.contractOwner = _newOwner; emit OwnershipTransferred(previousOwner, _newOwner); } function contractOwner() internal view returns (address contractOwner_) { contractOwner_ = diamondStorage().contractOwner; } function enforceIsContractOwner() internal view { require(msg.sender == diamondStorage().contractOwner, "LibDiamond: Must be contract owner"); } event DiamondCut(IDiamondCut.FacetCut[] _diamondCut, address _init, bytes _calldata); // Internal function version of diamondCut function diamondCut( IDiamondCut.FacetCut[] memory _diamondCut, address _init, bytes memory _calldata ) internal { for (uint256 facetIndex; facetIndex < _diamondCut.length; ) { IDiamondCut.FacetCutAction action = _diamondCut[facetIndex].action; if (action == IDiamondCut.FacetCutAction.Add) { addFunctions(_diamondCut[facetIndex].facetAddress, _diamondCut[facetIndex].functionSelectors); } else if (action == IDiamondCut.FacetCutAction.Replace) { replaceFunctions(_diamondCut[facetIndex].facetAddress, _diamondCut[facetIndex].functionSelectors); } else if (action == IDiamondCut.FacetCutAction.Remove) { removeFunctions(_diamondCut[facetIndex].facetAddress, _diamondCut[facetIndex].functionSelectors); } else { revert IncorrectFacetCutAction(); } unchecked { ++facetIndex; } } emit DiamondCut(_diamondCut, _init, _calldata); initializeDiamondCut(_init, _calldata); } function addFunctions(address _facetAddress, bytes4[] memory _functionSelectors) internal { if (_facetAddress == address(0)) { revert FacetAddressIsZero(); } if (_functionSelectors.length == 0) { revert NoSelectorsInFacet(); } DiamondStorage storage ds = diamondStorage(); uint96 selectorPosition = uint96(ds.facetFunctionSelectors[_facetAddress].functionSelectors.length); // add new facet address if it does not exist if (selectorPosition == 0) { addFacet(ds, _facetAddress); } for (uint256 selectorIndex; selectorIndex < _functionSelectors.length; ) { bytes4 selector = _functionSelectors[selectorIndex]; address oldFacetAddress = ds.selectorToFacetAndPosition[selector].facetAddress; if (oldFacetAddress != address(0)) { revert FunctionAlreadyExists(); } addFunction(ds, selector, selectorPosition, _facetAddress); unchecked { ++selectorPosition; ++selectorIndex; } } } function replaceFunctions(address _facetAddress, bytes4[] memory _functionSelectors) internal { if (_functionSelectors.length == 0) { revert NoSelectorsInFacet(); } if (_facetAddress == address(0)) { revert FacetAddressIsZero(); } DiamondStorage storage ds = diamondStorage(); uint96 selectorPosition = uint96(ds.facetFunctionSelectors[_facetAddress].functionSelectors.length); // add new facet address if it does not exist if (selectorPosition == 0) { addFacet(ds, _facetAddress); } for (uint256 selectorIndex; selectorIndex < _functionSelectors.length; ) { bytes4 selector = _functionSelectors[selectorIndex]; address oldFacetAddress = ds.selectorToFacetAndPosition[selector].facetAddress; if (oldFacetAddress == _facetAddress) { revert FunctionAlreadyExists(); } removeFunction(ds, oldFacetAddress, selector); addFunction(ds, selector, selectorPosition, _facetAddress); unchecked { ++selectorPosition; ++selectorIndex; } } } function removeFunctions(address _facetAddress, bytes4[] memory _functionSelectors) internal { if (_functionSelectors.length == 0) { revert NoSelectorsInFacet(); } DiamondStorage storage ds = diamondStorage(); // if function does not exist then do nothing and return if (_facetAddress != address(0)) { revert FacetAddressIsNotZero(); } for (uint256 selectorIndex; selectorIndex < _functionSelectors.length; ) { bytes4 selector = _functionSelectors[selectorIndex]; address oldFacetAddress = ds.selectorToFacetAndPosition[selector].facetAddress; removeFunction(ds, oldFacetAddress, selector); unchecked { ++selectorIndex; } } } function addFacet(DiamondStorage storage ds, address _facetAddress) internal { enforceHasContractCode(_facetAddress); ds.facetFunctionSelectors[_facetAddress].facetAddressPosition = ds.facetAddresses.length; ds.facetAddresses.push(_facetAddress); } function addFunction( DiamondStorage storage ds, bytes4 _selector, uint96 _selectorPosition, address _facetAddress ) internal { ds.selectorToFacetAndPosition[_selector].functionSelectorPosition = _selectorPosition; ds.facetFunctionSelectors[_facetAddress].functionSelectors.push(_selector); ds.selectorToFacetAndPosition[_selector].facetAddress = _facetAddress; } function removeFunction( DiamondStorage storage ds, address _facetAddress, bytes4 _selector ) internal { if (_facetAddress == address(0)) { revert FunctionDoesNotExist(); } // an immutable function is a function defined directly in a diamond if (_facetAddress == address(this)) { revert FunctionIsImmutable(); } // replace selector with last selector, then delete last selector uint256 selectorPosition = ds.selectorToFacetAndPosition[_selector].functionSelectorPosition; uint256 lastSelectorPosition = ds.facetFunctionSelectors[_facetAddress].functionSelectors.length - 1; // if not the same then replace _selector with lastSelector if (selectorPosition != lastSelectorPosition) { bytes4 lastSelector = ds.facetFunctionSelectors[_facetAddress].functionSelectors[lastSelectorPosition]; ds.facetFunctionSelectors[_facetAddress].functionSelectors[selectorPosition] = lastSelector; ds.selectorToFacetAndPosition[lastSelector].functionSelectorPosition = uint96(selectorPosition); } // delete the last selector ds.facetFunctionSelectors[_facetAddress].functionSelectors.pop(); delete ds.selectorToFacetAndPosition[_selector]; // if no more selectors for facet address then delete the facet address if (lastSelectorPosition == 0) { // replace facet address with last facet address and delete last facet address uint256 lastFacetAddressPosition = ds.facetAddresses.length - 1; uint256 facetAddressPosition = ds.facetFunctionSelectors[_facetAddress].facetAddressPosition; if (facetAddressPosition != lastFacetAddressPosition) { address lastFacetAddress = ds.facetAddresses[lastFacetAddressPosition]; ds.facetAddresses[facetAddressPosition] = lastFacetAddress; ds.facetFunctionSelectors[lastFacetAddress].facetAddressPosition = facetAddressPosition; } ds.facetAddresses.pop(); delete ds.facetFunctionSelectors[_facetAddress].facetAddressPosition; } } function initializeDiamondCut(address _init, bytes memory _calldata) internal { if (_init == address(0)) { if (_calldata.length != 0) { revert InitZeroButCalldataNotEmpty(); } } else { if (_calldata.length == 0) { revert CalldataEmptyButInitNotZero(); } if (_init != address(this)) { enforceHasContractCode(_init); } // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory error) = _init.delegatecall(_calldata); if (!success) { if (error.length > 0) { // bubble up the error revert(string(error)); } else { revert InitReverted(); } } } } function enforceHasContractCode(address _contract) internal view { uint256 contractSize; // solhint-disable-next-line no-inline-assembly assembly { contractSize := extcodesize(_contract) } if (contractSize == 0) { revert FacetContainsNoCode(); } } }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity 0.8.16; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "../interfaces/IUniswapV2.sol"; import "../interfaces/IUniswapV3.sol"; import "../interfaces/IWETH.sol"; import "../interfaces/IRangoStargate.sol"; import "../interfaces/IStargateReceiver.sol"; import "../interfaces/IRangoMessageReceiver.sol"; import "./LibSwapper.sol"; library LibInterchain { /// @dev keccak256("exchange.rango.library.interchain") bytes32 internal constant BASE_MESSAGING_CONTRACT_NAMESPACE = hex"ff95014231b901d2b22bd69b4e83dacd84ac05e8c2d1e9fba0c7e2f3ed0db0eb"; struct BaseInterchainStorage { mapping (address => bool) whitelistMessagingContracts; } // @notice Adds a contract to the whitelisted messaging dApps that can be called /// @param _dapp The address of dApp function addMessagingDApp(address _dapp) internal { BaseInterchainStorage storage baseStorage = getBaseMessagingContractStorage(); baseStorage.whitelistMessagingContracts[_dapp] = true; } /// @notice Removes a contract from dApps that can be called /// @param _dapp The address of dApp function removeMessagingDApp(address _dapp) internal { BaseInterchainStorage storage baseStorage = getBaseMessagingContractStorage(); require(baseStorage.whitelistMessagingContracts[_dapp], "contract not whitelisted"); delete baseStorage.whitelistMessagingContracts[_dapp]; } /// @notice This event indicates that a dApp used Rango messaging (dAppMessage field) and we delivered the message to it /// @param _receiverContract The address of dApp's contract that was called /// @param _token The address of the token that is sent to the dApp, ETH for native token /// @param _amount The amount of the token sent to them /// @param _status The status of operation, informing the dApp that the whole process was a success or refund /// @param _appMessage The custom message that the dApp asked Rango to deliver /// @param success Indicates that the function call to the dApp encountered error or not /// @param failReason If success = false, failReason will be the string reason of the failure (aka message of require) event CrossChainMessageCalled( address _receiverContract, address _token, uint _amount, IRangoMessageReceiver.ProcessStatus _status, bytes _appMessage, bool success, string failReason ); event ActionDone(Interchain.ActionType actionType, address contractAddress, bool success, string reason); event SubActionDone(Interchain.CallSubActionType subActionType, address contractAddress, bool success, string reason); function encodeIm(Interchain.RangoInterChainMessage memory im) external pure returns (bytes memory) { return abi.encode(im); } function handleDestinationMessage( address _token, uint _amount, Interchain.RangoInterChainMessage memory m ) internal returns (address, uint256 dstAmount, IRango.CrossChainOperationStatus status) { LibSwapper.BaseSwapperStorage storage baseStorage = LibSwapper.getBaseSwapperStorage(); address sourceToken = m.bridgeRealOutput == LibSwapper.ETH && _token == baseStorage.WETH ? LibSwapper.ETH : _token; bool ok = true; address receivedToken = sourceToken; dstAmount = _amount; if (m.actionType == Interchain.ActionType.UNI_V2) (ok, dstAmount, receivedToken) = _handleUniswapV2(sourceToken, _amount, m, baseStorage); else if (m.actionType == Interchain.ActionType.UNI_V3) (ok, dstAmount, receivedToken) = _handleUniswapV3(sourceToken, _amount, m, baseStorage); else if (m.actionType == Interchain.ActionType.CALL) (ok, dstAmount, receivedToken) = _handleCall(sourceToken, _amount, m, baseStorage); else if (m.actionType != Interchain.ActionType.NO_ACTION) revert("Unsupported actionType"); if (ok && m.postAction != Interchain.CallSubActionType.NO_ACTION) { (ok, dstAmount, receivedToken) = _handlePostAction(receivedToken, dstAmount, m.postAction, baseStorage); } status = ok ? IRango.CrossChainOperationStatus.Succeeded : IRango.CrossChainOperationStatus.RefundInDestination; IRangoMessageReceiver.ProcessStatus dAppStatus = ok ? IRangoMessageReceiver.ProcessStatus.SUCCESS : IRangoMessageReceiver.ProcessStatus.REFUND_IN_DESTINATION; _sendTokenWithDApp(receivedToken, dstAmount, m.recipient, m.dAppMessage, m.dAppDestContract, dAppStatus); return (receivedToken, dstAmount, status); } /// @notice Performs a uniswap-v2 operation /// @param _message The interchain message that contains the swap info /// @param _amount The amount of input token /// @return ok Indicates that the swap operation was success or fail /// @return amountOut If ok = true, amountOut is the output amount of the swap function _handleUniswapV2( address _token, uint _amount, Interchain.RangoInterChainMessage memory _message, LibSwapper.BaseSwapperStorage storage baseStorage ) private returns (bool ok, uint256 amountOut, address outToken) { Interchain.UniswapV2Action memory action = abi.decode((_message.action), (Interchain.UniswapV2Action)); if (baseStorage.whitelistContracts[action.dexAddress] != true) { // "Dex address is not whitelisted" return (false, _amount, _token); } if (action.path.length < 2) { // "Invalid uniswap-V2 path" return (false, _amount, _token); } bool shouldDeposit = _token == LibSwapper.ETH && action.path[0] == baseStorage.WETH; if (!shouldDeposit) { if (_token != action.path[0]) { // "bridged token must be the same as the first token in destination swap path" return (false, _amount, _token); } } else { IWETH(baseStorage.WETH).deposit{value : _amount}(); } LibSwapper.approve(action.path[0], action.dexAddress, _amount); address toToken = action.path[action.path.length - 1]; uint toBalanceBefore = LibSwapper.getBalanceOf(toToken); try IUniswapV2(action.dexAddress).swapExactTokensForTokens( _amount, action.amountOutMin, action.path, address(this), action.deadline ) returns (uint256[] memory) { emit ActionDone(Interchain.ActionType.UNI_V2, action.dexAddress, true, ""); // Note: instead of using return amounts of swapExactTokensForTokens, // we get the diff balance of before and after. This prevents errors for tokens with transfer fees uint toBalanceAfter = LibSwapper.getBalanceOf(toToken); SafeERC20.safeApprove(IERC20(action.path[0]), action.dexAddress, 0); return (true, toBalanceAfter - toBalanceBefore, toToken); } catch { emit ActionDone(Interchain.ActionType.UNI_V2, action.dexAddress, true, "Uniswap-V2 call failed"); SafeERC20.safeApprove(IERC20(action.path[0]), action.dexAddress, 0); return (false, _amount, shouldDeposit ? baseStorage.WETH : _token); } } /// @notice Performs a uniswap-v3 operation /// @param _message The interchain message that contains the swap info /// @param _amount The amount of input token /// @return ok Indicates that the swap operation was success or fail /// @return amountOut If ok = true, amountOut is the output amount of the swap function _handleUniswapV3( address _token, uint _amount, Interchain.RangoInterChainMessage memory _message, LibSwapper.BaseSwapperStorage storage baseStorage ) private returns (bool, uint256, address) { Interchain.UniswapV3ActionExactInputSingleParams memory action = abi .decode((_message.action), (Interchain.UniswapV3ActionExactInputSingleParams)); if (baseStorage.whitelistContracts[action.dexAddress] != true) { // "Dex address is not whitelisted" return (false, _amount, _token); } bool shouldDeposit = _token == LibSwapper.ETH && action.tokenIn == baseStorage.WETH; if (!shouldDeposit) { if (_token != action.tokenIn) { // "bridged token must be the same as the tokenIn in uniswapV3" return (false, _amount, _token); } } else { IWETH(baseStorage.WETH).deposit{value : _amount}(); } LibSwapper.approve(action.tokenIn, action.dexAddress, _amount); uint toBalanceBefore = LibSwapper.getBalanceOf(action.tokenOut); try IUniswapV3(action.dexAddress).exactInputSingle(IUniswapV3.ExactInputSingleParams({ tokenIn : action.tokenIn, tokenOut : action.tokenOut, fee : action.fee, recipient : address(this), deadline : action.deadline, amountIn : _amount, amountOutMinimum : action.amountOutMinimum, sqrtPriceLimitX96 : action.sqrtPriceLimitX96 })) returns (uint) { emit ActionDone(Interchain.ActionType.UNI_V3, action.dexAddress, true, ""); // Note: instead of using return amounts of exactInputSingle, // we get the diff balance of before and after. This prevents errors for tokens with transfer fees. uint toBalanceAfter = LibSwapper.getBalanceOf(action.tokenOut); SafeERC20.safeApprove(IERC20(action.tokenIn), action.dexAddress, 0); return (true, toBalanceAfter - toBalanceBefore, action.tokenOut); } catch { emit ActionDone(Interchain.ActionType.UNI_V3, action.dexAddress, false, "Uniswap-V3 call failed"); SafeERC20.safeApprove(IERC20(action.tokenIn), action.dexAddress, 0); return (false, _amount, shouldDeposit ? baseStorage.WETH : _token); } } /// @notice Performs a uniswap-v2 operation /// @param _message The interchain message that contains the swap info /// @param _amount The amount of input token /// @return ok Indicates that the swap operation was success or fail /// @return amountOut If ok = true, amountOut is the output amount of the swap function _handleCall( address _token, uint _amount, Interchain.RangoInterChainMessage memory _message, LibSwapper.BaseSwapperStorage storage baseStorage ) private returns (bool ok, uint256 amountOut, address outToken) { Interchain.CallAction memory action = abi.decode((_message.action), (Interchain.CallAction)); if (baseStorage.whitelistContracts[action.target] != true) { // "Action.target is not whitelisted" return (false, _amount, _token); } if (baseStorage.whitelistContracts[action.spender] != true) { // "Action.spender is not whitelisted" return (false, _amount, _token); } address sourceToken = _token; if (action.preAction == Interchain.CallSubActionType.WRAP) { if (_token != LibSwapper.ETH) { // "Cannot wrap non-native" return (false, _amount, _token); } if (action.tokenIn != baseStorage.WETH) { // "action.tokenIn must be WETH" return (false, _amount, _token); } (ok, amountOut, sourceToken) = _handleWrap(_token, _amount, baseStorage); } else if (action.preAction == Interchain.CallSubActionType.UNWRAP) { if (_token != baseStorage.WETH) { // "Cannot unwrap non-WETH" return (false, _amount, _token); } if (action.tokenIn != LibSwapper.ETH) { // "action.tokenIn must be ETH" return (false, _amount, _token); } (ok, amountOut, sourceToken) = _handleUnwrap(_token, _amount, baseStorage); } else { ok = true; if (action.tokenIn != _token) { // "_message.tokenIn mismatch in call" return (false, _amount, _token); } } if (!ok) return (false, _amount, _token); if (sourceToken != LibSwapper.ETH) LibSwapper.approve(sourceToken, action.spender, _amount); uint value = sourceToken == LibSwapper.ETH ? _amount : 0; uint toBalanceBefore = LibSwapper.getBalanceOf(_message.toToken); (bool success, bytes memory ret) = action.target.call{value: value}(action.callData); if (sourceToken != LibSwapper.ETH) SafeERC20.safeApprove(IERC20(sourceToken), action.spender, 0); if (success) { emit ActionDone(Interchain.ActionType.CALL, action.target, true, ""); uint toBalanceAfter = LibSwapper.getBalanceOf(_message.toToken); return (true, toBalanceAfter - toBalanceBefore, _message.toToken); } else { emit ActionDone(Interchain.ActionType.CALL, action.target, false, LibSwapper._getRevertMsg(ret)); return (false, _amount, sourceToken); } } /// @notice Performs a uniswap-v2 operation /// @param _postAction The type of action to perform such as WRAP, UNWRAP /// @param _amount The amount of input token /// @return ok Indicates that the swap operation was success or fail /// @return amountOut If ok = true, amountOut is the output amount of the swap function _handlePostAction( address _token, uint _amount, Interchain.CallSubActionType _postAction, LibSwapper.BaseSwapperStorage storage baseStorage ) private returns (bool ok, uint256 amountOut, address outToken) { if (_postAction == Interchain.CallSubActionType.WRAP) { if (_token != LibSwapper.ETH) { // "Cannot wrap non-native" return (false, _amount, _token); } (ok, amountOut, outToken) = _handleWrap(_token, _amount, baseStorage); } else if (_postAction == Interchain.CallSubActionType.UNWRAP) { if (_token != baseStorage.WETH) { // "Cannot unwrap non-WETH" return (false, _amount, _token); } (ok, amountOut, outToken) = _handleUnwrap(_token, _amount, baseStorage); } else { // revert("Unsupported post-action"); return (false, _amount, _token); } if (!ok) return (false, _amount, _token); return (ok, amountOut, outToken); } /// @notice Performs a WETH.deposit operation /// @param _amount The amount of input token /// @return ok Indicates that the swap operation was success or fail /// @return amountOut If ok = true, amountOut is the output amount of the swap function _handleWrap( address _token, uint _amount, LibSwapper.BaseSwapperStorage storage baseStorage ) private returns (bool ok, uint256 amountOut, address outToken) { if (_token != LibSwapper.ETH) { // "Cannot wrap non-ETH tokens" return (false, _amount, _token); } IWETH(baseStorage.WETH).deposit{value: _amount}(); emit SubActionDone(Interchain.CallSubActionType.WRAP, baseStorage.WETH, true, ""); return (true, _amount, baseStorage.WETH); } /// @notice Performs a WETH.deposit operation /// @param _amount The amount of input token /// @return ok Indicates that the swap operation was success or fail /// @return amountOut If ok = true, amountOut is the output amount of the swap function _handleUnwrap( address _token, uint _amount, LibSwapper.BaseSwapperStorage storage baseStorage ) private returns (bool ok, uint256 amountOut, address outToken) { if (_token != baseStorage.WETH) // revert("Non-WETH tokens unwrapped"); return (false, _amount, _token); IWETH(baseStorage.WETH).withdraw(_amount); emit SubActionDone(Interchain.CallSubActionType.UNWRAP, baseStorage.WETH, true, ""); return (true, _amount, LibSwapper.ETH); } /// @notice An internal function to send a token from the current contract to another contract or wallet /// @dev This function also can convert WETH to ETH before sending if _withdraw flat is set to true /// @dev To send native token _nativeOut param should be set to true, otherwise we assume it's an ERC20 transfer /// @dev If there is a message from a dApp it sends the money to the contract instead of the end-user and calls its handleRangoMessage /// @param _token The token that is going to be sent to a wallet, ZERO address for native /// @param _amount The sent amount /// @param _receiver The receiver wallet address or contract function _sendTokenWithDApp( address _token, uint256 _amount, address _receiver, bytes memory _dAppMessage, address _dAppReceiverContract, IRangoMessageReceiver.ProcessStatus processStatus ) internal { bool thereIsAMessage = _dAppReceiverContract != LibSwapper.ETH; address immediateReceiver = thereIsAMessage ? _dAppReceiverContract : _receiver; BaseInterchainStorage storage messagingStorage = getBaseMessagingContractStorage(); emit LibSwapper.SendToken(_token, _amount, immediateReceiver); if (_token == LibSwapper.ETH) { LibSwapper._sendNative(immediateReceiver, _amount); } else { SafeERC20.safeTransfer(IERC20(_token), immediateReceiver, _amount); } if (thereIsAMessage) { require( messagingStorage.whitelistMessagingContracts[_dAppReceiverContract], "3rd-party contract not whitelisted" ); try IRangoMessageReceiver(_dAppReceiverContract) .handleRangoMessage(_token, _amount, processStatus, _dAppMessage) { emit CrossChainMessageCalled(_dAppReceiverContract, _token, _amount, processStatus, _dAppMessage, true, ""); } catch Error(string memory reason) { emit CrossChainMessageCalled(_dAppReceiverContract, _token, _amount, processStatus, _dAppMessage, false, reason); } catch (bytes memory lowLevelData) { emit CrossChainMessageCalled(_dAppReceiverContract, _token, _amount, processStatus, _dAppMessage, false, LibSwapper._getRevertMsg(lowLevelData)); } } } /// @notice A utility function to fetch storage from a predefined random slot using assembly /// @return s The storage object function getBaseMessagingContractStorage() internal pure returns (BaseInterchainStorage storage s) { bytes32 namespace = BASE_MESSAGING_CONTRACT_NAMESPACE; // solhint-disable-next-line no-inline-assembly assembly { s.slot := namespace } } }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity 0.8.16; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "../interfaces/IWETH.sol"; import "../interfaces/IRango.sol"; /// @title BaseSwapper /// @author 0xiden /// @notice library to provide swap functionality library LibSwapper { /// @dev keccak256("exchange.rango.library.swapper") bytes32 internal constant BASE_SWAPPER_NAMESPACE = hex"43da06808a8e54e76a41d6f7b48ddfb23969b1387a8710ef6241423a5aefe64a"; address payable constant ETH = payable(0x0000000000000000000000000000000000000000); struct BaseSwapperStorage { address payable feeContractAddress; address WETH; mapping(address => bool) whitelistContracts; mapping(address => mapping(bytes4 => bool)) whitelistMethods; } /// @notice Emitted if any fee transfer was required /// @param token The address of received token, address(0) for native /// @param affiliatorAddress The address of affiliate wallet /// @param platformFee The amount received as platform fee /// @param destinationExecutorFee The amount received to execute transaction on destination (only for cross chain txs) /// @param affiliateFee The amount received by affiliate /// @param dAppTag Optional identifier to make tracking easier. event FeeInfo( address token, address indexed affiliatorAddress, uint platformFee, uint destinationExecutorFee, uint affiliateFee, uint16 indexed dAppTag ); /// @notice A call to another dex or contract done and here is the result /// @param target The address of dex or contract that is called /// @param success A boolean indicating that the call was success or not /// @param returnData The response of function call event CallResult(address target, bool success, bytes returnData); /// @notice A swap request is done and we also emit the output /// @param requestId Optional parameter to make tracking of transaction easier /// @param fromToken Input token address to be swapped from /// @param toToken Output token address to be swapped to /// @param amountIn Input amount of fromToken that is being swapped /// @param dAppTag Optional identifier to make tracking easier /// @param outputAmount The output amount of the swap, measured by the balance change before and after the swap /// @param receiver The address to receive the output of swap. Can be address(0) when swap is before a bridge action event RangoSwap( address indexed requestId, address fromToken, address toToken, uint amountIn, uint minimumAmountExpected, uint16 indexed dAppTag, uint outputAmount, address receiver ); /// @notice Output amount of a dex calls is logged /// @param _token The address of output token, ZERO address for native /// @param amount The amount of output event DexOutput(address _token, uint amount); /// @notice The output money (ERC20/Native) is sent to a wallet /// @param _token The token that is sent to a wallet, ZERO address for native /// @param _amount The sent amount /// @param _receiver The receiver wallet address event SendToken(address _token, uint256 _amount, address _receiver); /// @notice Notifies that Rango's fee receiver address updated /// @param _oldAddress The previous fee wallet address /// @param _newAddress The new fee wallet address event FeeContractAddressUpdated(address _oldAddress, address _newAddress); /// @notice Notifies that WETH address is updated /// @param _oldAddress The previous weth address /// @param _newAddress The new weth address event WethContractAddressUpdated(address _oldAddress, address _newAddress); /// @notice Notifies that admin manually refunded some money /// @param _token The address of refunded token, 0x000..00 address for native token /// @param _amount The amount that is refunded event Refunded(address _token, uint _amount); /// @notice The requested call data which is computed off-chain and passed to the contract /// @dev swapFromToken and amount parameters are only helper params and the actual amount and /// token are set in callData /// @param spender The contract which the approval is given to if swapFromToken is not native. /// @param target The dex contract address that should be called /// @param swapFromToken Token address of to be used in the swap. /// @param amount The amount to be approved or native amount sent. /// @param callData The required data field that should be give to the dex contract to perform swap struct Call { address spender; address payable target; address swapFromToken; address swapToToken; bool needsTransferFromUser; uint amount; bytes callData; } /// @notice General swap request which is given to us in all relevant functions /// @param requestId The request id passed to make tracking transactions easier /// @param fromToken The source token that is going to be swapped (in case of simple swap or swap + bridge) or the briding token (in case of solo bridge) /// @param toToken The output token of swapping. This is the output of DEX step and is also input of bridging step /// @param amountIn The amount of input token to be swapped /// @param platformFee The amount of fee charged by platform /// @param destinationExecutorFee The amount of fee required for relayer execution on the destination /// @param affiliateFee The amount of fee charged by affiliator dApp /// @param affiliatorAddress The wallet address that the affiliator fee should be sent to /// @param minimumAmountExpected The minimum amount of toToken expected after executing Calls /// @param dAppTag An optional parameter struct SwapRequest { address requestId; address fromToken; address toToken; uint amountIn; uint platformFee; uint destinationExecutorFee; uint affiliateFee; address payable affiliatorAddress; uint minimumAmountExpected; uint16 dAppTag; } /// @notice initializes the base swapper and sets the init params (such as Wrapped token address) /// @param _weth Address of wrapped token (WETH, WBNB, etc.) on the current chain function setWeth(address _weth) internal { BaseSwapperStorage storage baseStorage = getBaseSwapperStorage(); address oldAddress = baseStorage.WETH; baseStorage.WETH = _weth; require(_weth != address(0), "Invalid WETH!"); emit WethContractAddressUpdated(oldAddress, _weth); } /// @notice Sets the wallet that receives Rango's fees from now on /// @param _address The receiver wallet address function updateFeeContractAddress(address payable _address) internal { BaseSwapperStorage storage baseSwapperStorage = getBaseSwapperStorage(); address oldAddress = baseSwapperStorage.feeContractAddress; baseSwapperStorage.feeContractAddress = _address; emit FeeContractAddressUpdated(oldAddress, _address); } /// Whitelist /// /// @notice Adds a contract to the whitelisted DEXes that can be called /// @param contractAddress The address of the DEX function addWhitelist(address contractAddress) internal { BaseSwapperStorage storage baseStorage = getBaseSwapperStorage(); baseStorage.whitelistContracts[contractAddress] = true; } /// @notice Adds a method of contract to the whitelisted DEXes that can be called /// @param contractAddress The address of the DEX /// @param methodIds The method of the DEX function addMethodWhitelists(address contractAddress, bytes4[] calldata methodIds) internal { BaseSwapperStorage storage baseStorage = getBaseSwapperStorage(); baseStorage.whitelistContracts[contractAddress] = true; for (uint i = 0; i < methodIds.length; i++) baseStorage.whitelistMethods[contractAddress][methodIds[i]] = true; } /// @notice Adds a method of contract to the whitelisted DEXes that can be called /// @param contractAddress The address of the DEX /// @param methodId The method of the DEX function addMethodWhitelist(address contractAddress, bytes4 methodId) internal { BaseSwapperStorage storage baseStorage = getBaseSwapperStorage(); baseStorage.whitelistContracts[contractAddress] = true; baseStorage.whitelistMethods[contractAddress][methodId] = true; } /// @notice Removes a contract from the whitelisted DEXes /// @param contractAddress The address of the DEX or dApp function removeWhitelist(address contractAddress) internal { BaseSwapperStorage storage baseStorage = getBaseSwapperStorage(); delete baseStorage.whitelistContracts[contractAddress]; } /// @notice Removes a method of contract from the whitelisted DEXes /// @param contractAddress The address of the DEX or dApp /// @param methodId The method of the DEX function removeMethodWhitelist(address contractAddress, bytes4 methodId) internal { BaseSwapperStorage storage baseStorage = getBaseSwapperStorage(); delete baseStorage.whitelistMethods[contractAddress][methodId]; } function onChainSwapsPreBridge( SwapRequest memory request, Call[] calldata calls, uint extraFee ) internal returns (uint out) { bool isNative = request.fromToken == ETH; uint minimumRequiredValue = (isNative ? request.platformFee + request.affiliateFee + request.amountIn + request.destinationExecutorFee : 0) + extraFee; require(msg.value >= minimumRequiredValue, 'Send more ETH to cover input amount + fee'); (, out) = onChainSwapsInternal(request, calls, extraFee); // when there is a bridge after swap, set the receiver in swap event to address(0) emitSwapEvent(request, out, ETH); return out; } /// @notice Internal function to compute output amount of DEXes /// @param request The general swap request containing from/to token and fee/affiliate rewards /// @param calls The list of DEX calls /// @param extraNativeFee The amount of native tokens to keep and not return to user as excess amount. /// @return The response of all DEX calls and the output amount of the whole process function onChainSwapsInternal( SwapRequest memory request, Call[] calldata calls, uint256 extraNativeFee ) internal returns (bytes[] memory, uint) { uint toBalanceBefore = getBalanceOf(request.toToken); uint fromBalanceBefore = getBalanceOf(request.fromToken); uint256[] memory initialBalancesList = getInitialBalancesList(calls); // transfer tokens from user for SwapRequest and Calls that require transfer from user. transferTokensFromUserForSwapRequest(request); transferTokensFromUserForCalls(calls); bytes[] memory result = callSwapsAndFees(request, calls); // check if any extra tokens were taken from contract and return excess tokens if any. returnExcessAmounts(request, calls, initialBalancesList); // get balance after returning excesses. uint fromBalanceAfter = getBalanceOf(request.fromToken); // check over-expense of fromToken and return excess if any. if (request.fromToken != ETH) { require(fromBalanceAfter >= fromBalanceBefore, "Source token balance on contract must not decrease after swap"); if (fromBalanceAfter > fromBalanceBefore) _sendToken(request.fromToken, fromBalanceAfter - fromBalanceBefore, msg.sender); } else { require(fromBalanceAfter >= fromBalanceBefore - msg.value, "Source token balance on contract must not decrease after swap"); // When we are keeping extraNativeFee for bridgingFee, we should consider it in calculations. if (fromBalanceAfter > fromBalanceBefore - msg.value + extraNativeFee) _sendToken(request.fromToken, fromBalanceAfter + msg.value - fromBalanceBefore - extraNativeFee, msg.sender); } uint toBalanceAfter = getBalanceOf(request.toToken); uint secondaryBalance = toBalanceAfter - toBalanceBefore; require(secondaryBalance >= request.minimumAmountExpected, "Output is less than minimum expected"); return (result, secondaryBalance); } /// @notice Private function to handle fetching money from wallet to contract, reduce fee/affiliate, perform DEX calls /// @param request The general swap request containing from/to token and fee/affiliate rewards /// @param calls The list of DEX calls /// @dev It checks the whitelisting of all DEX addresses + having enough msg.value as input /// @return The bytes of all DEX calls response function callSwapsAndFees(SwapRequest memory request, Call[] calldata calls) private returns (bytes[] memory) { bool isSourceNative = request.fromToken == ETH; BaseSwapperStorage storage baseSwapperStorage = getBaseSwapperStorage(); for (uint256 i = 0; i < calls.length; i++) { require(baseSwapperStorage.whitelistContracts[calls[i].spender], "Contract spender not whitelisted"); require(baseSwapperStorage.whitelistContracts[calls[i].target], "Contract target not whitelisted"); bytes4 sig = bytes4(calls[i].callData[: 4]); require(baseSwapperStorage.whitelistMethods[calls[i].target][sig], "Unauthorized call data!"); } // Get Platform fee bool hasPlatformFee = request.platformFee > 0; bool hasDestExecutorFee = request.destinationExecutorFee > 0; bool hasAffiliateFee = request.affiliateFee > 0; if (hasPlatformFee || hasDestExecutorFee) { require(baseSwapperStorage.feeContractAddress != ETH, "Fee contract address not set"); _sendToken(request.fromToken, request.platformFee + request.destinationExecutorFee, baseSwapperStorage.feeContractAddress, isSourceNative, false); } // Get affiliate fee if (hasAffiliateFee) { require(request.affiliatorAddress != ETH, "Invalid affiliatorAddress"); _sendToken(request.fromToken, request.affiliateFee, request.affiliatorAddress, isSourceNative, false); } // emit Fee event if (hasPlatformFee || hasDestExecutorFee || hasAffiliateFee) { emit FeeInfo( request.fromToken, request.affiliatorAddress, request.platformFee, request.destinationExecutorFee, request.affiliateFee, request.dAppTag ); } // Execute swap Calls bytes[] memory returnData = new bytes[](calls.length); address tmpSwapFromToken; for (uint256 i = 0; i < calls.length; i++) { tmpSwapFromToken = calls[i].swapFromToken; bool isTokenNative = tmpSwapFromToken == ETH; if (isTokenNative == false) approveMax(tmpSwapFromToken, calls[i].spender, calls[i].amount); (bool success, bytes memory ret) = isTokenNative ? calls[i].target.call{value : calls[i].amount}(calls[i].callData) : calls[i].target.call(calls[i].callData); emit CallResult(calls[i].target, success, ret); if (!success) revert(_getRevertMsg(ret)); returnData[i] = ret; } return returnData; } /// @notice Approves an ERC20 token to a contract to transfer from the current contract /// @param token The address of an ERC20 token /// @param spender The contract address that should be approved /// @param value The amount that should be approved function approve(address token, address spender, uint value) internal { SafeERC20.safeApprove(IERC20(token), spender, 0); SafeERC20.safeIncreaseAllowance(IERC20(token), spender, value); } /// @notice Approves an ERC20 token to a contract to transfer from the current contract, approves for inf value /// @param token The address of an ERC20 token /// @param spender The contract address that should be approved /// @param value The desired allowance. If current allowance is less than this value, infinite allowance will be given function approveMax(address token, address spender, uint value) internal { uint256 currentAllowance = IERC20(token).allowance(address(this), spender); if (currentAllowance < value) { if (currentAllowance != 0) { // We set allowance to 0 if not already. tokens such as USDT require zero allowance first. SafeERC20.safeApprove(IERC20(token), spender, 0); } SafeERC20.safeIncreaseAllowance(IERC20(token), spender, type(uint256).max); } } function _sendToken(address _token, uint256 _amount, address _receiver) internal { (_token == ETH) ? _sendNative(_receiver, _amount) : SafeERC20.safeTransfer(IERC20(_token), _receiver, _amount); } function sumFees(IRango.RangoBridgeRequest memory request) internal pure returns (uint256) { return request.platformFee + request.affiliateFee + request.destinationExecutorFee; } function sumFees(SwapRequest memory request) internal pure returns (uint256) { return request.platformFee + request.affiliateFee + request.destinationExecutorFee; } function collectFees(IRango.RangoBridgeRequest memory request) internal { // Get Platform fee bool hasPlatformFee = request.platformFee > 0; bool hasDestExecutorFee = request.destinationExecutorFee > 0; bool hasAffiliateFee = request.affiliateFee > 0; bool hasAnyFee = hasPlatformFee || hasDestExecutorFee || hasAffiliateFee; if (!hasAnyFee) { return; } bool isSourceNative = request.token == ETH; BaseSwapperStorage storage baseSwapperStorage = getBaseSwapperStorage(); if (hasPlatformFee || hasDestExecutorFee) { require(baseSwapperStorage.feeContractAddress != ETH, "Fee contract address not set"); _sendToken(request.token, request.platformFee + request.destinationExecutorFee, baseSwapperStorage.feeContractAddress, isSourceNative, false); } // Get affiliate fee if (hasAffiliateFee) { require(request.affiliatorAddress != ETH, "Invalid affiliatorAddress"); _sendToken(request.token, request.affiliateFee, request.affiliatorAddress, isSourceNative, false); } // emit Fee event emit FeeInfo( request.token, request.affiliatorAddress, request.platformFee, request.destinationExecutorFee, request.affiliateFee, request.dAppTag ); } function collectFeesFromSender(IRango.RangoBridgeRequest memory request) internal { // Get Platform fee bool hasPlatformFee = request.platformFee > 0; bool hasDestExecutorFee = request.destinationExecutorFee > 0; bool hasAffiliateFee = request.affiliateFee > 0; bool hasAnyFee = hasPlatformFee || hasDestExecutorFee || hasAffiliateFee; if (!hasAnyFee) { return; } bool isSourceNative = request.token == ETH; BaseSwapperStorage storage baseSwapperStorage = getBaseSwapperStorage(); if (hasPlatformFee || hasDestExecutorFee) { require(baseSwapperStorage.feeContractAddress != ETH, "Fee contract address not set"); if (isSourceNative) _sendToken(request.token, request.platformFee + request.destinationExecutorFee, baseSwapperStorage.feeContractAddress, isSourceNative, false); else SafeERC20.safeTransferFrom( IERC20(request.token), msg.sender, baseSwapperStorage.feeContractAddress, request.platformFee + request.destinationExecutorFee ); } // Get affiliate fee if (hasAffiliateFee) { require(request.affiliatorAddress != ETH, "Invalid affiliatorAddress"); if (isSourceNative) _sendToken(request.token, request.affiliateFee, request.affiliatorAddress, isSourceNative, false); else SafeERC20.safeTransferFrom( IERC20(request.token), msg.sender, request.affiliatorAddress, request.affiliateFee ); } // emit Fee event emit FeeInfo( request.token, request.affiliatorAddress, request.platformFee, request.destinationExecutorFee, request.affiliateFee, request.dAppTag ); } /// @notice An internal function to send a token from the current contract to another contract or wallet /// @dev This function also can convert WETH to ETH before sending if _withdraw flat is set to true /// @dev To send native token _nativeOut param should be set to true, otherwise we assume it's an ERC20 transfer /// @param _token The token that is going to be sent to a wallet, ZERO address for native /// @param _amount The sent amount /// @param _receiver The receiver wallet address or contract /// @param _nativeOut means the output is native token /// @param _withdraw If true, indicates that we should swap WETH to ETH before sending the money and _nativeOut must also be true function _sendToken( address _token, uint256 _amount, address _receiver, bool _nativeOut, bool _withdraw ) internal { BaseSwapperStorage storage baseStorage = getBaseSwapperStorage(); emit SendToken(_token, _amount, _receiver); if (_nativeOut) { if (_withdraw) { require(_token == baseStorage.WETH, "token mismatch"); IWETH(baseStorage.WETH).withdraw(_amount); } _sendNative(_receiver, _amount); } else { SafeERC20.safeTransfer(IERC20(_token), _receiver, _amount); } } /// @notice An internal function to send native token to a contract or wallet /// @param _receiver The address that will receive the native token /// @param _amount The amount of the native token that should be sent function _sendNative(address _receiver, uint _amount) internal { (bool sent,) = _receiver.call{value : _amount}(""); require(sent, "failed to send native"); } /// @notice A utility function to fetch storage from a predefined random slot using assembly /// @return s The storage object function getBaseSwapperStorage() internal pure returns (BaseSwapperStorage storage s) { bytes32 namespace = BASE_SWAPPER_NAMESPACE; // solhint-disable-next-line no-inline-assembly assembly { s.slot := namespace } } /// @notice To extract revert message from a DEX/contract call to represent to the end-user in the blockchain /// @param _returnData The resulting bytes of a failed call to a DEX or contract /// @return A string that describes what was the error function _getRevertMsg(bytes memory _returnData) internal pure returns (string memory) { // If the _res length is less than 68, then the transaction failed silently (without a revert message) if (_returnData.length < 68) return 'Transaction reverted silently'; assembly { // Slice the sighash. _returnData := add(_returnData, 0x04) } return abi.decode(_returnData, (string)); // All that remains is the revert string } function getBalanceOf(address token) internal view returns (uint) { return token == ETH ? address(this).balance : IERC20(token).balanceOf(address(this)); } /// @notice Fetches the balances of swapToTokens. /// @dev this fetches the balances for swapToToken of swap Calls. If native eth is received, the balance has already increased so we subtract msg.value. function getInitialBalancesList(Call[] calldata calls) internal view returns (uint256[] memory) { uint callsLength = calls.length; uint256[] memory balancesList = new uint256[](callsLength); address token; for (uint256 i = 0; i < callsLength; i++) { token = calls[i].swapToToken; balancesList[i] = getBalanceOf(token); if (token == ETH) balancesList[i] -= msg.value; } return balancesList; } /// This function transfers tokens from users based on the SwapRequest, it transfers amountIn + fees. function transferTokensFromUserForSwapRequest(SwapRequest memory request) private { uint transferAmount = request.amountIn + sumFees(request); if (request.fromToken != ETH) SafeERC20.safeTransferFrom(IERC20(request.fromToken), msg.sender, address(this), transferAmount); else require(msg.value >= transferAmount); } /// This function iterates on calls and if needsTransferFromUser, transfers tokens from user function transferTokensFromUserForCalls(Call[] calldata calls) private { uint callsLength = calls.length; Call calldata call; address token; for (uint256 i = 0; i < callsLength; i++) { call = calls[i]; token = call.swapFromToken; if (call.needsTransferFromUser && token != ETH) SafeERC20.safeTransferFrom(IERC20(call.swapFromToken), msg.sender, address(this), call.amount); } } /// @dev returns any excess token left by the contract. /// We iterate over `swapToToken`s because each swapToToken is either the request.toToken or is the output of /// another `Call` in the list of swaps which itself either has transferred tokens from user, /// or is a middle token that is the output of another `Call`. function returnExcessAmounts( SwapRequest memory request, Call[] calldata calls, uint256[] memory initialBalancesList) internal { uint excessAmountToToken; address tmpSwapToToken; uint currentBalanceTo; for (uint256 i = 0; i < calls.length; i++) { tmpSwapToToken = calls[i].swapToToken; currentBalanceTo = getBalanceOf(tmpSwapToToken); excessAmountToToken = currentBalanceTo - initialBalancesList[i]; if (excessAmountToToken > 0 && tmpSwapToToken != request.toToken) { _sendToken(tmpSwapToToken, excessAmountToToken, msg.sender); } } } function emitSwapEvent(SwapRequest memory request, uint output, address receiver) internal { emit RangoSwap( request.requestId, request.fromToken, request.toToken, request.amountIn, request.minimumAmountExpected, request.dAppTag, output, receiver ); } }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity 0.8.16; /// @title Reentrancy Guard /// @author /// @notice Abstract contract to provide protection against reentrancy abstract contract ReentrancyGuard { /// Storage /// /// @dev keccak256("exchange.rango.reentrancyguard"); bytes32 private constant NAMESPACE = hex"4fe94118b1030ac5f570795d403ee5116fd91b8f0b5d11f2487377c2b0ab2559"; /// Types /// struct ReentrancyStorage { uint256 status; } /// Errors /// error ReentrancyError(); /// Constants /// uint256 private constant _NOT_ENTERED = 0; uint256 private constant _ENTERED = 1; /// Modifiers /// modifier nonReentrant() { ReentrancyStorage storage s = reentrancyStorage(); if (s.status == _ENTERED) revert ReentrancyError(); s.status = _ENTERED; _; s.status = _NOT_ENTERED; } /// Private Methods /// /// @dev fetch local storage function reentrancyStorage() private pure returns (ReentrancyStorage storage data) { bytes32 position = NAMESPACE; // solhint-disable-next-line no-inline-assembly assembly { data.slot := position } } }
{ "optimizer": { "enabled": true, "runs": 10000 }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "metadata": { "useLiteralContent": true }, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[],"name":"ReentrancyError","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"contractAddress","type":"address"},{"indexed":false,"internalType":"bytes4[]","name":"methods","type":"bytes4[]"}],"name":"ContractAndMethodsBlacklisted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"contractAddress","type":"address"},{"indexed":false,"internalType":"bytes4[]","name":"methods","type":"bytes4[]"}],"name":"ContractAndMethodsWhitelisted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_address","type":"address"}],"name":"ContractBlacklisted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_address","type":"address"}],"name":"ContractWhitelisted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_dapp","type":"address"}],"name":"MessagingDAppBlacklisted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_dapp","type":"address"}],"name":"MessagingDAppWhitelisted","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":[{"indexed":true,"internalType":"address","name":"requestId","type":"address"},{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":true,"internalType":"address","name":"originalSender","type":"address"},{"indexed":false,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"enum IRango.CrossChainOperationStatus","name":"status","type":"uint8"},{"indexed":false,"internalType":"uint16","name":"dAppTag","type":"uint16"}],"name":"RangoBridgeCompleted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"requestId","type":"address"},{"indexed":false,"internalType":"address","name":"bridgeToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"bridgeAmount","type":"uint256"},{"indexed":false,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"destinationChainId","type":"uint256"},{"indexed":false,"internalType":"bool","name":"hasInterchainMessage","type":"bool"},{"indexed":false,"internalType":"bool","name":"hasDestinationSwap","type":"bool"},{"indexed":true,"internalType":"uint8","name":"bridgeId","type":"uint8"},{"indexed":true,"internalType":"uint16","name":"dAppTag","type":"uint16"}],"name":"RangoBridgeInitiated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldAddress","type":"address"},{"indexed":false,"internalType":"address","name":"newAddress","type":"address"}],"name":"RangoDiamondAddressUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_token","type":"address"},{"indexed":false,"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"Refunded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldAddress","type":"address"},{"indexed":false,"internalType":"address","name":"newAddress","type":"address"}],"name":"StargateRouterAddressUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldAddress","type":"address"},{"indexed":false,"internalType":"address","name":"newAddress","type":"address"}],"name":"WethAddressUpdated","type":"event"},{"inputs":[{"internalType":"address[]","name":"_dapps","type":"address[]"}],"name":"addMessagingDAppsMiddleWare","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"contractAddress","type":"address"},{"internalType":"bytes4[]","name":"methodIds","type":"bytes4[]"}],"internalType":"struct RangoBaseInterchainMiddleware.whitelistRequest[]","name":"req","type":"tuple[]"}],"name":"addWhitelistContractMiddleWare","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"address","name":"_rangoDiamond","type":"address"},{"internalType":"address","name":"_weth","type":"address"}],"name":"initBaseMiddleware","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"address","name":"_stargateRouter","type":"address"},{"internalType":"address","name":"_weth","type":"address"}],"name":"initStargateMiddleware","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_tokenAddress","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"refund","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"refundNative","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"contractAddress","type":"address"},{"internalType":"bytes4[]","name":"methodIds","type":"bytes4[]"}],"name":"removeContractAndMethodIdsFromWhitelist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_dapp","type":"address"}],"name":"removeMessagingDAppContractMiddleWare","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"contractAddress","type":"address"}],"name":"removeWhitelistMiddleWare","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"","type":"uint16"},{"internalType":"bytes","name":"","type":"bytes"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"_token","type":"address"},{"internalType":"uint256","name":"amountLD","type":"uint256"},{"internalType":"bytes","name":"payload","type":"bytes"}],"name":"sgReceive","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"newAddress","type":"address"}],"name":"updateOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newAddress","type":"address"}],"name":"updateRangoDiamondAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newAddress","type":"address"}],"name":"updateStargateRouter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newAddress","type":"address"}],"name":"updateWethAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]
Contract Creation Code
60806040523480156200001157600080fd5b506200001d3262000023565b620000b8565b7fad914d4300c64e1902ca499875cd8a76ae717047bcfaa9e806ff7ea4f691126980546001600160a01b031981166001600160a01b038481169182179093556040517fad914d4300c64e1902ca499875cd8a76ae717047bcfaa9e806ff7ea4f6911268939092169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3505050565b6140a080620000c86000396000f3fe6080604052600436106100e15760003560e01c80639f49428b1161007f578063ab8236f311610059578063ab8236f31461022f578063c8a430af14610242578063de96441f14610262578063f0ee655b1461028257600080fd5b80639f49428b146101cf5780639fae52e6146101ef578063a510e6001461020f57600080fd5b8063410085df116100bb578063410085df1461014f5780637f10a9221461016f5780638808074a1461018f578063880cdc31146101af57600080fd5b806303de9416146100ed57806305369efa1461010f5780630a6dc7ba1461012f57600080fd5b366100e857005b600080fd5b3480156100f957600080fd5b5061010d6101083660046130ca565b6102a2565b005b34801561011b57600080fd5b5061010d61012a36600461311f565b6104a3565b34801561013b57600080fd5b5061010d61014a36600461311f565b61052b565b34801561015b57600080fd5b5061010d61016a36600461313c565b6105b0565b34801561017b57600080fd5b5061010d61018a366004613168565b610757565b34801561019b57600080fd5b5061010d6101aa36600461311f565b610801565b3480156101bb57600080fd5b5061010d6101ca36600461311f565b610886565b3480156101db57600080fd5b5061010d6101ea3660046131b3565b61090b565b3480156101fb57600080fd5b5061010d61020a3660046131f5565b610a52565b34801561021b57600080fd5b5061010d61022a36600461311f565b610bee565b61010d61023d36600461336e565b610cb3565b34801561024e57600080fd5b5061010d61025d366004613168565b610e7c565b34801561026e57600080fd5b5061010d61027d36600461311f565b610f0d565b34801561028e57600080fd5b5061010d61029d3660046131b3565b611006565b7fad914d4300c64e1902ca499875cd8a76ae717047bcfaa9e806ff7ea4f6911268600101546001600160a01b031633146103235760405162461bcd60e51b815260206004820152601e60248201527f73686f756c642062652063616c6c6564206f6e6c79206279206f776e6572000060448201526064015b60405180910390fd5b610367836001600160a01b031660009081527f43da06808a8e54e76a41d6f7b48ddfb23969b1387a8710ef6241423a5aefe64c60205260409020805460ff19169055565b6040516001600160a01b03841681527ffb00f7adc91459b5048ecdc60b783959a583bafb1e3041f3e1f017540a0e47d39060200160405180910390a160005b8181101561045b57610449848484848181106103c4576103c461340b565b90506020020160208101906103d9919061346f565b6001600160a01b039190911660009081527f43da06808a8e54e76a41d6f7b48ddfb23969b1387a8710ef6241423a5aefe64d602090815260408083207fffffffff00000000000000000000000000000000000000000000000000000000909416835292905220805460ff19169055565b80610453816134b9565b9150506103a6565b50801561049e577f9128663055bb19dea35383762b39d6420bbfdb1d4ead002518dd0e8062fabf60838383604051610495939291906134f1565b60405180910390a15b505050565b7fad914d4300c64e1902ca499875cd8a76ae717047bcfaa9e806ff7ea4f6911268600101546001600160a01b0316331461051f5760405162461bcd60e51b815260206004820152601e60248201527f73686f756c642062652063616c6c6564206f6e6c79206279206f776e65720000604482015260640161031a565b61052881611215565b50565b7fad914d4300c64e1902ca499875cd8a76ae717047bcfaa9e806ff7ea4f6911268600101546001600160a01b031633146105a75760405162461bcd60e51b815260206004820152601e60248201527f73686f756c642062652063616c6c6564206f6e6c79206279206f776e65720000604482015260640161031a565b610528816112a6565b7fad914d4300c64e1902ca499875cd8a76ae717047bcfaa9e806ff7ea4f6911268600101546001600160a01b0316331461062c5760405162461bcd60e51b815260206004820152601e60248201527f73686f756c642062652063616c6c6564206f6e6c79206279206f776e65720000604482015260640161031a565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015282906000906001600160a01b038316906370a0823190602401602060405180830381865afa15801561068e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106b29190613569565b9050828110156107045760405162461bcd60e51b815260206004820152601460248201527f496e73756666696369656e742062616c616e6365000000000000000000000000604482015260640161031a565b61070f8433856113ad565b604080516001600160a01b0386168152602081018590527fd7dee2702d63ad89917b6a4da9981c90c4d24f8c2bdfd64c604ecae57d8d0651910160405180910390a150505050565b7fad914d4300c64e1902ca499875cd8a76ae717047bcfaa9e806ff7ea4f6911268600101546001600160a01b031633146107d35760405162461bcd60e51b815260206004820152601e60248201527f73686f756c642062652063616c6c6564206f6e6c79206279206f776e65720000604482015260640161031a565b6001600160a01b0383166107e657600080fd5b6107ef83611456565b6107f882611215565b61049e816112a6565b7fad914d4300c64e1902ca499875cd8a76ae717047bcfaa9e806ff7ea4f6911268600101546001600160a01b0316331461087d5760405162461bcd60e51b815260206004820152601e60248201527f73686f756c642062652063616c6c6564206f6e6c79206279206f776e65720000604482015260640161031a565b61052881611503565b7fad914d4300c64e1902ca499875cd8a76ae717047bcfaa9e806ff7ea4f6911268600101546001600160a01b031633146109025760405162461bcd60e51b815260206004820152601e60248201527f73686f756c642062652063616c6c6564206f6e6c79206279206f776e65720000604482015260640161031a565b61052881611456565b7fad914d4300c64e1902ca499875cd8a76ae717047bcfaa9e806ff7ea4f6911268600101546001600160a01b031633146109875760405162461bcd60e51b815260206004820152601e60248201527f73686f756c642062652063616c6c6564206f6e6c79206279206f776e65720000604482015260640161031a565b6000805b82811015610a4c578383828181106109a5576109a561340b565b90506020020160208101906109ba919061311f565b6001600160a01b03811660009081527fff95014231b901d2b22bd69b4e83dacd84ac05e8c2d1e9fba0c7e2f3ed0db0eb60205260409020805460ff1916600117905591506040516001600160a01b03831681527f15d897562fef1ba129ba167650a82269469efce20aece13fe277d18a02fdd0a69060200160405180910390a180610a44816134b9565b91505061098b565b50505050565b7fad914d4300c64e1902ca499875cd8a76ae717047bcfaa9e806ff7ea4f6911268600101546001600160a01b03163314610ace5760405162461bcd60e51b815260206004820152601e60248201527f73686f756c642062652063616c6c6564206f6e6c79206279206f776e65720000604482015260640161031a565b4781811015610b1f5760405162461bcd60e51b815260206004820152601460248201527f496e73756666696369656e742062616c616e6365000000000000000000000000604482015260640161031a565b604051600090339084908381818185875af1925050503d8060008114610b61576040519150601f19603f3d011682016040523d82523d6000602084013e610b66565b606091505b5050905080610bb75760405162461bcd60e51b815260206004820152601560248201527f6661696c656420746f2073656e64206e61746976650000000000000000000000604482015260640161031a565b6040805160008152602081018590527fd7dee2702d63ad89917b6a4da9981c90c4d24f8c2bdfd64c604ecae57d8d06519101610495565b7fad914d4300c64e1902ca499875cd8a76ae717047bcfaa9e806ff7ea4f6911268600101546001600160a01b03163314610c6a5760405162461bcd60e51b815260206004820152601e60248201527f73686f756c642062652063616c6c6564206f6e6c79206279206f776e65720000604482015260640161031a565b610c73816115ea565b6040516001600160a01b03821681527f5276dbc01d9903b8a77951393384788da9d9e89410a1a5e89fb9aacd7a1bfb08906020015b60405180910390a150565b7f4fe94118b1030ac5f570795d403ee5116fd91b8f0b5d11f2487377c2b0ab255980547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01610d2e576040517f29f745a700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600181557f8f95700cb6d0d3fbe23970b0fed4ae8d3a19af1ff9db49b72f280b34bdf7bad8546001600160a01b03163314610dd15760405162461bcd60e51b815260206004820152603860248201527f7367526563656976652066756e6374696f6e2063616e206f6e6c79206265206360448201527f616c6c656420627920537461726761746520726f757465720000000000000000606482015260840161031a565b600082806020019051810190610de79190613656565b90506000806000610df9888886611698565b92509250925083608001516001600160a01b0316836001600160a01b031685600001516001600160a01b03167f71e2229d8c5917bef9d5c3b4b1df412ba65253373b25d1c117223dbaaaa7c8d88760a0015186868a6101200151604051610e6394939291906137ee565b60405180910390a4505060009092555050505050505050565b7fad914d4300c64e1902ca499875cd8a76ae717047bcfaa9e806ff7ea4f6911268600101546001600160a01b03163314610ef85760405162461bcd60e51b815260206004820152601e60248201527f73686f756c642062652063616c6c6564206f6e6c79206279206f776e65720000604482015260640161031a565b610f0483600083610757565b61049e82611503565b7fad914d4300c64e1902ca499875cd8a76ae717047bcfaa9e806ff7ea4f6911268600101546001600160a01b03163314610f895760405162461bcd60e51b815260206004820152601e60248201527f73686f756c642062652063616c6c6564206f6e6c79206279206f776e65720000604482015260640161031a565b610fcd816001600160a01b031660009081527f43da06808a8e54e76a41d6f7b48ddfb23969b1387a8710ef6241423a5aefe64c60205260409020805460ff19169055565b6040516001600160a01b03821681527ffb00f7adc91459b5048ecdc60b783959a583bafb1e3041f3e1f017540a0e47d390602001610ca8565b7fad914d4300c64e1902ca499875cd8a76ae717047bcfaa9e806ff7ea4f6911268600101546001600160a01b031633146110825760405162461bcd60e51b815260206004820152601e60248201527f73686f756c642062652063616c6c6564206f6e6c79206279206f776e65720000604482015260640161031a565b60005b8181101561049e576110f98383838181106110a2576110a261340b565b90506020028101906110b49190613827565b6110c290602081019061311f565b8484848181106110d4576110d461340b565b90506020028101906110e69190613827565b6110f4906020810190613865565b611882565b7ff3db38b8bdedf89980dccafd178b958066ba0cfbf68c8b37b9845a176438a33283838381811061112c5761112c61340b565b905060200281019061113e9190613827565b61114c90602081019061311f565b84848481811061115e5761115e61340b565b90506020028101906111709190613827565b61117e906020810190613865565b60405161118d939291906134f1565b60405180910390a17fb7269578552456138d47dc37471d94886205143f138387446eff0148047965f68383838181106111c8576111c861340b565b90506020028101906111da9190613827565b6111e890602081019061311f565b6040516001600160a01b03909116815260200160405180910390a18061120d816134b9565b915050611085565b7fad914d4300c64e1902ca499875cd8a76ae717047bcfaa9e806ff7ea4f691126880546001600160a01b038381167fffffffffffffffffffffffff000000000000000000000000000000000000000083168117845560408051929093168083526020830191909152917fde99b0d1dabdadb73ebd7438dd737500ed3a596a247ba47febd9108f98c111749101610495565b7f43da06808a8e54e76a41d6f7b48ddfb23969b1387a8710ef6241423a5aefe64b80546001600160a01b038381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681179093557f43da06808a8e54e76a41d6f7b48ddfb23969b1387a8710ef6241423a5aefe64a9291169061136d5760405162461bcd60e51b815260206004820152600d60248201527f496e76616c696420574554482100000000000000000000000000000000000000604482015260640161031a565b604080516001600160a01b038084168252851660208201527fcfdb33c0c0613c5035b8848040c10b9625e341ee97988ee2720de0ffbe5392d29101610495565b6040516001600160a01b03831660248201526044810182905261049e9084907fa9059cbb00000000000000000000000000000000000000000000000000000000906064015b60408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009093169290921790915261198d565b7fad914d4300c64e1902ca499875cd8a76ae717047bcfaa9e806ff7ea4f691126980547fffffffffffffffffffffffff000000000000000000000000000000000000000081166001600160a01b038481169182179093556040517fad914d4300c64e1902ca499875cd8a76ae717047bcfaa9e806ff7ea4f6911268939092169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3505050565b6001600160a01b0381166115595760405162461bcd60e51b815260206004820152601660248201527f496e76616c6964205374617267617465526f7574657200000000000000000000604482015260640161031a565b7f8f95700cb6d0d3fbe23970b0fed4ae8d3a19af1ff9db49b72f280b34bdf7bad880546001600160a01b038381167fffffffffffffffffffffffff000000000000000000000000000000000000000083168117845560408051929093168083526020830191909152917f9e570c65d81efd98e14a6caae822e62bc30fabbe8c89431b82187a215233444c9101610495565b6001600160a01b03811660009081527fff95014231b901d2b22bd69b4e83dacd84ac05e8c2d1e9fba0c7e2f3ed0db0eb602081905260409091205460ff166116745760405162461bcd60e51b815260206004820152601860248201527f636f6e7472616374206e6f742077686974656c69737465640000000000000000604482015260640161031a565b6001600160a01b03909116600090815260209190915260409020805460ff19169055565b60008080807f43da06808a8e54e76a41d6f7b48ddfb23969b1387a8710ef6241423a5aefe64a60408601519091506000906001600160a01b03161580156116ee575060018201546001600160a01b038981169116145b6116f857876116fb565b60005b8794509050600181818860c00151600381111561171a5761171a6137af565b036117385761172b838a8a87611a72565b90975090925090506117ef565b60028860c001516003811115611750576117506137af565b036117615761172b838a8a87611e1f565b60038860c001516003811115611779576117796137af565b0361178a5761172b838a8a876121c5565b60008860c0015160038111156117a2576117a26137af565b146117ef5760405162461bcd60e51b815260206004820152601660248201527f556e737570706f7274656420616374696f6e5479706500000000000000000000604482015260640161031a565b818015611813575060028861010001516002811115611810576118106137af565b14155b156118325761182981878a610100015187612546565b90975090925090505b8161183e576002611841565b60005b9450600082611851576002611854565b60005b905061187282888b60a001518c61014001518d610180015186612600565b5095505050505b93509350939050565b6001600160a01b03831660009081527f43da06808a8e54e76a41d6f7b48ddfb23969b1387a8710ef6241423a5aefe64c60205260408120805460ff191660011790557f43da06808a8e54e76a41d6f7b48ddfb23969b1387a8710ef6241423a5aefe64a905b82811015611986576001600160a01b0385166000908152600383016020526040812060019186868581811061191e5761191e61340b565b9050602002016020810190611933919061346f565b7fffffffff000000000000000000000000000000000000000000000000000000001681526020810191909152604001600020805460ff19169115159190911790558061197e816134b9565b9150506118e7565b5050505050565b60006119e2826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166128f59092919063ffffffff16565b80519091501561049e5780806020019051810190611a0091906138cd565b61049e5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f74207375636365656400000000000000000000000000000000000000000000606482015260840161031a565b6000806000808560e00151806020019051810190611a909190613913565b80516001600160a01b0316600090815260028701602052604090205490915060ff161515600114611acb576000878993509350935050611e15565b60028160400151511015611ae9576000878993509350935050611e15565b60006001600160a01b038916158015611b3757506001860154604083015180516001600160a01b0390921691600090611b2457611b2461340b565b60200260200101516001600160a01b0316145b905080611b8b578160400151600081518110611b5557611b5561340b565b60200260200101516001600160a01b0316896001600160a01b031614611b86576000888a9450945094505050611e15565b611bf7565b8560010160009054906101000a90046001600160a01b03166001600160a01b031663d0e30db0896040518263ffffffff1660e01b81526004016000604051808303818588803b158015611bdd57600080fd5b505af1158015611bf1573d6000803e3d6000fd5b50505050505b611c248260400151600081518110611c1157611c1161340b565b602002602001015183600001518a61290e565b6040820151805160009190611c3b90600190613a14565b81518110611c4b57611c4b61340b565b602002602001015190506000611c6082612925565b905083600001516001600160a01b03166338ed17398b866020015187604001513089606001516040518663ffffffff1660e01b8152600401611ca6959493929190613a27565b6000604051808303816000875af1925050508015611ce657506040513d6000823e601f3d908101601f19168201604052611ce39190810190613a98565b60015b611d825783516040517f98970fb6752fa5c55ab7355a52caf0811312939be1b8fc26b0305fe02224c65291611d2091600191908290613b29565b60405180910390a1611d568460400151600081518110611d4257611d4261340b565b6020026020010151856000015160006129c4565b60008a84611d64578c611d73565b60018a01546001600160a01b03165b96509650965050505050611e15565b84516040517f98970fb6752fa5c55ab7355a52caf0811312939be1b8fc26b0305fe02224c65291611db891600191908290613b88565b60405180910390a16000611dcb84612925565b9050611dfb8660400151600081518110611de757611de761340b565b6020026020010151876000015160006129c4565b6001611e078483613a14565b859850985098505050505050505b9450945094915050565b6000806000808560e00151806020019051810190611e3d9190613bd4565b80516001600160a01b0316600090815260028701602052604090205490915060ff161515600114611e78576000878993509350935050611e15565b60006001600160a01b038916158015611ea55750600186015460208301516001600160a01b039081169116145b905080611edf5781602001516001600160a01b0316896001600160a01b031614611eda576000888a9450945094505050611e15565b611f4b565b8560010160009054906101000a90046001600160a01b03166001600160a01b031663d0e30db0896040518263ffffffff1660e01b81526004016000604051808303818588803b158015611f3157600080fd5b505af1158015611f45573d6000803e3d6000fd5b50505050505b611f5e826020015183600001518a61290e565b6000611f6d8360400151612925565b905082600001516001600160a01b031663414bf38960405180610100016040528086602001516001600160a01b0316815260200186604001516001600160a01b03168152602001866060015162ffffff168152602001306001600160a01b03168152602001866080015181526020018c81526020018660a0015181526020018660c001516001600160a01b03168152506040518263ffffffff1660e01b815260040161208391906000610100820190506001600160a01b0380845116835280602085015116602084015262ffffff60408501511660408401528060608501511660608401526080840151608084015260a084015160a084015260c084015160c08401528060e08501511660e08401525092915050565b6020604051808303816000875af19250505080156120be575060408051601f3d908101601f191682019092526120bb91810190613569565b60015b6121405782516040517f98970fb6752fa5c55ab7355a52caf0811312939be1b8fc26b0305fe02224c652916120f99160029190600090613c7a565b60405180910390a16121158360200151846000015160006129c4565b60008983612123578b612132565b60018901546001600160a01b03165b955095509550505050611e15565b83516040517f98970fb6752fa5c55ab7355a52caf0811312939be1b8fc26b0305fe02224c652916121779160029190600190613b88565b60405180910390a1600061218e8560400151612925565b90506121a48560200151866000015160006129c4565b60016121b08483613a14565b86604001519750975097505050505050611e15565b6000806000808560e001518060200190518101906121e39190613cd9565b60608101516001600160a01b0316600090815260028701602052604090205490915060ff161515600114612221576000878993509350935050611e15565b6020808201516001600160a01b0316600090815260028701909152604090205460ff16151560011461225d576000878993509350935050611e15565b87600082604001516002811115612276576122766137af565b036122dc576001600160a01b0389161561229b576000888a9450945094505050611e15565b600186015482516001600160a01b039081169116146122c5576000888a9450945094505050611e15565b6122d0898988612b12565b9196509450905061237f565b6001826040015160028111156122f4576122f46137af565b0361234d5760018601546001600160a01b038a8116911614612321576000888a9450945094505050611e15565b81516001600160a01b031615612342576000888a9450945094505050611e15565b6122d0898988612c01565b60019450886001600160a01b031682600001516001600160a01b03161461237f576000888a9450945094505050611e15565b84612395576000888a9450945094505050611e15565b6001600160a01b038116156123b3576123b38183602001518a61290e565b60006001600160a01b038216156123cb5760006123cd565b885b905060006123de8960600151612925565b905060008085606001516001600160a01b03168487608001516040516124049190613d94565b60006040518083038185875af1925050503d8060008114612441576040519150601f19603f3d011682016040523d82523d6000602084013e612446565b606091505b5090925090506001600160a01b0385161561246b5761246b85876020015160006129c4565b81156124e6577f98970fb6752fa5c55ab7355a52caf0811312939be1b8fc26b0305fe02224c6526003876060015160016040516124aa93929190613b88565b60405180910390a160006124c18c60600151612925565b905060016124cf8583613a14565b8d6060015199509950995050505050505050611e15565b7f98970fb6752fa5c55ab7355a52caf0811312939be1b8fc26b0305fe02224c65260038760600151600061251985612d02565b6040516125299493929190613dd2565b60405180910390a160008c86985098509850505050505050611e15565b600080808085600281111561255d5761255d6137af565b03612596576001600160a01b0387161561257f57506000915084905085611e15565b61258a878786612b12565b919450925090506125ed565b60018560028111156125aa576125aa6137af565b036125df5760018401546001600160a01b038881169116146125d457506000915084905085611e15565b61258a878786612c01565b506000915084905085611e15565b82611e1557506000915084905085611e15565b6001600160a01b0382161515600081612619578561261b565b835b604080516001600160a01b038b81168252602082018b905283168183015290519192507fff95014231b901d2b22bd69b4e83dacd84ac05e8c2d1e9fba0c7e2f3ed0db0eb917fdf4363408b2d9811d1e5c23efdb5bae0b7a68bd9de2de1cbae18a11be3e67ef59181900360600190a16001600160a01b0389166126a7576126a28289612d61565b6126b2565b6126b289838a6113ad565b82156128ea576001600160a01b03851660009081526020829052604090205460ff166127465760405162461bcd60e51b815260206004820152602260248201527f3372642d706172747920636f6e7472616374206e6f742077686974656c69737460448201527f6564000000000000000000000000000000000000000000000000000000000000606482015260840161031a565b6040517f910b32a30000000000000000000000000000000000000000000000000000000081526001600160a01b0386169063910b32a390612791908c908c9089908c90600401613e25565b600060405180830381600087803b1580156127ab57600080fd5b505af19250505080156127bc575060015b6128a7576127c8613e5c565b806308c379a00361283157506127dc613e78565b806127e75750612833565b7ffe3b53aeaf88b6a28abd020460eefc20897bd3db095a4b8b21a7b9007cf52ef7868b8b888b6000876040516128239796959493929190613f20565b60405180910390a1506128ea565b505b3d80801561285d576040519150601f19603f3d011682016040523d82523d6000602084013e612862565b606091505b507ffe3b53aeaf88b6a28abd020460eefc20897bd3db095a4b8b21a7b9007cf52ef7868b8b888b600061289488612d02565b6040516128239796959493929190613f20565b7ffe3b53aeaf88b6a28abd020460eefc20897bd3db095a4b8b21a7b9007cf52ef7858a8a878a60016040516128e196959493929190613f88565b60405180910390a15b505050505050505050565b60606129048484600085612e04565b90505b9392505050565b61291a838360006129c4565b61049e838383612f4c565b60006001600160a01b038216156129bc576040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b038316906370a0823190602401602060405180830381865afa158015612993573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129b79190613569565b6129be565b475b92915050565b801580612a5757506040517fdd62ed3e0000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b03838116602483015284169063dd62ed3e90604401602060405180830381865afa158015612a31573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a559190613569565b155b612ac95760405162461bcd60e51b815260206004820152603660248201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60448201527f20746f206e6f6e2d7a65726f20616c6c6f77616e636500000000000000000000606482015260840161031a565b6040516001600160a01b03831660248201526044810182905261049e9084907f095ea7b300000000000000000000000000000000000000000000000000000000906064016113f2565b600080806001600160a01b03861615612b3357506000915083905084611879565b8360010160009054906101000a90046001600160a01b03166001600160a01b031663d0e30db0866040518263ffffffff1660e01b81526004016000604051808303818588803b158015612b8557600080fd5b505af1158015612b99573d6000803e3d6000fd5b505050506001858101546040517f13fee4cd47ddae5c78a79ac9e0f49f3bc079fd45b98cb6bf0a8698624a7cc0bd9350612bdf926000926001600160a01b031691613fea565b60405180910390a150505060019081015490939192506001600160a01b031690565b6001810154600090819081906001600160a01b03878116911614612c2d57506000915083905084611879565b60018401546040517f2e1a7d4d000000000000000000000000000000000000000000000000000000008152600481018790526001600160a01b0390911690632e1a7d4d90602401600060405180830381600087803b158015612c8e57600080fd5b505af1158015612ca2573d6000803e3d6000fd5b505050506001848101546040517f13fee4cd47ddae5c78a79ac9e0f49f3bc079fd45b98cb6bf0a8698624a7cc0bd92612cea9290916001600160a01b03909116908290613fea565b60405180910390a15060019593945060009392505050565b6060604482511015612d4757505060408051808201909152601d81527f5472616e73616374696f6e2072657665727465642073696c656e746c79000000602082015290565b600482019150818060200190518101906129be9190613ff3565b6000826001600160a01b03168260405160006040518083038185875af1925050503d8060008114612dae576040519150601f19603f3d011682016040523d82523d6000602084013e612db3565b606091505b505090508061049e5760405162461bcd60e51b815260206004820152601560248201527f6661696c656420746f2073656e64206e61746976650000000000000000000000604482015260640161031a565b606082471015612e7c5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c0000000000000000000000000000000000000000000000000000606482015260840161031a565b6001600160a01b0385163b612ed35760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015260640161031a565b600080866001600160a01b03168587604051612eef9190613d94565b60006040518083038185875af1925050503d8060008114612f2c576040519150601f19603f3d011682016040523d82523d6000602084013e612f31565b606091505b5091509150612f41828286613030565b979650505050505050565b6040517fdd62ed3e0000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b038381166024830152600091839186169063dd62ed3e90604401602060405180830381865afa158015612fb6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612fda9190613569565b612fe49190614044565b6040516001600160a01b038516602482015260448101829052909150610a4c9085907f095ea7b300000000000000000000000000000000000000000000000000000000906064016113f2565b6060831561303f575081612907565b82511561304f5782518084602001fd5b8160405162461bcd60e51b815260040161031a9190614057565b6001600160a01b038116811461052857600080fd5b60008083601f84011261309057600080fd5b50813567ffffffffffffffff8111156130a857600080fd5b6020830191508360208260051b85010111156130c357600080fd5b9250929050565b6000806000604084860312156130df57600080fd5b83356130ea81613069565b9250602084013567ffffffffffffffff81111561310657600080fd5b6131128682870161307e565b9497909650939450505050565b60006020828403121561313157600080fd5b813561290781613069565b6000806040838503121561314f57600080fd5b823561315a81613069565b946020939093013593505050565b60008060006060848603121561317d57600080fd5b833561318881613069565b9250602084013561319881613069565b915060408401356131a881613069565b809150509250925092565b600080602083850312156131c657600080fd5b823567ffffffffffffffff8111156131dd57600080fd5b6131e98582860161307e565b90969095509350505050565b60006020828403121561320757600080fd5b5035919050565b61ffff8116811461052857600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6080810181811067ffffffffffffffff8211171561326d5761326d61321e565b60405250565b60a0810181811067ffffffffffffffff8211171561326d5761326d61321e565b601f19601f830116810181811067ffffffffffffffff821117156132b9576132b961321e565b6040525050565b6040516101a0810167ffffffffffffffff811182821017156132e4576132e461321e565b60405290565b600067ffffffffffffffff8211156133045761330461321e565b50601f01601f191660200190565b600082601f83011261332357600080fd5b813561332e816132ea565b60405161333b8282613293565b82815285602084870101111561335057600080fd5b82602086016020830137600092810160200192909252509392505050565b60008060008060008060c0878903121561338757600080fd5b86356133928161320e565b9550602087013567ffffffffffffffff808211156133af57600080fd5b6133bb8a838b01613312565b965060408901359550606089013591506133d482613069565b9093506080880135925060a088013590808211156133f157600080fd5b506133fe89828a01613312565b9150509295509295509295565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b80357fffffffff000000000000000000000000000000000000000000000000000000008116811461346a57600080fd5b919050565b60006020828403121561348157600080fd5b6129078261343a565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036134ea576134ea61348a565b5060010190565b6001600160a01b038416815260406020808301829052908201839052600090849060608401835b8681101561355d577fffffffff0000000000000000000000000000000000000000000000000000000061354a8561343a565b1682529282019290820190600101613518565b50979650505050505050565b60006020828403121561357b57600080fd5b5051919050565b805161346a81613069565b805167ffffffffffffffff8116811461346a57600080fd5b80516004811061346a57600080fd5b60005b838110156135cf5781810151838201526020016135b7565b50506000910152565b60006135e3836132ea565b6040516135f08282613293565b80925084815285858501111561360557600080fd5b6136138560208301866135b4565b50509392505050565b600082601f83011261362d57600080fd5b612907838351602085016135d8565b80516003811061346a57600080fd5b805161346a8161320e565b60006020828403121561366857600080fd5b815167ffffffffffffffff8082111561368057600080fd5b908301906101a0828603121561369557600080fd5b61369d6132c0565b6136a683613582565b81526136b46020840161358d565b60208201526136c560408401613582565b60408201526136d660608401613582565b60608201526136e760808401613582565b60808201526136f860a08401613582565b60a082015261370960c084016135a5565b60c082015260e08301518281111561372057600080fd5b61372c8782860161361c565b60e08301525061010061374081850161363c565b9082015261012061375284820161364b565b90820152610140838101518381111561376a57600080fd5b6137768882870161361c565b828401525050610160915061378c828401613582565b8282015261018091506137a0828401613582565b91810191909152949350505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b60048110610528576105286137af565b6001600160a01b0385168152602081018490526080810161380e846137de565b83604083015261ffff8316606083015295945050505050565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc183360301811261385b57600080fd5b9190910192915050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261389a57600080fd5b83018035915067ffffffffffffffff8211156138b557600080fd5b6020019150600581901b36038213156130c357600080fd5b6000602082840312156138df57600080fd5b8151801515811461290757600080fd5b600067ffffffffffffffff8211156139095761390961321e565b5060051b60200190565b6000602080838503121561392657600080fd5b825167ffffffffffffffff8082111561393e57600080fd5b908401906080828703121561395257600080fd5b60405161395e8161324d565b825161396981613069565b8152828401518482015260408301518281111561398557600080fd5b83019150601f8201871361399857600080fd5b81516139a3816138ef565b6040516139b08282613293565b82815260059290921b84018601918681019150898311156139d057600080fd5b938601935b828510156139f75784516139e881613069565b825293860193908601906139d5565b604084015250506060928301519281019290925250949350505050565b818103818111156129be576129be61348a565b600060a082018783526020878185015260a0604085015281875180845260c086019150828901935060005b81811015613a775784516001600160a01b031683529383019391830191600101613a52565b50506001600160a01b03969096166060850152505050608001529392505050565b60006020808385031215613aab57600080fd5b825167ffffffffffffffff811115613ac257600080fd5b8301601f81018513613ad357600080fd5b8051613ade816138ef565b604051613aeb8282613293565b82815260059290921b8301840191848101915087831115613b0b57600080fd5b928401925b82841015612f4157835182529284019290840190613b10565b613b32846137de565b9283526001600160a01b03919091166020830152151560408201526080606082018190526016908201527f556e69737761702d56322063616c6c206661696c65640000000000000000000060a082015260c00190565b613b91846137de565b9283526001600160a01b039190911660208301521515604082015260806060820181905260009082015260a00190565b805162ffffff8116811461346a57600080fd5b600060e08284031215613be657600080fd5b60405160e0810181811067ffffffffffffffff82111715613c0957613c0961321e565b6040528251613c1781613069565b81526020830151613c2781613069565b6020820152613c3860408401613582565b6040820152613c4960608401613bc1565b60608201526080830151608082015260a083015160a0820152613c6e60c08401613582565b60c08201529392505050565b613c83846137de565b9283526001600160a01b03919091166020830152151560408201526080606082018190526016908201527f556e69737761702d56332063616c6c206661696c65640000000000000000000060a082015260c00190565b600060208284031215613ceb57600080fd5b815167ffffffffffffffff80821115613d0357600080fd5b9083019060a08286031215613d1757600080fd5b604051613d2381613273565b8251613d2e81613069565b81526020830151613d3e81613069565b6020820152613d4f6040840161363c565b60408201526060830151613d6281613069565b6060820152608083015182811115613d7957600080fd5b613d858782860161361c565b60808301525095945050505050565b6000825161385b8184602087016135b4565b60008151808452613dbe8160208601602086016135b4565b601f01601f19169290920160200192915050565b613ddb856137de565b8481526001600160a01b03841660208201528215156040820152608060608201526000613e0b6080830184613da6565b9695505050505050565b60038110610528576105286137af565b6001600160a01b0385168152836020820152613e4083613e15565b826040820152608060608201526000613e0b6080830184613da6565b600060033d1115613e755760046000803e5060005160e01c5b90565b600060443d1015613e865790565b6040517ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc803d016004833e81513d67ffffffffffffffff8160248401118184111715613ed457505050505090565b8285019150815181811115613eec5750505050505090565b843d8701016020828501011115613f065750505050505090565b613f1560208286010187613293565b509095945050505050565b6001600160a01b03888116825287166020820152604081018690526000613f4686613e15565b85606083015260e06080830152613f6060e0830186613da6565b84151560a084015282810360c0840152613f7a8185613da6565b9a9950505050505050505050565b6001600160a01b03878116825286166020820152604081018590526000613fae85613e15565b84606083015260e06080830152613fc860e0830185613da6565b92151560a08301525080820360c0909101526000815260200195945050505050565b613b9184613e15565b60006020828403121561400557600080fd5b815167ffffffffffffffff81111561401c57600080fd5b8201601f8101841361402d57600080fd5b61403c848251602084016135d8565b949350505050565b808201808211156129be576129be61348a565b6020815260006129076020830184613da656fea2646970667358221220da27615fd2ab6793f521733433cbcf0bc329fbc56b8950d2a6740ab3579c7f3f64736f6c63430008100033
Deployed Bytecode
0x6080604052600436106100e15760003560e01c80639f49428b1161007f578063ab8236f311610059578063ab8236f31461022f578063c8a430af14610242578063de96441f14610262578063f0ee655b1461028257600080fd5b80639f49428b146101cf5780639fae52e6146101ef578063a510e6001461020f57600080fd5b8063410085df116100bb578063410085df1461014f5780637f10a9221461016f5780638808074a1461018f578063880cdc31146101af57600080fd5b806303de9416146100ed57806305369efa1461010f5780630a6dc7ba1461012f57600080fd5b366100e857005b600080fd5b3480156100f957600080fd5b5061010d6101083660046130ca565b6102a2565b005b34801561011b57600080fd5b5061010d61012a36600461311f565b6104a3565b34801561013b57600080fd5b5061010d61014a36600461311f565b61052b565b34801561015b57600080fd5b5061010d61016a36600461313c565b6105b0565b34801561017b57600080fd5b5061010d61018a366004613168565b610757565b34801561019b57600080fd5b5061010d6101aa36600461311f565b610801565b3480156101bb57600080fd5b5061010d6101ca36600461311f565b610886565b3480156101db57600080fd5b5061010d6101ea3660046131b3565b61090b565b3480156101fb57600080fd5b5061010d61020a3660046131f5565b610a52565b34801561021b57600080fd5b5061010d61022a36600461311f565b610bee565b61010d61023d36600461336e565b610cb3565b34801561024e57600080fd5b5061010d61025d366004613168565b610e7c565b34801561026e57600080fd5b5061010d61027d36600461311f565b610f0d565b34801561028e57600080fd5b5061010d61029d3660046131b3565b611006565b7fad914d4300c64e1902ca499875cd8a76ae717047bcfaa9e806ff7ea4f6911268600101546001600160a01b031633146103235760405162461bcd60e51b815260206004820152601e60248201527f73686f756c642062652063616c6c6564206f6e6c79206279206f776e6572000060448201526064015b60405180910390fd5b610367836001600160a01b031660009081527f43da06808a8e54e76a41d6f7b48ddfb23969b1387a8710ef6241423a5aefe64c60205260409020805460ff19169055565b6040516001600160a01b03841681527ffb00f7adc91459b5048ecdc60b783959a583bafb1e3041f3e1f017540a0e47d39060200160405180910390a160005b8181101561045b57610449848484848181106103c4576103c461340b565b90506020020160208101906103d9919061346f565b6001600160a01b039190911660009081527f43da06808a8e54e76a41d6f7b48ddfb23969b1387a8710ef6241423a5aefe64d602090815260408083207fffffffff00000000000000000000000000000000000000000000000000000000909416835292905220805460ff19169055565b80610453816134b9565b9150506103a6565b50801561049e577f9128663055bb19dea35383762b39d6420bbfdb1d4ead002518dd0e8062fabf60838383604051610495939291906134f1565b60405180910390a15b505050565b7fad914d4300c64e1902ca499875cd8a76ae717047bcfaa9e806ff7ea4f6911268600101546001600160a01b0316331461051f5760405162461bcd60e51b815260206004820152601e60248201527f73686f756c642062652063616c6c6564206f6e6c79206279206f776e65720000604482015260640161031a565b61052881611215565b50565b7fad914d4300c64e1902ca499875cd8a76ae717047bcfaa9e806ff7ea4f6911268600101546001600160a01b031633146105a75760405162461bcd60e51b815260206004820152601e60248201527f73686f756c642062652063616c6c6564206f6e6c79206279206f776e65720000604482015260640161031a565b610528816112a6565b7fad914d4300c64e1902ca499875cd8a76ae717047bcfaa9e806ff7ea4f6911268600101546001600160a01b0316331461062c5760405162461bcd60e51b815260206004820152601e60248201527f73686f756c642062652063616c6c6564206f6e6c79206279206f776e65720000604482015260640161031a565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015282906000906001600160a01b038316906370a0823190602401602060405180830381865afa15801561068e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106b29190613569565b9050828110156107045760405162461bcd60e51b815260206004820152601460248201527f496e73756666696369656e742062616c616e6365000000000000000000000000604482015260640161031a565b61070f8433856113ad565b604080516001600160a01b0386168152602081018590527fd7dee2702d63ad89917b6a4da9981c90c4d24f8c2bdfd64c604ecae57d8d0651910160405180910390a150505050565b7fad914d4300c64e1902ca499875cd8a76ae717047bcfaa9e806ff7ea4f6911268600101546001600160a01b031633146107d35760405162461bcd60e51b815260206004820152601e60248201527f73686f756c642062652063616c6c6564206f6e6c79206279206f776e65720000604482015260640161031a565b6001600160a01b0383166107e657600080fd5b6107ef83611456565b6107f882611215565b61049e816112a6565b7fad914d4300c64e1902ca499875cd8a76ae717047bcfaa9e806ff7ea4f6911268600101546001600160a01b0316331461087d5760405162461bcd60e51b815260206004820152601e60248201527f73686f756c642062652063616c6c6564206f6e6c79206279206f776e65720000604482015260640161031a565b61052881611503565b7fad914d4300c64e1902ca499875cd8a76ae717047bcfaa9e806ff7ea4f6911268600101546001600160a01b031633146109025760405162461bcd60e51b815260206004820152601e60248201527f73686f756c642062652063616c6c6564206f6e6c79206279206f776e65720000604482015260640161031a565b61052881611456565b7fad914d4300c64e1902ca499875cd8a76ae717047bcfaa9e806ff7ea4f6911268600101546001600160a01b031633146109875760405162461bcd60e51b815260206004820152601e60248201527f73686f756c642062652063616c6c6564206f6e6c79206279206f776e65720000604482015260640161031a565b6000805b82811015610a4c578383828181106109a5576109a561340b565b90506020020160208101906109ba919061311f565b6001600160a01b03811660009081527fff95014231b901d2b22bd69b4e83dacd84ac05e8c2d1e9fba0c7e2f3ed0db0eb60205260409020805460ff1916600117905591506040516001600160a01b03831681527f15d897562fef1ba129ba167650a82269469efce20aece13fe277d18a02fdd0a69060200160405180910390a180610a44816134b9565b91505061098b565b50505050565b7fad914d4300c64e1902ca499875cd8a76ae717047bcfaa9e806ff7ea4f6911268600101546001600160a01b03163314610ace5760405162461bcd60e51b815260206004820152601e60248201527f73686f756c642062652063616c6c6564206f6e6c79206279206f776e65720000604482015260640161031a565b4781811015610b1f5760405162461bcd60e51b815260206004820152601460248201527f496e73756666696369656e742062616c616e6365000000000000000000000000604482015260640161031a565b604051600090339084908381818185875af1925050503d8060008114610b61576040519150601f19603f3d011682016040523d82523d6000602084013e610b66565b606091505b5050905080610bb75760405162461bcd60e51b815260206004820152601560248201527f6661696c656420746f2073656e64206e61746976650000000000000000000000604482015260640161031a565b6040805160008152602081018590527fd7dee2702d63ad89917b6a4da9981c90c4d24f8c2bdfd64c604ecae57d8d06519101610495565b7fad914d4300c64e1902ca499875cd8a76ae717047bcfaa9e806ff7ea4f6911268600101546001600160a01b03163314610c6a5760405162461bcd60e51b815260206004820152601e60248201527f73686f756c642062652063616c6c6564206f6e6c79206279206f776e65720000604482015260640161031a565b610c73816115ea565b6040516001600160a01b03821681527f5276dbc01d9903b8a77951393384788da9d9e89410a1a5e89fb9aacd7a1bfb08906020015b60405180910390a150565b7f4fe94118b1030ac5f570795d403ee5116fd91b8f0b5d11f2487377c2b0ab255980547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01610d2e576040517f29f745a700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600181557f8f95700cb6d0d3fbe23970b0fed4ae8d3a19af1ff9db49b72f280b34bdf7bad8546001600160a01b03163314610dd15760405162461bcd60e51b815260206004820152603860248201527f7367526563656976652066756e6374696f6e2063616e206f6e6c79206265206360448201527f616c6c656420627920537461726761746520726f757465720000000000000000606482015260840161031a565b600082806020019051810190610de79190613656565b90506000806000610df9888886611698565b92509250925083608001516001600160a01b0316836001600160a01b031685600001516001600160a01b03167f71e2229d8c5917bef9d5c3b4b1df412ba65253373b25d1c117223dbaaaa7c8d88760a0015186868a6101200151604051610e6394939291906137ee565b60405180910390a4505060009092555050505050505050565b7fad914d4300c64e1902ca499875cd8a76ae717047bcfaa9e806ff7ea4f6911268600101546001600160a01b03163314610ef85760405162461bcd60e51b815260206004820152601e60248201527f73686f756c642062652063616c6c6564206f6e6c79206279206f776e65720000604482015260640161031a565b610f0483600083610757565b61049e82611503565b7fad914d4300c64e1902ca499875cd8a76ae717047bcfaa9e806ff7ea4f6911268600101546001600160a01b03163314610f895760405162461bcd60e51b815260206004820152601e60248201527f73686f756c642062652063616c6c6564206f6e6c79206279206f776e65720000604482015260640161031a565b610fcd816001600160a01b031660009081527f43da06808a8e54e76a41d6f7b48ddfb23969b1387a8710ef6241423a5aefe64c60205260409020805460ff19169055565b6040516001600160a01b03821681527ffb00f7adc91459b5048ecdc60b783959a583bafb1e3041f3e1f017540a0e47d390602001610ca8565b7fad914d4300c64e1902ca499875cd8a76ae717047bcfaa9e806ff7ea4f6911268600101546001600160a01b031633146110825760405162461bcd60e51b815260206004820152601e60248201527f73686f756c642062652063616c6c6564206f6e6c79206279206f776e65720000604482015260640161031a565b60005b8181101561049e576110f98383838181106110a2576110a261340b565b90506020028101906110b49190613827565b6110c290602081019061311f565b8484848181106110d4576110d461340b565b90506020028101906110e69190613827565b6110f4906020810190613865565b611882565b7ff3db38b8bdedf89980dccafd178b958066ba0cfbf68c8b37b9845a176438a33283838381811061112c5761112c61340b565b905060200281019061113e9190613827565b61114c90602081019061311f565b84848481811061115e5761115e61340b565b90506020028101906111709190613827565b61117e906020810190613865565b60405161118d939291906134f1565b60405180910390a17fb7269578552456138d47dc37471d94886205143f138387446eff0148047965f68383838181106111c8576111c861340b565b90506020028101906111da9190613827565b6111e890602081019061311f565b6040516001600160a01b03909116815260200160405180910390a18061120d816134b9565b915050611085565b7fad914d4300c64e1902ca499875cd8a76ae717047bcfaa9e806ff7ea4f691126880546001600160a01b038381167fffffffffffffffffffffffff000000000000000000000000000000000000000083168117845560408051929093168083526020830191909152917fde99b0d1dabdadb73ebd7438dd737500ed3a596a247ba47febd9108f98c111749101610495565b7f43da06808a8e54e76a41d6f7b48ddfb23969b1387a8710ef6241423a5aefe64b80546001600160a01b038381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681179093557f43da06808a8e54e76a41d6f7b48ddfb23969b1387a8710ef6241423a5aefe64a9291169061136d5760405162461bcd60e51b815260206004820152600d60248201527f496e76616c696420574554482100000000000000000000000000000000000000604482015260640161031a565b604080516001600160a01b038084168252851660208201527fcfdb33c0c0613c5035b8848040c10b9625e341ee97988ee2720de0ffbe5392d29101610495565b6040516001600160a01b03831660248201526044810182905261049e9084907fa9059cbb00000000000000000000000000000000000000000000000000000000906064015b60408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009093169290921790915261198d565b7fad914d4300c64e1902ca499875cd8a76ae717047bcfaa9e806ff7ea4f691126980547fffffffffffffffffffffffff000000000000000000000000000000000000000081166001600160a01b038481169182179093556040517fad914d4300c64e1902ca499875cd8a76ae717047bcfaa9e806ff7ea4f6911268939092169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3505050565b6001600160a01b0381166115595760405162461bcd60e51b815260206004820152601660248201527f496e76616c6964205374617267617465526f7574657200000000000000000000604482015260640161031a565b7f8f95700cb6d0d3fbe23970b0fed4ae8d3a19af1ff9db49b72f280b34bdf7bad880546001600160a01b038381167fffffffffffffffffffffffff000000000000000000000000000000000000000083168117845560408051929093168083526020830191909152917f9e570c65d81efd98e14a6caae822e62bc30fabbe8c89431b82187a215233444c9101610495565b6001600160a01b03811660009081527fff95014231b901d2b22bd69b4e83dacd84ac05e8c2d1e9fba0c7e2f3ed0db0eb602081905260409091205460ff166116745760405162461bcd60e51b815260206004820152601860248201527f636f6e7472616374206e6f742077686974656c69737465640000000000000000604482015260640161031a565b6001600160a01b03909116600090815260209190915260409020805460ff19169055565b60008080807f43da06808a8e54e76a41d6f7b48ddfb23969b1387a8710ef6241423a5aefe64a60408601519091506000906001600160a01b03161580156116ee575060018201546001600160a01b038981169116145b6116f857876116fb565b60005b8794509050600181818860c00151600381111561171a5761171a6137af565b036117385761172b838a8a87611a72565b90975090925090506117ef565b60028860c001516003811115611750576117506137af565b036117615761172b838a8a87611e1f565b60038860c001516003811115611779576117796137af565b0361178a5761172b838a8a876121c5565b60008860c0015160038111156117a2576117a26137af565b146117ef5760405162461bcd60e51b815260206004820152601660248201527f556e737570706f7274656420616374696f6e5479706500000000000000000000604482015260640161031a565b818015611813575060028861010001516002811115611810576118106137af565b14155b156118325761182981878a610100015187612546565b90975090925090505b8161183e576002611841565b60005b9450600082611851576002611854565b60005b905061187282888b60a001518c61014001518d610180015186612600565b5095505050505b93509350939050565b6001600160a01b03831660009081527f43da06808a8e54e76a41d6f7b48ddfb23969b1387a8710ef6241423a5aefe64c60205260408120805460ff191660011790557f43da06808a8e54e76a41d6f7b48ddfb23969b1387a8710ef6241423a5aefe64a905b82811015611986576001600160a01b0385166000908152600383016020526040812060019186868581811061191e5761191e61340b565b9050602002016020810190611933919061346f565b7fffffffff000000000000000000000000000000000000000000000000000000001681526020810191909152604001600020805460ff19169115159190911790558061197e816134b9565b9150506118e7565b5050505050565b60006119e2826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166128f59092919063ffffffff16565b80519091501561049e5780806020019051810190611a0091906138cd565b61049e5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f74207375636365656400000000000000000000000000000000000000000000606482015260840161031a565b6000806000808560e00151806020019051810190611a909190613913565b80516001600160a01b0316600090815260028701602052604090205490915060ff161515600114611acb576000878993509350935050611e15565b60028160400151511015611ae9576000878993509350935050611e15565b60006001600160a01b038916158015611b3757506001860154604083015180516001600160a01b0390921691600090611b2457611b2461340b565b60200260200101516001600160a01b0316145b905080611b8b578160400151600081518110611b5557611b5561340b565b60200260200101516001600160a01b0316896001600160a01b031614611b86576000888a9450945094505050611e15565b611bf7565b8560010160009054906101000a90046001600160a01b03166001600160a01b031663d0e30db0896040518263ffffffff1660e01b81526004016000604051808303818588803b158015611bdd57600080fd5b505af1158015611bf1573d6000803e3d6000fd5b50505050505b611c248260400151600081518110611c1157611c1161340b565b602002602001015183600001518a61290e565b6040820151805160009190611c3b90600190613a14565b81518110611c4b57611c4b61340b565b602002602001015190506000611c6082612925565b905083600001516001600160a01b03166338ed17398b866020015187604001513089606001516040518663ffffffff1660e01b8152600401611ca6959493929190613a27565b6000604051808303816000875af1925050508015611ce657506040513d6000823e601f3d908101601f19168201604052611ce39190810190613a98565b60015b611d825783516040517f98970fb6752fa5c55ab7355a52caf0811312939be1b8fc26b0305fe02224c65291611d2091600191908290613b29565b60405180910390a1611d568460400151600081518110611d4257611d4261340b565b6020026020010151856000015160006129c4565b60008a84611d64578c611d73565b60018a01546001600160a01b03165b96509650965050505050611e15565b84516040517f98970fb6752fa5c55ab7355a52caf0811312939be1b8fc26b0305fe02224c65291611db891600191908290613b88565b60405180910390a16000611dcb84612925565b9050611dfb8660400151600081518110611de757611de761340b565b6020026020010151876000015160006129c4565b6001611e078483613a14565b859850985098505050505050505b9450945094915050565b6000806000808560e00151806020019051810190611e3d9190613bd4565b80516001600160a01b0316600090815260028701602052604090205490915060ff161515600114611e78576000878993509350935050611e15565b60006001600160a01b038916158015611ea55750600186015460208301516001600160a01b039081169116145b905080611edf5781602001516001600160a01b0316896001600160a01b031614611eda576000888a9450945094505050611e15565b611f4b565b8560010160009054906101000a90046001600160a01b03166001600160a01b031663d0e30db0896040518263ffffffff1660e01b81526004016000604051808303818588803b158015611f3157600080fd5b505af1158015611f45573d6000803e3d6000fd5b50505050505b611f5e826020015183600001518a61290e565b6000611f6d8360400151612925565b905082600001516001600160a01b031663414bf38960405180610100016040528086602001516001600160a01b0316815260200186604001516001600160a01b03168152602001866060015162ffffff168152602001306001600160a01b03168152602001866080015181526020018c81526020018660a0015181526020018660c001516001600160a01b03168152506040518263ffffffff1660e01b815260040161208391906000610100820190506001600160a01b0380845116835280602085015116602084015262ffffff60408501511660408401528060608501511660608401526080840151608084015260a084015160a084015260c084015160c08401528060e08501511660e08401525092915050565b6020604051808303816000875af19250505080156120be575060408051601f3d908101601f191682019092526120bb91810190613569565b60015b6121405782516040517f98970fb6752fa5c55ab7355a52caf0811312939be1b8fc26b0305fe02224c652916120f99160029190600090613c7a565b60405180910390a16121158360200151846000015160006129c4565b60008983612123578b612132565b60018901546001600160a01b03165b955095509550505050611e15565b83516040517f98970fb6752fa5c55ab7355a52caf0811312939be1b8fc26b0305fe02224c652916121779160029190600190613b88565b60405180910390a1600061218e8560400151612925565b90506121a48560200151866000015160006129c4565b60016121b08483613a14565b86604001519750975097505050505050611e15565b6000806000808560e001518060200190518101906121e39190613cd9565b60608101516001600160a01b0316600090815260028701602052604090205490915060ff161515600114612221576000878993509350935050611e15565b6020808201516001600160a01b0316600090815260028701909152604090205460ff16151560011461225d576000878993509350935050611e15565b87600082604001516002811115612276576122766137af565b036122dc576001600160a01b0389161561229b576000888a9450945094505050611e15565b600186015482516001600160a01b039081169116146122c5576000888a9450945094505050611e15565b6122d0898988612b12565b9196509450905061237f565b6001826040015160028111156122f4576122f46137af565b0361234d5760018601546001600160a01b038a8116911614612321576000888a9450945094505050611e15565b81516001600160a01b031615612342576000888a9450945094505050611e15565b6122d0898988612c01565b60019450886001600160a01b031682600001516001600160a01b03161461237f576000888a9450945094505050611e15565b84612395576000888a9450945094505050611e15565b6001600160a01b038116156123b3576123b38183602001518a61290e565b60006001600160a01b038216156123cb5760006123cd565b885b905060006123de8960600151612925565b905060008085606001516001600160a01b03168487608001516040516124049190613d94565b60006040518083038185875af1925050503d8060008114612441576040519150601f19603f3d011682016040523d82523d6000602084013e612446565b606091505b5090925090506001600160a01b0385161561246b5761246b85876020015160006129c4565b81156124e6577f98970fb6752fa5c55ab7355a52caf0811312939be1b8fc26b0305fe02224c6526003876060015160016040516124aa93929190613b88565b60405180910390a160006124c18c60600151612925565b905060016124cf8583613a14565b8d6060015199509950995050505050505050611e15565b7f98970fb6752fa5c55ab7355a52caf0811312939be1b8fc26b0305fe02224c65260038760600151600061251985612d02565b6040516125299493929190613dd2565b60405180910390a160008c86985098509850505050505050611e15565b600080808085600281111561255d5761255d6137af565b03612596576001600160a01b0387161561257f57506000915084905085611e15565b61258a878786612b12565b919450925090506125ed565b60018560028111156125aa576125aa6137af565b036125df5760018401546001600160a01b038881169116146125d457506000915084905085611e15565b61258a878786612c01565b506000915084905085611e15565b82611e1557506000915084905085611e15565b6001600160a01b0382161515600081612619578561261b565b835b604080516001600160a01b038b81168252602082018b905283168183015290519192507fff95014231b901d2b22bd69b4e83dacd84ac05e8c2d1e9fba0c7e2f3ed0db0eb917fdf4363408b2d9811d1e5c23efdb5bae0b7a68bd9de2de1cbae18a11be3e67ef59181900360600190a16001600160a01b0389166126a7576126a28289612d61565b6126b2565b6126b289838a6113ad565b82156128ea576001600160a01b03851660009081526020829052604090205460ff166127465760405162461bcd60e51b815260206004820152602260248201527f3372642d706172747920636f6e7472616374206e6f742077686974656c69737460448201527f6564000000000000000000000000000000000000000000000000000000000000606482015260840161031a565b6040517f910b32a30000000000000000000000000000000000000000000000000000000081526001600160a01b0386169063910b32a390612791908c908c9089908c90600401613e25565b600060405180830381600087803b1580156127ab57600080fd5b505af19250505080156127bc575060015b6128a7576127c8613e5c565b806308c379a00361283157506127dc613e78565b806127e75750612833565b7ffe3b53aeaf88b6a28abd020460eefc20897bd3db095a4b8b21a7b9007cf52ef7868b8b888b6000876040516128239796959493929190613f20565b60405180910390a1506128ea565b505b3d80801561285d576040519150601f19603f3d011682016040523d82523d6000602084013e612862565b606091505b507ffe3b53aeaf88b6a28abd020460eefc20897bd3db095a4b8b21a7b9007cf52ef7868b8b888b600061289488612d02565b6040516128239796959493929190613f20565b7ffe3b53aeaf88b6a28abd020460eefc20897bd3db095a4b8b21a7b9007cf52ef7858a8a878a60016040516128e196959493929190613f88565b60405180910390a15b505050505050505050565b60606129048484600085612e04565b90505b9392505050565b61291a838360006129c4565b61049e838383612f4c565b60006001600160a01b038216156129bc576040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b038316906370a0823190602401602060405180830381865afa158015612993573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129b79190613569565b6129be565b475b92915050565b801580612a5757506040517fdd62ed3e0000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b03838116602483015284169063dd62ed3e90604401602060405180830381865afa158015612a31573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a559190613569565b155b612ac95760405162461bcd60e51b815260206004820152603660248201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60448201527f20746f206e6f6e2d7a65726f20616c6c6f77616e636500000000000000000000606482015260840161031a565b6040516001600160a01b03831660248201526044810182905261049e9084907f095ea7b300000000000000000000000000000000000000000000000000000000906064016113f2565b600080806001600160a01b03861615612b3357506000915083905084611879565b8360010160009054906101000a90046001600160a01b03166001600160a01b031663d0e30db0866040518263ffffffff1660e01b81526004016000604051808303818588803b158015612b8557600080fd5b505af1158015612b99573d6000803e3d6000fd5b505050506001858101546040517f13fee4cd47ddae5c78a79ac9e0f49f3bc079fd45b98cb6bf0a8698624a7cc0bd9350612bdf926000926001600160a01b031691613fea565b60405180910390a150505060019081015490939192506001600160a01b031690565b6001810154600090819081906001600160a01b03878116911614612c2d57506000915083905084611879565b60018401546040517f2e1a7d4d000000000000000000000000000000000000000000000000000000008152600481018790526001600160a01b0390911690632e1a7d4d90602401600060405180830381600087803b158015612c8e57600080fd5b505af1158015612ca2573d6000803e3d6000fd5b505050506001848101546040517f13fee4cd47ddae5c78a79ac9e0f49f3bc079fd45b98cb6bf0a8698624a7cc0bd92612cea9290916001600160a01b03909116908290613fea565b60405180910390a15060019593945060009392505050565b6060604482511015612d4757505060408051808201909152601d81527f5472616e73616374696f6e2072657665727465642073696c656e746c79000000602082015290565b600482019150818060200190518101906129be9190613ff3565b6000826001600160a01b03168260405160006040518083038185875af1925050503d8060008114612dae576040519150601f19603f3d011682016040523d82523d6000602084013e612db3565b606091505b505090508061049e5760405162461bcd60e51b815260206004820152601560248201527f6661696c656420746f2073656e64206e61746976650000000000000000000000604482015260640161031a565b606082471015612e7c5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c0000000000000000000000000000000000000000000000000000606482015260840161031a565b6001600160a01b0385163b612ed35760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015260640161031a565b600080866001600160a01b03168587604051612eef9190613d94565b60006040518083038185875af1925050503d8060008114612f2c576040519150601f19603f3d011682016040523d82523d6000602084013e612f31565b606091505b5091509150612f41828286613030565b979650505050505050565b6040517fdd62ed3e0000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b038381166024830152600091839186169063dd62ed3e90604401602060405180830381865afa158015612fb6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612fda9190613569565b612fe49190614044565b6040516001600160a01b038516602482015260448101829052909150610a4c9085907f095ea7b300000000000000000000000000000000000000000000000000000000906064016113f2565b6060831561303f575081612907565b82511561304f5782518084602001fd5b8160405162461bcd60e51b815260040161031a9190614057565b6001600160a01b038116811461052857600080fd5b60008083601f84011261309057600080fd5b50813567ffffffffffffffff8111156130a857600080fd5b6020830191508360208260051b85010111156130c357600080fd5b9250929050565b6000806000604084860312156130df57600080fd5b83356130ea81613069565b9250602084013567ffffffffffffffff81111561310657600080fd5b6131128682870161307e565b9497909650939450505050565b60006020828403121561313157600080fd5b813561290781613069565b6000806040838503121561314f57600080fd5b823561315a81613069565b946020939093013593505050565b60008060006060848603121561317d57600080fd5b833561318881613069565b9250602084013561319881613069565b915060408401356131a881613069565b809150509250925092565b600080602083850312156131c657600080fd5b823567ffffffffffffffff8111156131dd57600080fd5b6131e98582860161307e565b90969095509350505050565b60006020828403121561320757600080fd5b5035919050565b61ffff8116811461052857600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6080810181811067ffffffffffffffff8211171561326d5761326d61321e565b60405250565b60a0810181811067ffffffffffffffff8211171561326d5761326d61321e565b601f19601f830116810181811067ffffffffffffffff821117156132b9576132b961321e565b6040525050565b6040516101a0810167ffffffffffffffff811182821017156132e4576132e461321e565b60405290565b600067ffffffffffffffff8211156133045761330461321e565b50601f01601f191660200190565b600082601f83011261332357600080fd5b813561332e816132ea565b60405161333b8282613293565b82815285602084870101111561335057600080fd5b82602086016020830137600092810160200192909252509392505050565b60008060008060008060c0878903121561338757600080fd5b86356133928161320e565b9550602087013567ffffffffffffffff808211156133af57600080fd5b6133bb8a838b01613312565b965060408901359550606089013591506133d482613069565b9093506080880135925060a088013590808211156133f157600080fd5b506133fe89828a01613312565b9150509295509295509295565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b80357fffffffff000000000000000000000000000000000000000000000000000000008116811461346a57600080fd5b919050565b60006020828403121561348157600080fd5b6129078261343a565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036134ea576134ea61348a565b5060010190565b6001600160a01b038416815260406020808301829052908201839052600090849060608401835b8681101561355d577fffffffff0000000000000000000000000000000000000000000000000000000061354a8561343a565b1682529282019290820190600101613518565b50979650505050505050565b60006020828403121561357b57600080fd5b5051919050565b805161346a81613069565b805167ffffffffffffffff8116811461346a57600080fd5b80516004811061346a57600080fd5b60005b838110156135cf5781810151838201526020016135b7565b50506000910152565b60006135e3836132ea565b6040516135f08282613293565b80925084815285858501111561360557600080fd5b6136138560208301866135b4565b50509392505050565b600082601f83011261362d57600080fd5b612907838351602085016135d8565b80516003811061346a57600080fd5b805161346a8161320e565b60006020828403121561366857600080fd5b815167ffffffffffffffff8082111561368057600080fd5b908301906101a0828603121561369557600080fd5b61369d6132c0565b6136a683613582565b81526136b46020840161358d565b60208201526136c560408401613582565b60408201526136d660608401613582565b60608201526136e760808401613582565b60808201526136f860a08401613582565b60a082015261370960c084016135a5565b60c082015260e08301518281111561372057600080fd5b61372c8782860161361c565b60e08301525061010061374081850161363c565b9082015261012061375284820161364b565b90820152610140838101518381111561376a57600080fd5b6137768882870161361c565b828401525050610160915061378c828401613582565b8282015261018091506137a0828401613582565b91810191909152949350505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b60048110610528576105286137af565b6001600160a01b0385168152602081018490526080810161380e846137de565b83604083015261ffff8316606083015295945050505050565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc183360301811261385b57600080fd5b9190910192915050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261389a57600080fd5b83018035915067ffffffffffffffff8211156138b557600080fd5b6020019150600581901b36038213156130c357600080fd5b6000602082840312156138df57600080fd5b8151801515811461290757600080fd5b600067ffffffffffffffff8211156139095761390961321e565b5060051b60200190565b6000602080838503121561392657600080fd5b825167ffffffffffffffff8082111561393e57600080fd5b908401906080828703121561395257600080fd5b60405161395e8161324d565b825161396981613069565b8152828401518482015260408301518281111561398557600080fd5b83019150601f8201871361399857600080fd5b81516139a3816138ef565b6040516139b08282613293565b82815260059290921b84018601918681019150898311156139d057600080fd5b938601935b828510156139f75784516139e881613069565b825293860193908601906139d5565b604084015250506060928301519281019290925250949350505050565b818103818111156129be576129be61348a565b600060a082018783526020878185015260a0604085015281875180845260c086019150828901935060005b81811015613a775784516001600160a01b031683529383019391830191600101613a52565b50506001600160a01b03969096166060850152505050608001529392505050565b60006020808385031215613aab57600080fd5b825167ffffffffffffffff811115613ac257600080fd5b8301601f81018513613ad357600080fd5b8051613ade816138ef565b604051613aeb8282613293565b82815260059290921b8301840191848101915087831115613b0b57600080fd5b928401925b82841015612f4157835182529284019290840190613b10565b613b32846137de565b9283526001600160a01b03919091166020830152151560408201526080606082018190526016908201527f556e69737761702d56322063616c6c206661696c65640000000000000000000060a082015260c00190565b613b91846137de565b9283526001600160a01b039190911660208301521515604082015260806060820181905260009082015260a00190565b805162ffffff8116811461346a57600080fd5b600060e08284031215613be657600080fd5b60405160e0810181811067ffffffffffffffff82111715613c0957613c0961321e565b6040528251613c1781613069565b81526020830151613c2781613069565b6020820152613c3860408401613582565b6040820152613c4960608401613bc1565b60608201526080830151608082015260a083015160a0820152613c6e60c08401613582565b60c08201529392505050565b613c83846137de565b9283526001600160a01b03919091166020830152151560408201526080606082018190526016908201527f556e69737761702d56332063616c6c206661696c65640000000000000000000060a082015260c00190565b600060208284031215613ceb57600080fd5b815167ffffffffffffffff80821115613d0357600080fd5b9083019060a08286031215613d1757600080fd5b604051613d2381613273565b8251613d2e81613069565b81526020830151613d3e81613069565b6020820152613d4f6040840161363c565b60408201526060830151613d6281613069565b6060820152608083015182811115613d7957600080fd5b613d858782860161361c565b60808301525095945050505050565b6000825161385b8184602087016135b4565b60008151808452613dbe8160208601602086016135b4565b601f01601f19169290920160200192915050565b613ddb856137de565b8481526001600160a01b03841660208201528215156040820152608060608201526000613e0b6080830184613da6565b9695505050505050565b60038110610528576105286137af565b6001600160a01b0385168152836020820152613e4083613e15565b826040820152608060608201526000613e0b6080830184613da6565b600060033d1115613e755760046000803e5060005160e01c5b90565b600060443d1015613e865790565b6040517ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc803d016004833e81513d67ffffffffffffffff8160248401118184111715613ed457505050505090565b8285019150815181811115613eec5750505050505090565b843d8701016020828501011115613f065750505050505090565b613f1560208286010187613293565b509095945050505050565b6001600160a01b03888116825287166020820152604081018690526000613f4686613e15565b85606083015260e06080830152613f6060e0830186613da6565b84151560a084015282810360c0840152613f7a8185613da6565b9a9950505050505050505050565b6001600160a01b03878116825286166020820152604081018590526000613fae85613e15565b84606083015260e06080830152613fc860e0830185613da6565b92151560a08301525080820360c0909101526000815260200195945050505050565b613b9184613e15565b60006020828403121561400557600080fd5b815167ffffffffffffffff81111561401c57600080fd5b8201601f8101841361402d57600080fd5b61403c848251602084016135d8565b949350505050565b808201808211156129be576129be61348a565b6020815260006129076020830184613da656fea2646970667358221220da27615fd2ab6793f521733433cbcf0bc329fbc56b8950d2a6740ab3579c7f3f64736f6c63430008100033
Loading...
Loading
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.