Address Details
contract
0xfFB8c7dFB83bD4384433995A74cdFe34aE6237FE
- Contract Name
- SwapExecutor
- Creator
- 0xe59f13–f1e2b8 at 0x0c1987–c81ae0
- Balance
- 0 CELO ( )
- Locked CELO Balance
- 0.00 CELO
- Voting CELO Balance
- 0.00 CELO
- Pending Unlocked Gold
- 0.00 CELO
- Tokens
-
Fetching tokens...
- Transactions
- 1 Transactions
- Transfers
- 5 Transfers
- Gas Used
- 757,029
- Last Balance Update
- 13556104
Transactions
Token Transfers
Internal Transactions
Coin Balance History
Logs
Code
Read Contract
Write Contract
This contract has been verified via Sourcify.
View contract in Sourcify repository
- Contract name:
- SwapExecutor
- Optimization enabled
- true
- Compiler version
- v0.8.13+commit.abaa5c0e
- Optimization runs
- 999999
- EVM Version
- london
- Verified at
- 2022-06-16T06:11:56.716050Z
contracts/SwapExecutor.sol
//SPDX-License-Identifier: Unlicense pragma solidity ^0.8.0; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; import "./interfaces/ISwappaRouter.sol"; import "./interfaces/ISwapper.sol"; import "./DCA.sol"; contract SwapExecutor is ISwapper, Ownable { event BeneficiarySet(address newBeneficiary); DCA private dca; ISwappaRouterV1 private swappaRouter; address public beneficiary; constructor( DCA _dca, ISwappaRouterV1 _swappaRouter, address _beneficiary ) { dca = _dca; swappaRouter = _swappaRouter; setBeneficiary(_beneficiary); } function setBeneficiary(address _beneficiary) public onlyOwner { beneficiary = _beneficiary; emit BeneficiarySet(_beneficiary); } function executeMezumoSwap( uint256 period, address[] calldata path, address[] calldata pairs, bytes[] calldata extras ) external { bytes memory params = abi.encode(path, pairs, extras); dca.executeOrder( path[0], path[path.length - 1], period, address(this), params ); } function swap( address _sellToken, address _buyToken, uint256 _inAmount, uint256 _outAmount, bytes calldata _params ) external { ( address[] memory path, address[] memory pairs, bytes[] memory extras ) = abi.decode(_params, (address[], address[], bytes[])); require( IERC20(_sellToken).approve(address(swappaRouter), _inAmount), "SwapExecutor: Approval to Swappa failed" ); swappaRouter.swapExactInputForOutput( path, pairs, extras, _inAmount, 0, address(this), block.timestamp ); require( IERC20(_buyToken).transfer(address(dca), 0), "SwapExecutor: Transfer to DCA failed" ); require( IERC20(_buyToken).transfer( beneficiary, IERC20(_buyToken).balanceOf(address(this)) ), "SwapExecutor: Transfer to DCA failed" ); } }
/_openzeppelin/contracts/access/Ownable.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (access/Ownable.sol) pragma solidity ^0.8.0; import "../utils/Context.sol"; /** * @dev Contract module which provides a basic access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * By default, the owner account will be the one that deploys the contract. This * can later be changed with {transferOwnership}. * * This module is used through inheritance. It will make available the modifier * `onlyOwner`, which can be applied to your functions to restrict their use to * the owner. */ abstract contract Ownable is Context { address private _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev Initializes the contract setting the deployer as the initial owner. */ constructor() { _transferOwnership(_msgSender()); } /** * @dev Returns the address of the current owner. */ function owner() public view virtual returns (address) { return _owner; } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { require(owner() == _msgSender(), "Ownable: caller is not the owner"); _; } /** * @dev Leaves the contract without owner. It will not be possible to call * `onlyOwner` functions anymore. Can only be called by the current owner. * * NOTE: Renouncing ownership will leave the contract without an owner, * thereby removing any functionality that is only available to the owner. */ function renounceOwnership() public virtual onlyOwner { _transferOwnership(address(0)); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Can only be called by the current owner. */ function transferOwnership(address newOwner) public virtual onlyOwner { require(newOwner != address(0), "Ownable: new owner is the zero address"); _transferOwnership(newOwner); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Internal function without access restriction. */ function _transferOwnership(address newOwner) internal virtual { address oldOwner = _owner; _owner = newOwner; emit OwnershipTransferred(oldOwner, newOwner); } }
/_openzeppelin/contracts/security/Pausable.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (security/Pausable.sol) pragma solidity ^0.8.0; import "../utils/Context.sol"; /** * @dev Contract module which allows children to implement an emergency stop * mechanism that can be triggered by an authorized account. * * This module is used through inheritance. It will make available the * modifiers `whenNotPaused` and `whenPaused`, which can be applied to * the functions of your contract. Note that they will not be pausable by * simply including this module, only once the modifiers are put in place. */ abstract contract Pausable is Context { /** * @dev Emitted when the pause is triggered by `account`. */ event Paused(address account); /** * @dev Emitted when the pause is lifted by `account`. */ event Unpaused(address account); bool private _paused; /** * @dev Initializes the contract in unpaused state. */ constructor() { _paused = false; } /** * @dev Returns true if the contract is paused, and false otherwise. */ function paused() public view virtual returns (bool) { return _paused; } /** * @dev Modifier to make a function callable only when the contract is not paused. * * Requirements: * * - The contract must not be paused. */ modifier whenNotPaused() { require(!paused(), "Pausable: paused"); _; } /** * @dev Modifier to make a function callable only when the contract is paused. * * Requirements: * * - The contract must be paused. */ modifier whenPaused() { require(paused(), "Pausable: not paused"); _; } /** * @dev Triggers stopped state. * * Requirements: * * - The contract must not be paused. */ function _pause() internal virtual whenNotPaused { _paused = true; emit Paused(_msgSender()); } /** * @dev Returns to normal state. * * Requirements: * * - The contract must be paused. */ function _unpause() internal virtual whenPaused { _paused = false; emit Unpaused(_msgSender()); } }
/_openzeppelin/contracts/token/ERC20/IERC20.sol
// 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); }
/_openzeppelin/contracts/utils/Context.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/Context.sol) pragma solidity ^0.8.0; /** * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract Context { function _msgSender() internal view virtual returns (address) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } }
/_openzeppelin/contracts/utils/math/Math.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.5.0) (utils/math/Math.sol) pragma solidity ^0.8.0; /** * @dev Standard math utilities missing in the Solidity language. */ library Math { /** * @dev Returns the largest of two numbers. */ function max(uint256 a, uint256 b) internal pure returns (uint256) { return a >= b ? a : b; } /** * @dev Returns the smallest of two numbers. */ function min(uint256 a, uint256 b) internal pure returns (uint256) { return a < b ? a : b; } /** * @dev Returns the average of two numbers. The result is rounded towards * zero. */ function average(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b) / 2 can overflow. return (a & b) + (a ^ b) / 2; } /** * @dev Returns the ceiling of the division of two numbers. * * This differs from standard division with `/` in that it rounds up instead * of rounding down. */ function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b - 1) / b can overflow on addition, so we distribute. return a / b + (a % b == 0 ? 0 : 1); } }
/contracts/DCA.sol
//SPDX-License-Identifier: Unlicense pragma solidity ^0.8.0; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/security/Pausable.sol"; import "@openzeppelin/contracts/utils/math/Math.sol"; import "./interfaces/ISwapper.sol"; import "./interfaces/IOracle.sol"; contract DCA is Ownable { uint256 public constant BLOCKS_PER_DAY = 17280; uint256 public constant MAX_FEE_NUMERATOR = 6_000; // max 60 bps. uint256 public constant FEE_DENOMINATOR = 1_000_000; event OrderCreated( address indexed userAddress, uint256 index, IERC20 indexed sellToken, IERC20 indexed buyToken, uint256 amountPerSwap, uint256 numberOfSwaps, uint256 startingPeriod ); event SwapExecuted( address indexed sellToken, address indexed buyToken, uint256 sellAmount, uint256 buyAmount, uint256 indexed period ); event SwappedWithdrawal( address indexed userAddress, uint256 indexed index, address indexed token, uint256 amount ); event RemainingWithdrawal( address indexed userAddress, uint256 indexed index, address indexed token, uint256 amount ); event TokenPairInitialized(address sellToken, address buyToken); event EmergencyWithdrawal(address token, uint256 amount, address to); event OracleSet(address oracle); event OracleAddressMappingSet(address from, address to); event BeneficiarySet(address newBeneficiary); event FeeNumeratorSet(uint256 feeNumerator); struct UserOrder { IERC20 sellToken; IERC20 buyToken; uint256 amountPerSwap; uint256 numberOfSwaps; uint256 startingPeriod; uint256 lastPeriodWithdrawal; } struct SwapOrder { uint256 amountToSwap; uint256 lastPeriod; // For each past period, what exchange rate was used. mapping(uint256 => uint256) swapExchangeRates; // For each future period, how much to reduce to |amountToSwap|. mapping(uint256 => uint256) amountsToReduce; } // sellToken => buyToken => SwapOrder mapping(address => mapping(address => SwapOrder)) public swapOrders; // userAddress => UserOrder list mapping(address => UserOrder[]) public orders; // For cUSD, we need to use mcUSD in the oracle because of Ubeswap liquidity. Same with cEUR/cREAL. mapping(address => address) public oracleAddresses; uint256 public feeNumerator; address public beneficiary; Oracle public oracle; constructor( Oracle _oracle, address _beneficiary, uint256 initialFee ) { setOracle(_oracle); setBeneficiary(_beneficiary); setFeeNumerator(initialFee); } function createOrder( IERC20 _sellToken, IERC20 _buyToken, uint256 _amountPerSwap, uint256 _numberOfSwaps ) external returns (uint256 index) { require( _sellToken.transferFrom( msg.sender, address(this), _amountPerSwap * _numberOfSwaps ), "DCA: Not enough funds" ); SwapOrder storage swapOrder = swapOrders[address(_sellToken)][ address(_buyToken) ]; if (swapOrder.lastPeriod == 0) { swapOrder.lastPeriod = getCurrentPeriod() - 1; emit TokenPairInitialized(address(_sellToken), address(_buyToken)); } uint256 startingPeriod = swapOrder.lastPeriod + 1; UserOrder memory newOrder = UserOrder( _sellToken, _buyToken, _amountPerSwap, _numberOfSwaps, startingPeriod, swapOrder.lastPeriod ); swapOrder.amountToSwap += _amountPerSwap; swapOrder.amountsToReduce[ startingPeriod + _numberOfSwaps - 1 ] += _amountPerSwap; index = orders[msg.sender].length; orders[msg.sender].push(newOrder); emit OrderCreated( msg.sender, index, _sellToken, _buyToken, _amountPerSwap, _numberOfSwaps, startingPeriod ); } function executeOrder( address _sellToken, address _buyToken, uint256 _period, address _swapper, bytes memory _params ) external { SwapOrder storage swapOrder = swapOrders[_sellToken][_buyToken]; require(swapOrder.lastPeriod + 1 == _period, "DCA: Invalid period"); require( _period <= getCurrentPeriod(), "DCA: Period cannot be in the future" ); uint256 swapAmount = swapOrder.amountToSwap; uint256 requiredAmount = oracle.consult( getOracleTokenAddress(_sellToken), swapAmount, getOracleTokenAddress(_buyToken) ); require(requiredAmount > 0, "DCA: Oracle failure"); swapOrder.lastPeriod++; swapOrder.swapExchangeRates[_period] = requiredAmount / swapOrder.amountToSwap; swapOrder.amountToSwap -= swapOrder.amountsToReduce[_period]; uint256 balanceBefore = IERC20(_buyToken).balanceOf(address(this)); require( IERC20(_sellToken).transfer(_swapper, swapAmount), "DCA: Transfer to Swapper failed" ); ISwapper(_swapper).swap( _sellToken, _buyToken, swapAmount, requiredAmount, _params ); // require( // balanceBefore + requiredAmount <= // IERC20(_buyToken).balanceOf(address(this)), // "DCA: Not enough balance returned" // ); emit SwapExecuted( _sellToken, _buyToken, swapAmount, requiredAmount, _period ); } function withdrawSwapped(uint256 index) public { UserOrder storage order = orders[msg.sender][index]; ( uint256 amountToWithdraw, uint256 finalPeriod ) = calculateAmountToWithdraw(order); order.lastPeriodWithdrawal = finalPeriod; require( order.buyToken.transfer(msg.sender, amountToWithdraw), "DCA: Not enough funds to withdraw" ); emit SwappedWithdrawal( msg.sender, index, address(order.buyToken), amountToWithdraw ); } function withdrawAll(uint256 index) external { withdrawSwapped(index); UserOrder storage order = orders[msg.sender][index]; SwapOrder storage swapOrder = swapOrders[address(order.sellToken)][ address(order.buyToken) ]; uint256 finalPeriod = order.startingPeriod + order.numberOfSwaps - 1; if (finalPeriod > swapOrder.lastPeriod) { swapOrder.amountToSwap -= order.amountPerSwap; swapOrder.amountsToReduce[finalPeriod] -= order.amountPerSwap; uint256 amountToWithdraw = order.amountPerSwap * (finalPeriod - swapOrder.lastPeriod); order.lastPeriodWithdrawal = finalPeriod; require( order.sellToken.transfer(msg.sender, amountToWithdraw), "DCA: Not enough funds to withdraw" ); emit RemainingWithdrawal( msg.sender, index, address(order.sellToken), amountToWithdraw ); } } function emergencyWithdrawal(IERC20 token, address to) external onlyOwner { uint256 balance = token.balanceOf(address(this)); require(token.transfer(to, balance), "DCA: Emergency transfer failed"); emit EmergencyWithdrawal(address(token), balance, to); } // Parameter setters function setOracle(Oracle _newOracle) public onlyOwner { oracle = _newOracle; emit OracleSet(address(oracle)); } function setBeneficiary(address _beneficiary) public onlyOwner { beneficiary = _beneficiary; emit BeneficiarySet(_beneficiary); } function setFeeNumerator(uint256 _feeNumerator) public onlyOwner { feeNumerator = _feeNumerator; emit FeeNumeratorSet(_feeNumerator); } function addAddressMapping(address _from, address _to) external onlyOwner { oracleAddresses[_from] = _to; emit OracleAddressMappingSet(_from, _to); } // Views function calculateAmountToWithdraw(UserOrder memory order) public view returns (uint256 amountToWithdraw, uint256 finalPeriod) { SwapOrder storage swapOrder = swapOrders[address(order.sellToken)][ address(order.buyToken) ]; finalPeriod = Math.min( swapOrder.lastPeriod, order.startingPeriod + order.numberOfSwaps - 1 ); amountToWithdraw = 0; for ( uint256 period = order.lastPeriodWithdrawal + 1; period <= finalPeriod; period++ ) { amountToWithdraw += swapOrder.swapExchangeRates[period] * order.amountPerSwap; } } function calculateAmountWithdrawn(UserOrder memory order) public view returns (uint256 amountWithdrawn) { SwapOrder storage swapOrder = swapOrders[address(order.sellToken)][ address(order.buyToken) ]; amountWithdrawn = 0; for ( uint256 period = order.startingPeriod; period <= swapOrder.lastPeriod; period++ ) { amountWithdrawn += swapOrder.swapExchangeRates[period] * order.amountPerSwap; } } function getUserOrders(address userAddress) external view returns (UserOrder[] memory) { return orders[userAddress]; } function getUserOrdersWithExtras(address userAddress) external view returns ( UserOrder[] memory, uint256[] memory, uint256[] memory, uint256[] memory, uint256 ) { UserOrder[] memory userOrders = orders[userAddress]; uint256[] memory ordersLastPeriod = new uint256[](userOrders.length); uint256[] memory amountsToWithdraw = new uint256[](userOrders.length); uint256[] memory amountsWithdrawn = new uint256[](userOrders.length); for (uint256 i = 0; i < userOrders.length; i++) { UserOrder memory order = userOrders[i]; ( uint256 amountToWithdraw, uint256 finalPeriod ) = calculateAmountToWithdraw(order); ordersLastPeriod[i] = finalPeriod; amountsToWithdraw[i] = amountToWithdraw; amountsWithdrawn[i] = calculateAmountWithdrawn(order); } return ( userOrders, ordersLastPeriod, amountsToWithdraw, amountsWithdrawn, getCurrentPeriod() ); } function getOrder(address userAddress, uint256 index) external view returns (UserOrder memory) { return orders[userAddress][index]; } function getSwapOrderAmountToReduce( address _sellToken, address _buyToken, uint256 _period ) external view returns (uint256) { return swapOrders[_sellToken][_buyToken].amountsToReduce[_period]; } function getSwapOrderExchangeRate( address _sellToken, address _buyToken, uint256 _period ) external view returns (uint256) { return swapOrders[_sellToken][_buyToken].swapExchangeRates[_period]; } function getOracleTokenAddress(address token) public view returns (address) { address mappedToken = oracleAddresses[token]; if (mappedToken != address(0)) { return mappedToken; } else { return token; } } function getCurrentPeriod() public view returns (uint256 period) { period = block.number / BLOCKS_PER_DAY; } }
/contracts/interfaces/IOracle.sol
pragma solidity ^0.8.0; interface Oracle { function consult( address tokenIn, uint256 amountIn, address tokenOut ) external view returns (uint256 amountOut); }
/contracts/interfaces/ISwappaRouter.sol
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface ISwappaRouterV1 { function getOutputAmount( address[] calldata path, address[] calldata pairs, bytes[] calldata extras, uint256 inputAmount ) external view returns (uint256 outputAmount); function swapExactInputForOutput( address[] calldata path, address[] calldata pairs, bytes[] calldata extras, uint256 inputAmount, uint256 minOutputAmount, address to, uint256 deadline ) external returns (uint256 outputAmount); function swapExactInputForOutputWithPrecheck( address[] calldata path, address[] calldata pairs, bytes[] calldata extras, uint256 inputAmount, uint256 minOutputAmount, address to, uint256 deadline ) external returns (uint256 outputAmount); }
/contracts/interfaces/ISwapper.sol
//SPDX-License-Identifier: Unlicense pragma solidity ^0.8.0; interface ISwapper { function swap( address _sellToken, address _buyToken, uint256 _inAmount, uint256 _outAmount, bytes calldata _params ) external; }
Contract ABI
[{"type":"constructor","stateMutability":"nonpayable","inputs":[{"type":"address","name":"_dca","internalType":"contract DCA"},{"type":"address","name":"_swappaRouter","internalType":"contract ISwappaRouterV1"},{"type":"address","name":"_beneficiary","internalType":"address"}]},{"type":"event","name":"BeneficiarySet","inputs":[{"type":"address","name":"newBeneficiary","internalType":"address","indexed":false}],"anonymous":false},{"type":"event","name":"OwnershipTransferred","inputs":[{"type":"address","name":"previousOwner","internalType":"address","indexed":true},{"type":"address","name":"newOwner","internalType":"address","indexed":true}],"anonymous":false},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"beneficiary","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"executeMezumoSwap","inputs":[{"type":"uint256","name":"period","internalType":"uint256"},{"type":"address[]","name":"path","internalType":"address[]"},{"type":"address[]","name":"pairs","internalType":"address[]"},{"type":"bytes[]","name":"extras","internalType":"bytes[]"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"owner","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"renounceOwnership","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setBeneficiary","inputs":[{"type":"address","name":"_beneficiary","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"swap","inputs":[{"type":"address","name":"_sellToken","internalType":"address"},{"type":"address","name":"_buyToken","internalType":"address"},{"type":"uint256","name":"_inAmount","internalType":"uint256"},{"type":"uint256","name":"_outAmount","internalType":"uint256"},{"type":"bytes","name":"_params","internalType":"bytes"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"transferOwnership","inputs":[{"type":"address","name":"newOwner","internalType":"address"}]}]
Contract Creation Code
0x60803461007e57601f61137d38819003918201601f19168301916001600160401b038311848410176100835780849260609460405283398101031261007e57805161006f9161004d82610099565b604060208201519161005e83610099565b01519161006a83610099565b6100aa565b60405161123b90816101428239f35b600080fd5b634e487b7160e01b600052604160045260246000fd5b6001600160a01b0381160361007e57565b907f04d55a8be181fb8d75b76f2d48aa0b2ee40f47e53d6e61763eeeec46feea8a24926020926000549160018060a01b03199233848216176000556040519460018060a01b0380948193823391167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060008ba3168560015416176001551683600254161760025516809160035416176003558152a156fe60806040526004361015610013575b600080fd5b6000803560e01c9081631c31f710146100a2575080632506c0181461009957806338af3eed146100905780635dbd944c14610087578063715018a61461007e5780638da5cb5b146100755763f2fde38b1461006d57600080fd5b61000e61060c565b5061000e6105b9565b5061000e610514565b5061000e61029f565b5061000e61021b565b5061000e61017c565b3461015b5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261015b577f04d55a8be181fb8d75b76f2d48aa0b2ee40f47e53d6e61763eeeec46feea8a2460206004356101008161015e565b73ffffffffffffffffffffffffffffffffffffffff90610124828654163314610700565b16807fffffffffffffffffffffffff00000000000000000000000000000000000000006003541617600355604051908152a1604051f35b80fd5b73ffffffffffffffffffffffffffffffffffffffff81160361000e57565b503461000e5760a07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e576004356101b88161015e565b602435906101c58261015e565b6084359167ffffffffffffffff9182841161000e573660238501121561000e57836004013592831161000e57366024848601011161000e57602461020e94019160443591610e98565b005b600091031261000e57565b503461000e5760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e57602073ffffffffffffffffffffffffffffffffffffffff60035416604051908152f35b9181601f8401121561000e5782359167ffffffffffffffff831161000e576020808501948460051b01011161000e57565b503461000e5760807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e5767ffffffffffffffff60243581811161000e576102f090369060040161026e565b909160443581811161000e5761030a90369060040161026e565b60643583811161000e576103269093929193369060040161026e565b939060405194859261037a602091606083870152610348608087018b8d6107d4565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe097888884030160408901526107d4565b8585820301606086015282815281810196828460051b83010197856000935b8685106104995750505050505050506103b892039081018352826108b9565b61041c6104076103f96103e060015473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff1690565b9461040c61040786836108fa565b610942565b946104168161094f565b91610932565b92803b1561000e576104689360008094604051968795869485937f06dc1f6a00000000000000000000000000000000000000000000000000000000855230916004359160048701610a1a565b03925af1801561048c575b61047957005b8061048661020e92610898565b80610210565b610494610a5f565b610473565b919395975091939597988982820301845288357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181121561000e57830190813586811161000e57803603851361000e576104fe899283928360019601610829565b9a0194019501929593918a979591999899610399565b503461000e576000807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261015b5780547fffffffffffffffffffffffff000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff82169161058b338414610700565b16825581604051917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08284a3f35b503461000e5760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e57602073ffffffffffffffffffffffffffffffffffffffff60005416604051908152f35b503461000e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e576004356106488161015e565b73ffffffffffffffffffffffffffffffffffffffff61066c81600054163314610700565b81161561067c5761020e90610765565b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f64647265737300000000000000000000000000000000000000000000000000006064820152fd5b1561070757565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602060248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152fd5b6000549073ffffffffffffffffffffffffffffffffffffffff80911691827fffffffffffffffffffffffff0000000000000000000000000000000000000000821617600055167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e06000604051a3565b91908082526020809201929160005b8281106107f1575050505090565b90919293828060019273ffffffffffffffffffffffffffffffffffffffff883561081a8161015e565b168152019501939291016107e3565b601f82602094937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0938186528686013760008582860101520116010190565b507f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b67ffffffffffffffff81116108ac57604052565b6108b4610868565b604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff8211176108ac57604052565b90156109035790565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b91908110156109035760051b0190565b3561094c8161015e565b90565b6001811061097c577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b91908251928382526000905b848210610a025750601f84602094957fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe093116109f5575b0116010190565b60008582860101526109ee565b906020908180828501015190828601015201906109b7565b91909360a09361094c969573ffffffffffffffffffffffffffffffffffffffff93848092168652166020850152604084015216606082015281608082015201906109ab565b506040513d6000823e3d90fd5b60209067ffffffffffffffff8111610a86575b60051b0190565b610a8e610868565b610a7f565b81601f8201121561000e57803591610aaa83610a6c565b92610ab860405194856108b9565b808452602092838086019260051b82010192831161000e578301905b828210610ae2575050505090565b8380918335610af08161015e565b815201910190610ad4565b909160608284031261000e5767ffffffffffffffff91803583811161000e5784610b26918301610a93565b936020908183013585811161000e5781610b41918501610a93565b94604093848101359082821161000e570191601f81818501121561000e57833591610b6b83610a6c565b96610b78815198896108b9565b838852868089019460051b8701019582871161000e57878101945b878610610ba65750505050505050505090565b853587811161000e57820184603f8201121561000e578981013591888311610c26575b8451610bfb8c7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08a87011601826108b9565b8381528686858501011161000e5760008c858196898397018386013783010152815201950194610b93565b610c2e610868565b610bc9565b9081602091031261000e5751801515810361000e5790565b15610c5257565b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602760248201527f537761704578656375746f723a20417070726f76616c20746f2053776170706160448201527f206661696c6564000000000000000000000000000000000000000000000000006064820152fd5b9081602091031261000e575190565b90815180825260208080930193019160005b828110610d05575050505090565b835173ffffffffffffffffffffffffffffffffffffffff1685529381019392810192600101610cf7565b93969594929190610d4b610d5a9160e0875260e0870190610ce5565b60209286820384880152610ce5565b9084820360408601528251908183528083019281808460051b8301019501936000915b848310610dc25750505050505060c09291610dbe919660608401526000608084015260a083019073ffffffffffffffffffffffffffffffffffffffff169052565b0152565b9091929394958480610dfe837fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe086600196030187528a516109ab565b9801930193019194939290610d7d565b15610e1557565b60846040517f08c379a0000000000000000000000000000000000000000000000000000000008152602060048201526024808201527f537761704578656375746f723a205472616e7366657220746f2044434120666160448201527f696c6564000000000000000000000000000000000000000000000000000000006064820152fd5b91939290928101610ea891610afb565b939092600254610ecb9073ffffffffffffffffffffffffffffffffffffffff1690565b6040517f095ea7b300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9182166004820152602481018890526000969095602095869593949192869283908a9060449082908e908b165af19a8b1561110f9c6110f69b610f55926111f8575b8d916111db575b50610c4b565b8a610f786103e060025473ffffffffffffffffffffffffffffffffffffffff1690565b92610fb6604051978896879586947f0862d12f0000000000000000000000000000000000000000000000000000000086524293309360048801610d2f565b03925af180156111ce575b6111b1575b5016610fea6103e060015473ffffffffffffffffffffffffffffffffffffffff1690565b6040517fa9059cbb0000000000000000000000000000000000000000000000000000000080825273ffffffffffffffffffffffffffffffffffffffff9290921660048201526000602482015290919061105b9084816044818b875af19081156111a4575b8891611187575b50610e0e565b6003546040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201529273ffffffffffffffffffffffffffffffffffffffff9091169087908585602481875afa94851561117a575b829561114b575b50604051978895869485938452600484016020909392919373ffffffffffffffffffffffffffffffffffffffff60408201951681520152565b03925af192831561113e575b92611111575b5050610e0e565b565b6111309250803d10611137575b61112881836108b9565b810190610c33565b3880611108565b503d61111e565b611146610a5f565b611102565b61116c919550863d8811611173575b61116481836108b9565b810190610cd6565b93386110bd565b503d61115a565b611182610a5f565b6110b6565b61119e9150853d87116111375761112881836108b9565b38611055565b6111ac610a5f565b61104e565b6111c790843d86116111735761116481836108b9565b5038610fc6565b6111d6610a5f565b610fc1565b6111f29150863d88116111375761112881836108b9565b38610f4f565b611200610a5f565b610f4856fea2646970667358221220b91642cd7dfcd68d26b8dab0ad7ad3f363f01ae7259df05cfe98752cad38902964736f6c634300080d0033000000000000000000000000a5690741a1ca30c6d787b6f17699b7d0bd84e0b8000000000000000000000000f35ed7156babf2541e032b3bb8625210316e283200000000000000000000000076efd61146049612a78fa3e0e9bd0a8febc9dce0
Deployed ByteCode
0x60806040526004361015610013575b600080fd5b6000803560e01c9081631c31f710146100a2575080632506c0181461009957806338af3eed146100905780635dbd944c14610087578063715018a61461007e5780638da5cb5b146100755763f2fde38b1461006d57600080fd5b61000e61060c565b5061000e6105b9565b5061000e610514565b5061000e61029f565b5061000e61021b565b5061000e61017c565b3461015b5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261015b577f04d55a8be181fb8d75b76f2d48aa0b2ee40f47e53d6e61763eeeec46feea8a2460206004356101008161015e565b73ffffffffffffffffffffffffffffffffffffffff90610124828654163314610700565b16807fffffffffffffffffffffffff00000000000000000000000000000000000000006003541617600355604051908152a1604051f35b80fd5b73ffffffffffffffffffffffffffffffffffffffff81160361000e57565b503461000e5760a07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e576004356101b88161015e565b602435906101c58261015e565b6084359167ffffffffffffffff9182841161000e573660238501121561000e57836004013592831161000e57366024848601011161000e57602461020e94019160443591610e98565b005b600091031261000e57565b503461000e5760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e57602073ffffffffffffffffffffffffffffffffffffffff60035416604051908152f35b9181601f8401121561000e5782359167ffffffffffffffff831161000e576020808501948460051b01011161000e57565b503461000e5760807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e5767ffffffffffffffff60243581811161000e576102f090369060040161026e565b909160443581811161000e5761030a90369060040161026e565b60643583811161000e576103269093929193369060040161026e565b939060405194859261037a602091606083870152610348608087018b8d6107d4565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe097888884030160408901526107d4565b8585820301606086015282815281810196828460051b83010197856000935b8685106104995750505050505050506103b892039081018352826108b9565b61041c6104076103f96103e060015473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff1690565b9461040c61040786836108fa565b610942565b946104168161094f565b91610932565b92803b1561000e576104689360008094604051968795869485937f06dc1f6a00000000000000000000000000000000000000000000000000000000855230916004359160048701610a1a565b03925af1801561048c575b61047957005b8061048661020e92610898565b80610210565b610494610a5f565b610473565b919395975091939597988982820301845288357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181121561000e57830190813586811161000e57803603851361000e576104fe899283928360019601610829565b9a0194019501929593918a979591999899610399565b503461000e576000807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261015b5780547fffffffffffffffffffffffff000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff82169161058b338414610700565b16825581604051917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08284a3f35b503461000e5760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e57602073ffffffffffffffffffffffffffffffffffffffff60005416604051908152f35b503461000e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261000e576004356106488161015e565b73ffffffffffffffffffffffffffffffffffffffff61066c81600054163314610700565b81161561067c5761020e90610765565b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f64647265737300000000000000000000000000000000000000000000000000006064820152fd5b1561070757565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602060248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152fd5b6000549073ffffffffffffffffffffffffffffffffffffffff80911691827fffffffffffffffffffffffff0000000000000000000000000000000000000000821617600055167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e06000604051a3565b91908082526020809201929160005b8281106107f1575050505090565b90919293828060019273ffffffffffffffffffffffffffffffffffffffff883561081a8161015e565b168152019501939291016107e3565b601f82602094937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0938186528686013760008582860101520116010190565b507f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b67ffffffffffffffff81116108ac57604052565b6108b4610868565b604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff8211176108ac57604052565b90156109035790565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b91908110156109035760051b0190565b3561094c8161015e565b90565b6001811061097c577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b91908251928382526000905b848210610a025750601f84602094957fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe093116109f5575b0116010190565b60008582860101526109ee565b906020908180828501015190828601015201906109b7565b91909360a09361094c969573ffffffffffffffffffffffffffffffffffffffff93848092168652166020850152604084015216606082015281608082015201906109ab565b506040513d6000823e3d90fd5b60209067ffffffffffffffff8111610a86575b60051b0190565b610a8e610868565b610a7f565b81601f8201121561000e57803591610aaa83610a6c565b92610ab860405194856108b9565b808452602092838086019260051b82010192831161000e578301905b828210610ae2575050505090565b8380918335610af08161015e565b815201910190610ad4565b909160608284031261000e5767ffffffffffffffff91803583811161000e5784610b26918301610a93565b936020908183013585811161000e5781610b41918501610a93565b94604093848101359082821161000e570191601f81818501121561000e57833591610b6b83610a6c565b96610b78815198896108b9565b838852868089019460051b8701019582871161000e57878101945b878610610ba65750505050505050505090565b853587811161000e57820184603f8201121561000e578981013591888311610c26575b8451610bfb8c7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08a87011601826108b9565b8381528686858501011161000e5760008c858196898397018386013783010152815201950194610b93565b610c2e610868565b610bc9565b9081602091031261000e5751801515810361000e5790565b15610c5257565b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602760248201527f537761704578656375746f723a20417070726f76616c20746f2053776170706160448201527f206661696c6564000000000000000000000000000000000000000000000000006064820152fd5b9081602091031261000e575190565b90815180825260208080930193019160005b828110610d05575050505090565b835173ffffffffffffffffffffffffffffffffffffffff1685529381019392810192600101610cf7565b93969594929190610d4b610d5a9160e0875260e0870190610ce5565b60209286820384880152610ce5565b9084820360408601528251908183528083019281808460051b8301019501936000915b848310610dc25750505050505060c09291610dbe919660608401526000608084015260a083019073ffffffffffffffffffffffffffffffffffffffff169052565b0152565b9091929394958480610dfe837fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe086600196030187528a516109ab565b9801930193019194939290610d7d565b15610e1557565b60846040517f08c379a0000000000000000000000000000000000000000000000000000000008152602060048201526024808201527f537761704578656375746f723a205472616e7366657220746f2044434120666160448201527f696c6564000000000000000000000000000000000000000000000000000000006064820152fd5b91939290928101610ea891610afb565b939092600254610ecb9073ffffffffffffffffffffffffffffffffffffffff1690565b6040517f095ea7b300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9182166004820152602481018890526000969095602095869593949192869283908a9060449082908e908b165af19a8b1561110f9c6110f69b610f55926111f8575b8d916111db575b50610c4b565b8a610f786103e060025473ffffffffffffffffffffffffffffffffffffffff1690565b92610fb6604051978896879586947f0862d12f0000000000000000000000000000000000000000000000000000000086524293309360048801610d2f565b03925af180156111ce575b6111b1575b5016610fea6103e060015473ffffffffffffffffffffffffffffffffffffffff1690565b6040517fa9059cbb0000000000000000000000000000000000000000000000000000000080825273ffffffffffffffffffffffffffffffffffffffff9290921660048201526000602482015290919061105b9084816044818b875af19081156111a4575b8891611187575b50610e0e565b6003546040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201529273ffffffffffffffffffffffffffffffffffffffff9091169087908585602481875afa94851561117a575b829561114b575b50604051978895869485938452600484016020909392919373ffffffffffffffffffffffffffffffffffffffff60408201951681520152565b03925af192831561113e575b92611111575b5050610e0e565b565b6111309250803d10611137575b61112881836108b9565b810190610c33565b3880611108565b503d61111e565b611146610a5f565b611102565b61116c919550863d8811611173575b61116481836108b9565b810190610cd6565b93386110bd565b503d61115a565b611182610a5f565b6110b6565b61119e9150853d87116111375761112881836108b9565b38611055565b6111ac610a5f565b61104e565b6111c790843d86116111735761116481836108b9565b5038610fc6565b6111d6610a5f565b610fc1565b6111f29150863d88116111375761112881836108b9565b38610f4f565b611200610a5f565b610f4856fea2646970667358221220b91642cd7dfcd68d26b8dab0ad7ad3f363f01ae7259df05cfe98752cad38902964736f6c634300080d0033