Advertisement
Guest User

Untitled

a guest
Aug 25th, 2019
229
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 6.39 KB | None | 0 0
  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. */
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement