Transaction Hash:
Block:
9863112 at Apr-13-2020 09:11:24 AM +UTC
Transaction Fee:
0.0000918684 ETH
$0.18
Gas Used:
39,260 Gas / 2.34 Gwei
Emitted Events:
140 |
MCHDailyActionV3.Action( user=[Sender] 0xd6facac594ca53e53a0a17e551044b1d1eb1d1a4, at=1586768440 )
|
Account State Difference:
Address | Before | After | State Difference | ||
---|---|---|---|---|---|
0x5A0b54D5...D3E029c4c
Miner
| (Spark Pool) | 23.140264901815832336 Eth | 23.140356770215832336 Eth | 0.0000918684 | |
0x5e07B6F1...F1c177753 | (MCH: Daily Action 3) | ||||
0xd6FacaC5...d1EB1D1a4 |
0.019804725952305628 Eth
Nonce: 338
|
0.019712857552305628 Eth
Nonce: 339
| 0.0000918684 |
Execution Trace
MCHDailyActionV3.requestDailyActionReward( _signature=0x7B831546D54D212C2CFBF0CAB358A90C9790BA02B845772055271BA98FE37F330610AA0D07DC0E0B5E7268B94498796B90EFF97A1172C1CF4C6181C58AE495A100, _time=1586768440 )
-
Null: 0x000...001.69fde7aa( )
requestDailyActionReward[MCHDailyActionV3 (ln:267)]
validateSig[MCHDailyActionV3 (ln:268)]
recover[MCHDailyActionV3 (ln:280)]
recover[MCHDailyActionV3 (ln:297)]
ecrecover[ECDSA (ln:143)]
ethSignedMessageHash[MCHDailyActionV3 (ln:280)]
toEthSignedMessageHash[MCHDailyActionV3 (ln:293)]
encodeData[MCHDailyActionV3 (ln:280)]
Action[MCHDailyActionV3 (ln:272)]
pragma solidity ^0.5.0; // produced by the Solididy File Flattener (c) David Appleton 2018 // contact : [email protected] // released under Apache 2.0 licence // input /Users/rmanzoku/src/github.com/doublejumptokyo/mch-dailyaction/contracts/MCHDailyActionV3.sol // flattened : Monday, 30-Sep-19 08:38:23 UTC contract Ownable { address private _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev The Ownable constructor sets the original `owner` of the contract to the sender * account. */ constructor () internal { _owner = msg.sender; emit OwnershipTransferred(address(0), _owner); } /** * @return the address of the owner. */ function owner() public view returns (address) { return _owner; } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { require(isOwner()); _; } /** * @return true if `msg.sender` is the owner of the contract. */ function isOwner() public view returns (bool) { return msg.sender == _owner; } /** * @dev Allows the current owner to relinquish control of the contract. * @notice Renouncing to ownership will leave the contract without an owner. * It will not be possible to call the functions with the `onlyOwner` * modifier anymore. */ function renounceOwnership() public onlyOwner { emit OwnershipTransferred(_owner, address(0)); _owner = address(0); } /** * @dev Allows the current owner to transfer control of the contract to a newOwner. * @param newOwner The address to transfer ownership to. */ function transferOwnership(address newOwner) public onlyOwner { _transferOwnership(newOwner); } /** * @dev Transfers control of the contract to a newOwner. * @param newOwner The address to transfer ownership to. */ function _transferOwnership(address newOwner) internal { require(newOwner != address(0)); emit OwnershipTransferred(_owner, newOwner); _owner = newOwner; } } library Roles { struct Role { mapping (address => bool) bearer; } /** * @dev give an account access to this role */ function add(Role storage role, address account) internal { require(account != address(0)); require(!has(role, account)); role.bearer[account] = true; } /** * @dev remove an account's access to this role */ function remove(Role storage role, address account) internal { require(account != address(0)); require(has(role, account)); role.bearer[account] = false; } /** * @dev check if an account has this role * @return bool */ function has(Role storage role, address account) internal view returns (bool) { require(account != address(0)); return role.bearer[account]; } } library ECDSA { /** * @dev Recover signer address from a message by using their signature * @param hash bytes32 message, the hash is the signed message. What is recovered is the signer address. * @param signature bytes signature, the signature is generated using web3.eth.sign() */ function recover(bytes32 hash, bytes memory signature) internal pure returns (address) { bytes32 r; bytes32 s; uint8 v; // Check the signature length if (signature.length != 65) { return (address(0)); } // Divide the signature in r, s and v variables // ecrecover takes the signature parameters, and the only way to get them // currently is to use assembly. // solhint-disable-next-line no-inline-assembly assembly { r := mload(add(signature, 0x20)) s := mload(add(signature, 0x40)) v := byte(0, mload(add(signature, 0x60))) } // Version of signature should be 27 or 28, but 0 and 1 are also possible versions if (v < 27) { v += 27; } // If the version is correct return the signer address if (v != 27 && v != 28) { return (address(0)); } else { return ecrecover(hash, v, r, s); } } /** * toEthSignedMessageHash * @dev prefix a bytes32 value with "\x19Ethereum Signed Message:" * and hash the result */ function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) { // 32 is the length in bytes of hash, // enforced by the type signature above return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash)); } } contract PauserRole { using Roles for Roles.Role; event PauserAdded(address indexed account); event PauserRemoved(address indexed account); Roles.Role private _pausers; constructor () internal { _addPauser(msg.sender); } modifier onlyPauser() { require(isPauser(msg.sender)); _; } function isPauser(address account) public view returns (bool) { return _pausers.has(account); } function addPauser(address account) public onlyPauser { _addPauser(account); } function renouncePauser() public { _removePauser(msg.sender); } function _addPauser(address account) internal { _pausers.add(account); emit PauserAdded(account); } function _removePauser(address account) internal { _pausers.remove(account); emit PauserRemoved(account); } } contract Pausable is PauserRole { event Paused(address account); event Unpaused(address account); bool private _paused; constructor () internal { _paused = false; } /** * @return true if the contract is paused, false otherwise. */ function paused() public view returns (bool) { return _paused; } /** * @dev Modifier to make a function callable only when the contract is not paused. */ modifier whenNotPaused() { require(!_paused); _; } /** * @dev Modifier to make a function callable only when the contract is paused. */ modifier whenPaused() { require(_paused); _; } /** * @dev called by the owner to pause, triggers stopped state */ function pause() public onlyPauser whenNotPaused { _paused = true; emit Paused(msg.sender); } /** * @dev called by the owner to unpause, returns to normal state */ function unpause() public onlyPauser whenPaused { _paused = false; emit Unpaused(msg.sender); } } contract MCHDailyActionV3 is Ownable, Pausable { address public validator; mapping(address => int64) public lastActionDate; event Action( address indexed user, int64 at ); constructor(address _varidator) public { validator = _varidator; } function setValidater(address _varidator) external onlyOwner() { validator = _varidator; } function requestDailyActionReward(bytes calldata _signature, int64 _time) external whenNotPaused() { require(validateSig(msg.sender, _time, _signature), "invalid signature"); int64 day = _time / 86400; require(lastActionDate[msg.sender] < day); lastActionDate[msg.sender] = day; emit Action( msg.sender, _time ); } function validateSig(address _from, int64 _time, bytes memory _signature) public view returns (bool) { require(validator != address(0)); address signer = recover(ethSignedMessageHash(encodeData(_from, _time)), _signature); return (signer == validator); } function encodeData(address _from, int64 _time) public pure returns (bytes32) { return keccak256(abi.encode( _from, _time ) ); } function ethSignedMessageHash(bytes32 _data) public pure returns (bytes32) { return ECDSA.toEthSignedMessageHash(_data); } function recover(bytes32 _data, bytes memory _signature) public pure returns (address) { return ECDSA.recover(_data, _signature); } }