Transaction Hash:
Block:
21116583 at Nov-04-2024 08:10:11 PM +UTC
Transaction Fee:
0.0008177 ETH
$2.05
Gas Used:
81,770 Gas / 10 Gwei
Account State Difference:
Address | Before | After | State Difference | ||
---|---|---|---|---|---|
0x2fb074FA...7782d41a4 |
0 Eth
Nonce: 0
|
0.1 Eth
Nonce: 0
| 0.1 | ||
0x4838B106...B0BAD5f97
Miner
| (Titan Builder) | 12.779394539760944619 Eth | 12.779785345486978329 Eth | 0.00039080572603371 | |
0x5D295C57...0c33b286a |
2.056739954963419513 Eth
Nonce: 14880
|
2.055922254963419513 Eth
Nonce: 14881
| 0.0008177 | ||
0xE25a329d...b99A5436e | (Paxos: Treasury) | 14,295.556292911209539159 Eth | 14,295.456292911209539159 Eth | 0.1 |
Execution Trace
SimpleMultiSig.execute( )
-
Null: 0x000...001.ac3aa080( )
-
Null: 0x000...001.ac3aa080( )
- ETH 0.1
0x2fb074fa59c9294c71246825c1c9a0c7782d41a4.CALL( )
execute[SimpleMultiSig (ln:71)]
encode[SimpleMultiSig (ln:77)]
ecrecover[SimpleMultiSig (ln:82)]
call[SimpleMultiSig (ln:90)]
pragma solidity ^0.6.11; contract SimpleMultiSig { // EIP712 Precomputed hashes: // keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract,bytes32 salt)") bytes32 constant EIP712DOMAINTYPE_HASH = 0xd87cd6ef79d4e2b95e15ce8abf732db51ec771f1ca2edccf22a46c729ac56472; // keccak256("Simple MultiSig") bytes32 constant NAME_HASH = 0xb7a0bfa1b79f2443f4d73ebb9259cddbcd510b18be6fc4da7d1aa7b1786e73e6; // keccak256("1") bytes32 constant VERSION_HASH = 0xc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6; // keccak256("MultiSigTransaction(address destination,uint256 value,bytes data,uint256 nonce,address executor,uint256 gasLimit)") bytes32 constant TXTYPE_HASH = 0x3ee892349ae4bbe61dce18f95115b5dc02daf49204cc602458cd4c1f540d56d7; bytes32 constant SALT = 0x251543af6a222378665a76fe38dbceae4871a070b7fdaf5c6c30cf758dc33cc0; uint public nonce; // mutable state uint public threshold; // mutable state mapping (address => bool) isOwner; // mutable state address[] public ownersArr; // mutable state bytes32 immutable DOMAIN_SEPARATOR; // hash for EIP712, computed from contract address function owners() external view returns (address[] memory) { return ownersArr; } // Note that owners_ must be strictly increasing, in order to prevent duplicates function setOwners_(uint threshold_, address[] memory owners_) private { require(owners_.length <= 20 && threshold_ <= owners_.length && threshold_ > 0); // remove old owners from map for (uint i = 0; i < ownersArr.length; i++) { isOwner[ownersArr[i]] = false; } // add new owners to map address lastAdd = address(0); for (uint i = 0; i < owners_.length; i++) { require(owners_[i] > lastAdd); isOwner[owners_[i]] = true; lastAdd = owners_[i]; } // set owners array and threshold ownersArr = owners_; threshold = threshold_; } constructor(uint threshold_, address[] memory owners_, uint chainId) public { setOwners_(threshold_, owners_); DOMAIN_SEPARATOR = keccak256(abi.encode(EIP712DOMAINTYPE_HASH, NAME_HASH, VERSION_HASH, chainId, this, SALT)); } // Requires a quorum of owners to call from this contract using execute function setOwners(uint threshold_, address[] memory owners_) external { require(msg.sender == address(this)); setOwners_(threshold_, owners_); } // Note that address recovered from signatures must be strictly increasing, in order to prevent duplicates function execute(uint8[] memory sigV, bytes32[] memory sigR, bytes32[] memory sigS, address destination, uint value, bytes memory data, address executor, uint gasLimit) external { require(sigR.length == threshold); require(sigR.length == sigS.length && sigR.length == sigV.length); require(executor == msg.sender || executor == address(0)); // EIP712 scheme: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-712.md bytes32 txInputHash = keccak256(abi.encode(TXTYPE_HASH, destination, value, keccak256(data), nonce, executor, gasLimit)); bytes32 totalHash = keccak256(abi.encodePacked("\x19\x01", DOMAIN_SEPARATOR, txInputHash)); address lastAdd = address(0); // cannot have address(0) as an owner for (uint i = 0; i < threshold; i++) { address recovered = ecrecover(totalHash, sigV[i], sigR[i], sigS[i]); require(recovered > lastAdd && isOwner[recovered]); lastAdd = recovered; } // If we make it here all signatures are accounted for. nonce = nonce + 1; bool success = false; (success,) = destination.call{value: value, gas: gasLimit}(data); require(success); } receive() external payable {} }