Address Contract Partially Verified
Address
0x432defD2b3733e6fEBb1bD4B17Ed85D15b882163
Balance
0 ETH
Nonce
1
Code Size
9205 bytes
Creator
Create2 Deployer at tx 0x276a9123...eeec42
Indexed Transactions
0
Contract Bytecode
9205 bytes
0x608060405234801561001057600080fd5b50600436106100cf5760003560e01c8063867519c61161008c5780639f255626116100665780639f255626146101fe578063b5021b161461021a578063d2c83b9a14610236578063f92c5f7c14610254576100cf565b8063867519c6146101a657806387d31313146101c25780639a8a0592146101e0576100cf565b8063231badaf146100d4578063392e53cd146100f0578063485cc9551461010e5780635afaa7bb1461012a57806373e5a13f1461014657806376db2b4c14610176575b600080fd5b6100ee60048036038101906100e99190611438565b610284565b005b6100f861037f565b6040516101059190611e71565b60405180910390f35b610128600480360381019061012391906115e0565b6103d5565b005b610144600480360381019061013f9190611563565b610561565b005b610160600480360381019061015b919061165d565b610739565b60405161016d9190611e8c565b60405180910390f35b610190600480360381019061018b919061161c565b61075e565b60405161019d9190611e8c565b60405180910390f35b6101c060048036038101906101bb91906113b9565b610788565b005b6101ca610799565b6040516101d79190611f07565b60405180910390f35b6101e86107bf565b6040516101f591906120a2565b60405180910390f35b610218600480360381019061021391906114f7565b6107c5565b005b610234600480360381019061022f9190611438565b6107d5565b005b61023e6108d1565b60405161024b9190611eec565b60405180910390f35b61026e60048036038101906102699190611390565b6108f7565b60405161027b91906120a2565b60405180910390f35b600460008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020548411610305576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016102fc90611f82565b60405180910390fd5b60006103258261031788888888610953565b6109b690919063ffffffff16565b905084600460008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555061037786828686610a71565b505050505050565b60008073ffffffffffffffffffffffffffffffffffffffff1660008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614905090565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163273ffffffffffffffffffffffffffffffffffffffff1614610463576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161045a90611f42565b60405180910390fd5b60008060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555081600260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555080600360006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055507f908408e307fc569b417f6cbec5d5a06f44a0a505ac0479b47d421a4b2fd6a1e6326040516105559190611def565b60405180910390a15050565b60008251116105a5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161059c90611fc2565b60405180910390fd5b600080600090505b83518110156106f35760003073ffffffffffffffffffffffffffffffffffffffff168583815181106105db57fe5b60200260200101516040516105f09190611d0b565b6000604051808303816000865af19150503d806000811461062d576040519150601f19603f3d011682016040523d82523d6000602084013e610632565b606091505b505090508315610681578061067c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161067390611f62565b60405180910390fd5b610697565b80801561068c575082155b1561069657600192505b5b7f361c14722cc344132c73396113f7164232448b09c544a149f09048648b43d872338684815181106106c557fe5b6020026020010151836040516106dd93929190611e0a565b60405180910390a15080806001019150506105ad565b5080610734576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161072b90612082565b60405180910390fd5b505050565b60006107578260000151836020015184604001518560600151610953565b9050919050565b600061078182600001518360200151846040015185606001518660800151610edc565b9050919050565b61079483338484610a71565b505050565b600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60015481565b6107d133338484610a71565b5050565b600460008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020548411610856576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161084d90611f82565b60405180910390fd5b600061087782610869888888883a610edc565b6109b690919063ffffffff16565b905084600460008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055506108c986828686610a71565b505050505050565b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600061094c6001600460008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054610f4290919063ffffffff16565b9050919050565b60006109ac7f6848d0622081db2451400280dead7a739a080cb93852607c381af11e289769b286868661098587610f97565b6040516020016109989493929190611c6e565b604051602081830303815290604052610ffa565b9050949350505050565b60008060009050604183511415610a675760008060006020860151925060408601519150606086015160001a9050601b8160ff1610156109f757601b810190505b601b8160ff161480610a0c5750601c8160ff16145b15610a635760018782858560405160008152602001604052604051610a349493929190611ea7565b6020604051602081039080840390855afa158015610a56573d6000803e3d6000fd5b5050506020604051035193505b5050505b8091505092915050565b600073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff161415610ae1576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610ad890611fa2565b60405180910390fd5b6000825111610b25576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b1c90612062565b60405180910390fd5b8151815114610b69576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b6090612002565b60405180910390fd5b8273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff1614610d3d57600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663bb890d3f85856040518363ffffffff1660e01b8152600401610bf9929190611e48565b60206040518083038186803b158015610c1157600080fd5b505afa158015610c25573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c4991906115b7565b80610cfd5750600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663bb890d3f85856040518363ffffffff1660e01b8152600401610cac929190611e48565b60206040518083038186803b158015610cc457600080fd5b505afa158015610cd8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610cfc91906115b7565b5b610d3c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610d3390611fe2565b60405180910390fd5b5b600080600090505b8251811015610ed457600073ffffffffffffffffffffffffffffffffffffffff16848281518110610d7257fe5b602002602001015173ffffffffffffffffffffffffffffffffffffffff161415610dd1576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610dc890611f22565b60405180910390fd5b838181518110610ddd57fe5b602002602001015173ffffffffffffffffffffffffffffffffffffffff16838281518110610e0757fe5b60200260200101518787604051602001610e2393929190611d22565b604051602081830303815290604052604051610e3f9190611d0b565b6000604051808303816000865af19150503d8060008114610e7c576040519150601f19603f3d011682016040523d82523d6000602084013e610e81565b606091505b50508092505081610ec7576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610ebe90612022565b60405180910390fd5b8080600101915050610d45565b505050505050565b6000610f377f6f4e1b2b1e5e49f4269e19e16e67a00cb0a796d96d30be3e4b540d3732e8bcad878787610f0e88610f97565b87604051602001610f23959493929190611cb4565b604051602081830303815290604052610ffa565b905095945050505050565b600080828401905083811015610f8d576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610f8490612042565b60405180910390fd5b8091505092915050565b60608060008351905060005b81811015610fef5782858281518110610fb857fe5b6020026020010151604051602001610fd1929190611d5b565b60405160208183030381529060405292508080600101915050610fa3565b508192505050919050565b60006110336001543085856040516020016110189493929190611da5565b6040516020818303038152906040528051906020012061103b565b905092915050565b60008160405160200161104e9190611d7f565b604051602081830303815290604052805190602001209050919050565b60008135905061107a81612375565b92915050565b600082601f83011261109157600080fd5b81356110a461109f826120ea565b6120bd565b915081818352602084019350602081019050838560208402820111156110c957600080fd5b60005b838110156110f957816110df888261106b565b8452602084019350602083019250506001810190506110cc565b5050505092915050565b600082601f83011261111457600080fd5b813561112761112282612112565b6120bd565b9150818183526020840193506020810190508360005b8381101561116d578135860161115388826111a1565b84526020840193506020830192505060018101905061113d565b5050505092915050565b6000813590506111868161238c565b92915050565b60008151905061119b8161238c565b92915050565b600082601f8301126111b257600080fd5b81356111c56111c08261213a565b6120bd565b915080825260208301602083018583830111156111e157600080fd5b6111ec8382846122dd565b50505092915050565b600081359050611204816123a3565b92915050565b600081359050611219816123ba565b92915050565b600060a0828403121561123157600080fd5b61123b60a06120bd565b9050600061124b8482850161106b565b600083015250602061125f8482850161137b565b602083015250604082013567ffffffffffffffff81111561127f57600080fd5b61128b84828501611080565b604083015250606082013567ffffffffffffffff8111156112ab57600080fd5b6112b784828501611103565b60608301525060806112cb8482850161137b565b60808301525092915050565b6000608082840312156112e957600080fd5b6112f360806120bd565b905060006113038482850161106b565b60008301525060206113178482850161137b565b602083015250604082013567ffffffffffffffff81111561133757600080fd5b61134384828501611080565b604083015250606082013567ffffffffffffffff81111561136357600080fd5b61136f84828501611103565b60608301525092915050565b60008135905061138a816123d1565b92915050565b6000602082840312156113a257600080fd5b60006113b08482850161106b565b91505092915050565b6000806000606084860312156113ce57600080fd5b60006113dc8682870161106b565b935050602084013567ffffffffffffffff8111156113f957600080fd5b61140586828701611080565b925050604084013567ffffffffffffffff81111561142257600080fd5b61142e86828701611103565b9150509250925092565b600080600080600060a0868803121561145057600080fd5b600061145e8882890161106b565b955050602061146f8882890161137b565b945050604086013567ffffffffffffffff81111561148c57600080fd5b61149888828901611080565b935050606086013567ffffffffffffffff8111156114b557600080fd5b6114c188828901611103565b925050608086013567ffffffffffffffff8111156114de57600080fd5b6114ea888289016111a1565b9150509295509295909350565b6000806040838503121561150a57600080fd5b600083013567ffffffffffffffff81111561152457600080fd5b61153085828601611080565b925050602083013567ffffffffffffffff81111561154d57600080fd5b61155985828601611103565b9150509250929050565b6000806040838503121561157657600080fd5b600083013567ffffffffffffffff81111561159057600080fd5b61159c85828601611103565b92505060206115ad85828601611177565b9150509250929050565b6000602082840312156115c957600080fd5b60006115d78482850161118c565b91505092915050565b600080604083850312156115f357600080fd5b6000611601858286016111f5565b92505060206116128582860161120a565b9150509250929050565b60006020828403121561162e57600080fd5b600082013567ffffffffffffffff81111561164857600080fd5b6116548482850161121f565b91505092915050565b60006020828403121561166f57600080fd5b600082013567ffffffffffffffff81111561168957600080fd5b611695848285016112d7565b91505092915050565b60006116aa83836116d4565b60208301905092915050565b6116bf8161225f565b82525050565b6116ce816121dc565b82525050565b6116dd816121dc565b82525050565b6116f46116ef826121dc565b61231f565b82525050565b600061170582612176565b61170f8185612199565b935061171a83612166565b8060005b8381101561174b578151611732888261169e565b975061173d8361218c565b92505060018101905061171e565b5085935050505092915050565b611761816121ee565b82525050565b611770816121fa565b82525050565b611787611782826121fa565b612331565b82525050565b600061179882612181565b6117a281856121a4565b93506117b28185602086016122ec565b6117bb81612357565b840191505092915050565b60006117d182612181565b6117db81856121b5565b93506117eb8185602086016122ec565b80840191505092915050565b61180081612271565b82525050565b61180f81612295565b82525050565b6000611822601b836121c0565b91507f476174657761793a2063616e6e6f742073656e6420746f2030783000000000006000830152602082019050919050565b6000611862601c836121d1565b91507f19457468657265756d205369676e6564204d6573736167653a0a3332000000006000830152601c82019050919050565b60006118a2602f836121c0565b91507f496e697469616c697a61626c653a2074782e6f726967696e206973206e6f742060008301527f74686520696e697469616c697a657200000000000000000000000000000000006020830152604082019050919050565b60006119086017836121c0565b91507f476174657761793a2062617463682072657665727465640000000000000000006000830152602082019050919050565b60006119486032836121c0565b91507f476174657761793a206e6f6e6365206973206c6f776572207468616e2063757260008301527f72656e74206163636f756e74206e6f6e636500000000000000000000000000006020830152604082019050919050565b60006119ae6025836121c0565b91507f476174657761793a2063616e6e6f742073656e642066726f6d2030783020616360008301527f636f756e740000000000000000000000000000000000000000000000000000006020830152604082019050919050565b6000611a146026836121c0565b91507f476174657761793a2063616e6e6f742064656c656761746520656d707479206260008301527f61746368657300000000000000000000000000000000000000000000000000006020830152604082019050919050565b6000611a7a6028836121c0565b91507f476174657761793a2073656e646572206973206e6f7420746865206163636f7560008301527f6e74206f776e65720000000000000000000000000000000000000000000000006020830152604082019050919050565b6000611ae06016836121c0565b91507f476174657761793a20696e76616c6964206261746368000000000000000000006000830152602082019050919050565b6000611b206023836121c0565b91507f476174657761793a206261746368207472616e73616374696f6e20726576657260008301527f74656400000000000000000000000000000000000000000000000000000000006020830152604082019050919050565b6000611b86601e836121c0565b91507f536166654d6174684c69623a206164646974696f6e206f766572666c6f7700006000830152602082019050919050565b6000611bc66020836121c0565b91507f476174657761793a2063616e6e6f742073656e6420656d7074792062617463686000830152602082019050919050565b6000611c06601d836121c0565b91507f476174657761793a20616c6c20626174636865732072657665727465640000006000830152602082019050919050565b611c4281612248565b82525050565b611c59611c5482612248565b61234d565b82525050565b611c6881612252565b82525050565b6000611c7a82876116e3565b601482019150611c8a8286611c48565b602082019150611c9a82856116fa565b9150611ca682846117c6565b915081905095945050505050565b6000611cc082886116e3565b601482019150611cd08287611c48565b602082019150611ce082866116fa565b9150611cec82856117c6565b9150611cf88284611c48565b6020820191508190509695505050505050565b6000611d1782846117c6565b915081905092915050565b6000611d2e82866117c6565b9150611d3a82856116e3565b601482019150611d4a82846116e3565b601482019150819050949350505050565b6000611d6782856117c6565b9150611d7382846117c6565b91508190509392505050565b6000611d8a82611855565b9150611d968284611776565b60208201915081905092915050565b6000611db18287611c48565b602082019150611dc182866116e3565b601482019150611dd18285611776565b602082019150611de182846117c6565b915081905095945050505050565b6000602082019050611e0460008301846116b6565b92915050565b6000606082019050611e1f60008301866116b6565b8181036020830152611e31818561178d565b9050611e406040830184611758565b949350505050565b6000604082019050611e5d60008301856116c5565b611e6a60208301846116c5565b9392505050565b6000602082019050611e866000830184611758565b92915050565b6000602082019050611ea16000830184611767565b92915050565b6000608082019050611ebc6000830187611767565b611ec96020830186611c5f565b611ed66040830185611767565b611ee36060830184611767565b95945050505050565b6000602082019050611f0160008301846117f7565b92915050565b6000602082019050611f1c6000830184611806565b92915050565b60006020820190508181036000830152611f3b81611815565b9050919050565b60006020820190508181036000830152611f5b81611895565b9050919050565b60006020820190508181036000830152611f7b816118fb565b9050919050565b60006020820190508181036000830152611f9b8161193b565b9050919050565b60006020820190508181036000830152611fbb816119a1565b9050919050565b60006020820190508181036000830152611fdb81611a07565b9050919050565b60006020820190508181036000830152611ffb81611a6d565b9050919050565b6000602082019050818103600083015261201b81611ad3565b9050919050565b6000602082019050818103600083015261203b81611b13565b9050919050565b6000602082019050818103600083015261205b81611b79565b9050919050565b6000602082019050818103600083015261207b81611bb9565b9050919050565b6000602082019050818103600083015261209b81611bf9565b9050919050565b60006020820190506120b76000830184611c39565b92915050565b6000604051905081810181811067ffffffffffffffff821117156120e057600080fd5b8060405250919050565b600067ffffffffffffffff82111561210157600080fd5b602082029050602081019050919050565b600067ffffffffffffffff82111561212957600080fd5b602082029050602081019050919050565b600067ffffffffffffffff82111561215157600080fd5b601f19601f8301169050602081019050919050565b6000819050602082019050919050565b600081519050919050565b600081519050919050565b6000602082019050919050565b600081905092915050565b600082825260208201905092915050565b600081905092915050565b600082825260208201905092915050565b600081905092915050565b60006121e782612228565b9050919050565b60008115159050919050565b6000819050919050565b600061220f826121dc565b9050919050565b6000612221826121dc565b9050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b600060ff82169050919050565b600061226a826122b9565b9050919050565b600061227c82612283565b9050919050565b600061228e82612228565b9050919050565b60006122a0826122a7565b9050919050565b60006122b282612228565b9050919050565b60006122c4826122cb565b9050919050565b60006122d682612228565b9050919050565b82818337600083830152505050565b60005b8381101561230a5780820151818401526020810190506122ef565b83811115612319576000848401525b50505050565b600061232a8261233b565b9050919050565b6000819050919050565b600061234682612368565b9050919050565b6000819050919050565b6000601f19601f8301169050919050565b60008160601b9050919050565b61237e816121dc565b811461238957600080fd5b50565b612395816121ee565b81146123a057600080fd5b50565b6123ac81612204565b81146123b757600080fd5b50565b6123c381612216565b81146123ce57600080fd5b50565b6123da81612248565b81146123e557600080fd5b5056fea164736f6c634300060c000a
Verified Source Code Partial Match
Compiler: v0.6.12+commit.27d51765
EVM: istanbul
Optimization: No
Gateway.sol 440 lines
// SPDX-License-Identifier: MIT pragma solidity ^0.6.12; pragma experimental ABIEncoderV2; import "../common/libs/ECDSALib.sol"; import "../common/libs/SafeMathLib.sol"; import "../common/lifecycle/Initializable.sol"; import "../common/signature/SignatureValidator.sol"; import "../external/ExternalAccountRegistry.sol"; import "../personal/PersonalAccountRegistry.sol"; /** * @title Gateway * * @notice GSN replacement * * @author Stanisław Głogowski <[email protected]> */ contract Gateway is Initializable, SignatureValidator { using ECDSALib for bytes32; using SafeMathLib for uint256; struct DelegatedBatch { address account; uint256 nonce; address[] to; bytes[] data; } struct DelegatedBatchWithGasPrice { address account; uint256 nonce; address[] to; bytes[] data; uint256 gasPrice; } bytes32 private constant HASH_PREFIX_DELEGATED_BATCH = keccak256( "DelegatedBatch(address account,uint256 nonce,address[] to,bytes[] data)" ); bytes32 private constant HASH_PREFIX_DELEGATED_BATCH_WITH_GAS_PRICE = keccak256( "DelegatedBatchWithGasPrice(address account,uint256 nonce,address[] to,bytes[] data,uint256 gasPrice)" ); ExternalAccountRegistry public externalAccountRegistry; PersonalAccountRegistry public personalAccountRegistry; mapping(address => uint256) private accountNonce; // events /** * @dev Emitted when the single batch is delegated * @param sender sender address * @param batch batch * @param succeeded if succeeded */ event BatchDelegated( address sender, bytes batch, bool succeeded ); /** * @dev Public constructor */ constructor() public Initializable() SignatureValidator() {} // external functions /** * @notice Initializes `Gateway` contract * @param externalAccountRegistry_ `ExternalAccountRegistry` contract address * @param personalAccountRegistry_ `PersonalAccountRegistry` contract address */ function initialize( ExternalAccountRegistry externalAccountRegistry_, PersonalAccountRegistry personalAccountRegistry_ ) external onlyInitializer { externalAccountRegistry = externalAccountRegistry_; personalAccountRegistry = personalAccountRegistry_; } // public functions /** * @notice Sends batch * @dev `GatewayRecipient` context api: * `_getContextAccount` will return `msg.sender` * `_getContextSender` will return `msg.sender` * * @param to array of batch recipients contracts * @param data array of batch data */ function sendBatch( address[] memory to, bytes[] memory data ) public { _sendBatch( msg.sender, msg.sender, to, data ); } /** * @notice Sends batch from the account * @dev `GatewayRecipient` context api: * `_getContextAccount` will return `account` arg * `_getContextSender` will return `msg.sender` * * @param account account address * @param to array of batch recipients contracts * @param data array of batch data */ function sendBatchFromAccount( address account, address[] memory to, bytes[] memory data ) public { _sendBatch( account, msg.sender, to, data ); } /** * @notice Delegates batch from the account * @dev Use `hashDelegatedBatch` to create sender message payload. * * `GatewayRecipient` context api: * `_getContextAccount` will return `account` arg * `_getContextSender` will return recovered address from `senderSignature` arg * * @param account account address * @param nonce next account nonce * @param to array of batch recipients contracts * @param data array of batch data * @param senderSignature sender signature */ function delegateBatch( address account, uint256 nonce, address[] memory to, bytes[] memory data, bytes memory senderSignature ) public { require( nonce > accountNonce[account], "Gateway: nonce is lower than current account nonce" ); address sender = _hashDelegatedBatch( account, nonce, to, data ).recoverAddress(senderSignature); accountNonce[account] = nonce; _sendBatch( account, sender, to, data ); } /** * @notice Delegates batch from the account (with gas price) * * @dev Use `hashDelegatedBatchWithGasPrice` to create sender message payload (tx.gasprice as gasPrice) * * `GatewayRecipient` context api: * `_getContextAccount` will return `account` arg * `_getContextSender` will return recovered address from `senderSignature` arg * * @param account account address * @param nonce next account nonce * @param to array of batch recipients contracts * @param data array of batch data * @param senderSignature sender signature */ function delegateBatchWithGasPrice( address account, uint256 nonce, address[] memory to, bytes[] memory data, bytes memory senderSignature ) public { require( nonce > accountNonce[account], "Gateway: nonce is lower than current account nonce" ); address sender = _hashDelegatedBatchWithGasPrice( account, nonce, to, data, tx.gasprice ).recoverAddress(senderSignature); accountNonce[account] = nonce; _sendBatch( account, sender, to, data ); } /** * @notice Delegates multiple batches * @dev It will revert when all batches fail * @param batches array of batches * @param revertOnFailure reverts on any error */ function delegateBatches( bytes[] memory batches, bool revertOnFailure ) public { require( batches.length > 0, "Gateway: cannot delegate empty batches" ); bool anySucceeded; for (uint256 i = 0; i < batches.length; i++) { // solhint-disable-next-line avoid-low-level-calls (bool succeeded,) = address(this).call(batches[i]); if (revertOnFailure) { require( succeeded, "Gateway: batch reverted" ); } else if (succeeded && !anySucceeded) { anySucceeded = true; } emit BatchDelegated( msg.sender, batches[i], succeeded ); } if (!anySucceeded) { revert("Gateway: all batches reverted"); } } // public functions (views) /** * @notice Hashes `DelegatedBatch` message payload * @param delegatedBatch struct * @return hash */ function hashDelegatedBatch( DelegatedBatch memory delegatedBatch ) public view returns (bytes32) { return _hashDelegatedBatch( delegatedBatch.account, delegatedBatch.nonce, delegatedBatch.to, delegatedBatch.data ); } /** * @notice Hashes `DelegatedBatchWithGasPrice` message payload * @param delegatedBatch struct * @return hash */ function hashDelegatedBatchWithGasPrice( DelegatedBatchWithGasPrice memory delegatedBatch ) public view returns (bytes32) { return _hashDelegatedBatchWithGasPrice( delegatedBatch.account, delegatedBatch.nonce, delegatedBatch.to, delegatedBatch.data, delegatedBatch.gasPrice ); } // external functions (views) /** * @notice Gets next account nonce * @param account account address * @return next nonce */ function getAccountNextNonce( address account ) external view returns (uint256) { return accountNonce[account].add(1); } // private functions function _sendBatch( address account, address sender, address[] memory to, bytes[] memory data ) private { require( account != address(0), "Gateway: cannot send from 0x0 account" ); require( to.length > 0, "Gateway: cannot send empty batch" ); require( data.length == to.length, "Gateway: invalid batch" ); if (account != sender) { require( personalAccountRegistry.verifyAccountOwner(account, sender) || externalAccountRegistry.verifyAccountOwner(account, sender), "Gateway: sender is not the account owner" ); } bool succeeded; for (uint256 i = 0; i < data.length; i++) { require( to[i] != address(0), "Gateway: cannot send to 0x0" ); // solhint-disable-next-line avoid-low-level-calls (succeeded,) = to[i].call(abi.encodePacked(data[i], account, sender)); require( succeeded, "Gateway: batch transaction reverted" ); } } // private functions (views) function _hashDelegatedBatch( address account, uint256 nonce, address[] memory to, bytes[] memory data ) private view returns (bytes32) { return _hashMessagePayload(HASH_PREFIX_DELEGATED_BATCH, abi.encodePacked( account, nonce, to, _concatBytes(data) )); } function _hashDelegatedBatchWithGasPrice( address account, uint256 nonce, address[] memory to, bytes[] memory data, uint256 gasPrice ) private view returns (bytes32) { return _hashMessagePayload(HASH_PREFIX_DELEGATED_BATCH_WITH_GAS_PRICE, abi.encodePacked( account, nonce, to, _concatBytes(data), gasPrice )); } // private functions (pure) function _concatBytes(bytes[] memory data) private pure returns (bytes memory) { bytes memory result; uint dataLen = data.length; for (uint i = 0 ; i < dataLen ; i++) { result = abi.encodePacked(result, data[i]); } return result; } }
BlockLib.sol 73 lines
// SPDX-License-Identifier: MIT pragma solidity ^0.6.12; /** * @title Block library * * @author Stanisław Głogowski <[email protected]> */ library BlockLib { struct BlockRelated { bool added; uint256 removedAtBlockNumber; } /** * @notice Verifies self struct at current block * @param self self struct * @return true on correct self struct */ function verifyAtCurrentBlock( BlockRelated memory self ) internal view returns (bool) { return verifyAtBlock(self, block.number); } /** * @notice Verifies self struct at any block * @param self self struct * @return true on correct self struct */ function verifyAtAnyBlock( BlockRelated memory self ) internal pure returns (bool) { return verifyAtBlock(self, 0); } /** * @notice Verifies self struct at specific block * @param self self struct * @param blockNumber block number to verify * @return true on correct self struct */ function verifyAtBlock( BlockRelated memory self, uint256 blockNumber ) internal pure returns (bool) { bool result = false; if (self.added) { if (self.removedAtBlockNumber == 0) { result = true; } else if (blockNumber == 0) { result = true; } else { result = self.removedAtBlockNumber > blockNumber; } } return result; } }
BytesLib.sol 36 lines
// SPDX-License-Identifier: MIT pragma solidity ^0.6.12; /** * @title Bytes library * * @author Stanisław Głogowski <[email protected]> */ library BytesLib { /** * @notice Converts bytes to address * @param data data * @return address */ function toAddress( bytes memory data ) internal pure returns (address) { address result; require( data.length == 20, "BytesLib: invalid data length" ); // solhint-disable-next-line no-inline-assembly assembly { result := div(mload(add(data, 0x20)), 0x1000000000000000000000000) } return result; } }
ECDSALib.sol 56 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.12;
/**
* @title ECDSA library
*
* @dev Based on https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.3.0/contracts/cryptography/ECDSA.sol#L26
*/
library ECDSALib {
function recoverAddress(
bytes32 messageHash,
bytes memory signature
)
internal
pure
returns (address)
{
address result = address(0);
if (signature.length == 65) {
bytes32 r;
bytes32 s;
uint8 v;
// 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)))
}
if (v < 27) {
v += 27;
}
if (v == 27 || v == 28) {
result = ecrecover(messageHash, v, r, s);
}
}
return result;
}
function toEthereumSignedMessageHash(
bytes32 messageHash
)
internal
pure
returns (bytes32)
{
return keccak256(abi.encodePacked(
"\x19Ethereum Signed Message:\n32",
messageHash
));
}
}
Guarded.sol 212 lines
// SPDX-License-Identifier: MIT pragma solidity ^0.6.12; import "../libs/ECDSALib.sol"; /** * @title Guarded * * @dev Contract module which provides a guardian-type control mechanism. * It allows key accounts to have guardians and restricts specific methods to be accessible by guardians only. * * Each guardian account can remove other guardians * * Use `_initializeGuarded` to initialize the contract * * @author Stanisław Głogowski <[email protected]> */ contract Guarded { using ECDSALib for bytes32; mapping(address => bool) private guardians; // events /** * @dev Emitted when a new guardian is added * @param sender sender address * @param guardian guardian address */ event GuardianAdded( address sender, address guardian ); /** * @dev Emitted when the existing guardian is removed * @param sender sender address * @param guardian guardian address */ event GuardianRemoved( address sender, address guardian ); // modifiers /** * @dev Throws if tx.origin is not a guardian account */ modifier onlyGuardian() { require( // solhint-disable-next-line avoid-tx-origin guardians[tx.origin], "Guarded: tx.origin is not the guardian" ); _; } /** * @dev Internal constructor */ constructor() internal {} // external functions /** * @notice Adds a new guardian * @param guardian guardian address */ function addGuardian( address guardian ) external onlyGuardian { _addGuardian(guardian); } /** * @notice Removes the existing guardian * @param guardian guardian address */ function removeGuardian( address guardian ) external onlyGuardian { require( // solhint-disable-next-line avoid-tx-origin tx.origin != guardian, "Guarded: cannot remove self" ); require( guardians[guardian], "Guarded: guardian doesn't exist" ); guardians[guardian] = false; emit GuardianRemoved( // solhint-disable-next-line avoid-tx-origin tx.origin, guardian ); } // external functions (views) /** * @notice Check if guardian exists * @param guardian guardian address * @return true when guardian exists */ function isGuardian( address guardian ) external view returns (bool) { return guardians[guardian]; } /** * @notice Verifies guardian signature * @param messageHash message hash * @param signature signature * @return true on correct guardian signature */ function verifyGuardianSignature( bytes32 messageHash, bytes calldata signature ) external view returns (bool) { return _verifyGuardianSignature( messageHash, signature ); } // internal functions /** * @notice Initializes `Guarded` contract * @dev If `guardians_` array is empty `tx.origin` is added as guardian account * @param guardians_ array of guardians addresses */ function _initializeGuarded( address[] memory guardians_ ) internal { if (guardians_.length == 0) { // solhint-disable-next-line avoid-tx-origin _addGuardian(tx.origin); } else { uint guardiansLen = guardians_.length; for (uint i = 0; i < guardiansLen; i++) { _addGuardian(guardians_[i]); } } } // internal functions (views) function _verifyGuardianSignature( bytes32 messageHash, bytes memory signature ) internal view returns (bool) { address guardian = messageHash.recoverAddress(signature); return guardians[guardian]; } // private functions function _addGuardian( address guardian ) private { require( guardian != address(0), "Guarded: cannot add 0x0 guardian" ); require( !guardians[guardian], "Guarded: guardian already exists" ); guardians[guardian] = true; emit GuardianAdded( // solhint-disable-next-line avoid-tx-origin tx.origin, guardian ); } }
Account.sol 114 lines
// SPDX-License-Identifier: MIT pragma solidity ^0.6.12; import "../access/Controlled.sol"; import "./AccountBase.sol"; /** * @title Account * * @author Stanisław Głogowski <[email protected]> */ contract Account is Controlled, AccountBase { address public implementation; /** * @dev Public constructor * @param registry_ account registry address * @param implementation_ account implementation address */ constructor( address registry_, address implementation_ ) public Controlled() { registry = registry_; implementation = implementation_; } // external functions /** * @notice Payable receive */ receive() external payable { // } /** * @notice Fallback */ // solhint-disable-next-line payable-fallback fallback() external { if (msg.data.length != 0) { address implementation_ = implementation; // solhint-disable-next-line no-inline-assembly assembly { let calldedatasize := calldatasize() calldatacopy(0, 0, calldedatasize) let result := delegatecall(gas(), implementation_, 0, calldedatasize, 0, 0) let returneddatasize := returndatasize() returndatacopy(0, 0, returneddatasize) switch result case 0 { revert(0, returneddatasize) } default { return(0, returneddatasize) } } } } /** * @notice Sets implementation * @param implementation_ implementation address */ function setImplementation( address implementation_ ) external onlyController { implementation = implementation_; } /** * @notice Executes transaction * @param to to address * @param value value * @param data data * @return transaction result */ function executeTransaction( address to, uint256 value, bytes calldata data ) external onlyController returns (bytes memory) { bytes memory result; bool succeeded; // solhint-disable-next-line avoid-call-value, avoid-low-level-calls (succeeded, result) = payable(to).call{value: value}(data); require( succeeded, "Account: transaction reverted" ); return result; } }
StringsLib.sol 40 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.12;
/**
* @title Strings library
*
* @dev Based on https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.3.0/contracts/utils/Strings.sol#L12
*/
library StringsLib {
function toString(
uint256 value
)
internal
pure
returns (string memory)
{
if (value == 0) {
return "0";
}
uint256 temp = value;
uint256 digits;
while (temp != 0) {
digits++;
temp /= 10;
}
bytes memory buffer = new bytes(digits);
uint256 index = digits - 1;
temp = value;
while (temp != 0) {
buffer[index--] = byte(uint8(48 + temp % 10));
temp /= 10;
}
return string(buffer);
}
}
SafeMathLib.sol 59 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.12;
/**
* @title Safe math library
*
* @dev Based on https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.3.0/contracts/math/SafeMath.sol
*/
library SafeMathLib {
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMathLib: addition overflow");
return c;
}
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
return sub(a, b, "SafeMathLib: subtraction overflow");
}
function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b <= a, errorMessage);
return a - b;
}
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b, "SafeMathLib: multiplication overflow");
return c;
}
function div(uint256 a, uint256 b) internal pure returns (uint256) {
return div(a, b, "SafeMathLib: division by zero");
}
function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b > 0, errorMessage);
return a / b;
}
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
return mod(a, b, "SafeMathLib: modulo by zero");
}
function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b != 0, errorMessage);
return a % b;
}
}
ERC20Token.sol 212 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.12;
import "../libs/SafeMathLib.sol";
/**
* @title ERC20 token
*
* @dev Based on https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.3.0/contracts/token/ERC20/ERC20.sol
*/
contract ERC20Token {
using SafeMathLib for uint256;
string public name;
string public symbol;
uint8 public decimals;
uint256 public totalSupply;
mapping(address => uint256) internal balances;
mapping(address => mapping(address => uint256)) internal allowances;
// events
event Transfer(
address indexed from,
address indexed to,
uint256 value
);
event Approval(
address indexed owner,
address indexed spender,
uint256 value
);
/**
* @dev internal constructor
*/
constructor() internal {}
// external functions
function transfer(
address to,
uint256 value
)
external
returns (bool)
{
_transfer(_getSender(), to, value);
return true;
}
function transferFrom(
address from,
address to,
uint256 value
)
virtual
external
returns (bool)
{
address sender = _getSender();
_transfer(from, to, value);
_approve(from, sender, allowances[from][sender].sub(value));
return true;
}
function approve(
address spender,
uint256 value
)
virtual
external
returns (bool)
{
_approve(_getSender(), spender, value);
return true;
}
// external functions (views)
function balanceOf(
address owner
)
virtual
external
view
returns (uint256)
{
return balances[owner];
}
function allowance(
address owner,
address spender
)
virtual
external
view
returns (uint256)
{
return allowances[owner][spender];
}
// internal functions
function _transfer(
address from,
address to,
uint256 value
)
virtual
internal
{
require(
from != address(0),
"ERC20Token: cannot transfer from 0x0 address"
);
require(
to != address(0),
"ERC20Token: cannot transfer to 0x0 address"
);
balances[from] = balances[from].sub(value);
balances[to] = balances[to].add(value);
emit Transfer(from, to, value);
}
function _approve(
address owner,
address spender,
uint256 value
)
virtual
internal
{
require(
owner != address(0),
"ERC20Token: cannot approve from 0x0 address"
);
require(
spender != address(0),
"ERC20Token: cannot approve to 0x0 address"
);
allowances[owner][spender] = value;
emit Approval(owner, spender, value);
}
function _mint(
address owner,
uint256 value
)
virtual
internal
{
require(
owner != address(0),
"ERC20Token: cannot mint to 0x0 address"
);
require(
value > 0,
"ERC20Token: cannot mint 0 value"
);
balances[owner] = balances[owner].add(value);
totalSupply = totalSupply.add(value);
emit Transfer(address(0), owner, value);
}
function _burn(
address owner,
uint256 value
)
virtual
internal
{
require(
owner != address(0),
"ERC20Token: cannot burn from 0x0 address"
);
balances[owner] = balances[owner].sub(
value,
"ERC20Token: burn value exceeds balance"
);
totalSupply = totalSupply.sub(value);
emit Transfer(owner, address(0), value);
}
// internal functions (views)
function _getSender()
virtual
internal
view
returns (address)
{
return msg.sender;
}
}
Controlled.sol 43 lines
// SPDX-License-Identifier: MIT pragma solidity ^0.6.12; /** * @title Controlled * * @dev Contract module which provides an access control mechanism. * It ensures there is only one controlling account of the smart contract * and grants that account exclusive access to specific functions. * * The controller account will be the one that deploys the contract. * * @author Stanisław Głogowski <[email protected]> */ contract Controlled { /** * @return controller account address */ address public controller; // modifiers /** * @dev Throws if msg.sender is not the controller */ modifier onlyController() { require( msg.sender == controller, "Controlled: msg.sender is not the controller" ); _; } /** * @dev Internal constructor */ constructor() internal { controller = msg.sender; } }
GatewayRecipient.sol 123 lines
// SPDX-License-Identifier: MIT pragma solidity ^0.6.12; import "../common/libs/BytesLib.sol"; /** * @title Gateway recipient * * @notice Gateway target contract * * @author Stanisław Głogowski <[email protected]> */ contract GatewayRecipient { using BytesLib for bytes; address public gateway; /** * @dev internal constructor */ constructor() internal {} // internal functions /** * @notice Initializes `GatewayRecipient` contract * @param gateway_ `Gateway` contract address */ function _initializeGatewayRecipient( address gateway_ ) internal { gateway = gateway_; } // internal functions (views) /** * @notice Gets gateway context account * @return context account address */ function _getContextAccount() internal view returns (address) { return _getContextAddress(40); } /** * @notice Gets gateway context sender * @return context sender address */ function _getContextSender() internal view returns (address) { return _getContextAddress(20); } /** * @notice Gets gateway context data * @return context data */ function _getContextData() internal view returns (bytes calldata) { bytes calldata result; if (_isGatewaySender()) { result = msg.data[:msg.data.length - 40]; } else { result = msg.data; } return result; } // private functions (views) function _getContextAddress( uint256 offset ) private view returns (address) { address result = address(0); if (_isGatewaySender()) { uint from = msg.data.length - offset; result = bytes(msg.data[from:from + 20]).toAddress(); } else { result = msg.sender; } return result; } function _isGatewaySender() private view returns (bool) { bool result; if (msg.sender == gateway) { require( msg.data.length >= 44, "GatewayRecipient: invalid msg.data" ); result = true; } return result; } }
AccountBase.sol 11 lines
// SPDX-License-Identifier: MIT pragma solidity ^0.6.12; /** * @title Account base * * @author Stanisław Głogowski <[email protected]> */ contract AccountBase { address public registry; }
ECDSAExtendedLib.sol 26 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.12;
import "./StringsLib.sol";
/**
* @title ECDSA extended library
*/
library ECDSAExtendedLib {
using StringsLib for uint;
function toEthereumSignedMessageHash(
bytes memory message
)
internal
pure
returns (bytes32)
{
return keccak256(abi.encodePacked(
"\x19Ethereum Signed Message:\n",
message.length.toString(),
abi.encodePacked(message)
));
}
}
AccountRegistry.sol 46 lines
// SPDX-License-Identifier: MIT pragma solidity ^0.6.12; import "./Account.sol"; /** * @title Account registry * * @author Stanisław Głogowski <[email protected]> */ abstract contract AccountRegistry { /** * @notice Verifies account signature * @param account account address * @param messageHash message hash * @param signature signature * @return true if valid */ function isValidAccountSignature( address account, bytes32 messageHash, bytes calldata signature ) virtual external view returns (bool); /** * @notice Verifies account signature * @param account account address * @param message message * @param signature signature * @return true if valid */ function isValidAccountSignature( address account, bytes calldata message, bytes calldata signature ) virtual external view returns (bool); }
Initializable.sol 75 lines
// SPDX-License-Identifier: MIT pragma solidity ^0.6.12; /** * @title Initializable * * @dev Contract module which provides access control mechanism, where * there is the initializer account that can be granted exclusive access to * specific functions. * * The initializer account will be tx.origin during contract deployment and will be removed on first use. * Use `onlyInitializer` modifier on contract initialize process. * * @author Stanisław Głogowski <[email protected]> */ contract Initializable { address private initializer; // events /** * @dev Emitted after `onlyInitializer` * @param initializer initializer address */ event Initialized( address initializer ); // modifiers /** * @dev Throws if tx.origin is not the initializer */ modifier onlyInitializer() { require( // solhint-disable-next-line avoid-tx-origin tx.origin == initializer, "Initializable: tx.origin is not the initializer" ); /// @dev removes initializer initializer = address(0); _; emit Initialized( // solhint-disable-next-line avoid-tx-origin tx.origin ); } /** * @dev Internal constructor */ constructor() internal { // solhint-disable-next-line avoid-tx-origin initializer = tx.origin; } // external functions (views) /** * @notice Check if contract is initialized * @return true when contract is initialized */ function isInitialized() external view returns (bool) { return initializer == address(0); } }
AccountController.sol 280 lines
// SPDX-License-Identifier: MIT pragma solidity ^0.6.12; import "./Account.sol"; /** * @title Account controller * * @dev Contract module which provides Account deployment mechanism * * @author Stanisław Głogowski <[email protected]> */ contract AccountController { address public accountRegistry; address public accountImplementation; // events /** * @dev Emitted when the account registry is updated * @param accountRegistry account registry address */ event AccountRegistryUpdated( address accountRegistry ); /** * @dev Emitted when the account implementation is updated * @param accountImplementation account implementation address */ event AccountImplementationUpdated( address accountImplementation ); /** * @dev Emitted when the account is deployed * @param account account address * @param accountImplementation account implementation address */ event AccountDeployed( address account, address accountImplementation ); /** * @dev Emitted when the account is upgraded * @param account account address * @param accountImplementation account implementation address */ event AccountUpgraded( address account, address accountImplementation ); /** * @dev Emitted when the transaction is executed * @param account account address * @param to to address * @param value value * @param data data * @param response response */ event AccountTransactionExecuted( address account, address to, uint256 value, bytes data, bytes response ); /** * @dev Internal constructor */ constructor() internal {} // internal functions /** * @notice Initializes `AccountController` contract * @param accountRegistry_ account registry address * @param accountImplementation_ account implementation address */ function _initializeAccountController( address accountRegistry_, address accountImplementation_ ) internal { _setAccountRegistry(accountRegistry_, false); _setAccountImplementation(accountImplementation_, false); } /** * @notice Sets account registry * @param accountRegistry_ account registry address * @param emitEvent it will emit event when flag is set to true */ function _setAccountRegistry( address accountRegistry_, bool emitEvent ) internal { require( accountRegistry_ != address(0), "AccountController: cannot set account registry to 0x0" ); accountRegistry = accountRegistry_; if (emitEvent) { emit AccountRegistryUpdated(accountRegistry); } } /** * @notice Sets account implementation * @param accountImplementation_ account implementation address * @param emitEvent it will emit event when flag is set to true */ function _setAccountImplementation( address accountImplementation_, bool emitEvent ) internal { require( accountImplementation_ != address(0), "AccountController: cannot set account Implementation to 0x0" ); accountImplementation = accountImplementation_; if (emitEvent) { emit AccountImplementationUpdated(accountImplementation); } } /** * @notice Deploys account * @param salt CREATE2 salt * @param emitEvent it will emit event when flag is set to true * @return account address */ function _deployAccount( bytes32 salt, bool emitEvent ) internal returns (address) { address account = address(new Account{salt: salt}( accountRegistry, accountImplementation )); if (emitEvent) { emit AccountDeployed( account, accountImplementation ); } return account; } /** * @notice Upgrades account * @param account account address * @param emitEvent it will emit event when flag is set to true */ function _upgradeAccount( address account, bool emitEvent ) internal { require( Account(payable(account)).implementation() != accountImplementation, "AccountController: account already upgraded" ); Account(payable(account)).setImplementation(accountImplementation); if (emitEvent) { emit AccountUpgraded( account, accountImplementation ); } } /** * @notice Executes transaction from the account * @param account account address * @param to to address * @param value value * @param data data * @param emitEvent it will emit event when flag is set to true * @return transaction result */ function _executeAccountTransaction( address account, address to, uint256 value, bytes memory data, bool emitEvent ) internal returns (bytes memory) { require( to != address(0), "AccountController: cannot send to 0x0" ); require( to != address(this), "AccountController: cannot send to controller" ); require( to != account, "AccountController: cannot send to self" ); bytes memory response = Account(payable(account)).executeTransaction( to, value, data ); if (emitEvent) { emit AccountTransactionExecuted( account, to, value, data, response ); } return response; } // internal functions (views) /** * @notice Computes account CREATE2 address * @param salt CREATE2 salt * @return account address */ function _computeAccountAddress( bytes32 salt ) internal view returns (address) { bytes memory creationCode = abi.encodePacked( type(Account).creationCode, bytes12(0), accountRegistry, bytes12(0), accountImplementation ); bytes32 data = keccak256( abi.encodePacked( bytes1(0xff), address(this), salt, keccak256(creationCode) ) ); return address(uint160(uint256(data))); } }
ExternalAccountRegistry.sol 250 lines
// SPDX-License-Identifier: MIT pragma solidity ^0.6.12; import "../common/libs/BlockLib.sol"; /** * @title External account registry * * @notice Global registry for keys and external (outside of the platform) contract based wallets * * @dev An account can call the registry to add (`addAccountOwner`) or remove (`removeAccountOwner`) its own owners. * When the owner has been added, information about that fact will live in the registry forever. * Removing an owner only affects the future blocks (until the owner is re-added). * * Given the fact, there is no way to sign the data using a contract based wallet, * we created a registry to store signed by the key wallet proofs. * ERC-1271 allows removing a signer after the signature was created. Thus store the signature for the later use * doesn't guarantee the signer is still has access to that smart account. * Because of that, the ERC1271's `isValidSignature()` cannot be used in e.g. `PaymentRegistry`.* * * An account can call the registry to add (`addAccountProof`) or remove (`removeAccountProof`) proof hash. * When the proof has been added, information about that fact will live in the registry forever. * Removing a proof only affects the future blocks (until the proof is re-added). * * @author Stanisław Głogowski <[email protected]> */ contract ExternalAccountRegistry { using BlockLib for BlockLib.BlockRelated; struct Account { mapping(address => BlockLib.BlockRelated) owners; mapping(bytes32 => BlockLib.BlockRelated) proofs; } mapping(address => Account) private accounts; // events /** * @dev Emitted when the new owner is added * @param account account address * @param owner owner address */ event AccountOwnerAdded( address account, address owner ); /** * @dev Emitted when the existing owner is removed * @param account account address * @param owner owner address */ event AccountOwnerRemoved( address account, address owner ); /** * @dev Emitted when the new proof is added * @param account account address * @param hash proof hash */ event AccountProofAdded( address account, bytes32 hash ); /** * @dev Emitted when the existing proof is removed * @param account account address * @param hash proof hash */ event AccountProofRemoved( address account, bytes32 hash ); // external functions /** * @notice Adds a new account owner * @param owner owner address */ function addAccountOwner( address owner ) external { require( owner != address(0), "ExternalAccountRegistry: cannot add 0x0 owner" ); require( !accounts[msg.sender].owners[owner].verifyAtCurrentBlock(), "ExternalAccountRegistry: owner already exists" ); accounts[msg.sender].owners[owner].added = true; accounts[msg.sender].owners[owner].removedAtBlockNumber = 0; emit AccountOwnerAdded( msg.sender, owner ); } /** * @notice Removes existing account owner * @param owner owner address */ function removeAccountOwner( address owner ) external { require( accounts[msg.sender].owners[owner].verifyAtCurrentBlock(), "ExternalAccountRegistry: owner doesn't exist" ); accounts[msg.sender].owners[owner].removedAtBlockNumber = block.number; emit AccountOwnerRemoved( msg.sender, owner ); } /** * @notice Adds a new account proof * @param hash proof hash */ function addAccountProof( bytes32 hash ) external { require( !accounts[msg.sender].proofs[hash].verifyAtCurrentBlock(), "ExternalAccountRegistry: proof already exists" ); accounts[msg.sender].proofs[hash].added = true; accounts[msg.sender].proofs[hash].removedAtBlockNumber = 0; emit AccountProofAdded( msg.sender, hash ); } /** * @notice Removes existing account proof * @param hash proof hash */ function removeAccountProof( bytes32 hash ) external { require( accounts[msg.sender].proofs[hash].verifyAtCurrentBlock(), "ExternalAccountRegistry: proof doesn't exist" ); accounts[msg.sender].proofs[hash].removedAtBlockNumber = block.number; emit AccountProofRemoved( msg.sender, hash ); } // external functions (views) /** * @notice Verifies the owner of the account at current block * @param account account address * @param owner owner address * @return true on correct account owner */ function verifyAccountOwner( address account, address owner ) external view returns (bool) { return accounts[account].owners[owner].verifyAtCurrentBlock(); } /** * @notice Verifies the owner of the account at specific block * @param account account address * @param owner owner address * @param blockNumber block number to verify * @return true on correct account owner */ function verifyAccountOwnerAtBlock( address account, address owner, uint256 blockNumber ) external view returns (bool) { return accounts[account].owners[owner].verifyAtBlock(blockNumber); } /** * @notice Verifies the proof of the account at current block * @param account account address * @param hash proof hash * @return true on correct account proof */ function verifyAccountProof( address account, bytes32 hash ) external view returns (bool) { return accounts[account].proofs[hash].verifyAtCurrentBlock(); } /** * @notice Verifies the proof of the account at specific block * @param account account address * @param hash proof hash * @param blockNumber block number to verify * @return true on correct account proof */ function verifyAccountProofAtBlock( address account, bytes32 hash, uint256 blockNumber ) external view returns (bool) { return accounts[account].proofs[hash].verifyAtBlock(blockNumber); } }
PersonalAccountRegistry.sol 507 lines
// SPDX-License-Identifier: MIT pragma solidity ^0.6.12; import "../common/access/Guarded.sol"; import "../common/account/AccountController.sol"; import "../common/account/AccountRegistry.sol"; import "../common/libs/BlockLib.sol"; import "../common/libs/ECDSALib.sol"; import "../common/libs/ECDSAExtendedLib.sol"; import "../common/libs/SafeMathLib.sol"; import "../common/lifecycle/Initializable.sol"; import "../common/token/ERC20Token.sol"; import "../gateway/GatewayRecipient.sol"; /** * @title Personal account registry * * @notice A registry for personal (controlled by owners) accounts * * @author Stanisław Głogowski <[email protected]> */ contract PersonalAccountRegistry is Guarded, AccountController, AccountRegistry, Initializable, GatewayRecipient { using BlockLib for BlockLib.BlockRelated; using SafeMathLib for uint256; using ECDSALib for bytes32; using ECDSAExtendedLib for bytes; struct Account { bool deployed; bytes32 salt; mapping(address => BlockLib.BlockRelated) owners; } mapping(address => Account) private accounts; // events /** * @dev Emitted when the new owner is added * @param account account address * @param owner owner address */ event AccountOwnerAdded( address account, address owner ); /** * @dev Emitted when the existing owner is removed * @param account account address * @param owner owner address */ event AccountOwnerRemoved( address account, address owner ); /** * @dev Emitted when the call is refunded * @param account account address * @param beneficiary beneficiary address * @param token token address * @param value value */ event AccountCallRefunded( address account, address beneficiary, address token, uint256 value ); /** * @dev Public constructor */ constructor() public Initializable() {} // external functions /** * @notice Initializes `PersonalAccountRegistry` contract * @param guardians_ array of guardians addresses * @param accountImplementation_ account implementation address * @param gateway_ `Gateway` contract address */ function initialize( address[] calldata guardians_, address accountImplementation_, address gateway_ ) external onlyInitializer { // Guarded _initializeGuarded(guardians_); // AccountController _initializeAccountController(address(this), accountImplementation_); // GatewayRecipient _initializeGatewayRecipient(gateway_); } /** * @notice Upgrades `PersonalAccountRegistry` contract * @param accountImplementation_ account implementation address */ function upgrade( address accountImplementation_ ) external onlyGuardian { _setAccountImplementation(accountImplementation_, true); } /** * @notice Deploys account * @param account account address */ function deployAccount( address account ) external { _verifySender(account); _deployAccount(account); } /** * @notice Upgrades account * @param account account address */ function upgradeAccount( address account ) external { _verifySender(account); _upgradeAccount(account, true); } /** * @notice Adds a new account owner * @param account account address * @param owner owner address */ function addAccountOwner( address account, address owner ) external { _verifySender(account); require( owner != address(0), "PersonalAccountRegistry: cannot add 0x0 owner" ); require( !accounts[account].owners[owner].verifyAtCurrentBlock(), "PersonalAccountRegistry: owner already exists" ); accounts[account].owners[owner].added = true; accounts[account].owners[owner].removedAtBlockNumber = 0; emit AccountOwnerAdded( account, owner ); } /** * @notice Removes the existing account owner * @param account account address * @param owner owner address */ function removeAccountOwner( address account, address owner ) external { address sender = _verifySender(account); require( owner != sender, "PersonalAccountRegistry: cannot remove self" ); require( accounts[account].owners[owner].verifyAtCurrentBlock(), "PersonalAccountRegistry: owner doesn't exist" ); accounts[account].owners[owner].removedAtBlockNumber = block.number; emit AccountOwnerRemoved( account, owner ); } /** * @notice Executes account transaction * @dev Deploys an account if not deployed yet * @param account account address * @param to to address * @param value value * @param data data */ function executeAccountTransaction( address account, address to, uint256 value, bytes calldata data ) external { _verifySender(account); _deployAccount(account); _executeAccountTransaction( account, to, value, data, true ); } /** * @notice Refunds account call * @dev Deploys an account if not deployed yet * @param account account address * @param token token address * @param value value */ function refundAccountCall( address account, address token, uint256 value ) external { _verifySender(account); _deployAccount(account); /* solhint-disable avoid-tx-origin */ if (token == address(0)) { _executeAccountTransaction( account, tx.origin, value, new bytes(0), false ); } else { bytes memory response = _executeAccountTransaction( account, token, 0, abi.encodeWithSelector( ERC20Token(token).transfer.selector, tx.origin, value ), false ); if (response.length > 0) { require( abi.decode(response, (bool)), "PersonalAccountRegistry: ERC20Token transfer reverted" ); } } emit AccountCallRefunded( account, tx.origin, token, value ); /* solhint-enable avoid-tx-origin */ } // external functions (views) /** * @notice Computes account address * @param saltOwner salt owner address * @return account address */ function computeAccountAddress( address saltOwner ) external view returns (address) { return _computeAccountAddress(saltOwner); } /** * @notice Checks if account is deployed * @param account account address * @return true when account is deployed */ function isAccountDeployed( address account ) external view returns (bool) { return accounts[account].deployed; } /** * @notice Verifies the owner of the account at the current block * @param account account address * @param owner owner address * @return true on correct account owner */ function verifyAccountOwner( address account, address owner ) external view returns (bool) { return _verifyAccountOwner(account, owner); } /** * @notice Verifies the owner of the account at a specific block * @param account account address * @param owner owner address * @param blockNumber block number to verify * @return true on correct account owner */ function verifyAccountOwnerAtBlock( address account, address owner, uint256 blockNumber ) external view returns (bool) { bool result = false; if (_verifyAccountOwner(account, owner)) { result = true; } else { result = accounts[account].owners[owner].verifyAtBlock(blockNumber); } return result; } /** * @notice Verifies account signature * @param account account address * @param messageHash message hash * @param signature signature * @return magic hash if valid */ function isValidAccountSignature( address account, bytes32 messageHash, bytes calldata signature ) override external view returns (bool) { return _verifyAccountOwner( account, messageHash.recoverAddress(signature) ); } /** * @notice Verifies account signature * @param account account address * @param message message * @param signature signature * @return magic hash if valid */ function isValidAccountSignature( address account, bytes calldata message, bytes calldata signature ) override external view returns (bool) { return _verifyAccountOwner( account, message.toEthereumSignedMessageHash().recoverAddress(signature) ); } // private functions function _verifySender( address account ) private returns (address) { address sender = _getContextSender(); if (accounts[account].owners[sender].added) { require( accounts[account].owners[sender].removedAtBlockNumber == 0, "PersonalAccountRegistry: sender is not the account owner" ); } else { require( accounts[account].salt == 0, "PersonalAccountRegistry: sender is not the account owner" ); bytes32 salt = keccak256( abi.encodePacked(sender) ); require( account == _computeAccountAddress(salt), "PersonalAccountRegistry: sender is not the account owner" ); accounts[account].salt = salt; accounts[account].owners[sender].added = true; emit AccountOwnerAdded( account, sender ); } return sender; } function _deployAccount( address account ) internal { if (!accounts[account].deployed) { _deployAccount( accounts[account].salt, true ); accounts[account].deployed = true; } } // private functions (views) function _computeAccountAddress( address saltOwner ) private view returns (address) { bytes32 salt = keccak256( abi.encodePacked(saltOwner) ); return _computeAccountAddress(salt); } function _verifyAccountOwner( address account, address owner ) private view returns (bool) { bool result; if (accounts[account].owners[owner].added) { result = accounts[account].owners[owner].removedAtBlockNumber == 0; } else if (accounts[account].salt == 0) { result = account == _computeAccountAddress(owner); } return result; } }
SignatureValidator.sol 47 lines
// SPDX-License-Identifier: MIT pragma solidity ^0.6.12; import "../libs/ECDSALib.sol"; /** * @title Signature validator * * @author Stanisław Głogowski <[email protected]> */ contract SignatureValidator { using ECDSALib for bytes32; uint256 public chainId; /** * @dev internal constructor */ constructor() internal { uint256 chainId_; // solhint-disable-next-line no-inline-assembly assembly { chainId_ := chainid() } chainId = chainId_; } // internal functions function _hashMessagePayload( bytes32 messagePrefix, bytes memory messagePayload ) internal view returns (bytes32) { return keccak256(abi.encodePacked( chainId, address(this), messagePrefix, messagePayload )).toEthereumSignedMessageHash(); } }
Read Contract
chainId 0x9a8a0592 → uint256
externalAccountRegistry 0xd2c83b9a → address
getAccountNextNonce 0xf92c5f7c → uint256
hashDelegatedBatch 0xc70c0cb7 → bytes32
hashDelegatedBatchWithGasPrice 0x3ac2ffd2 → bytes32
isInitialized 0x392e53cd → bool
personalAccountRegistry 0x87d31313 → address
Write Contract 6 functions
These functions modify contract state and require a wallet transaction to execute.
delegateBatch 0x231badaf
address account
uint256 nonce
address[] to
bytes[] data
bytes senderSignature
delegateBatchWithGasPrice 0xb5021b16
address account
uint256 nonce
address[] to
bytes[] data
bytes senderSignature
delegateBatches 0x5afaa7bb
bytes[] batches
bool revertOnFailure
initialize 0x485cc955
address externalAccountRegistry_
address personalAccountRegistry_
sendBatch 0x9f255626
address[] to
bytes[] data
sendBatchFromAccount 0x867519c6
address account
address[] to
bytes[] data
Recent Transactions
No transactions found for this address