Invoice
Deploy on AlchemyContract Information
The following smart contract is called Invoice and it is used to accept payments in either Ether or ERC20 tokens. It verifies the payment using a signature and stores the payment status in a mapping. The contract also allows the owner to withdraw the funds and change the signer address. The SafeERC20 library is used to perform safe transfers of ERC20 tokens.
More Info
{"IERC20.sol":{"content":"pragma solidity ^0.4.23;\n\ncontract IERC20 {\n function totalSupply() public constant returns (uint);\n function balanceOf(address tokenOwner) public constant returns (uint balance);\n function allowance(address tokenOwner, address spender) public constant returns (uint remaining);\n function transfer(address to, uint tokens) public returns (bool success);\n function approve(address spender, uint tokens) public returns (bool success);\n function transferFrom(address from, address to, uint tokens) public returns (bool success);\n\n event Transfer(address indexed from, address indexed to, uint tokens);\n event Approval(address indexed tokenOwner, address indexed spender, uint tokens);\n}"},"Invoice.sol":{"content":"pragma solidity ^0.4.23;\n\nimport \"./IERC20.sol\";\nimport \"./SafeERC20.sol\";\n\ncontract Invoice {\n using SafeERC20 for IERC20;\n\n address public owner;\n address public quoteSigner;\n mapping(bytes32 =\u003e bool) public isPaid;\n\n event PaymentAccepted(bytes32 indexed hash, address indexed tokenContract, uint time, uint value);\n\n\n constructor(address valueSigner) public {\n owner = msg.sender;\n quoteSigner = valueSigner;\n }\n\n function isValidPayment(\n uint value,\n uint gasPrice,\n uint expiration,\n bytes32 payload,\n bytes32 hash,\n uint8 v,\n bytes32 r,\n bytes32 s,\n address tokenContract\n ) public view returns(bool valid) {\n bool isValid = !isPaid[payload];\n isValid = isValid \u0026\u0026 block.timestamp \u003c= expiration;\n bytes memory prefix = \"\\x19Ethereum Signed Message:\\n32\";\n bytes32 ourHash = keccak256(abi.encodePacked(value, gasPrice, expiration, payload, tokenContract));\n bytes32 payloadHash = keccak256(abi.encodePacked(prefix, ourHash));\n isValid = isValid \u0026\u0026 ourHash == hash;\n isValid = isValid \u0026\u0026 (ecrecover(payloadHash, v, r, s) == quoteSigner);\n return isValid;\n }\n\n function validatePayment(\n uint value,\n uint gasPrice,\n uint expiration,\n bytes32 payload,\n bytes32 hash,\n uint8 v,\n bytes32 r,\n bytes32 s,\n address tokenContract\n ) public view returns(bool valid) {\n require(isPaid[payload] == false, \"Already been paid\");\n require(block.timestamp \u003c= expiration, \"Payment is late\");\n bytes memory prefix = \"\\x19Ethereum Signed Message:\\n32\";\n bytes32 ourHash = keccak256(abi.encodePacked(value, gasPrice, expiration, payload, tokenContract));\n bytes32 payloadHash = keccak256(abi.encodePacked(prefix, ourHash));\n require(ourHash == hash, \"Hash mismatch\");\n require(ecrecover(payloadHash, v, r, s) == quoteSigner, \"Signature mismatch for quote\");\n return true;\n }\n\n\n function pay(\n uint value,\n uint gasPrice,\n uint expiration,\n bytes32 payload,\n bytes32 hash,\n uint8 v,\n bytes32 r,\n bytes32 s,\n address tokenContract\n ) public payable {\n if(tokenContract == 0x0) {\n require(validatePayment(msg.value, gasPrice, expiration, payload, hash, v, r, s, tokenContract), \"Only accept valid payments\");\n } else {\n IERC20 token = IERC20(tokenContract);\n require(token.allowance(msg.sender, address(this)) \u003e= value, \"Must have enough tokens to pay\");\n require(validatePayment(value, gasPrice, expiration, payload, hash, v, r, s, tokenContract), \"Only accept valid payments\");\n require(token.safeTransferFrom(msg.sender, address(this), value), \"Transfer must succeed\");\n }\n isPaid[payload] = true;\n emit PaymentAccepted(hash, tokenContract, block.timestamp, value);\n }\n\n modifier isAdmin() {\n require(msg.sender == owner, \"Must be the contract owner\");\n _;\n }\n\n function withdraw(address tokenContract) public isAdmin {\n if(tokenContract == 0x0) {\n owner.transfer(address(this).balance);\n } else {\n IERC20 token = IERC20(tokenContract);\n uint balance = token.balanceOf(address(this));\n require(token.safeTransfer(owner, balance), \"Must succeed withdrawing tokens\");\n }\n }\n\n function setSigner(address newQuoteSigner) public isAdmin {\n quoteSigner = newQuoteSigner;\n }\n function setAdmin(address newAdmin) public isAdmin {\n owner = newAdmin;\n }\n}\n"},"SafeERC20.sol":{"content":"pragma solidity ^0.4.23;\n\nimport \"./IERC20.sol\";\n/**\n* @dev Library to perform safe calls to standard method for ERC20 tokens.\n*\n* Why Transfers: transfer methods could have a return value (bool), throw or revert for insufficient funds or\n* unathorized value.\n*\n* Why Approve: approve method could has a return value (bool) or does not accept 0 as a valid value (BNB token).\n* The common strategy used to clean approvals.\n*\n* We use the Solidity call instead of interface methods because in the case of transfer, it will fail\n* for tokens with an implementation without returning a value.\n* Since versions of Solidity 0.4.22 the EVM has a new opcode, called RETURNDATASIZE.\n* This opcode stores the size of the returned data of an external call. The code checks the size of the return value\n* after an external call and reverts the transaction in case the return data is shorter than expected\n*/\nlibrary SafeERC20 {\n /**\n * @dev Transfer token for a specified address\n * @param _token erc20 The address of the ERC20 contract\n * @param _to address The address which you want to transfer to\n * @param _value uint256 the _value of tokens to be transferred\n * @return bool whether the transfer was successful or not\n */\n function safeTransfer(IERC20 _token, address _to, uint256 _value) internal returns (bool) {\n uint256 prevBalance = _token.balanceOf(address(this));\n\n if (prevBalance \u003c _value) {\n // Insufficient funds\n return false;\n }\n\n address(_token).call(\n abi.encodeWithSignature(\"transfer(address,uint256)\", _to, _value)\n );\n\n // Fail if the new balance its not equal than previous balance sub _value\n return prevBalance - _value == _token.balanceOf(address(this));\n }\n\n /**\n * @dev Transfer tokens from one address to another\n * @param _token erc20 The address of the ERC20 contract\n * @param _from address The address which you want to send tokens from\n * @param _to address The address which you want to transfer to\n * @param _value uint256 the _value of tokens to be transferred\n * @return bool whether the transfer was successful or not\n */\n function safeTransferFrom(\n IERC20 _token,\n address _from,\n address _to,\n uint256 _value\n ) internal returns (bool)\n {\n uint256 prevBalance = _token.balanceOf(_from);\n\n if (\n prevBalance \u003c _value || // Insufficient funds\n _token.allowance(_from, address(this)) \u003c _value // Insufficient allowance\n ) {\n return false;\n }\n\n address(_token).call(\n abi.encodeWithSignature(\"transferFrom(address,address,uint256)\", _from, _to, _value)\n );\n\n // Fail if the new balance its not equal than previous balance sub _value\n return prevBalance - _value == _token.balanceOf(_from);\n }\n\n /**\n * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender.\n *\n * Beware that changing an allowance with this method brings the risk that someone may use both the old\n * and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this\n * race condition is to first reduce the spender\u0027s allowance to 0 and set the desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * @param _token erc20 The address of the ERC20 contract\n * @param _spender The address which will spend the funds.\n * @param _value The amount of tokens to be spent.\n * @return bool whether the approve was successful or not\n */\n function safeApprove(IERC20 _token, address _spender, uint256 _value) internal returns (bool) {\n address(_token).call(\n abi.encodeWithSignature(\"approve(address,uint256)\",_spender, _value)\n );\n\n // Fail if the new allowance its not equal than _value\n return _token.allowance(address(this), _spender) == _value;\n }\n\n /**\n * @dev Clear approval\n * Note that if 0 is not a valid value it will be set to 1.\n * @param _token erc20 The address of the ERC20 contract\n * @param _spender The address which will spend the funds.\n */\n function clearApprove(IERC20 _token, address _spender) internal returns (bool) {\n bool success = safeApprove(_token, _spender, 0);\n\n if (!success) {\n success = safeApprove(_token, _spender, 1);\n }\n\n return success;\n }\n}"}}
[{"constant":true,"inputs":[{"name":"value","type":"uint256"},{"name":"gasPrice","type":"uint256"},{"name":"expiration","type":"uint256"},{"name":"payload","type":"bytes32"},{"name":"hash","type":"bytes32"},{"name":"v","type":"uint8"},{"name":"r","type":"bytes32"},{"name":"s","type":"bytes32"},{"name":"tokenContract","type":"address"}],"name":"validatePayment","outputs":[{"name":"valid","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"tokenContract","type":"address"}],"name":"withdraw","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"newQuoteSigner","type":"address"}],"name":"setSigner","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"newAdmin","type":"address"}],"name":"setAdmin","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"value","type":"uint256"},{"name":"gasPrice","type":"uint256"},{"name":"expiration","type":"uint256"},{"name":"payload","type":"bytes32"},{"name":"hash","type":"bytes32"},{"name":"v","type":"uint8"},{"name":"r","type":"bytes32"},{"name":"s","type":"bytes32"},{"name":"tokenContract","type":"address"}],"name":"isValidPayment","outputs":[{"name":"valid","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"value","type":"uint256"},{"name":"gasPrice","type":"uint256"},{"name":"expiration","type":"uint256"},{"name":"payload","type":"bytes32"},{"name":"hash","type":"bytes32"},{"name":"v","type":"uint8"},{"name":"r","type":"bytes32"},{"name":"s","type":"bytes32"},{"name":"tokenContract","type":"address"}],"name":"pay","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[],"name":"quoteSigner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"bytes32"}],"name":"isPaid","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"valueSigner","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"name":"hash","type":"bytes32"},{"indexed":true,"name":"tokenContract","type":"address"},{"indexed":false,"name":"time","type":"uint256"},{"indexed":false,"name":"value","type":"uint256"}],"name":"PaymentAccepted","type":"event"}]
[{"constant":true,"inputs":[{"name":"value","type":"uint256"},{"name":"gasPrice","type":"uint256"},{"name":"expiration","type":"uint256"},{"name":"payload","type":"bytes32"},{"name":"hash","type":"bytes32"},{"name":"v","type":"uint8"},{"name":"r","type":"bytes32"},{"name":"s","type":"bytes32"},{"name":"tokenContract","type":"address"}],"name":"validatePayment","outputs":[{"name":"valid","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"tokenContract","type":"address"}],"name":"withdraw","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"newQuoteSigner","type":"address"}],"name":"setSigner","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"newAdmin","type":"address"}],"name":"setAdmin","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"value","type":"uint256"},{"name":"gasPrice","type":"uint256"},{"name":"expiration","type":"uint256"},{"name":"payload","type":"bytes32"},{"name":"hash","type":"bytes32"},{"name":"v","type":"uint8"},{"name":"r","type":"bytes32"},{"name":"s","type":"bytes32"},{"name":"tokenContract","type":"address"}],"name":"isValidPayment","outputs":[{"name":"valid","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"value","type":"uint256"},{"name":"gasPrice","type":"uint256"},{"name":"expiration","type":"uint256"},{"name":"payload","type":"bytes32"},{"name":"hash","type":"bytes32"},{"name":"v","type":"uint8"},{"name":"r","type":"bytes32"},{"name":"s","type":"bytes32"},{"name":"tokenContract","type":"address"}],"name":"pay","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[],"name":"quoteSigner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"bytes32"}],"name":"isPaid","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"valueSigner","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"name":"hash","type":"bytes32"},{"indexed":true,"name":"tokenContract","type":"address"},{"indexed":false,"name":"time","type":"uint256"},{"indexed":false,"name":"value","type":"uint256"}],"name":"PaymentAccepted","type":"event"}]
608060405234801561001057600080fd5b50604051602080611e9883398101806040528101908080519060200190929190505050336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555080600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050611dd4806100c46000396000f300608060405260043610610099576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680630b0898211461009e57806351cff8d91461015c5780636c19e7831461019f578063704b6c02146101e25780638da5cb5b1461022557806398a89c1b1461027c578063b6b4af051461033a578063f413bdb3146103d3578063feef66401461042a575b600080fd5b3480156100aa57600080fd5b5061014260048036038101908080359060200190929190803590602001909291908035906020019092919080356000191690602001909291908035600019169060200190929190803560ff16906020019092919080356000191690602001909291908035600019169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610473565b604051808215151515815260200191505060405180910390f35b34801561016857600080fd5b5061019d600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061096e565b005b3480156101ab57600080fd5b506101e0600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610c79565b005b3480156101ee57600080fd5b50610223600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610d81565b005b34801561023157600080fd5b5061023a610e88565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34801561028857600080fd5b5061032060048036038101908080359060200190929190803590602001909291908035906020019092919080356000191690602001909291908035600019169060200190929190803560ff16906020019092919080356000191690602001909291908035600019169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610ead565b604051808215151515815260200191505060405180910390f35b6103d160048036038101908080359060200190929190803590602001909291908035906020019092919080356000191690602001909291908035600019169060200190929190803560ff16906020019092919080356000191690602001909291908035600019169060200190929190803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506111f6565b005b3480156103df57600080fd5b506103e86115e6565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34801561043657600080fd5b50610459600480360381019080803560001916906020019092919050505061160c565b604051808215151515815260200191505060405180910390f35b6000606060008060001515600260008c6000191660001916815260200190815260200160002060009054906101000a900460ff16151514151561051e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260118152602001807f416c7265616479206265656e207061696400000000000000000000000000000081525060200191505060405180910390fd5b8a4211151515610596576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600f8152602001807f5061796d656e74206973206c617465000000000000000000000000000000000081525060200191505060405180910390fd5b6040805190810160405280601c81526020017f19457468657265756d205369676e6564204d6573736167653a0a33320000000081525092508c8c8c8c886040516020018086815260200185815260200184815260200183600019166000191681526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166c01000000000000000000000000028152601401955050505050506040516020818303038152906040526040518082805190602001908083835b6020831015156106865780518252602082019150602081019050602083039250610661565b6001836020036101000a0380198251168184511680821785525050505050509050019150506040518091039020915082826040516020018083805190602001908083835b6020831015156106ef57805182526020820191506020810190506020830392506106ca565b6001836020036101000a0380198251168184511680821785525050505050509050018260001916600019168152602001925050506040516020818303038152906040526040518082805190602001908083835b6020831015156107675780518252602082019150602081019050602083039250610742565b6001836020036101000a0380198251168184511680821785525050505050509050019150506040518091039020905088600019168260001916141515610815576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600d8152602001807f48617368206d69736d617463680000000000000000000000000000000000000081525060200191505060405180910390fd5b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166001828a8a8a604051600081526020016040526040518085600019166000191681526020018460ff1660ff1681526020018360001916600019168152602001826000191660001916815260200194505050505060206040516020810390808403906000865af11580156108c5573d6000803e3d6000fd5b5050506020604051035173ffffffffffffffffffffffffffffffffffffffff1614151561095a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601c8152602001807f5369676e6174757265206d69736d6174636820666f722071756f74650000000081525060200191505060405180910390fd5b600193505050509998505050505050505050565b6000806000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141515610a35576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f4d7573742062652074686520636f6e7472616374206f776e657200000000000081525060200191505060405180910390fd5b60008373ffffffffffffffffffffffffffffffffffffffff161415610ad8576000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166108fc3073ffffffffffffffffffffffffffffffffffffffff16319081150290604051600060405180830381858888f19350505050158015610ad2573d6000803e3d6000fd5b50610c74565b8291508173ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001915050602060405180830381600087803b158015610b7657600080fd5b505af1158015610b8a573d6000803e3d6000fd5b505050506040513d6020811015610ba057600080fd5b81019080805190602001909291905050509050610bff6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff16828473ffffffffffffffffffffffffffffffffffffffff1661162c9092919063ffffffff16565b1515610c73576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f4d7573742073756363656564207769746864726177696e6720746f6b656e730081525060200191505060405180910390fd5b5b505050565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141515610d3d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f4d7573742062652074686520636f6e7472616374206f776e657200000000000081525060200191505060405180910390fd5b80600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141515610e45576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f4d7573742062652074686520636f6e7472616374206f776e657200000000000081525060200191505060405180910390fd5b806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000806060600080600260008c6000191660001916815260200190815260200160002060009054906101000a900460ff16159350838015610eee57508b4211155b93506040805190810160405280601c81526020017f19457468657265756d205369676e6564204d6573736167653a0a33320000000081525092508d8d8d8d896040516020018086815260200185815260200184815260200183600019166000191681526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166c01000000000000000000000000028152601401955050505050506040516020818303038152906040526040518082805190602001908083835b602083101515610fe05780518252602082019150602081019050602083039250610fbb565b6001836020036101000a0380198251168184511680821785525050505050509050019150506040518091039020915082826040516020018083805190602001908083835b6020831015156110495780518252602082019150602081019050602083039250611024565b6001836020036101000a0380198251168184511680821785525050505050509050018260001916600019168152602001925050506040516020818303038152906040526040518082805190602001908083835b6020831015156110c1578051825260208201915060208101905060208303925061109c565b6001836020036101000a03801982511681845116808217855250505050505090500191505060405180910390209050838015611104575089600019168260001916145b93508380156111e05750600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166001828b8b8b604051600081526020016040526040518085600019166000191681526020018460ff1660ff1681526020018360001916600019168152602001826000191660001916815260200194505050505060206040516020810390808403906000865af11580156111be573d6000803e3d6000fd5b5050506020604051035173ffffffffffffffffffffffffffffffffffffffff16145b9350839450505050509998505050505050505050565b6000808273ffffffffffffffffffffffffffffffffffffffff1614156112a057611227348a8a8a8a8a8a8a8a610473565b151561129b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f4f6e6c79206163636570742076616c6964207061796d656e747300000000000081525060200191505060405180910390fd5b61154b565b819050898173ffffffffffffffffffffffffffffffffffffffff1663dd62ed3e33306040518363ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200192505050602060405180830381600087803b15801561137357600080fd5b505af1158015611387573d6000803e3d6000fd5b505050506040513d602081101561139d57600080fd5b810190808051906020019092919050505010151515611424576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601e8152602001807f4d757374206861766520656e6f75676820746f6b656e7320746f20706179000081525060200191505060405180910390fd5b6114358a8a8a8a8a8a8a8a8a610473565b15156114a9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f4f6e6c79206163636570742076616c6964207061796d656e747300000000000081525060200191505060405180910390fd5b6114d633308c8473ffffffffffffffffffffffffffffffffffffffff16611946909392919063ffffffff16565b151561154a576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f5472616e73666572206d7573742073756363656564000000000000000000000081525060200191505060405180910390fd5b5b600160026000896000191660001916815260200190815260200160002060006101000a81548160ff0219169083151502179055508173ffffffffffffffffffffffffffffffffffffffff1686600019167f826e7792f434a28ba302e6767da85b4b8e56b83a5e028687f30e848e32667f95428d604051808381526020018281526020019250505060405180910390a350505050505050505050565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60026020528060005260406000206000915054906101000a900460ff1681565b6000808473ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001915050602060405180830381600087803b1580156116ca57600080fd5b505af11580156116de573d6000803e3d6000fd5b505050506040513d60208110156116f457600080fd5b8101908080519060200190929190505050905082811015611718576000915061193e565b8473ffffffffffffffffffffffffffffffffffffffff168484604051602401808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001828152602001925050506040516020818303038152906040527fa9059cbb000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505060405180828051906020019080838360005b8381101561181e578082015181840152602081019050611803565b50505050905090810190601f16801561184b5780820380516001836020036101000a031916815260200191505b509150506000604051808303816000865af1915050508473ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001915050602060405180830381600087803b1580156118fc57600080fd5b505af1158015611910573d6000803e3d6000fd5b505050506040513d602081101561192657600080fd5b81019080805190602001909291905050508382031491505b509392505050565b6000808573ffffffffffffffffffffffffffffffffffffffff166370a08231866040518263ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001915050602060405180830381600087803b1580156119e457600080fd5b505af11580156119f8573d6000803e3d6000fd5b505050506040513d6020811015611a0e57600080fd5b8101908080519060200190929190505050905082811080611b375750828673ffffffffffffffffffffffffffffffffffffffff1663dd62ed3e87306040518363ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200192505050602060405180830381600087803b158015611afa57600080fd5b505af1158015611b0e573d6000803e3d6000fd5b505050506040513d6020811015611b2457600080fd5b8101908080519060200190929190505050105b15611b455760009150611d9f565b8573ffffffffffffffffffffffffffffffffffffffff16858585604051602401808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200182815260200193505050506040516020818303038152906040527f23b872dd000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505060405180828051906020019080838360005b83811015611c7f578082015181840152602081019050611c64565b50505050905090810190601f168015611cac5780820380516001836020036101000a031916815260200191505b509150506000604051808303816000865af1915050508573ffffffffffffffffffffffffffffffffffffffff166370a08231866040518263ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001915050602060405180830381600087803b158015611d5d57600080fd5b505af1158015611d71573d6000803e3d6000fd5b505050506040513d6020811015611d8757600080fd5b81019080805190602001909291905050508382031491505b509493505050505600a165627a7a7230582084ea8b0fc708d384163b217b0469bcdaed7970e5c5d34d783e960923add11a820029000000000000000000000000a97ceb180ca61d124b6a4aadc5530bc53198922f
608060405234801561001057600080fd5b50604051602080611e9883398101806040528101908080519060200190929190505050336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555080600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050611dd4806100c46000396000f300608060405260043610610099576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680630b0898211461009e57806351cff8d91461015c5780636c19e7831461019f578063704b6c02146101e25780638da5cb5b1461022557806398a89c1b1461027c578063b6b4af051461033a578063f413bdb3146103d3578063feef66401461042a575b600080fd5b3480156100aa57600080fd5b5061014260048036038101908080359060200190929190803590602001909291908035906020019092919080356000191690602001909291908035600019169060200190929190803560ff16906020019092919080356000191690602001909291908035600019169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610473565b604051808215151515815260200191505060405180910390f35b34801561016857600080fd5b5061019d600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061096e565b005b3480156101ab57600080fd5b506101e0600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610c79565b005b3480156101ee57600080fd5b50610223600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610d81565b005b34801561023157600080fd5b5061023a610e88565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34801561028857600080fd5b5061032060048036038101908080359060200190929190803590602001909291908035906020019092919080356000191690602001909291908035600019169060200190929190803560ff16906020019092919080356000191690602001909291908035600019169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610ead565b604051808215151515815260200191505060405180910390f35b6103d160048036038101908080359060200190929190803590602001909291908035906020019092919080356000191690602001909291908035600019169060200190929190803560ff16906020019092919080356000191690602001909291908035600019169060200190929190803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506111f6565b005b3480156103df57600080fd5b506103e86115e6565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34801561043657600080fd5b50610459600480360381019080803560001916906020019092919050505061160c565b604051808215151515815260200191505060405180910390f35b6000606060008060001515600260008c6000191660001916815260200190815260200160002060009054906101000a900460ff16151514151561051e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260118152602001807f416c7265616479206265656e207061696400000000000000000000000000000081525060200191505060405180910390fd5b8a4211151515610596576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600f8152602001807f5061796d656e74206973206c617465000000000000000000000000000000000081525060200191505060405180910390fd5b6040805190810160405280601c81526020017f19457468657265756d205369676e6564204d6573736167653a0a33320000000081525092508c8c8c8c886040516020018086815260200185815260200184815260200183600019166000191681526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166c01000000000000000000000000028152601401955050505050506040516020818303038152906040526040518082805190602001908083835b6020831015156106865780518252602082019150602081019050602083039250610661565b6001836020036101000a0380198251168184511680821785525050505050509050019150506040518091039020915082826040516020018083805190602001908083835b6020831015156106ef57805182526020820191506020810190506020830392506106ca565b6001836020036101000a0380198251168184511680821785525050505050509050018260001916600019168152602001925050506040516020818303038152906040526040518082805190602001908083835b6020831015156107675780518252602082019150602081019050602083039250610742565b6001836020036101000a0380198251168184511680821785525050505050509050019150506040518091039020905088600019168260001916141515610815576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600d8152602001807f48617368206d69736d617463680000000000000000000000000000000000000081525060200191505060405180910390fd5b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166001828a8a8a604051600081526020016040526040518085600019166000191681526020018460ff1660ff1681526020018360001916600019168152602001826000191660001916815260200194505050505060206040516020810390808403906000865af11580156108c5573d6000803e3d6000fd5b5050506020604051035173ffffffffffffffffffffffffffffffffffffffff1614151561095a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601c8152602001807f5369676e6174757265206d69736d6174636820666f722071756f74650000000081525060200191505060405180910390fd5b600193505050509998505050505050505050565b6000806000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141515610a35576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f4d7573742062652074686520636f6e7472616374206f776e657200000000000081525060200191505060405180910390fd5b60008373ffffffffffffffffffffffffffffffffffffffff161415610ad8576000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166108fc3073ffffffffffffffffffffffffffffffffffffffff16319081150290604051600060405180830381858888f19350505050158015610ad2573d6000803e3d6000fd5b50610c74565b8291508173ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001915050602060405180830381600087803b158015610b7657600080fd5b505af1158015610b8a573d6000803e3d6000fd5b505050506040513d6020811015610ba057600080fd5b81019080805190602001909291905050509050610bff6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff16828473ffffffffffffffffffffffffffffffffffffffff1661162c9092919063ffffffff16565b1515610c73576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f4d7573742073756363656564207769746864726177696e6720746f6b656e730081525060200191505060405180910390fd5b5b505050565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141515610d3d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f4d7573742062652074686520636f6e7472616374206f776e657200000000000081525060200191505060405180910390fd5b80600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141515610e45576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f4d7573742062652074686520636f6e7472616374206f776e657200000000000081525060200191505060405180910390fd5b806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000806060600080600260008c6000191660001916815260200190815260200160002060009054906101000a900460ff16159350838015610eee57508b4211155b93506040805190810160405280601c81526020017f19457468657265756d205369676e6564204d6573736167653a0a33320000000081525092508d8d8d8d896040516020018086815260200185815260200184815260200183600019166000191681526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166c01000000000000000000000000028152601401955050505050506040516020818303038152906040526040518082805190602001908083835b602083101515610fe05780518252602082019150602081019050602083039250610fbb565b6001836020036101000a0380198251168184511680821785525050505050509050019150506040518091039020915082826040516020018083805190602001908083835b6020831015156110495780518252602082019150602081019050602083039250611024565b6001836020036101000a0380198251168184511680821785525050505050509050018260001916600019168152602001925050506040516020818303038152906040526040518082805190602001908083835b6020831015156110c1578051825260208201915060208101905060208303925061109c565b6001836020036101000a03801982511681845116808217855250505050505090500191505060405180910390209050838015611104575089600019168260001916145b93508380156111e05750600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166001828b8b8b604051600081526020016040526040518085600019166000191681526020018460ff1660ff1681526020018360001916600019168152602001826000191660001916815260200194505050505060206040516020810390808403906000865af11580156111be573d6000803e3d6000fd5b5050506020604051035173ffffffffffffffffffffffffffffffffffffffff16145b9350839450505050509998505050505050505050565b6000808273ffffffffffffffffffffffffffffffffffffffff1614156112a057611227348a8a8a8a8a8a8a8a610473565b151561129b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f4f6e6c79206163636570742076616c6964207061796d656e747300000000000081525060200191505060405180910390fd5b61154b565b819050898173ffffffffffffffffffffffffffffffffffffffff1663dd62ed3e33306040518363ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200192505050602060405180830381600087803b15801561137357600080fd5b505af1158015611387573d6000803e3d6000fd5b505050506040513d602081101561139d57600080fd5b810190808051906020019092919050505010151515611424576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601e8152602001807f4d757374206861766520656e6f75676820746f6b656e7320746f20706179000081525060200191505060405180910390fd5b6114358a8a8a8a8a8a8a8a8a610473565b15156114a9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601a8152602001807f4f6e6c79206163636570742076616c6964207061796d656e747300000000000081525060200191505060405180910390fd5b6114d633308c8473ffffffffffffffffffffffffffffffffffffffff16611946909392919063ffffffff16565b151561154a576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f5472616e73666572206d7573742073756363656564000000000000000000000081525060200191505060405180910390fd5b5b600160026000896000191660001916815260200190815260200160002060006101000a81548160ff0219169083151502179055508173ffffffffffffffffffffffffffffffffffffffff1686600019167f826e7792f434a28ba302e6767da85b4b8e56b83a5e028687f30e848e32667f95428d604051808381526020018281526020019250505060405180910390a350505050505050505050565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60026020528060005260406000206000915054906101000a900460ff1681565b6000808473ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001915050602060405180830381600087803b1580156116ca57600080fd5b505af11580156116de573d6000803e3d6000fd5b505050506040513d60208110156116f457600080fd5b8101908080519060200190929190505050905082811015611718576000915061193e565b8473ffffffffffffffffffffffffffffffffffffffff168484604051602401808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001828152602001925050506040516020818303038152906040527fa9059cbb000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505060405180828051906020019080838360005b8381101561181e578082015181840152602081019050611803565b50505050905090810190601f16801561184b5780820380516001836020036101000a031916815260200191505b509150506000604051808303816000865af1915050508473ffffffffffffffffffffffffffffffffffffffff166370a08231306040518263ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001915050602060405180830381600087803b1580156118fc57600080fd5b505af1158015611910573d6000803e3d6000fd5b505050506040513d602081101561192657600080fd5b81019080805190602001909291905050508382031491505b509392505050565b6000808573ffffffffffffffffffffffffffffffffffffffff166370a08231866040518263ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001915050602060405180830381600087803b1580156119e457600080fd5b505af11580156119f8573d6000803e3d6000fd5b505050506040513d6020811015611a0e57600080fd5b8101908080519060200190929190505050905082811080611b375750828673ffffffffffffffffffffffffffffffffffffffff1663dd62ed3e87306040518363ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200192505050602060405180830381600087803b158015611afa57600080fd5b505af1158015611b0e573d6000803e3d6000fd5b505050506040513d6020811015611b2457600080fd5b8101908080519060200190929190505050105b15611b455760009150611d9f565b8573ffffffffffffffffffffffffffffffffffffffff16858585604051602401808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200182815260200193505050506040516020818303038152906040527f23b872dd000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505060405180828051906020019080838360005b83811015611c7f578082015181840152602081019050611c64565b50505050905090810190601f168015611cac5780820380516001836020036101000a031916815260200191505b509150506000604051808303816000865af1915050508573ffffffffffffffffffffffffffffffffffffffff166370a08231866040518263ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001915050602060405180830381600087803b158015611d5d57600080fd5b505af1158015611d71573d6000803e3d6000fd5b505050506040513d6020811015611d8757600080fd5b81019080805190602001909291905050508382031491505b509493505050505600a165627a7a7230582084ea8b0fc708d384163b217b0469bcdaed7970e5c5d34d783e960923add11a820029000000000000000000000000a97ceb180ca61d124b6a4aadc5530bc53198922f