Transaction Hash:
Block:
20366022 at Jul-23-2024 01:43:11 AM +UTC
Transaction Fee:
0.000256862847650877 ETH
$0.65
Gas Used:
56,601 Gas / 4.538132677 Gwei
Emitted Events:
165 |
Invoice.PaymentAccepted( hash=6AAE3A38DEDF459D2C355F99C3BBDFB8DCFFCDEB23E47C3E20F7EB60204711BA, tokenContract=0x00000000...000000000, time=1721698991, value=29061000000000000 )
|
Account State Difference:
Address | Before | After | State Difference | ||
---|---|---|---|---|---|
0x34E15888...b738711ee | (BitPay: Invoice) | 9.859545000000000009 Eth | 9.888606000000000009 Eth | 0.029061 | |
0x4838B106...B0BAD5f97
Miner
| (Titan Builder) | 6.288113200853939477 Eth | 6.288226313949333752 Eth | 0.000113113095394275 | |
0x546Ce7C3...dE0F86BBA |
0.209461494775834873 Eth
Nonce: 93
|
0.180143631928183996 Eth
Nonce: 94
| 0.029317862847650877 |
Execution Trace
ETH 0.029061
Invoice.pay( value=29061000000000000, gasPrice=4537678909, expiration=1721700978, payload=867CE97DDC2A36C798775C9F9CC4B200683E20EB151BA8DA30E07395A60912DE, hash=6AAE3A38DEDF459D2C355F99C3BBDFB8DCFFCDEB23E47C3E20F7EB60204711BA, v=28, r=A5EC4A99639556790DAE7A9F61D75550A41A2B63BB4657F92BB4A60F089AC9C9, s=44EEE948C481241631E5554B6AE5AB49587057B6A781FF3CD6C76F36636A0D65, tokenContract=0x0000000000000000000000000000000000000000 )
-
Null: 0x000...001.abdf303e( )
{"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}"}}