SHARE
TWEET

Untitled

a guest Aug 25th, 2019 150 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. /**
  2.   * @title MultiSignatureWalletDailyLimit
  3.   * @author Nick Dodson <thenickdodson@gmail.com>
  4.   * @notice 364 byte Weighted EIP712 Signing Compliant Delegate-Call Enabled MultiSignature Wallet with Daily Delayed Pre-Approved Transactions for the Ethereum Virtual Machine
  5.   */
  6. object "MultiSignatureWalletDailyLimit" {
  7.   code {
  8.     // constructor: uint256(signatures required) + address[] signatories (bytes32 sep|chunks|data...)
  9.     codecopy(0, 364, codesize()) // setup constructor args: mem positon 0 | code size 280 (before args)
  10.  
  11.     for { let i := 96 } gt(mload(i), 0) { i := add(i, 32) } { // iterate through signatory addresses, address > 0
  12.         sstore(mload(i), 1) // address => 1 (weight map
  13.     }
  14.  
  15.     sstore(1, mload(0)) // map contract address => signatures required (moved ahead of user initiated address => weight setting)
  16.  
  17.     datacopy(0, dataoffset("Runtime"), datasize("Runtime")) // now switch over to runtime code from constructor
  18.     return(0, datasize("Runtime"))
  19.   }
  20.   object "Runtime" {
  21.     code {
  22.         if eq(calldatasize(), 0) {
  23.             mstore(0, callvalue())
  24.             log1(0, 32, caller()) // log caller / value
  25.             stop()
  26.         } // fallback log zero
  27.  
  28.         // call data: bytes4(sig) bytes32(dest) bytes32(gasLimit) bytes(data) bytes32[](signatures) | supports fallback
  29.         calldatacopy(188, 0, calldatasize()) // copy calldata to memory, 4 behind 188 (remove 4 byte signature)
  30.  
  31.         let dataSize := mload(320) // size of the bytes data
  32.         let nonce := sload(0)
  33.         let isDailyWithdrawMethod := eq(mload(156), 0xdd)
  34.  
  35.         // build EIP712 release hash
  36.         mstore(32, 0x1901)
  37.         mstore(64, 0xb0609d81c5f719d8a516ae2f25079b20fb63da3e07590e23fbf0028e6745e5f2) // EIP712 Domain Seperator: EIP712Domain(string name,string version,uint256 chainId)
  38.         mstore(96, nonce) // map wallet nonce to memory (nonce: storage(address + 1))
  39.         mstore(128, 0x1) // chain ID
  40.         mstore(160, address()) // use the contract address as salt for replay protection
  41.         mstore(256, keccak256(352, dataSize)) // we have to hash the bytes data due to EIP712... why....
  42.         mstore(64, keccak256(64, 224)) // domain seporator hash
  43.  
  44.         let eip712Hash := keccak256(62, 34) // EIP712 final signing hash
  45.         let signatureMemoryPosition := add(192, mload(288)) // new memory position -32 bytes from sig start
  46.         let previousAddress := 1 // comparison variable, used to check for duplicate signer accounts
  47.         let i := sload(caller()) // load caller weight
  48.  
  49.         // check if transaction is pre-approved with current caller
  50.         // Last Used Hash Scheme: mapping(keccak256(caller(), gasLimit, dataHash) => dayLastUsed)
  51.         // Caller Weight Scheme: mapping(keccak256(chainId, caller(), gasLimit, dataHash) => callerWeight)
  52.         if and(i, isDailyWithdrawMethod) { // must be a signer and is daily withdrawl
  53.             let dayNumber := div(timestamp(), 86400) // deterministic day number
  54.             mstore(160, caller()) // caller set in memory
  55.             let dailyWithdrawlHash := keccak256(160, 96) // daily withdrawl hash
  56.             let dayLastUsed := sload(dailyWithdrawlHash) // last used
  57.             if and(gt(dayLastUsed, 0), lt(dayLastUsed, dayNumber)) { // hash activated and less than current dat
  58.                 i := sload(keccak256(128, 128)) // set i to required signatures for daily withdrawl
  59.                 if eq(dayLastUsed, 1) { // activated / delayed daily withdrawl
  60.                     sstore(dailyWithdrawlHash, add(dayNumber, 1)) // delay usage by one day for setup
  61.                     stop() // stop runtime
  62.                 }
  63.                 sstore(dailyWithdrawlHash, dayNumber) // set daily withdrawl hash last used day to current
  64.             }
  65.         }
  66.  
  67.         for {} and(lt(i, 5), lt(i, sload(1))) {} { // signature validation: loop through signatures (i < required signatures < 5) | 5 limit for formal verification
  68.             mstore(signatureMemoryPosition, eip712Hash) // place hash before each sig in memory: hash + v + r + s | hash + vN + rN + sN
  69.  
  70.             let ecrecoverResult := call(3000, 1, 0, signatureMemoryPosition, 128, 0, 32) // call ecrecover precompile with ecrecover(hash,v,r,s) | failing is okay here
  71.             let recoveredAddress := mload(0)
  72.  
  73.             if or(iszero(ecrecoverResult), or(eq(caller(), recoveredAddress), iszero(gt(recoveredAddress, previousAddress)))) {
  74.                 revert(0, 0)
  75.             }
  76.             // ecrecover must be success | recoveredAddress cannot be caller
  77.             // | recovered address must be unique / grater than previous | recovered address must be greater than 1
  78.  
  79.             previousAddress := recoveredAddress // set previous address for future comparison
  80.             signatureMemoryPosition := add(signatureMemoryPosition, 96)
  81.             i := add(i, sload(recoveredAddress))
  82.         }
  83.  
  84.         sstore(0, add(1, nonce)) // increase nonce: nonce = nonce + 1
  85.  
  86.         if iszero(delegatecall(mload(224), mload(192), 352, dataSize, 0, 0)) { revert(0, 0) }
  87.     }
  88.   }
  89. }
  90.  
  91. /*
  92. ==============================
  93. Contract Storage Layout
  94. ==============================
  95.  
  96. 0                   | Nonce
  97. 1                   | Required Signatures
  98. [signatory address] => signatory weight
  99.  
  100. ==============================
  101. Constructor Memory Layout
  102. ==============================
  103.  
  104. 0     | Signatory Threshold -- uint256 weightedThreshold
  105. 32    | Signatory Array Length -- address[] signatories
  106. 64    | Number of Signatories -- uint256
  107. 96    | First Signatory -- address
  108. +32   | .. N Signatory -- address
  109.  
  110. ==============================
  111. Runtime Memory Layout
  112. ==============================
  113.  
  114. 0       | ECRecovered Address    | ecrecover address
  115. 32      | EIP712 Prefix          | 0x1901
  116. 64      | Domain Seperator Hash  | keccak256("EIP712Domain(string name,string version,uint256 chainId)")
  117. 96      | Nonce                  | sload(add(address(), 1)) // used for double spend prevention
  118. 128     | Chain ID               | 0x1 for mainnet
  119. 160     | Contract Address       | address() // used for replay attack prevention
  120. 192     | Destination            | delegate call target (specified in calldata)
  121. 224     | Gas Limit              | delegate call gas limit (specified in calldata)
  122. 256     | Hash of Data           | keccak256(of data)
  123. 288     | End of Bytes Data      | End of bytes data (specified in calldata)
  124. 320     | Data size              | bytes data raw size (specified in calldata)
  125. 352     | Bytes Data             | raw bytes data (specified in calldata)
  126. */
RAW Paste Data
We use cookies for various purposes including analytics. By continuing to use Pastebin, you agree to our use of cookies as described in the Cookies Policy. OK, I Understand
 
Top