Transaction Hash:
Block:
20389683 at Jul-26-2024 08:58:59 AM +UTC
Transaction Fee:
0.000429913637862576 ETH
$0.95
Gas Used:
138,516 Gas / 3.103711036 Gwei
Emitted Events:
229 |
GraphToken.Transfer( from=Proxy, to=0xAcA25dd25FDFdD7e3f85ddB8D4F36d690b1ba637, value=169620000000000000000000 )
|
230 |
Proxy.0x7d2476ab50663f025cff0be85655bcf355f62768615c0c478f3cd5293f807365( 0x7d2476ab50663f025cff0be85655bcf355f62768615c0c478f3cd5293f807365, 0x0000000000000000000000009d58779365b067d5d3fcc6e92d237acd06f1e6a1, 0x000000000000000000000000c944e90c64b2c07662a292be6244bdf05cda44a7, 0x0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000020, 0000000000000000000000000000000000000000000000000000000000000044, a9059cbb000000000000000000000000aca25dd25fdfdd7e3f85ddb8d4f36d69, 0b1ba6370000000000000000000000000000000000000000000023eb1e5e32b9, 5dd0000000000000000000000000000000000000000000000000000000000000 )
|
231 |
Proxy.0x7d2476ab50663f025cff0be85655bcf355f62768615c0c478f3cd5293f807365( 0x7d2476ab50663f025cff0be85655bcf355f62768615c0c478f3cd5293f807365, 0x0000000000000000000000009d58779365b067d5d3fcc6e92d237acd06f1e6a1, 0x000000000000000000000000482579f93dc13e6b434e38b5a0447ca543d88a46, 0x000000000000000000000000000000000000000000000000000186ecd5cad220, 0000000000000000000000000000000000000000000000000000000000000020, 0000000000000000000000000000000000000000000000000000000000000000 )
|
232 |
ArgentModule.Refund( wallet=Proxy, refundAddress=0x482579f93dc13e6b434e38b5a0447ca543d88a46, refundToken=0x00000000...000000000, refundAmount=429826733953568 )
|
233 |
ArgentModule.TransactionExecuted( wallet=Proxy, success=True, returnData=0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001, signedHash=D4822FA4FDA10D9F339C5C61AF5500104E8D7780D293430D8F2377BD214C2408 )
|
Account State Difference:
Address | Before | After | State Difference | ||
---|---|---|---|---|---|
0x0cf0C8eC...018e3392B | 0.435879610174779746 Eth | 0.435449783440826178 Eth | 0.000429826733953568 | ||
0x482579F9...543D88A46 | 9.40880346175328079 Eth | 9.409233288487234358 Eth | 0.000429826733953568 | ||
0x95222290...5CC4BAfe5
Miner
| (beaverbuild) | 16.268957599330456914 Eth | 16.268967295450456914 Eth | 0.00000969612 | |
0x9D587793...d06F1e6a1 | (Argent: Argent Module) | ||||
0xc944E90C...05Cda44a7 | |||||
0xF27696C8...f59342fA6 | (Argent: Relayer 3) |
4.684514097361995691 Eth
Nonce: 375396
|
4.684084183724133115 Eth
Nonce: 375397
| 0.000429913637862576 |
Execution Trace
ArgentModule.execute( _wallet=0x0cf0C8eCABb20ce048E4F86B417EABb018e3392B, _data=0xA5EFB2350000000000000000000000000CF0C8ECABB20CE048E4F86B417EABB018E3392B000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000C944E90C64B2C07662A292BE6244BDF05CDA44A7000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044A9059CBB000000000000000000000000ACA25DD25FDFDD7E3F85DDB8D4F36D690B1BA6370000000000000000000000000000000000000000000023EB1E5E32B95DD0000000000000000000000000000000000000000000000000000000000000, _nonce=6938249251725254411586826892408590531018581325, _signatures=0xEE1162CEDA7AF74661BA5F92C8478131368C863AD5F1124BAF16B52C8D647747260B779F38458B222A952EA7A3268100BC6D9FD3C2EDB265B3F47C1C701E76F61B, _gasPrice=3850000000, _gasLimit=141094, _refundToken=0x0000000000000000000000000000000000000000, _refundAddress=0x482579F93dC13e6B434E38b5a0447ca543D88A46 ) => ( True )
-
Null: 0x000...001.d4822fa4( )
Proxy.STATICCALL( )
-
BaseWallet.DELEGATECALL( )
-
ArgentModule.multiCall( _wallet=0x0cf0C8eCABb20ce048E4F86B417EABb018e3392B, _transactions= ) => ( [AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE=] )
-
0x391f0e86da951c03b1183c60b195090671adea88.13f4a0ea( )
Proxy.8f6f0332( )
BaseWallet.invoke( _target=0xc944E90C64B2c07662A292be6244BDf05Cda44a7, _value=0, _data=0xA9059CBB000000000000000000000000ACA25DD25FDFDD7E3F85DDB8D4F36D690B1BA6370000000000000000000000000000000000000000000023EB1E5E32B95DD00000 ) => ( _result=0x0000000000000000000000000000000000000000000000000000000000000001 )
-
GraphToken.transfer( recipient=0xAcA25dd25FDFdD7e3f85ddB8D4F36d690b1ba637, amount=169620000000000000000000 ) => ( True )
-
-
-
DappRegistry.isAuthorised( _wallet=0x0cf0C8eCABb20ce048E4F86B417EABb018e3392B, _spender=0x482579F93dC13e6B434E38b5a0447ca543D88A46, _to=0x0000000000000000000000000000000000000000, _data=0x ) => ( True )
Proxy.8f6f0332( )
BaseWallet.invoke( _target=0x482579F93dC13e6B434E38b5a0447ca543D88A46, _value=429826733953568, _data=0x ) => ( _result=0x )
- ETH 0.000429826733953568
0x482579f93dc13e6b434e38b5a0447ca543d88a46.CALL( )
- ETH 0.000429826733953568
File 1 of 5: ArgentModule
File 2 of 5: Proxy
File 3 of 5: GraphToken
File 4 of 5: BaseWallet
File 5 of 5: DappRegistry
// Copyright (C) 2021 Argent Labs Ltd. <https://argent.xyz> // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. // SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.3; interface IAuthoriser { function isAuthorised(address _sender, address _spender, address _to, bytes calldata _data) external view returns (bool); function areAuthorised( address _spender, address[] calldata _spenders, address[] calldata _to, bytes[] calldata _data ) external view returns (bool); }// Copyright (C) 2020 Argent Labs Ltd. <https://argent.xyz> // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. // SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.5.4 <0.9.0; /** * @title IModuleRegistry * @notice Interface for the registry of authorised modules. */ interface IModuleRegistry { function registerModule(address _module, bytes32 _name) external; function deregisterModule(address _module) external; function registerUpgrader(address _upgrader, bytes32 _name) external; function deregisterUpgrader(address _upgrader) external; function recoverToken(address _token) external; function moduleInfo(address _module) external view returns (bytes32); function upgraderInfo(address _upgrader) external view returns (bytes32); function isRegisteredModule(address _module) external view returns (bool); function isRegisteredModule(address[] calldata _modules) external view returns (bool); function isRegisteredUpgrader(address _upgrader) external view returns (bool); }// Copyright (C) 2018 Argent Labs Ltd. <https://argent.xyz> // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. // SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.5.4 <0.9.0; interface IGuardianStorage { /** * @notice Lets an authorised module add a guardian to a wallet. * @param _wallet The target wallet. * @param _guardian The guardian to add. */ function addGuardian(address _wallet, address _guardian) external; /** * @notice Lets an authorised module revoke a guardian from a wallet. * @param _wallet The target wallet. * @param _guardian The guardian to revoke. */ function revokeGuardian(address _wallet, address _guardian) external; /** * @notice Checks if an account is a guardian for a wallet. * @param _wallet The target wallet. * @param _guardian The account. * @return true if the account is a guardian for a wallet. */ function isGuardian(address _wallet, address _guardian) external view returns (bool); function isLocked(address _wallet) external view returns (bool); function getLock(address _wallet) external view returns (uint256); function getLocker(address _wallet) external view returns (address); function setLock(address _wallet, uint256 _releaseAfter) external; function getGuardians(address _wallet) external view returns (address[] memory); function guardianCount(address _wallet) external view returns (uint256); }// Copyright (C) 2020 Argent Labs Ltd. <https://argent.xyz> // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. // SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.5.4 <0.9.0; /** * @title ITransferStorage * @notice TransferStorage interface */ interface ITransferStorage { function setWhitelist(address _wallet, address _target, uint256 _value) external; function getWhitelist(address _wallet, address _target) external view returns (uint256); }// Copyright (C) 2021 Argent Labs Ltd. <https://argent.xyz> // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. // SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.3; import "./common/Utils.sol"; import "./common/BaseModule.sol"; import "./RelayerManager.sol"; import "./SecurityManager.sol"; import "./TransactionManager.sol"; /** * @title ArgentModule * @notice Single module for the Argent wallet. * @author Julien Niset - <[email protected]> */ contract ArgentModule is BaseModule, RelayerManager, SecurityManager, TransactionManager { bytes32 constant public NAME = "ArgentModule"; constructor ( IModuleRegistry _registry, IGuardianStorage _guardianStorage, ITransferStorage _userWhitelist, IAuthoriser _authoriser, address _uniswapRouter, uint256 _securityPeriod, uint256 _securityWindow, uint256 _recoveryPeriod, uint256 _lockPeriod ) BaseModule(_registry, _guardianStorage, _userWhitelist, _authoriser, NAME) SecurityManager(_recoveryPeriod, _securityPeriod, _securityWindow, _lockPeriod) TransactionManager(_securityPeriod) RelayerManager(_uniswapRouter) { } /** * @inheritdoc IModule */ function init(address _wallet) external override onlyWallet(_wallet) { enableDefaultStaticCalls(_wallet); } /** * @inheritdoc IModule */ function addModule(address _wallet, address _module) external override onlyWalletOwnerOrSelf(_wallet) onlyWhenUnlocked(_wallet) { require(registry.isRegisteredModule(_module), "AM: module is not registered"); IWallet(_wallet).authoriseModule(_module, true); } /** * @inheritdoc RelayerManager */ function getRequiredSignatures(address _wallet, bytes calldata _data) public view override returns (uint256, OwnerSignature) { bytes4 methodId = Utils.functionPrefix(_data); if (methodId == TransactionManager.multiCall.selector || methodId == TransactionManager.addToWhitelist.selector || methodId == TransactionManager.removeFromWhitelist.selector || methodId == TransactionManager.enableERC1155TokenReceiver.selector || methodId == TransactionManager.clearSession.selector || methodId == ArgentModule.addModule.selector || methodId == SecurityManager.addGuardian.selector || methodId == SecurityManager.revokeGuardian.selector || methodId == SecurityManager.cancelGuardianAddition.selector || methodId == SecurityManager.cancelGuardianRevokation.selector) { // owner return (1, OwnerSignature.Required); } if (methodId == TransactionManager.multiCallWithSession.selector) { return (1, OwnerSignature.Session); } if (methodId == SecurityManager.executeRecovery.selector) { // majority of guardians uint numberOfSignaturesRequired = _majorityOfGuardians(_wallet); require(numberOfSignaturesRequired > 0, "AM: no guardians set on wallet"); return (numberOfSignaturesRequired, OwnerSignature.Disallowed); } if (methodId == SecurityManager.cancelRecovery.selector) { // majority of (owner + guardians) uint numberOfSignaturesRequired = Utils.ceil(recoveryConfigs[_wallet].guardianCount + 1, 2); return (numberOfSignaturesRequired, OwnerSignature.Optional); } if (methodId == TransactionManager.multiCallWithGuardians.selector || methodId == TransactionManager.multiCallWithGuardiansAndStartSession.selector || methodId == SecurityManager.transferOwnership.selector) { // owner + majority of guardians uint majorityGuardians = _majorityOfGuardians(_wallet); uint numberOfSignaturesRequired = majorityGuardians + 1; return (numberOfSignaturesRequired, OwnerSignature.Required); } if (methodId == SecurityManager.finalizeRecovery.selector || methodId == SecurityManager.confirmGuardianAddition.selector || methodId == SecurityManager.confirmGuardianRevokation.selector) { // anyone return (0, OwnerSignature.Anyone); } if (methodId == SecurityManager.lock.selector || methodId == SecurityManager.unlock.selector) { // any guardian return (1, OwnerSignature.Disallowed); } revert("SM: unknown method"); } function _majorityOfGuardians(address _wallet) internal view returns (uint) { return Utils.ceil(guardianStorage.guardianCount(_wallet), 2); } }// Copyright (C) 2018 Argent Labs Ltd. <https://argent.xyz> // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. // SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.3; import "@openzeppelin/contracts/utils/math/Math.sol"; import "./common/Utils.sol"; import "./common/BaseModule.sol"; import "./common/SimpleOracle.sol"; import "../infrastructure/storage/IGuardianStorage.sol"; /** * @title RelayerManager * @notice Abstract Module to execute transactions signed by ETH-less accounts and sent by a relayer. * @author Julien Niset <[email protected]>, Olivier VDB <[email protected]> */ abstract contract RelayerManager is BaseModule, SimpleOracle { uint256 constant internal BLOCKBOUND = 10000; mapping (address => RelayerConfig) internal relayer; struct RelayerConfig { uint256 nonce; mapping (bytes32 => bool) executedTx; } // Used to avoid stack too deep error struct StackExtension { uint256 requiredSignatures; OwnerSignature ownerSignatureRequirement; bytes32 signHash; bool success; bytes returnData; } event TransactionExecuted(address indexed wallet, bool indexed success, bytes returnData, bytes32 signedHash); event Refund(address indexed wallet, address indexed refundAddress, address refundToken, uint256 refundAmount); // *************** Constructor ************************ // constructor(address _uniswapRouter) SimpleOracle(_uniswapRouter) { } /* ***************** External methods ************************* */ /** * @notice Gets the number of valid signatures that must be provided to execute a * specific relayed transaction. * @param _wallet The target wallet. * @param _data The data of the relayed transaction. * @return The number of required signatures and the wallet owner signature requirement. */ function getRequiredSignatures(address _wallet, bytes calldata _data) public view virtual returns (uint256, OwnerSignature); /** * @notice Executes a relayed transaction. * @param _wallet The target wallet. * @param _data The data for the relayed transaction * @param _nonce The nonce used to prevent replay attacks. * @param _signatures The signatures as a concatenated byte array. * @param _gasPrice The max gas price (in token) to use for the gas refund. * @param _gasLimit The max gas limit to use for the gas refund. * @param _refundToken The token to use for the gas refund. * @param _refundAddress The address refunded to prevent front-running. */ function execute( address _wallet, bytes calldata _data, uint256 _nonce, bytes calldata _signatures, uint256 _gasPrice, uint256 _gasLimit, address _refundToken, address _refundAddress ) external returns (bool) { // initial gas = 21k + non_zero_bytes * 16 + zero_bytes * 4 // ~= 21k + calldata.length * [1/3 * 16 + 2/3 * 4] uint256 startGas = gasleft() + 21000 + msg.data.length * 8; require(startGas >= _gasLimit, "RM: not enough gas provided"); require(verifyData(_wallet, _data), "RM: Target of _data != _wallet"); require(!_isLocked(_wallet) || _gasPrice == 0, "RM: Locked wallet refund"); StackExtension memory stack; (stack.requiredSignatures, stack.ownerSignatureRequirement) = getRequiredSignatures(_wallet, _data); require(stack.requiredSignatures > 0 || stack.ownerSignatureRequirement == OwnerSignature.Anyone, "RM: Wrong signature requirement"); require(stack.requiredSignatures * 65 == _signatures.length, "RM: Wrong number of signatures"); stack.signHash = getSignHash( address(this), 0, _data, _nonce, _gasPrice, _gasLimit, _refundToken, _refundAddress); require(checkAndUpdateUniqueness( _wallet, _nonce, stack.signHash, stack.requiredSignatures, stack.ownerSignatureRequirement), "RM: Duplicate request"); if (stack.ownerSignatureRequirement == OwnerSignature.Session) { require(validateSession(_wallet, stack.signHash, _signatures), "RM: Invalid session"); } else { require(validateSignatures(_wallet, stack.signHash, _signatures, stack.ownerSignatureRequirement), "RM: Invalid signatures"); } (stack.success, stack.returnData) = address(this).call(_data); refund( _wallet, startGas, _gasPrice, _gasLimit, _refundToken, _refundAddress, stack.requiredSignatures, stack.ownerSignatureRequirement); emit TransactionExecuted(_wallet, stack.success, stack.returnData, stack.signHash); return stack.success; } /** * @notice Gets the current nonce for a wallet. * @param _wallet The target wallet. */ function getNonce(address _wallet) external view returns (uint256 nonce) { return relayer[_wallet].nonce; } /** * @notice Checks if a transaction identified by its sign hash has already been executed. * @param _wallet The target wallet. * @param _signHash The sign hash of the transaction. */ function isExecutedTx(address _wallet, bytes32 _signHash) external view returns (bool executed) { return relayer[_wallet].executedTx[_signHash]; } /** * @notice Gets the last stored session for a wallet. * @param _wallet The target wallet. */ function getSession(address _wallet) external view returns (address key, uint64 expires) { return (sessions[_wallet].key, sessions[_wallet].expires); } /* ***************** Internal & Private methods ************************* */ /** * @notice Generates the signed hash of a relayed transaction according to ERC 1077. * @param _from The starting address for the relayed transaction (should be the relayer module) * @param _value The value for the relayed transaction. * @param _data The data for the relayed transaction which includes the wallet address. * @param _nonce The nonce used to prevent replay attacks. * @param _gasPrice The max gas price (in token) to use for the gas refund. * @param _gasLimit The max gas limit to use for the gas refund. * @param _refundToken The token to use for the gas refund. * @param _refundAddress The address refunded to prevent front-running. */ function getSignHash( address _from, uint256 _value, bytes memory _data, uint256 _nonce, uint256 _gasPrice, uint256 _gasLimit, address _refundToken, address _refundAddress ) internal view returns (bytes32) { return keccak256( abi.encodePacked( "\\x19Ethereum Signed Message:\ 32", keccak256(abi.encodePacked( bytes1(0x19), bytes1(0), _from, _value, _data, block.chainid, _nonce, _gasPrice, _gasLimit, _refundToken, _refundAddress)) )); } /** * @notice Checks if the relayed transaction is unique. If yes the state is updated. * For actions requiring 1 signature by the owner or a session key we use the incremental nonce. * For all other actions we check/store the signHash in a mapping. * @param _wallet The target wallet. * @param _nonce The nonce. * @param _signHash The signed hash of the transaction. * @param requiredSignatures The number of signatures required. * @param ownerSignatureRequirement The wallet owner signature requirement. * @return true if the transaction is unique. */ function checkAndUpdateUniqueness( address _wallet, uint256 _nonce, bytes32 _signHash, uint256 requiredSignatures, OwnerSignature ownerSignatureRequirement ) internal returns (bool) { if (requiredSignatures == 1 && (ownerSignatureRequirement == OwnerSignature.Required || ownerSignatureRequirement == OwnerSignature.Session)) { // use the incremental nonce if (_nonce <= relayer[_wallet].nonce) { return false; } uint256 nonceBlock = (_nonce & 0xffffffffffffffffffffffffffffffff00000000000000000000000000000000) >> 128; if (nonceBlock > block.number + BLOCKBOUND) { return false; } relayer[_wallet].nonce = _nonce; return true; } else { // use the txHash map if (relayer[_wallet].executedTx[_signHash] == true) { return false; } relayer[_wallet].executedTx[_signHash] = true; return true; } } /** * @notice Validates the signatures provided with a relayed transaction. * @param _wallet The target wallet. * @param _signHash The signed hash representing the relayed transaction. * @param _signatures The signatures as a concatenated bytes array. * @param _option An OwnerSignature enum indicating whether the owner is required, optional or disallowed. * @return A boolean indicating whether the signatures are valid. */ function validateSignatures(address _wallet, bytes32 _signHash, bytes memory _signatures, OwnerSignature _option) internal view returns (bool) { if (_signatures.length == 0) { return true; } address lastSigner = address(0); address[] memory guardians; if (_option != OwnerSignature.Required || _signatures.length > 65) { guardians = guardianStorage.getGuardians(_wallet); // guardians are only read if they may be needed } bool isGuardian; for (uint256 i = 0; i < _signatures.length / 65; i++) { address signer = Utils.recoverSigner(_signHash, _signatures, i); if (i == 0) { if (_option == OwnerSignature.Required) { // First signer must be owner if (_isOwner(_wallet, signer)) { continue; } return false; } else if (_option == OwnerSignature.Optional) { // First signer can be owner if (_isOwner(_wallet, signer)) { continue; } } } if (signer <= lastSigner) { return false; // Signers must be different } lastSigner = signer; (isGuardian, guardians) = Utils.isGuardianOrGuardianSigner(guardians, signer); if (!isGuardian) { return false; } } return true; } /** * @notice Validates the signature provided when a session key was used. * @param _wallet The target wallet. * @param _signHash The signed hash representing the relayed transaction. * @param _signatures The signatures as a concatenated bytes array. * @return A boolean indicating whether the signature is valid. */ function validateSession(address _wallet, bytes32 _signHash, bytes calldata _signatures) internal view returns (bool) { Session memory session = sessions[_wallet]; address signer = Utils.recoverSigner(_signHash, _signatures, 0); return (signer == session.key && session.expires >= block.timestamp); } /** * @notice Refunds the gas used to the Relayer. * @param _wallet The target wallet. * @param _startGas The gas provided at the start of the execution. * @param _gasPrice The max gas price (in token) for the refund. * @param _gasLimit The max gas limit for the refund. * @param _refundToken The token to use for the gas refund. * @param _refundAddress The address refunded to prevent front-running. * @param _requiredSignatures The number of signatures required. * @param _option An OwnerSignature enum indicating the signature requirement. */ function refund( address _wallet, uint _startGas, uint _gasPrice, uint _gasLimit, address _refundToken, address _refundAddress, uint256 _requiredSignatures, OwnerSignature _option ) internal { // Only refund when the owner is one of the signers or a session key was used if (_gasPrice > 0 && (_option == OwnerSignature.Required || _option == OwnerSignature.Session)) { address refundAddress = _refundAddress == address(0) ? msg.sender : _refundAddress; if (_requiredSignatures == 1 && _option == OwnerSignature.Required) { // refundAddress must be whitelisted/authorised if (!authoriser.isAuthorised(_wallet, refundAddress, address(0), EMPTY_BYTES)) { uint whitelistAfter = userWhitelist.getWhitelist(_wallet, refundAddress); require(whitelistAfter > 0 && whitelistAfter < block.timestamp, "RM: refund not authorised"); } } uint256 refundAmount; if (_refundToken == ETH_TOKEN) { // 23k as an upper bound to cover the rest of refund logic uint256 gasConsumed = _startGas - gasleft() + 23000; refundAmount = Math.min(gasConsumed, _gasLimit) * (Math.min(_gasPrice, tx.gasprice)); invokeWallet(_wallet, refundAddress, refundAmount, EMPTY_BYTES); } else { // 37.5k as an upper bound to cover the rest of refund logic uint256 gasConsumed = _startGas - gasleft() + 37500; uint256 tokenGasPrice = inToken(_refundToken, tx.gasprice); refundAmount = Math.min(gasConsumed, _gasLimit) * (Math.min(_gasPrice, tokenGasPrice)); bytes memory methodData = abi.encodeWithSelector(ERC20.transfer.selector, refundAddress, refundAmount); bytes memory transferSuccessBytes = invokeWallet(_wallet, _refundToken, 0, methodData); // Check token refund is successful, when `transfer` returns a success bool result if (transferSuccessBytes.length > 0) { require(abi.decode(transferSuccessBytes, (bool)), "RM: Refund transfer failed"); } } emit Refund(_wallet, refundAddress, _refundToken, refundAmount); } } /** * @notice Checks that the wallet address provided as the first parameter of _data matches _wallet * @return false if the addresses are different. */ function verifyData(address _wallet, bytes calldata _data) internal pure returns (bool) { require(_data.length >= 36, "RM: Invalid dataWallet"); address dataWallet = abi.decode(_data[4:], (address)); return dataWallet == _wallet; } }// Copyright (C) 2018 Argent Labs Ltd. <https://argent.xyz> // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. // SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.3; import "@openzeppelin/contracts/utils/math/SafeCast.sol"; import "./common/Utils.sol"; import "./common/BaseModule.sol"; import "../wallet/IWallet.sol"; /** * @title SecurityManager * @notice Abstract module implementing the key security features of the wallet: guardians, lock and recovery. * @author Julien Niset - <[email protected]> * @author Olivier Van Den Biggelaar - <[email protected]> */ abstract contract SecurityManager is BaseModule { struct RecoveryConfig { address recovery; uint64 executeAfter; uint32 guardianCount; } struct GuardianManagerConfig { // The time at which a guardian addition or revokation will be confirmable by the owner mapping (bytes32 => uint256) pending; } // Wallet specific storage for recovery mapping (address => RecoveryConfig) internal recoveryConfigs; // Wallet specific storage for pending guardian addition/revokation mapping (address => GuardianManagerConfig) internal guardianConfigs; // Recovery period uint256 internal immutable recoveryPeriod; // Lock period uint256 internal immutable lockPeriod; // The security period to add/remove guardians uint256 internal immutable securityPeriod; // The security window uint256 internal immutable securityWindow; // *************** Events *************************** // event RecoveryExecuted(address indexed wallet, address indexed _recovery, uint64 executeAfter); event RecoveryFinalized(address indexed wallet, address indexed _recovery); event RecoveryCanceled(address indexed wallet, address indexed _recovery); event OwnershipTransfered(address indexed wallet, address indexed _newOwner); event Locked(address indexed wallet, uint64 releaseAfter); event Unlocked(address indexed wallet); event GuardianAdditionRequested(address indexed wallet, address indexed guardian, uint256 executeAfter); event GuardianRevokationRequested(address indexed wallet, address indexed guardian, uint256 executeAfter); event GuardianAdditionCancelled(address indexed wallet, address indexed guardian); event GuardianRevokationCancelled(address indexed wallet, address indexed guardian); event GuardianAdded(address indexed wallet, address indexed guardian); event GuardianRevoked(address indexed wallet, address indexed guardian); // *************** Modifiers ************************ // /** * @notice Throws if there is no ongoing recovery procedure. */ modifier onlyWhenRecovery(address _wallet) { require(recoveryConfigs[_wallet].executeAfter > 0, "SM: no ongoing recovery"); _; } /** * @notice Throws if there is an ongoing recovery procedure. */ modifier notWhenRecovery(address _wallet) { require(recoveryConfigs[_wallet].executeAfter == 0, "SM: ongoing recovery"); _; } /** * @notice Throws if the caller is not a guardian for the wallet or the module itself. */ modifier onlyGuardianOrSelf(address _wallet) { require(_isSelf(msg.sender) || isGuardian(_wallet, msg.sender), "SM: must be guardian/self"); _; } // *************** Constructor ************************ // constructor( uint256 _recoveryPeriod, uint256 _securityPeriod, uint256 _securityWindow, uint256 _lockPeriod ) { // For the wallet to be secure we must have recoveryPeriod >= securityPeriod + securityWindow // where securityPeriod and securityWindow are the security parameters of adding/removing guardians. require(_lockPeriod >= _recoveryPeriod, "SM: insecure lock period"); require(_recoveryPeriod >= _securityPeriod + _securityWindow, "SM: insecure security periods"); recoveryPeriod = _recoveryPeriod; lockPeriod = _lockPeriod; securityWindow = _securityWindow; securityPeriod = _securityPeriod; } // *************** External functions ************************ // // *************** Recovery functions ************************ // /** * @notice Lets the guardians start the execution of the recovery procedure. * Once triggered the recovery is pending for the security period before it can be finalised. * Must be confirmed by N guardians, where N = ceil(Nb Guardians / 2). * @param _wallet The target wallet. * @param _recovery The address to which ownership should be transferred. */ function executeRecovery(address _wallet, address _recovery) external onlySelf() notWhenRecovery(_wallet) { validateNewOwner(_wallet, _recovery); uint64 executeAfter = uint64(block.timestamp + recoveryPeriod); recoveryConfigs[_wallet] = RecoveryConfig(_recovery, executeAfter, uint32(guardianStorage.guardianCount(_wallet))); _setLock(_wallet, block.timestamp + lockPeriod, SecurityManager.executeRecovery.selector); emit RecoveryExecuted(_wallet, _recovery, executeAfter); } /** * @notice Finalizes an ongoing recovery procedure if the security period is over. * The method is public and callable by anyone to enable orchestration. * @param _wallet The target wallet. */ function finalizeRecovery(address _wallet) external onlyWhenRecovery(_wallet) { RecoveryConfig storage config = recoveryConfigs[_wallet]; require(uint64(block.timestamp) > config.executeAfter, "SM: ongoing recovery period"); address recoveryOwner = config.recovery; delete recoveryConfigs[_wallet]; _clearSession(_wallet); IWallet(_wallet).setOwner(recoveryOwner); _setLock(_wallet, 0, bytes4(0)); emit RecoveryFinalized(_wallet, recoveryOwner); } /** * @notice Lets the owner cancel an ongoing recovery procedure. * Must be confirmed by N guardians, where N = ceil(Nb Guardian at executeRecovery + 1) / 2) - 1. * @param _wallet The target wallet. */ function cancelRecovery(address _wallet) external onlySelf() onlyWhenRecovery(_wallet) { address recoveryOwner = recoveryConfigs[_wallet].recovery; delete recoveryConfigs[_wallet]; _setLock(_wallet, 0, bytes4(0)); emit RecoveryCanceled(_wallet, recoveryOwner); } /** * @notice Lets the owner transfer the wallet ownership. This is executed immediately. * @param _wallet The target wallet. * @param _newOwner The address to which ownership should be transferred. */ function transferOwnership(address _wallet, address _newOwner) external onlySelf() onlyWhenUnlocked(_wallet) { validateNewOwner(_wallet, _newOwner); IWallet(_wallet).setOwner(_newOwner); emit OwnershipTransfered(_wallet, _newOwner); } /** * @notice Gets the details of the ongoing recovery procedure if any. * @param _wallet The target wallet. */ function getRecovery(address _wallet) external view returns(address _address, uint64 _executeAfter, uint32 _guardianCount) { RecoveryConfig storage config = recoveryConfigs[_wallet]; return (config.recovery, config.executeAfter, config.guardianCount); } // *************** Lock functions ************************ // /** * @notice Lets a guardian lock a wallet. * @param _wallet The target wallet. */ function lock(address _wallet) external onlyGuardianOrSelf(_wallet) onlyWhenUnlocked(_wallet) { _setLock(_wallet, block.timestamp + lockPeriod, SecurityManager.lock.selector); emit Locked(_wallet, uint64(block.timestamp + lockPeriod)); } /** * @notice Lets a guardian unlock a locked wallet. * @param _wallet The target wallet. */ function unlock(address _wallet) external onlyGuardianOrSelf(_wallet) onlyWhenLocked(_wallet) { require(locks[_wallet].locker == SecurityManager.lock.selector, "SM: cannot unlock"); _setLock(_wallet, 0, bytes4(0)); emit Unlocked(_wallet); } /** * @notice Returns the release time of a wallet lock or 0 if the wallet is unlocked. * @param _wallet The target wallet. * @return _releaseAfter The epoch time at which the lock will release (in seconds). */ function getLock(address _wallet) external view returns(uint64 _releaseAfter) { return _isLocked(_wallet) ? locks[_wallet].release : 0; } /** * @notice Checks if a wallet is locked. * @param _wallet The target wallet. * @return _isLocked `true` if the wallet is locked otherwise `false`. */ function isLocked(address _wallet) external view returns (bool) { return _isLocked(_wallet); } // *************** Guardian functions ************************ // /** * @notice Lets the owner add a guardian to its wallet. * The first guardian is added immediately. All following additions must be confirmed * by calling the confirmGuardianAddition() method. * @param _wallet The target wallet. * @param _guardian The guardian to add. */ function addGuardian(address _wallet, address _guardian) external onlyWalletOwnerOrSelf(_wallet) onlyWhenUnlocked(_wallet) { require(!_isOwner(_wallet, _guardian), "SM: guardian cannot be owner"); require(!isGuardian(_wallet, _guardian), "SM: duplicate guardian"); // Guardians must either be an EOA or a contract with an owner() // method that returns an address with a 25000 gas stipend. // Note that this test is not meant to be strict and can be bypassed by custom malicious contracts. (bool success,) = _guardian.call{gas: 25000}(abi.encodeWithSignature("owner()")); require(success, "SM: must be EOA/Argent wallet"); bytes32 id = keccak256(abi.encodePacked(_wallet, _guardian, "addition")); GuardianManagerConfig storage config = guardianConfigs[_wallet]; require( config.pending[id] == 0 || block.timestamp > config.pending[id] + securityWindow, "SM: duplicate pending addition"); config.pending[id] = block.timestamp + securityPeriod; emit GuardianAdditionRequested(_wallet, _guardian, block.timestamp + securityPeriod); } /** * @notice Confirms the pending addition of a guardian to a wallet. * The method must be called during the confirmation window and can be called by anyone to enable orchestration. * @param _wallet The target wallet. * @param _guardian The guardian. */ function confirmGuardianAddition(address _wallet, address _guardian) external onlyWhenUnlocked(_wallet) { bytes32 id = keccak256(abi.encodePacked(_wallet, _guardian, "addition")); GuardianManagerConfig storage config = guardianConfigs[_wallet]; require(config.pending[id] > 0, "SM: unknown pending addition"); require(config.pending[id] < block.timestamp, "SM: pending addition not over"); require(block.timestamp < config.pending[id] + securityWindow, "SM: pending addition expired"); guardianStorage.addGuardian(_wallet, _guardian); emit GuardianAdded(_wallet, _guardian); delete config.pending[id]; } /** * @notice Lets the owner cancel a pending guardian addition. * @param _wallet The target wallet. * @param _guardian The guardian. */ function cancelGuardianAddition(address _wallet, address _guardian) external onlyWalletOwnerOrSelf(_wallet) onlyWhenUnlocked(_wallet) { bytes32 id = keccak256(abi.encodePacked(_wallet, _guardian, "addition")); GuardianManagerConfig storage config = guardianConfigs[_wallet]; require(config.pending[id] > 0, "SM: unknown pending addition"); delete config.pending[id]; emit GuardianAdditionCancelled(_wallet, _guardian); } /** * @notice Lets the owner revoke a guardian from its wallet. * @dev Revokation must be confirmed by calling the confirmGuardianRevokation() method. * @param _wallet The target wallet. * @param _guardian The guardian to revoke. */ function revokeGuardian(address _wallet, address _guardian) external onlyWalletOwnerOrSelf(_wallet) { require(isGuardian(_wallet, _guardian), "SM: must be existing guardian"); bytes32 id = keccak256(abi.encodePacked(_wallet, _guardian, "revokation")); GuardianManagerConfig storage config = guardianConfigs[_wallet]; require( config.pending[id] == 0 || block.timestamp > config.pending[id] + securityWindow, "SM: duplicate pending revoke"); // TODO need to allow if confirmation window passed config.pending[id] = block.timestamp + securityPeriod; emit GuardianRevokationRequested(_wallet, _guardian, block.timestamp + securityPeriod); } /** * @notice Confirms the pending revokation of a guardian to a wallet. * The method must be called during the confirmation window and can be called by anyone to enable orchestration. * @param _wallet The target wallet. * @param _guardian The guardian. */ function confirmGuardianRevokation(address _wallet, address _guardian) external { bytes32 id = keccak256(abi.encodePacked(_wallet, _guardian, "revokation")); GuardianManagerConfig storage config = guardianConfigs[_wallet]; require(config.pending[id] > 0, "SM: unknown pending revoke"); require(config.pending[id] < block.timestamp, "SM: pending revoke not over"); require(block.timestamp < config.pending[id] + securityWindow, "SM: pending revoke expired"); guardianStorage.revokeGuardian(_wallet, _guardian); emit GuardianRevoked(_wallet, _guardian); delete config.pending[id]; } /** * @notice Lets the owner cancel a pending guardian revokation. * @param _wallet The target wallet. * @param _guardian The guardian. */ function cancelGuardianRevokation(address _wallet, address _guardian) external onlyWalletOwnerOrSelf(_wallet) onlyWhenUnlocked(_wallet) { bytes32 id = keccak256(abi.encodePacked(_wallet, _guardian, "revokation")); GuardianManagerConfig storage config = guardianConfigs[_wallet]; require(config.pending[id] > 0, "SM: unknown pending revoke"); delete config.pending[id]; emit GuardianRevokationCancelled(_wallet, _guardian); } /** * @notice Checks if an address is a guardian for a wallet. * @param _wallet The target wallet. * @param _guardian The address to check. * @return _isGuardian `true` if the address is a guardian for the wallet otherwise `false`. */ function isGuardian(address _wallet, address _guardian) public view returns (bool _isGuardian) { return guardianStorage.isGuardian(_wallet, _guardian); } /** * @notice Checks if an address is a guardian or an account authorised to sign on behalf of a smart-contract guardian. * @param _wallet The target wallet. * @param _guardian the address to test * @return _isGuardian `true` if the address is a guardian for the wallet otherwise `false`. */ function isGuardianOrGuardianSigner(address _wallet, address _guardian) external view returns (bool _isGuardian) { (_isGuardian, ) = Utils.isGuardianOrGuardianSigner(guardianStorage.getGuardians(_wallet), _guardian); } /** * @notice Counts the number of active guardians for a wallet. * @param _wallet The target wallet. * @return _count The number of active guardians for a wallet. */ function guardianCount(address _wallet) external view returns (uint256 _count) { return guardianStorage.guardianCount(_wallet); } /** * @notice Get the active guardians for a wallet. * @param _wallet The target wallet. * @return _guardians the active guardians for a wallet. */ function getGuardians(address _wallet) external view returns (address[] memory _guardians) { return guardianStorage.getGuardians(_wallet); } // *************** Internal Functions ********************* // function validateNewOwner(address _wallet, address _newOwner) internal view { require(_newOwner != address(0), "SM: new owner cannot be null"); require(!isGuardian(_wallet, _newOwner), "SM: new owner cannot be guardian"); } function _setLock(address _wallet, uint256 _releaseAfter, bytes4 _locker) internal { locks[_wallet] = Lock(SafeCast.toUint64(_releaseAfter), _locker); } }// Copyright (C) 2018 Argent Labs Ltd. <https://argent.xyz> // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. // SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.3; import "@openzeppelin/contracts/utils/math/SafeCast.sol"; import "./common/Utils.sol"; import "./common/BaseModule.sol"; import "../../lib_0.5/other/ERC20.sol"; /** * @title TransactionManager * @notice Module to execute transactions in sequence to e.g. transfer tokens (ETH, ERC20, ERC721, ERC1155) or call third-party contracts. * @author Julien Niset - <[email protected]> */ abstract contract TransactionManager is BaseModule { // Static calls bytes4 private constant ERC1271_IS_VALID_SIGNATURE = bytes4(keccak256("isValidSignature(bytes32,bytes)")); bytes4 private constant ERC721_RECEIVED = bytes4(keccak256("onERC721Received(address,address,uint256,bytes)")); bytes4 private constant ERC1155_RECEIVED = bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)")); bytes4 private constant ERC1155_BATCH_RECEIVED = bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)")); bytes4 private constant ERC165_INTERFACE = bytes4(keccak256("supportsInterface(bytes4)")); struct Call { address to; uint256 value; bytes data; } // The time delay for adding a trusted contact uint256 internal immutable whitelistPeriod; // *************** Events *************************** // event AddedToWhitelist(address indexed wallet, address indexed target, uint64 whitelistAfter); event RemovedFromWhitelist(address indexed wallet, address indexed target); event SessionCreated(address indexed wallet, address sessionKey, uint64 expires); event SessionCleared(address indexed wallet, address sessionKey); // *************** Constructor ************************ // constructor(uint256 _whitelistPeriod) { whitelistPeriod = _whitelistPeriod; } // *************** External functions ************************ // /** * @notice Makes the target wallet execute a sequence of transactions authorised by the wallet owner. * The method reverts if any of the inner transactions reverts. * The method reverts if any of the inner transaction is not to a trusted contact or an authorised dapp. * @param _wallet The target wallet. * @param _transactions The sequence of transactions. */ function multiCall( address _wallet, Call[] calldata _transactions ) external onlySelf() onlyWhenUnlocked(_wallet) returns (bytes[] memory) { bytes[] memory results = new bytes[](_transactions.length); for(uint i = 0; i < _transactions.length; i++) { address spender = Utils.recoverSpender(_transactions[i].to, _transactions[i].data); require( (_transactions[i].value == 0 || spender == _transactions[i].to) && (isWhitelisted(_wallet, spender) || authoriser.isAuthorised(_wallet, spender, _transactions[i].to, _transactions[i].data)), "TM: call not authorised"); results[i] = invokeWallet(_wallet, _transactions[i].to, _transactions[i].value, _transactions[i].data); } return results; } /** * @notice Makes the target wallet execute a sequence of transactions authorised by a session key. * The method reverts if any of the inner transactions reverts. * @param _wallet The target wallet. * @param _transactions The sequence of transactions. */ function multiCallWithSession( address _wallet, Call[] calldata _transactions ) external onlySelf() onlyWhenUnlocked(_wallet) returns (bytes[] memory) { return multiCallWithApproval(_wallet, _transactions); } /** * @notice Makes the target wallet execute a sequence of transactions approved by a majority of guardians. * The method reverts if any of the inner transactions reverts. * @param _wallet The target wallet. * @param _transactions The sequence of transactions. */ function multiCallWithGuardians( address _wallet, Call[] calldata _transactions ) external onlySelf() onlyWhenUnlocked(_wallet) returns (bytes[] memory) { return multiCallWithApproval(_wallet, _transactions); } /** * @notice Makes the target wallet execute a sequence of transactions approved by a majority of guardians. * The method reverts if any of the inner transactions reverts. * Upon success a new session is started. * @param _wallet The target wallet. * @param _transactions The sequence of transactions. */ function multiCallWithGuardiansAndStartSession( address _wallet, Call[] calldata _transactions, address _sessionUser, uint64 _duration ) external onlySelf() onlyWhenUnlocked(_wallet) returns (bytes[] memory) { startSession(_wallet, _sessionUser, _duration); return multiCallWithApproval(_wallet, _transactions); } /** * @notice Clears the active session of a wallet if any. * @param _wallet The target wallet. */ function clearSession(address _wallet) external onlyWalletOwnerOrSelf(_wallet) onlyWhenUnlocked(_wallet) { emit SessionCleared(_wallet, sessions[_wallet].key); _clearSession(_wallet); } /** * @notice Adds an address to the list of trusted contacts. * @param _wallet The target wallet. * @param _target The address to add. */ function addToWhitelist(address _wallet, address _target) external onlyWalletOwnerOrSelf(_wallet) onlyWhenUnlocked(_wallet) { require(_target != _wallet, "TM: Cannot whitelist wallet"); require(!registry.isRegisteredModule(_target), "TM: Cannot whitelist module"); require(!isWhitelisted(_wallet, _target), "TM: target already whitelisted"); uint256 whitelistAfter = block.timestamp + whitelistPeriod; setWhitelist(_wallet, _target, whitelistAfter); emit AddedToWhitelist(_wallet, _target, uint64(whitelistAfter)); } /** * @notice Removes an address from the list of trusted contacts. * @param _wallet The target wallet. * @param _target The address to remove. */ function removeFromWhitelist(address _wallet, address _target) external onlyWalletOwnerOrSelf(_wallet) onlyWhenUnlocked(_wallet) { setWhitelist(_wallet, _target, 0); emit RemovedFromWhitelist(_wallet, _target); } /** * @notice Checks if an address is a trusted contact for a wallet. * @param _wallet The target wallet. * @param _target The address. * @return _isWhitelisted true if the address is a trusted contact. */ function isWhitelisted(address _wallet, address _target) public view returns (bool _isWhitelisted) { uint whitelistAfter = userWhitelist.getWhitelist(_wallet, _target); return whitelistAfter > 0 && whitelistAfter < block.timestamp; } /* * @notice Enable the static calls required to make the wallet compatible with the ERC1155TokenReceiver * interface (see https://eips.ethereum.org/EIPS/eip-1155#erc-1155-token-receiver). This method only * needs to be called for wallets deployed in version lower or equal to 2.4.0 as the ERC1155 static calls * are not available by default for these versions of BaseWallet * @param _wallet The target wallet. */ function enableERC1155TokenReceiver(address _wallet) external onlyWalletOwnerOrSelf(_wallet) onlyWhenUnlocked(_wallet) { IWallet(_wallet).enableStaticCall(address(this), ERC165_INTERFACE); IWallet(_wallet).enableStaticCall(address(this), ERC1155_RECEIVED); IWallet(_wallet).enableStaticCall(address(this), ERC1155_BATCH_RECEIVED); } /** * @inheritdoc IModule */ function supportsStaticCall(bytes4 _methodId) external pure override returns (bool _isSupported) { return _methodId == ERC1271_IS_VALID_SIGNATURE || _methodId == ERC721_RECEIVED || _methodId == ERC165_INTERFACE || _methodId == ERC1155_RECEIVED || _methodId == ERC1155_BATCH_RECEIVED; } /** ******************* Callbacks ************************** */ /** * @notice Returns true if this contract implements the interface defined by * `interfaceId` (see https://eips.ethereum.org/EIPS/eip-165). */ function supportsInterface(bytes4 _interfaceID) external pure returns (bool) { return _interfaceID == ERC165_INTERFACE || _interfaceID == (ERC1155_RECEIVED ^ ERC1155_BATCH_RECEIVED); } /** * @notice Implementation of EIP 1271. * Should return whether the signature provided is valid for the provided data. * @param _msgHash Hash of a message signed on the behalf of address(this) * @param _signature Signature byte array associated with _msgHash */ function isValidSignature(bytes32 _msgHash, bytes memory _signature) external view returns (bytes4) { require(_signature.length == 65, "TM: invalid signature length"); address signer = Utils.recoverSigner(_msgHash, _signature, 0); require(_isOwner(msg.sender, signer), "TM: Invalid signer"); return ERC1271_IS_VALID_SIGNATURE; } fallback() external { bytes4 methodId = Utils.functionPrefix(msg.data); if(methodId == ERC721_RECEIVED || methodId == ERC1155_RECEIVED || methodId == ERC1155_BATCH_RECEIVED) { // solhint-disable-next-line no-inline-assembly assembly { calldatacopy(0, 0, 0x04) return (0, 0x20) } } } // *************** Internal Functions ********************* // function enableDefaultStaticCalls(address _wallet) internal { // setup the static calls that are available for free for all wallets IWallet(_wallet).enableStaticCall(address(this), ERC1271_IS_VALID_SIGNATURE); IWallet(_wallet).enableStaticCall(address(this), ERC721_RECEIVED); } function multiCallWithApproval(address _wallet, Call[] calldata _transactions) internal returns (bytes[] memory) { bytes[] memory results = new bytes[](_transactions.length); for(uint i = 0; i < _transactions.length; i++) { results[i] = invokeWallet(_wallet, _transactions[i].to, _transactions[i].value, _transactions[i].data); } return results; } function startSession(address _wallet, address _sessionUser, uint64 _duration) internal { require(_sessionUser != address(0), "TM: Invalid session user"); require(_duration > 0, "TM: Invalid session duration"); uint64 expiry = SafeCast.toUint64(block.timestamp + _duration); sessions[_wallet] = Session(_sessionUser, expiry); emit SessionCreated(_wallet, _sessionUser, expiry); } function setWhitelist(address _wallet, address _target, uint256 _whitelistAfter) internal { userWhitelist.setWhitelist(_wallet, _target, _whitelistAfter); } }// Copyright (C) 2018 Argent Labs Ltd. <https://argent.xyz> // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. // SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.3; import "../../wallet/IWallet.sol"; import "../../infrastructure/IModuleRegistry.sol"; import "../../infrastructure/storage/IGuardianStorage.sol"; import "../../infrastructure/IAuthoriser.sol"; import "../../infrastructure/storage/ITransferStorage.sol"; import "./IModule.sol"; import "../../../lib_0.5/other/ERC20.sol"; /** * @title BaseModule * @notice Base Module contract that contains methods common to all Modules. * @author Julien Niset - <[email protected]>, Olivier VDB - <[email protected]> */ abstract contract BaseModule is IModule { // Empty calldata bytes constant internal EMPTY_BYTES = ""; // Mock token address for ETH address constant internal ETH_TOKEN = address(0); // The module registry IModuleRegistry internal immutable registry; // The guardians storage IGuardianStorage internal immutable guardianStorage; // The trusted contacts storage ITransferStorage internal immutable userWhitelist; // The authoriser IAuthoriser internal immutable authoriser; event ModuleCreated(bytes32 name); enum OwnerSignature { Anyone, // Anyone Required, // Owner required Optional, // Owner and/or guardians Disallowed, // Guardians only Session // Session only } struct Session { address key; uint64 expires; } // Maps wallet to session mapping (address => Session) internal sessions; struct Lock { // the lock's release timestamp uint64 release; // the signature of the method that set the last lock bytes4 locker; } // Wallet specific lock storage mapping (address => Lock) internal locks; /** * @notice Throws if the wallet is not locked. */ modifier onlyWhenLocked(address _wallet) { require(_isLocked(_wallet), "BM: wallet must be locked"); _; } /** * @notice Throws if the wallet is locked. */ modifier onlyWhenUnlocked(address _wallet) { require(!_isLocked(_wallet), "BM: wallet locked"); _; } /** * @notice Throws if the sender is not the module itself. */ modifier onlySelf() { require(_isSelf(msg.sender), "BM: must be module"); _; } /** * @notice Throws if the sender is not the module itself or the owner of the target wallet. */ modifier onlyWalletOwnerOrSelf(address _wallet) { require(_isSelf(msg.sender) || _isOwner(_wallet, msg.sender), "BM: must be wallet owner/self"); _; } /** * @dev Throws if the sender is not the target wallet of the call. */ modifier onlyWallet(address _wallet) { require(msg.sender == _wallet, "BM: caller must be wallet"); _; } constructor( IModuleRegistry _registry, IGuardianStorage _guardianStorage, ITransferStorage _userWhitelist, IAuthoriser _authoriser, bytes32 _name ) { registry = _registry; guardianStorage = _guardianStorage; userWhitelist = _userWhitelist; authoriser = _authoriser; emit ModuleCreated(_name); } /** * @notice Moves tokens that have been sent to the module by mistake. * @param _token The target token. */ function recoverToken(address _token) external { uint total = ERC20(_token).balanceOf(address(this)); ERC20(_token).transfer(address(registry), total); } function _clearSession(address _wallet) internal { delete sessions[_wallet]; } /** * @notice Helper method to check if an address is the owner of a target wallet. * @param _wallet The target wallet. * @param _addr The address. */ function _isOwner(address _wallet, address _addr) internal view returns (bool) { return IWallet(_wallet).owner() == _addr; } /** * @notice Helper method to check if a wallet is locked. * @param _wallet The target wallet. */ function _isLocked(address _wallet) internal view returns (bool) { return locks[_wallet].release > uint64(block.timestamp); } /** * @notice Helper method to check if an address is the module itself. * @param _addr The target address. */ function _isSelf(address _addr) internal view returns (bool) { return _addr == address(this); } /** * @notice Helper method to invoke a wallet. * @param _wallet The target wallet. * @param _to The target address for the transaction. * @param _value The value of the transaction. * @param _data The data of the transaction. */ function invokeWallet(address _wallet, address _to, uint256 _value, bytes memory _data) internal returns (bytes memory _res) { bool success; (success, _res) = _wallet.call(abi.encodeWithSignature("invoke(address,uint256,bytes)", _to, _value, _data)); if (success && _res.length > 0) { //_res is empty if _wallet is an "old" BaseWallet that can't return output values (_res) = abi.decode(_res, (bytes)); } else if (_res.length > 0) { // solhint-disable-next-line no-inline-assembly assembly { returndatacopy(0, 0, returndatasize()) revert(0, returndatasize()) } } else if (!success) { revert("BM: wallet invoke reverted"); } } }// Copyright (C) 2018 Argent Labs Ltd. <https://argent.xyz> // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. // SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.3; /** * @title IModule * @notice Interface for a Module. * @author Julien Niset - <[email protected]>, Olivier VDB - <[email protected]> */ interface IModule { /**\t * @notice Adds a module to a wallet. Cannot execute when wallet is locked (or under recovery)\t * @param _wallet The target wallet.\t * @param _module The modules to authorise.\t */\t function addModule(address _wallet, address _module) external; /** * @notice Inits a Module for a wallet by e.g. setting some wallet specific parameters in storage. * @param _wallet The wallet. */ function init(address _wallet) external; /** * @notice Returns whether the module implements a callback for a given static call method. * @param _methodId The method id. */ function supportsStaticCall(bytes4 _methodId) external view returns (bool _isSupported); }// Copyright (C) 2021 Argent Labs Ltd. <https://argent.xyz> // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. // SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.3; import "@uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router01.sol"; import "@uniswap/v2-core/contracts/interfaces/IUniswapV2Pair.sol"; contract SimpleOracle { address internal immutable weth; address internal immutable uniswapV2Factory; constructor(address _uniswapRouter) { weth = IUniswapV2Router01(_uniswapRouter).WETH(); uniswapV2Factory = IUniswapV2Router01(_uniswapRouter).factory(); } function inToken(address _token, uint256 _ethAmount) internal view returns (uint256) { (uint256 wethReserve, uint256 tokenReserve) = getReservesForTokenPool(_token); return _ethAmount * tokenReserve / wethReserve; } function getReservesForTokenPool(address _token) internal view returns (uint256 wethReserve, uint256 tokenReserve) { if (weth < _token) { address pair = getPairForSorted(weth, _token); (wethReserve, tokenReserve,) = IUniswapV2Pair(pair).getReserves(); } else { address pair = getPairForSorted(_token, weth); (tokenReserve, wethReserve,) = IUniswapV2Pair(pair).getReserves(); } require(wethReserve != 0 && tokenReserve != 0, "SO: no liquidity"); } function getPairForSorted(address tokenA, address tokenB) internal virtual view returns (address pair) { pair = address(uint160(uint256(keccak256(abi.encodePacked( hex'ff', uniswapV2Factory, keccak256(abi.encodePacked(tokenA, tokenB)), hex'96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f' ))))); } }// Copyright (C) 2020 Argent Labs Ltd. <https://argent.xyz> // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. // SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.3; /** * @title Utils * @notice Common utility methods used by modules. */ library Utils { // ERC20, ERC721 & ERC1155 transfers & approvals bytes4 private constant ERC20_TRANSFER = bytes4(keccak256("transfer(address,uint256)")); bytes4 private constant ERC20_APPROVE = bytes4(keccak256("approve(address,uint256)")); bytes4 private constant ERC721_SET_APPROVAL_FOR_ALL = bytes4(keccak256("setApprovalForAll(address,bool)")); bytes4 private constant ERC721_TRANSFER_FROM = bytes4(keccak256("transferFrom(address,address,uint256)")); bytes4 private constant ERC721_SAFE_TRANSFER_FROM = bytes4(keccak256("safeTransferFrom(address,address,uint256)")); bytes4 private constant ERC721_SAFE_TRANSFER_FROM_BYTES = bytes4(keccak256("safeTransferFrom(address,address,uint256,bytes)")); bytes4 private constant ERC1155_SAFE_TRANSFER_FROM = bytes4(keccak256("safeTransferFrom(address,address,uint256,uint256,bytes)")); bytes4 private constant OWNER_SIG = 0x8da5cb5b; /** * @notice Helper method to recover the signer at a given position from a list of concatenated signatures. * @param _signedHash The signed hash * @param _signatures The concatenated signatures. * @param _index The index of the signature to recover. */ function recoverSigner(bytes32 _signedHash, bytes memory _signatures, uint _index) internal pure returns (address) { uint8 v; bytes32 r; bytes32 s; // we jump 32 (0x20) as the first slot of bytes contains the length // we jump 65 (0x41) per signature // for v we load 32 bytes ending with v (the first 31 come from s) then apply a mask // solhint-disable-next-line no-inline-assembly assembly { r := mload(add(_signatures, add(0x20,mul(0x41,_index)))) s := mload(add(_signatures, add(0x40,mul(0x41,_index)))) v := and(mload(add(_signatures, add(0x41,mul(0x41,_index)))), 0xff) } require(v == 27 || v == 28, "Utils: bad v value in signature"); address recoveredAddress = ecrecover(_signedHash, v, r, s); require(recoveredAddress != address(0), "Utils: ecrecover returned 0"); return recoveredAddress; } /** * @notice Helper method to recover the spender from a contract call. * The method returns the contract unless the call is to a standard method of a ERC20/ERC721/ERC1155 token * in which case the spender is recovered from the data. * @param _to The target contract. * @param _data The data payload. */ function recoverSpender(address _to, bytes memory _data) internal pure returns (address spender) { if(_data.length >= 68) { bytes4 methodId; // solhint-disable-next-line no-inline-assembly assembly { methodId := mload(add(_data, 0x20)) } if( methodId == ERC20_TRANSFER || methodId == ERC20_APPROVE || methodId == ERC721_SET_APPROVAL_FOR_ALL) { // solhint-disable-next-line no-inline-assembly assembly { spender := mload(add(_data, 0x24)) } return spender; } if( methodId == ERC721_TRANSFER_FROM || methodId == ERC721_SAFE_TRANSFER_FROM || methodId == ERC721_SAFE_TRANSFER_FROM_BYTES || methodId == ERC1155_SAFE_TRANSFER_FROM) { // solhint-disable-next-line no-inline-assembly assembly { spender := mload(add(_data, 0x44)) } return spender; } } spender = _to; } /** * @notice Helper method to parse data and extract the method signature. */ function functionPrefix(bytes memory _data) internal pure returns (bytes4 prefix) { require(_data.length >= 4, "Utils: Invalid functionPrefix"); // solhint-disable-next-line no-inline-assembly assembly { prefix := mload(add(_data, 0x20)) } } /** * @notice Checks if an address is a contract. * @param _addr The address. */ function isContract(address _addr) internal view returns (bool) { uint32 size; // solhint-disable-next-line no-inline-assembly assembly { size := extcodesize(_addr) } return (size > 0); } /** * @notice Checks if an address is a guardian or an account authorised to sign on behalf of a smart-contract guardian * given a list of guardians. * @param _guardians the list of guardians * @param _guardian the address to test * @return true and the list of guardians minus the found guardian upon success, false and the original list of guardians if not found. */ function isGuardianOrGuardianSigner(address[] memory _guardians, address _guardian) internal view returns (bool, address[] memory) { if (_guardians.length == 0 || _guardian == address(0)) { return (false, _guardians); } bool isFound = false; address[] memory updatedGuardians = new address[](_guardians.length - 1); uint256 index = 0; for (uint256 i = 0; i < _guardians.length; i++) { if (!isFound) { // check if _guardian is an account guardian if (_guardian == _guardians[i]) { isFound = true; continue; } // check if _guardian is the owner of a smart contract guardian if (isContract(_guardians[i]) && isGuardianOwner(_guardians[i], _guardian)) { isFound = true; continue; } } if (index < updatedGuardians.length) { updatedGuardians[index] = _guardians[i]; index++; } } return isFound ? (true, updatedGuardians) : (false, _guardians); } /** * @notice Checks if an address is the owner of a guardian contract. * The method does not revert if the call to the owner() method consumes more then 25000 gas. * @param _guardian The guardian contract * @param _owner The owner to verify. */ function isGuardianOwner(address _guardian, address _owner) internal view returns (bool) { address owner = address(0); // solhint-disable-next-line no-inline-assembly assembly { let ptr := mload(0x40) mstore(ptr,OWNER_SIG) let result := staticcall(25000, _guardian, ptr, 0x20, ptr, 0x20) if eq(result, 1) { owner := mload(ptr) } } return owner == _owner; } /** * @notice Returns ceil(a / b). */ function ceil(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a / b; if (a % b == 0) { return c; } else { return c + 1; } } } // Copyright (C) 2018 Argent Labs Ltd. <https://argent.xyz> // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. // SPDX-License-Identifier: GPL-3.0-only pragma solidity >=0.5.4 <0.9.0; /** * @title IWallet * @notice Interface for the BaseWallet */ interface IWallet { /** * @notice Returns the wallet owner. * @return The wallet owner address. */ function owner() external view returns (address); /** * @notice Returns the number of authorised modules. * @return The number of authorised modules. */ function modules() external view returns (uint); /** * @notice Sets a new owner for the wallet. * @param _newOwner The new owner. */ function setOwner(address _newOwner) external; /** * @notice Checks if a module is authorised on the wallet. * @param _module The module address to check. * @return `true` if the module is authorised, otherwise `false`. */ function authorised(address _module) external view returns (bool); /** * @notice Returns the module responsible for a static call redirection. * @param _sig The signature of the static call. * @return the module doing the redirection */ function enabled(bytes4 _sig) external view returns (address); /** * @notice Enables/Disables a module. * @param _module The target module. * @param _value Set to `true` to authorise the module. */ function authoriseModule(address _module, bool _value) external; /** * @notice Enables a static method by specifying the target module to which the call must be delegated. * @param _module The target module. * @param _method The static method signature. */ function enableStaticCall(address _module, bytes4 _method) external; }pragma solidity >=0.5.4 <0.9.0; /** * ERC20 contract interface. */ interface ERC20 { function totalSupply() external view returns (uint); function decimals() external view returns (uint); function balanceOf(address tokenOwner) external view returns (uint balance); function allowance(address tokenOwner, address spender) external view returns (uint remaining); function transfer(address to, uint tokens) external returns (bool success); function approve(address spender, uint tokens) external returns (bool success); function transferFrom(address from, address to, uint tokens) external returns (bool success); }// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /** * @dev Standard math utilities missing in the Solidity language. */ library Math { /** * @dev Returns the largest of two numbers. */ function max(uint256 a, uint256 b) internal pure returns (uint256) { return a >= b ? a : b; } /** * @dev Returns the smallest of two numbers. */ function min(uint256 a, uint256 b) internal pure returns (uint256) { return a < b ? a : b; } /** * @dev Returns the average of two numbers. The result is rounded towards * zero. */ function average(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b) / 2 can overflow, so we distribute return (a / 2) + (b / 2) + ((a % 2 + b % 2) / 2); } } // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /** * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow * checks. * * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can * easily result in undesired exploitation or bugs, since developers usually * assume that overflows raise errors. `SafeCast` restores this intuition by * reverting the transaction when such an operation overflows. * * Using this library instead of the unchecked operations eliminates an entire * class of bugs, so it's recommended to use it always. * * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing * all math on `uint256` and `int256` and then downcasting. */ library SafeCast { /** * @dev Returns the downcasted uint128 from uint256, reverting on * overflow (when the input is greater than largest uint128). * * Counterpart to Solidity's `uint128` operator. * * Requirements: * * - input must fit into 128 bits */ function toUint128(uint256 value) internal pure returns (uint128) { require(value < 2**128, "SafeCast: value doesn\\'t fit in 128 bits"); return uint128(value); } /** * @dev Returns the downcasted uint64 from uint256, reverting on * overflow (when the input is greater than largest uint64). * * Counterpart to Solidity's `uint64` operator. * * Requirements: * * - input must fit into 64 bits */ function toUint64(uint256 value) internal pure returns (uint64) { require(value < 2**64, "SafeCast: value doesn\\'t fit in 64 bits"); return uint64(value); } /** * @dev Returns the downcasted uint32 from uint256, reverting on * overflow (when the input is greater than largest uint32). * * Counterpart to Solidity's `uint32` operator. * * Requirements: * * - input must fit into 32 bits */ function toUint32(uint256 value) internal pure returns (uint32) { require(value < 2**32, "SafeCast: value doesn\\'t fit in 32 bits"); return uint32(value); } /** * @dev Returns the downcasted uint16 from uint256, reverting on * overflow (when the input is greater than largest uint16). * * Counterpart to Solidity's `uint16` operator. * * Requirements: * * - input must fit into 16 bits */ function toUint16(uint256 value) internal pure returns (uint16) { require(value < 2**16, "SafeCast: value doesn\\'t fit in 16 bits"); return uint16(value); } /** * @dev Returns the downcasted uint8 from uint256, reverting on * overflow (when the input is greater than largest uint8). * * Counterpart to Solidity's `uint8` operator. * * Requirements: * * - input must fit into 8 bits. */ function toUint8(uint256 value) internal pure returns (uint8) { require(value < 2**8, "SafeCast: value doesn\\'t fit in 8 bits"); return uint8(value); } /** * @dev Converts a signed int256 into an unsigned uint256. * * Requirements: * * - input must be greater than or equal to 0. */ function toUint256(int256 value) internal pure returns (uint256) { require(value >= 0, "SafeCast: value must be positive"); return uint256(value); } /** * @dev Returns the downcasted int128 from int256, reverting on * overflow (when the input is less than smallest int128 or * greater than largest int128). * * Counterpart to Solidity's `int128` operator. * * Requirements: * * - input must fit into 128 bits * * _Available since v3.1._ */ function toInt128(int256 value) internal pure returns (int128) { require(value >= -2**127 && value < 2**127, "SafeCast: value doesn\\'t fit in 128 bits"); return int128(value); } /** * @dev Returns the downcasted int64 from int256, reverting on * overflow (when the input is less than smallest int64 or * greater than largest int64). * * Counterpart to Solidity's `int64` operator. * * Requirements: * * - input must fit into 64 bits * * _Available since v3.1._ */ function toInt64(int256 value) internal pure returns (int64) { require(value >= -2**63 && value < 2**63, "SafeCast: value doesn\\'t fit in 64 bits"); return int64(value); } /** * @dev Returns the downcasted int32 from int256, reverting on * overflow (when the input is less than smallest int32 or * greater than largest int32). * * Counterpart to Solidity's `int32` operator. * * Requirements: * * - input must fit into 32 bits * * _Available since v3.1._ */ function toInt32(int256 value) internal pure returns (int32) { require(value >= -2**31 && value < 2**31, "SafeCast: value doesn\\'t fit in 32 bits"); return int32(value); } /** * @dev Returns the downcasted int16 from int256, reverting on * overflow (when the input is less than smallest int16 or * greater than largest int16). * * Counterpart to Solidity's `int16` operator. * * Requirements: * * - input must fit into 16 bits * * _Available since v3.1._ */ function toInt16(int256 value) internal pure returns (int16) { require(value >= -2**15 && value < 2**15, "SafeCast: value doesn\\'t fit in 16 bits"); return int16(value); } /** * @dev Returns the downcasted int8 from int256, reverting on * overflow (when the input is less than smallest int8 or * greater than largest int8). * * Counterpart to Solidity's `int8` operator. * * Requirements: * * - input must fit into 8 bits. * * _Available since v3.1._ */ function toInt8(int256 value) internal pure returns (int8) { require(value >= -2**7 && value < 2**7, "SafeCast: value doesn\\'t fit in 8 bits"); return int8(value); } /** * @dev Converts an unsigned uint256 into a signed int256. * * Requirements: * * - input must be less than or equal to maxInt256. */ function toInt256(uint256 value) internal pure returns (int256) { require(value < 2**255, "SafeCast: value doesn't fit in an int256"); return int256(value); } } pragma solidity >=0.5.0; interface IUniswapV2Pair { event Approval(address indexed owner, address indexed spender, uint value); event Transfer(address indexed from, address indexed to, uint value); function name() external pure returns (string memory); function symbol() external pure returns (string memory); function decimals() external pure returns (uint8); function totalSupply() external view returns (uint); function balanceOf(address owner) external view returns (uint); function allowance(address owner, address spender) external view returns (uint); function approve(address spender, uint value) external returns (bool); function transfer(address to, uint value) external returns (bool); function transferFrom(address from, address to, uint value) external returns (bool); function DOMAIN_SEPARATOR() external view returns (bytes32); function PERMIT_TYPEHASH() external pure returns (bytes32); function nonces(address owner) external view returns (uint); function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external; event Mint(address indexed sender, uint amount0, uint amount1); event Burn(address indexed sender, uint amount0, uint amount1, address indexed to); event Swap( address indexed sender, uint amount0In, uint amount1In, uint amount0Out, uint amount1Out, address indexed to ); event Sync(uint112 reserve0, uint112 reserve1); function MINIMUM_LIQUIDITY() external pure returns (uint); function factory() external view returns (address); function token0() external view returns (address); function token1() external view returns (address); function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast); function price0CumulativeLast() external view returns (uint); function price1CumulativeLast() external view returns (uint); function kLast() external view returns (uint); function mint(address to) external returns (uint liquidity); function burn(address to) external returns (uint amount0, uint amount1); function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external; function skim(address to) external; function sync() external; function initialize(address, address) external; } pragma solidity >=0.6.2; interface IUniswapV2Router01 { function factory() external pure returns (address); function WETH() external pure returns (address); function addLiquidity( address tokenA, address tokenB, uint amountADesired, uint amountBDesired, uint amountAMin, uint amountBMin, address to, uint deadline ) external returns (uint amountA, uint amountB, uint liquidity); function addLiquidityETH( address token, uint amountTokenDesired, uint amountTokenMin, uint amountETHMin, address to, uint deadline ) external payable returns (uint amountToken, uint amountETH, uint liquidity); function removeLiquidity( address tokenA, address tokenB, uint liquidity, uint amountAMin, uint amountBMin, address to, uint deadline ) external returns (uint amountA, uint amountB); function removeLiquidityETH( address token, uint liquidity, uint amountTokenMin, uint amountETHMin, address to, uint deadline ) external returns (uint amountToken, uint amountETH); function removeLiquidityWithPermit( address tokenA, address tokenB, uint liquidity, uint amountAMin, uint amountBMin, address to, uint deadline, bool approveMax, uint8 v, bytes32 r, bytes32 s ) external returns (uint amountA, uint amountB); function removeLiquidityETHWithPermit( address token, uint liquidity, uint amountTokenMin, uint amountETHMin, address to, uint deadline, bool approveMax, uint8 v, bytes32 r, bytes32 s ) external returns (uint amountToken, uint amountETH); function swapExactTokensForTokens( uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline ) external returns (uint[] memory amounts); function swapTokensForExactTokens( uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline ) external returns (uint[] memory amounts); function swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline) external payable returns (uint[] memory amounts); function swapTokensForExactETH(uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline) external returns (uint[] memory amounts); function swapExactTokensForETH(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline) external returns (uint[] memory amounts); function swapETHForExactTokens(uint amountOut, address[] calldata path, address to, uint deadline) external payable returns (uint[] memory amounts); function quote(uint amountA, uint reserveA, uint reserveB) external pure returns (uint amountB); function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) external pure returns (uint amountOut); function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) external pure returns (uint amountIn); function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts); function getAmountsIn(uint amountOut, address[] calldata path) external view returns (uint[] memory amounts); }
File 2 of 5: Proxy
// Copyright (C) 2018 Argent Labs Ltd. <https://argent.xyz> // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. pragma solidity ^0.5.4; /** * @title Proxy * @dev Basic proxy that delegates all calls to a fixed implementing contract. * The implementing contract cannot be upgraded. * @author Julien Niset - <[email protected]> */ contract Proxy { address implementation; event Received(uint indexed value, address indexed sender, bytes data); constructor(address _implementation) public { implementation = _implementation; } function() external payable { if (msg.data.length == 0 && msg.value > 0) { emit Received(msg.value, msg.sender, msg.data); } else { // solium-disable-next-line security/no-inline-assembly assembly { let target := sload(0) calldatacopy(0, 0, calldatasize()) let result := delegatecall(gas, target, 0, calldatasize(), 0, 0) returndatacopy(0, 0, returndatasize()) switch result case 0 {revert(0, returndatasize())} default {return (0, returndatasize())} } } } }
File 3 of 5: GraphToken
// Sources flattened with hardhat v2.0.2 https://hardhat.org // File @openzeppelin/contracts/GSN/[email protected] // SPDX-License-Identifier: MIT pragma solidity ^0.7.0; /* * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with GSN meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract Context { function _msgSender() internal view virtual returns (address payable) { return msg.sender; } function _msgData() internal view virtual returns (bytes memory) { this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691 return msg.data; } } // File @openzeppelin/contracts/token/ERC20/[email protected] pragma solidity ^0.7.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `recipient`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address recipient, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `sender` to `recipient` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); } // File @openzeppelin/contracts/math/[email protected] pragma solidity ^0.7.0; /** * @dev Wrappers over Solidity's arithmetic operations with added overflow * checks. * * Arithmetic operations in Solidity wrap on overflow. This can easily result * in bugs, because programmers usually assume that an overflow raises an * error, which is the standard behavior in high level programming languages. * `SafeMath` restores this intuition by reverting the transaction when an * operation overflows. * * Using this library instead of the unchecked operations eliminates an entire * class of bugs, so it's recommended to use it always. */ library SafeMath { /** * @dev Returns the addition of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `+` operator. * * Requirements: * * - Addition cannot overflow. */ function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; require(c >= a, "SafeMath: addition overflow"); return c; } /** * @dev Returns the subtraction of two unsigned integers, reverting on * overflow (when the result is negative). * * Counterpart to Solidity's `-` operator. * * Requirements: * * - Subtraction cannot overflow. */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { return sub(a, b, "SafeMath: subtraction overflow"); } /** * @dev Returns the subtraction of two unsigned integers, reverting with custom message on * overflow (when the result is negative). * * Counterpart to Solidity's `-` operator. * * Requirements: * * - Subtraction cannot overflow. */ function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b <= a, errorMessage); uint256 c = a - b; return c; } /** * @dev Returns the multiplication of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `*` operator. * * Requirements: * * - Multiplication cannot overflow. */ function mul(uint256 a, uint256 b) internal pure returns (uint256) { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 if (a == 0) { return 0; } uint256 c = a * b; require(c / a == b, "SafeMath: multiplication overflow"); return c; } /** * @dev Returns the integer division of two unsigned integers. Reverts on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function div(uint256 a, uint256 b) internal pure returns (uint256) { return div(a, b, "SafeMath: division by zero"); } /** * @dev Returns the integer division of two unsigned integers. Reverts with custom message on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b > 0, errorMessage); uint256 c = a / b; // assert(a == b * c + a % b); // There is no case in which this doesn't hold return c; } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * Reverts when dividing by zero. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function mod(uint256 a, uint256 b) internal pure returns (uint256) { return mod(a, b, "SafeMath: modulo by zero"); } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * Reverts with custom message when dividing by zero. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b != 0, errorMessage); return a % b; } } // File @openzeppelin/contracts/token/ERC20/[email protected] pragma solidity ^0.7.0; /** * @dev Implementation of the {IERC20} interface. * * This implementation is agnostic to the way tokens are created. This means * that a supply mechanism has to be added in a derived contract using {_mint}. * For a generic mechanism see {ERC20PresetMinterPauser}. * * TIP: For a detailed writeup see our guide * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How * to implement supply mechanisms]. * * We have followed general OpenZeppelin guidelines: functions revert instead * of returning `false` on failure. This behavior is nonetheless conventional * and does not conflict with the expectations of ERC20 applications. * * Additionally, an {Approval} event is emitted on calls to {transferFrom}. * This allows applications to reconstruct the allowance for all accounts just * by listening to said events. Other implementations of the EIP may not emit * these events, as it isn't required by the specification. * * Finally, the non-standard {decreaseAllowance} and {increaseAllowance} * functions have been added to mitigate the well-known issues around setting * allowances. See {IERC20-approve}. */ contract ERC20 is Context, IERC20 { using SafeMath for uint256; mapping (address => uint256) private _balances; mapping (address => mapping (address => uint256)) private _allowances; uint256 private _totalSupply; string private _name; string private _symbol; uint8 private _decimals; /** * @dev Sets the values for {name} and {symbol}, initializes {decimals} with * a default value of 18. * * To select a different value for {decimals}, use {_setupDecimals}. * * All three of these values are immutable: they can only be set once during * construction. */ constructor (string memory name_, string memory symbol_) { _name = name_; _symbol = symbol_; _decimals = 18; } /** * @dev Returns the name of the token. */ function name() public view returns (string memory) { return _name; } /** * @dev Returns the symbol of the token, usually a shorter version of the * name. */ function symbol() public view returns (string memory) { return _symbol; } /** * @dev Returns the number of decimals used to get its user representation. * For example, if `decimals` equals `2`, a balance of `505` tokens should * be displayed to a user as `5,05` (`505 / 10 ** 2`). * * Tokens usually opt for a value of 18, imitating the relationship between * Ether and Wei. This is the value {ERC20} uses, unless {_setupDecimals} is * called. * * NOTE: This information is only used for _display_ purposes: it in * no way affects any of the arithmetic of the contract, including * {IERC20-balanceOf} and {IERC20-transfer}. */ function decimals() public view returns (uint8) { return _decimals; } /** * @dev See {IERC20-totalSupply}. */ function totalSupply() public view override returns (uint256) { return _totalSupply; } /** * @dev See {IERC20-balanceOf}. */ function balanceOf(address account) public view override returns (uint256) { return _balances[account]; } /** * @dev See {IERC20-transfer}. * * Requirements: * * - `recipient` cannot be the zero address. * - the caller must have a balance of at least `amount`. */ function transfer(address recipient, uint256 amount) public virtual override returns (bool) { _transfer(_msgSender(), recipient, amount); return true; } /** * @dev See {IERC20-allowance}. */ function allowance(address owner, address spender) public view virtual override returns (uint256) { return _allowances[owner][spender]; } /** * @dev See {IERC20-approve}. * * Requirements: * * - `spender` cannot be the zero address. */ function approve(address spender, uint256 amount) public virtual override returns (bool) { _approve(_msgSender(), spender, amount); return true; } /** * @dev See {IERC20-transferFrom}. * * Emits an {Approval} event indicating the updated allowance. This is not * required by the EIP. See the note at the beginning of {ERC20}. * * Requirements: * * - `sender` and `recipient` cannot be the zero address. * - `sender` must have a balance of at least `amount`. * - the caller must have allowance for ``sender``'s tokens of at least * `amount`. */ function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) { _transfer(sender, recipient, amount); _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance")); return true; } /** * @dev Atomically increases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. */ function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue)); return true; } /** * @dev Atomically decreases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. * - `spender` must have allowance for the caller of at least * `subtractedValue`. */ function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero")); return true; } /** * @dev Moves tokens `amount` from `sender` to `recipient`. * * This is internal function is equivalent to {transfer}, and can be used to * e.g. implement automatic token fees, slashing mechanisms, etc. * * Emits a {Transfer} event. * * Requirements: * * - `sender` cannot be the zero address. * - `recipient` cannot be the zero address. * - `sender` must have a balance of at least `amount`. */ function _transfer(address sender, address recipient, uint256 amount) internal virtual { require(sender != address(0), "ERC20: transfer from the zero address"); require(recipient != address(0), "ERC20: transfer to the zero address"); _beforeTokenTransfer(sender, recipient, amount); _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance"); _balances[recipient] = _balances[recipient].add(amount); emit Transfer(sender, recipient, amount); } /** @dev Creates `amount` tokens and assigns them to `account`, increasing * the total supply. * * Emits a {Transfer} event with `from` set to the zero address. * * Requirements: * * - `to` cannot be the zero address. */ function _mint(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: mint to the zero address"); _beforeTokenTransfer(address(0), account, amount); _totalSupply = _totalSupply.add(amount); _balances[account] = _balances[account].add(amount); emit Transfer(address(0), account, amount); } /** * @dev Destroys `amount` tokens from `account`, reducing the * total supply. * * Emits a {Transfer} event with `to` set to the zero address. * * Requirements: * * - `account` cannot be the zero address. * - `account` must have at least `amount` tokens. */ function _burn(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: burn from the zero address"); _beforeTokenTransfer(account, address(0), amount); _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance"); _totalSupply = _totalSupply.sub(amount); emit Transfer(account, address(0), amount); } /** * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens. * * This internal function is equivalent to `approve`, and can be used to * e.g. set automatic allowances for certain subsystems, etc. * * Emits an {Approval} event. * * Requirements: * * - `owner` cannot be the zero address. * - `spender` cannot be the zero address. */ function _approve(address owner, address spender, uint256 amount) internal virtual { require(owner != address(0), "ERC20: approve from the zero address"); require(spender != address(0), "ERC20: approve to the zero address"); _allowances[owner][spender] = amount; emit Approval(owner, spender, amount); } /** * @dev Sets {decimals} to a value other than the default one of 18. * * WARNING: This function should only be called from the constructor. Most * applications that interact with token contracts will not expect * {decimals} to ever change, and may work incorrectly if it does. */ function _setupDecimals(uint8 decimals_) internal { _decimals = decimals_; } /** * @dev Hook that is called before any transfer of tokens. This includes * minting and burning. * * Calling conditions: * * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens * will be to transferred to `to`. * - when `from` is zero, `amount` tokens will be minted for `to`. * - when `to` is zero, `amount` of ``from``'s tokens will be burned. * - `from` and `to` are never both zero. * * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. */ function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { } } // File @openzeppelin/contracts/token/ERC20/[email protected] pragma solidity ^0.7.0; /** * @dev Extension of {ERC20} that allows token holders to destroy both their own * tokens and those that they have an allowance for, in a way that can be * recognized off-chain (via event analysis). */ abstract contract ERC20Burnable is Context, ERC20 { using SafeMath for uint256; /** * @dev Destroys `amount` tokens from the caller. * * See {ERC20-_burn}. */ function burn(uint256 amount) public virtual { _burn(_msgSender(), amount); } /** * @dev Destroys `amount` tokens from `account`, deducting from the caller's * allowance. * * See {ERC20-_burn} and {ERC20-allowance}. * * Requirements: * * - the caller must have allowance for ``accounts``'s tokens of at least * `amount`. */ function burnFrom(address account, uint256 amount) public virtual { uint256 decreasedAllowance = allowance(account, _msgSender()).sub(amount, "ERC20: burn amount exceeds allowance"); _approve(account, _msgSender(), decreasedAllowance); _burn(account, amount); } } // File @openzeppelin/contracts/cryptography/[email protected] pragma solidity ^0.7.0; /** * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations. * * These functions can be used to verify that a message was signed by the holder * of the private keys of a given address. */ library ECDSA { /** * @dev Returns the address that signed a hashed message (`hash`) with * `signature`. This address can then be used for verification purposes. * * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures: * this function rejects them by requiring the `s` value to be in the lower * half order, and the `v` value to be either 27 or 28. * * IMPORTANT: `hash` _must_ be the result of a hash operation for the * verification to be secure: it is possible to craft signatures that * recover to arbitrary addresses for non-hashed data. A safe way to ensure * this is by receiving a hash of the original message (which may otherwise * be too long), and then calling {toEthSignedMessageHash} on it. */ function recover(bytes32 hash, bytes memory signature) internal pure returns (address) { // Check the signature length if (signature.length != 65) { revert("ECDSA: invalid signature length"); } // Divide the signature in r, s and v variables bytes32 r; bytes32 s; uint8 v; // 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))) } // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines // the valid range for s in (281): 0 < s < secp256k1n ÷ 2 + 1, and for v in (282): v ∈ {27, 28}. Most // signatures from current libraries generate a unique signature with an s-value in the lower half order. // // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept // these malleable signatures as well. require(uint256(s) <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0, "ECDSA: invalid signature 's' value"); require(v == 27 || v == 28, "ECDSA: invalid signature 'v' value"); // If the signature is valid (and not malleable), return the signer address address signer = ecrecover(hash, v, r, s); require(signer != address(0), "ECDSA: invalid signature"); return signer; } /** * @dev Returns an Ethereum Signed Message, created from a `hash`. This * replicates the behavior of the * https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sign[`eth_sign`] * JSON-RPC method. * * See {recover}. */ 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)); } } // File contracts/governance/Governed.sol pragma solidity ^0.7.3; /** * @title Graph Governance contract * @dev All contracts that will be owned by a Governor entity should extend this contract. */ contract Governed { // -- State -- address public governor; address public pendingGovernor; // -- Events -- event NewPendingOwnership(address indexed from, address indexed to); event NewOwnership(address indexed from, address indexed to); /** * @dev Check if the caller is the governor. */ modifier onlyGovernor { require(msg.sender == governor, "Only Governor can call"); _; } /** * @dev Initialize the governor to the contract caller. */ function _initialize(address _initGovernor) internal { governor = _initGovernor; } /** * @dev Admin function to begin change of governor. The `_newGovernor` must call * `acceptOwnership` to finalize the transfer. * @param _newGovernor Address of new `governor` */ function transferOwnership(address _newGovernor) external onlyGovernor { require(_newGovernor != address(0), "Governor must be set"); address oldPendingGovernor = pendingGovernor; pendingGovernor = _newGovernor; emit NewPendingOwnership(oldPendingGovernor, pendingGovernor); } /** * @dev Admin function for pending governor to accept role and update governor. * This function must called by the pending governor. */ function acceptOwnership() external { require( pendingGovernor != address(0) && msg.sender == pendingGovernor, "Caller must be pending governor" ); address oldGovernor = governor; address oldPendingGovernor = pendingGovernor; governor = pendingGovernor; pendingGovernor = address(0); emit NewOwnership(oldGovernor, governor); emit NewPendingOwnership(oldPendingGovernor, pendingGovernor); } } // File contracts/token/GraphToken.sol pragma solidity ^0.7.3; /** * @title GraphToken contract * @dev This is the implementation of the ERC20 Graph Token. * The implementation exposes a Permit() function to allow for a spender to send a signed message * and approve funds to a spender following EIP2612 to make integration with other contracts easier. * * The token is initially owned by the deployer address that can mint tokens to create the initial * distribution. For convenience, an initial supply can be passed in the constructor that will be * assigned to the deployer. * * The governor can add the RewardsManager contract to mint indexing rewards. * */ contract GraphToken is Governed, ERC20, ERC20Burnable { using SafeMath for uint256; // -- EIP712 -- // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-712.md#definition-of-domainseparator bytes32 private constant DOMAIN_TYPE_HASH = keccak256( "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract,bytes32 salt)" ); bytes32 private constant DOMAIN_NAME_HASH = keccak256("Graph Token"); bytes32 private constant DOMAIN_VERSION_HASH = keccak256("0"); bytes32 private constant DOMAIN_SALT = 0x51f3d585afe6dfeb2af01bba0889a36c1db03beec88c6a4d0c53817069026afa; // Randomly generated salt bytes32 private constant PERMIT_TYPEHASH = keccak256( "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)" ); // -- State -- bytes32 private DOMAIN_SEPARATOR; mapping(address => bool) private _minters; mapping(address => uint256) public nonces; // -- Events -- event MinterAdded(address indexed account); event MinterRemoved(address indexed account); modifier onlyMinter() { require(isMinter(msg.sender), "Only minter can call"); _; } /** * @dev Graph Token Contract Constructor. * @param _initialSupply Initial supply of GRT */ constructor(uint256 _initialSupply) ERC20("Graph Token", "GRT") { Governed._initialize(msg.sender); // The Governor has the initial supply of tokens _mint(msg.sender, _initialSupply); // The Governor is the default minter _addMinter(msg.sender); // EIP-712 domain separator DOMAIN_SEPARATOR = keccak256( abi.encode( DOMAIN_TYPE_HASH, DOMAIN_NAME_HASH, DOMAIN_VERSION_HASH, _getChainID(), address(this), DOMAIN_SALT ) ); } /** * @dev Approve token allowance by validating a message signed by the holder. * @param _owner Address of the token holder * @param _spender Address of the approved spender * @param _value Amount of tokens to approve the spender * @param _deadline Expiration time of the signed permit * @param _v Signature version * @param _r Signature r value * @param _s Signature s value */ function permit( address _owner, address _spender, uint256 _value, uint256 _deadline, uint8 _v, bytes32 _r, bytes32 _s ) external { bytes32 digest = keccak256( abi.encodePacked( "\x19\x01", DOMAIN_SEPARATOR, keccak256( abi.encode( PERMIT_TYPEHASH, _owner, _spender, _value, nonces[_owner], _deadline ) ) ) ); nonces[_owner] = nonces[_owner].add(1); address recoveredAddress = ECDSA.recover(digest, abi.encodePacked(_r, _s, _v)); require(_owner == recoveredAddress, "GRT: invalid permit"); require(_deadline == 0 || block.timestamp <= _deadline, "GRT: expired permit"); _approve(_owner, _spender, _value); } /** * @dev Add a new minter. * @param _account Address of the minter */ function addMinter(address _account) external onlyGovernor { _addMinter(_account); } /** * @dev Remove a minter. * @param _account Address of the minter */ function removeMinter(address _account) external onlyGovernor { _removeMinter(_account); } /** * @dev Renounce to be a minter. */ function renounceMinter() external { _removeMinter(msg.sender); } /** * @dev Mint new tokens. * @param _to Address to send the newly minted tokens * @param _amount Amount of tokens to mint */ function mint(address _to, uint256 _amount) external onlyMinter { _mint(_to, _amount); } /** * @dev Return if the `_account` is a minter or not. * @param _account Address to check * @return True if the `_account` is minter */ function isMinter(address _account) public view returns (bool) { return _minters[_account]; } /** * @dev Add a new minter. * @param _account Address of the minter */ function _addMinter(address _account) private { _minters[_account] = true; emit MinterAdded(_account); } /** * @dev Remove a minter. * @param _account Address of the minter */ function _removeMinter(address _account) private { _minters[_account] = false; emit MinterRemoved(_account); } /** * @dev Get the running network chain ID. * @return The chain ID */ function _getChainID() private pure returns (uint256) { uint256 id; assembly { id := chainid() } return id; } }
File 4 of 5: BaseWallet
// Copyright (C) 2018 Argent Labs Ltd. <https://argent.xyz> // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. pragma solidity ^0.5.4; /** * @title Module * @dev Interface for a module. * A module MUST implement the addModule() method to ensure that a wallet with at least one module * can never end up in a "frozen" state. * @author Julien Niset - <[email protected]> */ interface Module { /** * @dev Inits a module for a wallet by e.g. setting some wallet specific parameters in storage. * @param _wallet The wallet. */ function init(BaseWallet _wallet) external; /** * @dev Adds a module to a wallet. * @param _wallet The target wallet. * @param _module The modules to authorise. */ function addModule(BaseWallet _wallet, Module _module) external; /** * @dev Utility method to recover any ERC20 token that was sent to the * module by mistake. * @param _token The token to recover. */ function recoverToken(address _token) external; } /** * @title BaseWallet * @dev Simple modular wallet that authorises modules to call its invoke() method. * @author Julien Niset - <[email protected]> */ contract BaseWallet { // The implementation of the proxy address public implementation; // The owner address public owner; // The authorised modules mapping (address => bool) public authorised; // The enabled static calls mapping (bytes4 => address) public enabled; // The number of modules uint public modules; event AuthorisedModule(address indexed module, bool value); event EnabledStaticCall(address indexed module, bytes4 indexed method); event Invoked(address indexed module, address indexed target, uint indexed value, bytes data); event Received(uint indexed value, address indexed sender, bytes data); event OwnerChanged(address owner); /** * @dev Throws if the sender is not an authorised module. */ modifier moduleOnly { require(authorised[msg.sender], "BW: msg.sender not an authorized module"); _; } /** * @dev Inits the wallet by setting the owner and authorising a list of modules. * @param _owner The owner. * @param _modules The modules to authorise. */ function init(address _owner, address[] calldata _modules) external { require(owner == address(0) && modules == 0, "BW: wallet already initialised"); require(_modules.length > 0, "BW: construction requires at least 1 module"); owner = _owner; modules = _modules.length; for (uint256 i = 0; i < _modules.length; i++) { require(authorised[_modules[i]] == false, "BW: module is already added"); authorised[_modules[i]] = true; Module(_modules[i]).init(this); emit AuthorisedModule(_modules[i], true); } if (address(this).balance > 0) { emit Received(address(this).balance, address(0), ""); } } /** * @dev Enables/Disables a module. * @param _module The target module. * @param _value Set to true to authorise the module. */ function authoriseModule(address _module, bool _value) external moduleOnly { if (authorised[_module] != _value) { emit AuthorisedModule(_module, _value); if (_value == true) { modules += 1; authorised[_module] = true; Module(_module).init(this); } else { modules -= 1; require(modules > 0, "BW: wallet must have at least one module"); delete authorised[_module]; } } } /** * @dev Enables a static method by specifying the target module to which the call * must be delegated. * @param _module The target module. * @param _method The static method signature. */ function enableStaticCall(address _module, bytes4 _method) external moduleOnly { require(authorised[_module], "BW: must be an authorised module for static call"); enabled[_method] = _module; emit EnabledStaticCall(_module, _method); } /** * @dev Sets a new owner for the wallet. * @param _newOwner The new owner. */ function setOwner(address _newOwner) external moduleOnly { require(_newOwner != address(0), "BW: address cannot be null"); owner = _newOwner; emit OwnerChanged(_newOwner); } /** * @dev Performs a generic transaction. * @param _target The address for the transaction. * @param _value The value of the transaction. * @param _data The data of the transaction. */ function invoke(address _target, uint _value, bytes calldata _data) external moduleOnly returns (bytes memory _result) { bool success; // solium-disable-next-line security/no-call-value (success, _result) = _target.call.value(_value)(_data); if (!success) { // solium-disable-next-line security/no-inline-assembly assembly { returndatacopy(0, 0, returndatasize) revert(0, returndatasize) } } emit Invoked(msg.sender, _target, _value, _data); } /** * @dev This method makes it possible for the wallet to comply to interfaces expecting the wallet to * implement specific static methods. It delegates the static call to a target contract if the data corresponds * to an enabled method, or logs the call otherwise. */ function() external payable { if (msg.data.length > 0) { address module = enabled[msg.sig]; if (module == address(0)) { emit Received(msg.value, msg.sender, msg.data); } else { require(authorised[module], "BW: must be an authorised module for static call"); // solium-disable-next-line security/no-inline-assembly assembly { calldatacopy(0, 0, calldatasize()) let result := staticcall(gas, module, 0, calldatasize(), 0, 0) returndatacopy(0, 0, returndatasize()) switch result case 0 {revert(0, returndatasize())} default {return (0, returndatasize())} } } } } }
File 5 of 5: DappRegistry
// Copyright (C) 2021 Argent Labs Ltd. <https://argent.xyz> // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. // SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.3; import "./IAuthoriser.sol"; import "./dapp/IFilter.sol"; contract DappRegistry is IAuthoriser { // The timelock period uint64 public timelockPeriod; // The new timelock period uint64 public newTimelockPeriod; // Time at which the new timelock becomes effective uint64 public timelockPeriodChangeAfter; // bit vector of enabled registry ids for each wallet mapping (address => bytes32) public enabledRegistryIds; // [wallet] => [bit vector of 256 registry ids] // authorised dapps and their filters for each registry id mapping (uint8 => mapping (address => bytes32)) public authorisations; // [registryId] => [dapp] => [{filter:160}{validAfter:64}] // pending authorised dapps and their filters for each registry id mapping (uint8 => mapping (address => bytes32)) public pendingFilterUpdates; // [registryId] => [dapp] => [{filter:160}{validAfter:64}] // owners for each registry id mapping (uint8 => address) public registryOwners; // [registryId] => [owner] event RegistryCreated(uint8 registryId, address registryOwner); event OwnerChanged(uint8 registryId, address newRegistryOwner); event TimelockChangeRequested(uint64 newTimelockPeriod); event TimelockChanged(uint64 newTimelockPeriod); event FilterUpdated(uint8 indexed registryId, address dapp, address filter, uint256 validAfter); event FilterUpdateRequested(uint8 indexed registryId, address dapp, address filter, uint256 validAfter); event DappAdded(uint8 indexed registryId, address dapp, address filter, uint256 validAfter); event DappRemoved(uint8 indexed registryId, address dapp); event ToggledRegistry(address indexed sender, uint8 registryId, bool enabled); modifier onlyOwner(uint8 _registryId) { validateOwner(_registryId); _; } constructor(uint64 _timelockPeriod) { // set the timelock period timelockPeriod = _timelockPeriod; // set the owner of the Argent Registry (registryId = 0) registryOwners[0] = msg.sender; emit RegistryCreated(0, msg.sender); emit TimelockChanged(_timelockPeriod); } /********* Wallet-centered functions *************/ /** * @notice Returns whether a registry is enabled for a wallet * @param _wallet The wallet * @param _registryId The registry id */ function isEnabledRegistry(address _wallet, uint8 _registryId) external view returns (bool isEnabled) { uint registries = uint(enabledRegistryIds[_wallet]); return (((registries >> _registryId) & 1) > 0) /* "is bit set for regId?" */ == (_registryId > 0) /* "not Argent registry?" */; } /** * @notice Returns whether a (_spender, _to, _data) call is authorised for a wallet * @param _wallet The wallet * @param _spender The spender of the tokens for token approvals, or the target of the transaction otherwise * @param _to The target of the transaction * @param _data The calldata of the transaction */ function isAuthorised(address _wallet, address _spender, address _to, bytes calldata _data) public view override returns (bool) { uint registries = uint(enabledRegistryIds[_wallet]); // Check Argent Default Registry first. It is enabled by default, implying that a zero // at position 0 of the `registries` bit vector means that the Argent Registry is enabled) for(uint registryId = 0; registryId == 0 || (registries >> registryId) > 0; registryId++) { bool isEnabled = (((registries >> registryId) & 1) > 0) /* "is bit set for regId?" */ == (registryId > 0) /* "not Argent registry?" */; if(isEnabled) { // if registryId is enabled uint auth = uint(authorisations[uint8(registryId)][_spender]); uint validAfter = auth & 0xffffffffffffffff; if (0 < validAfter && validAfter <= block.timestamp) { // if the current time is greater than the validity time address filter = address(uint160(auth >> 64)); if(filter == address(0) || IFilter(filter).isValid(_wallet, _spender, _to, _data)) { return true; } } } } return false; } /** * @notice Returns whether a collection of (_spender, _to, _data) calls are authorised for a wallet * @param _wallet The wallet * @param _spenders The spenders of the tokens for token approvals, or the targets of the transaction otherwise * @param _to The targets of the transaction * @param _data The calldata of the transaction */ function areAuthorised( address _wallet, address[] calldata _spenders, address[] calldata _to, bytes[] calldata _data ) external view override returns (bool) { for(uint i = 0; i < _spenders.length; i++) { if(!isAuthorised(_wallet, _spenders[i], _to[i], _data[i])) { return false; } } return true; } /** * @notice Allows a wallet to decide whether _registryId should be part of the list of enabled registries for that wallet * @param _registryId The id of the registry to enable/disable * @param _enabled Whether the registry should be enabled (true) or disabled (false) */ function toggleRegistry(uint8 _registryId, bool _enabled) external { require(registryOwners[_registryId] != address(0), "DR: unknown registry"); uint registries = uint(enabledRegistryIds[msg.sender]); bool current = (((registries >> _registryId) & 1) > 0) /* "is bit set for regId?" */ == (_registryId > 0) /* "not Argent registry?" */; if(current != _enabled) { enabledRegistryIds[msg.sender] = bytes32(registries ^ (uint(1) << _registryId)); // toggle [_registryId]^th bit emit ToggledRegistry(msg.sender, _registryId, _enabled); } } /************** Management of registry list *****************/ /** * @notice Create a new registry. Only the owner of the Argent registry (i.e. the registry with id 0 -- hence the use of `onlyOwner(0)`) * can create a new registry. * @param _registryId The id of the registry to create * @param _registryOwner The owner of that new registry */ function createRegistry(uint8 _registryId, address _registryOwner) external onlyOwner(0) { require(_registryOwner != address(0), "DR: registry owner is 0"); require(registryOwners[_registryId] == address(0), "DR: duplicate registry"); registryOwners[_registryId] = _registryOwner; emit RegistryCreated(_registryId, _registryOwner); } // Note: removeRegistry is not supported because that would allow the owner to replace registries that // have already been enabled by users with a new (potentially maliciously populated) registry /** * @notice Lets a registry owner change the owner of the registry. * @param _registryId The id of the registry * @param _newRegistryOwner The new owner of the registry */ function changeOwner(uint8 _registryId, address _newRegistryOwner) external onlyOwner(_registryId) { require(_newRegistryOwner != address(0), "DR: new registry owner is 0"); registryOwners[_registryId] = _newRegistryOwner; emit OwnerChanged(_registryId, _newRegistryOwner); } /** * @notice Request a change of the timelock value. Only the owner of the Argent registry (i.e. the registry with id 0 -- * hence the use of `onlyOwner(0)`) can perform that action. This action can be confirmed after the (old) timelock period. * @param _newTimelockPeriod The new timelock period */ function requestTimelockChange(uint64 _newTimelockPeriod) external onlyOwner(0) { newTimelockPeriod = _newTimelockPeriod; timelockPeriodChangeAfter = uint64(block.timestamp) + timelockPeriod; emit TimelockChangeRequested(_newTimelockPeriod); } /** * @notice Confirm a change of the timelock value requested by `requestTimelockChange()`. */ function confirmTimelockChange() external { uint64 newPeriod = newTimelockPeriod; require(timelockPeriodChangeAfter > 0 && timelockPeriodChangeAfter <= block.timestamp, "DR: can't (yet) change timelock"); timelockPeriod = newPeriod; newTimelockPeriod = 0; timelockPeriodChangeAfter = 0; emit TimelockChanged(newPeriod); } /************** Management of registries' content *****************/ /** * @notice Returns the (filter, validAfter) tuple recorded for a dapp in a given registry. * `filter` is the authorisation filter stored for the dapp (if any) and `validAfter` is the * timestamp after which the filter becomes active. * @param _registryId The registry id * @param _dapp The dapp */ function getAuthorisation(uint8 _registryId, address _dapp) external view returns (address filter, uint64 validAfter) { uint auth = uint(authorisations[_registryId][_dapp]); filter = address(uint160(auth >> 64)); validAfter = uint64(auth & 0xffffffffffffffff); } /** * @notice Add a new dapp to the registry with an optional filter * @param _registryId The id of the registry to modify * @param _dapp The address of the dapp contract to authorise. * @param _filter The address of the filter contract to use, if any. */ function addDapp(uint8 _registryId, address _dapp, address _filter) external onlyOwner(_registryId) { require(authorisations[_registryId][_dapp] == bytes32(0), "DR: dapp already added"); uint validAfter = block.timestamp + timelockPeriod; // Store the new authorisation as {filter:160}{validAfter:64}. authorisations[_registryId][_dapp] = bytes32((uint(uint160(_filter)) << 64) | validAfter); emit DappAdded(_registryId, _dapp, _filter, validAfter); } /** * @notice Deauthorise a dapp in a registry * @param _registryId The id of the registry to modify * @param _dapp The address of the dapp contract to deauthorise. */ function removeDapp(uint8 _registryId, address _dapp) external onlyOwner(_registryId) { require(authorisations[_registryId][_dapp] != bytes32(0), "DR: unknown dapp"); delete authorisations[_registryId][_dapp]; delete pendingFilterUpdates[_registryId][_dapp]; emit DappRemoved(_registryId, _dapp); } /** * @notice Request to change an authorisation filter for a dapp that has previously been authorised. We cannot * immediately override the existing filter and need to store the new filter for a timelock period before being * able to change the filter. * @param _registryId The id of the registry to modify * @param _dapp The address of the dapp contract to authorise. * @param _filter The address of the new filter contract to use. */ function requestFilterUpdate(uint8 _registryId, address _dapp, address _filter) external onlyOwner(_registryId) { require(authorisations[_registryId][_dapp] != bytes32(0), "DR: unknown dapp"); uint validAfter = block.timestamp + timelockPeriod; // Store the future authorisation as {filter:160}{validAfter:64} pendingFilterUpdates[_registryId][_dapp] = bytes32((uint(uint160(_filter)) << 64) | validAfter); emit FilterUpdateRequested(_registryId, _dapp, _filter, validAfter); } /** * @notice Confirm the filter change requested by `requestFilterUpdate` * @param _registryId The id of the registry to modify * @param _dapp The address of the dapp contract to authorise. */ function confirmFilterUpdate(uint8 _registryId, address _dapp) external { uint newAuth = uint(pendingFilterUpdates[_registryId][_dapp]); require(newAuth > 0, "DR: no pending filter update"); uint validAfter = newAuth & 0xffffffffffffffff; require(validAfter <= block.timestamp, "DR: too early to confirm auth"); authorisations[_registryId][_dapp] = bytes32(newAuth); emit FilterUpdated(_registryId, _dapp, address(uint160(newAuth >> 64)), validAfter); delete pendingFilterUpdates[_registryId][_dapp]; } /******** Internal Functions ***********/ function validateOwner(uint8 _registryId) internal view { address owner = registryOwners[_registryId]; require(owner != address(0), "DR: unknown registry"); require(msg.sender == owner, "DR: sender != registry owner"); } }// Copyright (C) 2021 Argent Labs Ltd. <https://argent.xyz> // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. // SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.3; interface IAuthoriser { function isAuthorised(address _sender, address _spender, address _to, bytes calldata _data) external view returns (bool); function areAuthorised( address _spender, address[] calldata _spenders, address[] calldata _to, bytes[] calldata _data ) external view returns (bool); }// Copyright (C) 2021 Argent Labs Ltd. <https://argent.xyz> // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. // SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.3; interface IFilter { function isValid(address _wallet, address _spender, address _to, bytes calldata _data) external view returns (bool valid); }