Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
ContractCreator
Latest 1 internal transaction
Advanced mode:
Parent Transaction Hash | Block | From | To | |||
---|---|---|---|---|---|---|
17932851 | 417 days ago | Contract Creation | 0 ETH |
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Similar Match Source Code This contract matches the deployed Bytecode of the Source Code for Contract 0xdf7E7f11...11B4F5557 The constructor portion of the code might be different and could alter the actual behaviour of the contract
Contract Name:
SmartWallet
Compiler Version
v0.7.6+commit.7338295f
Optimization Enabled:
Yes with 100000 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: GPL-2.0-or-later // Copyright 2017 Loopring Technology Limited. pragma solidity ^0.7.0; pragma experimental ABIEncoderV2; import "../iface/ILoopringWalletV2.sol"; import "../lib/EIP712.sol"; import "../lib/ERC20.sol"; import "../lib/ERC1271.sol"; import "../lib/ReentrancyGuard.sol"; import "../thirdparty/erc165/IERC165.sol"; import "../thirdparty/erc1155/ERC1155Holder.sol"; import "../thirdparty/erc721/ERC721Holder.sol"; import "./libwallet/ERC20Lib.sol"; import "./libwallet/ERC1271Lib.sol"; import "./libwallet/WalletData.sol"; import "./libwallet/LockLib.sol"; import "./libwallet/GuardianLib.sol"; import "./libwallet/InheritanceLib.sol"; import "./libwallet/MetaTxLib.sol"; import "./libwallet/WhitelistLib.sol"; import "./libwallet/QuotaLib.sol"; import "./libwallet/RecoverLib.sol"; import "./libwallet/UpgradeLib.sol"; /// @title SmartWallet /// @dev Main smart wallet contract /// @author Brecht Devos - <[email protected]> contract SmartWallet is ILoopringWalletV2, ERC1271, IERC165, ERC721Holder, ERC1155Holder { using ERC20Lib for Wallet; using ERC1271Lib for Wallet; using LockLib for Wallet; using GuardianLib for Wallet; using InheritanceLib for Wallet; using MetaTxLib for Wallet; using WhitelistLib for Wallet; using QuotaLib for Wallet; using RecoverLib for Wallet; using UpgradeLib for Wallet; bytes32 public immutable DOMAIN_SEPARATOR; PriceOracle public immutable priceOracle; address public immutable blankOwner; // WARNING: Do not delete wallet state data to make this implementation // compatible with early versions. // // ----- DATA LAYOUT BEGINS ----- // Always needs to be first address internal masterCopy; bool internal isImplementationContract; Wallet public wallet; // ----- DATA LAYOUT ENDS ----- /// @dev We need to make sure the implemenation contract cannot be initialized /// and used to do delegate calls to arbitrary contracts. modifier disableInImplementationContract { require(!isImplementationContract, "DISALLOWED_ON_IMPLEMENTATION_CONTRACT"); _; } modifier onlyFromWalletOrOwnerWhenUnlocked() { // If the wallet's signature verfication passes, the wallet must be unlocked. require( msg.sender == address(this) || (msg.sender == wallet.owner && !wallet.locked), "NOT_FROM_WALLET_OR_OWNER_OR_WALLET_LOCKED" ); wallet.touchLastActiveWhenRequired(); _; } modifier canTransferOwnership() { require( msg.sender == blankOwner && wallet.owner == blankOwner, "NOT_ALLOWED_TO_SET_OWNER" ); _; } constructor( PriceOracle _priceOracle, address _blankOwner ) { isImplementationContract = true; DOMAIN_SEPARATOR = EIP712.hash( EIP712.Domain("LoopringWallet", "2.0.0", address(this)) ); priceOracle = _priceOracle; blankOwner = _blankOwner; } /// @dev Set up this wallet. /// /// Note that calling this method more than once will throw. /// /// @param owner The owner of this wallet, must not be address(0). /// @param guardians The guardians of this wallet. function initialize( address owner, address[] calldata guardians, uint quota, address inheritor, address feeRecipient, address feeToken, uint feeAmount ) external override disableInImplementationContract { require(wallet.owner == address(0), "INITIALIZED_ALREADY"); require(owner != address(0), "INVALID_OWNER"); wallet.owner = owner; wallet.creationTimestamp = uint64(block.timestamp); wallet.addGuardiansImmediately(guardians); if (quota != 0) { wallet.setQuota(quota, 0); } if (inheritor != address(0)) { wallet.setInheritor(inheritor, 365 days); } // Pay for the wallet creation using wallet funds if (feeRecipient != address(0) && feeAmount > 0) { ERC20Lib.transfer(feeToken, feeRecipient, feeAmount); } } receive() external payable { } function getOwner() public view override returns (address) { return wallet.owner; } function getCreationTimestamp() public view override returns (uint64) { return wallet.creationTimestamp; } // // Owner // function transferOwnership( address _owner ) external canTransferOwnership { require(_owner != address(0), "INVALID_OWNER"); wallet.owner = _owner; } // // ERC1271 // function isValidSignature( bytes32 signHash, bytes memory signature ) public view override returns (bytes4 magicValue) { return wallet.isValidSignature( ERC1271_MAGICVALUE, signHash, signature ); } // // Upgrade // function changeMasterCopy( Approval calldata approval, address newMasterCopy ) external returns (bytes32 approvedHash) { approvedHash = wallet.changeMasterCopy( DOMAIN_SEPARATOR, approval, newMasterCopy ); masterCopy = newMasterCopy; } function getMasterCopy() public view returns (address) { return masterCopy; } // // Guardians // function addGuardian( address guardian ) external onlyFromWalletOrOwnerWhenUnlocked { wallet.addGuardian(guardian); } function addGuardianWA( Approval calldata approval, address guardian ) external returns (bytes32 approvedHash) { approvedHash = wallet.addGuardianWA(DOMAIN_SEPARATOR, approval, guardian); } function removeGuardian( address guardian ) external onlyFromWalletOrOwnerWhenUnlocked { wallet.removeGuardian(guardian); } function removeGuardianWA( Approval calldata approval, address guardian ) external returns (bytes32 approvedHash) { approvedHash = wallet.removeGuardianWA(DOMAIN_SEPARATOR, approval, guardian); } function resetGuardians( address[] calldata newGuardians ) external onlyFromWalletOrOwnerWhenUnlocked { wallet.resetGuardians(newGuardians); } function resetGuardiansWA( Approval calldata approval, address[] calldata newGuardians ) external returns (bytes32 approvedHash) { approvedHash = wallet.resetGuardiansWA(DOMAIN_SEPARATOR, approval, newGuardians); } function isGuardian(address addr, bool includePendingAddition) public view returns (bool) { return wallet.isGuardian(addr, includePendingAddition); } function getGuardians(bool includePendingAddition) public view returns (Guardian[] memory ) { return GuardianLib.guardians(wallet, includePendingAddition); } // // Inheritance // function setInheritor( address inheritor, uint32 waitingPeriod ) external onlyFromWalletOrOwnerWhenUnlocked { wallet.setInheritor(inheritor, waitingPeriod); } function inherit( address newOwner, bool removeGuardians ) external { wallet.inherit(newOwner, removeGuardians); } // // Lock // function lock() external { wallet.lock(); } function unlock( Approval calldata approval ) external returns (bytes32 approvedHash) { approvedHash = wallet.unlock(DOMAIN_SEPARATOR, approval); } // // Quota // function changeDailyQuota( uint newQuota ) external onlyFromWalletOrOwnerWhenUnlocked { wallet.changeDailyQuota(newQuota); } function changeDailyQuotaWA( Approval calldata approval, uint newQuota ) external returns (bytes32 approvedHash) { approvedHash = wallet.changeDailyQuotaWA(DOMAIN_SEPARATOR, approval, newQuota); } // // MetaTx // function executeMetaTx( address to, uint nonce, address gasToken, uint gasPrice, uint gasLimit, uint gasOverhead, address feeRecipient, bool requiresSuccess, bytes calldata data, bytes memory signature ) external returns (bool) { MetaTxLib.MetaTx memory metaTx = MetaTxLib.MetaTx( to, nonce, gasToken, gasPrice, gasLimit, gasOverhead, feeRecipient, requiresSuccess, data, signature ); return wallet.executeMetaTx( DOMAIN_SEPARATOR, priceOracle, metaTx ); } function selfBatchCall( bytes[] calldata data ) external onlyFromWalletOrOwnerWhenUnlocked { wallet.selfBatchCall(data); } // // Recover // function recover( Approval calldata approval, address newOwner, address[] calldata newGuardians ) external returns (bytes32 approvedHash) { approvedHash = wallet.recover( DOMAIN_SEPARATOR, approval, newOwner, newGuardians ); } // // Whitelist // function addToWhitelist( address addr ) external onlyFromWalletOrOwnerWhenUnlocked { wallet.addToWhitelist(addr); } function addToWhitelistWA( Approval calldata approval, address addr ) external returns (bytes32 approvedHash) { approvedHash = wallet.addToWhitelistWA( DOMAIN_SEPARATOR, approval, addr ); } function removeFromWhitelist( address addr ) external onlyFromWalletOrOwnerWhenUnlocked { wallet.removeFromWhitelist(addr); } function getWhitelistEffectiveTime( address addr ) public view returns (uint) { return wallet.whitelisted[addr]; } function isWhitelisted( address addr ) public view returns (bool) { return wallet.isAddressWhitelisted(addr); } // // ERC20 // function transferToken( address token, address to, uint amount, bytes calldata logdata, bool forceUseQuota ) external onlyFromWalletOrOwnerWhenUnlocked { wallet.transferToken( priceOracle, token, to, amount, logdata, forceUseQuota ); } function transferTokenWA( Approval calldata approval, address token, address to, uint amount, bytes calldata logdata ) external returns (bytes32 approvedHash) { approvedHash = wallet.transferTokenWA( DOMAIN_SEPARATOR, approval, token, to, amount, logdata ); } function callContract( address to, uint value, bytes calldata data, bool forceUseQuota ) external onlyFromWalletOrOwnerWhenUnlocked returns (bytes memory) { return wallet.callContract( priceOracle, to, value, data, forceUseQuota ); } function callContractWA( Approval calldata approval, address to, uint value, bytes calldata data ) external returns (bytes32 approvedHash, bytes memory returnData) { (approvedHash, returnData) = wallet.callContractWA( DOMAIN_SEPARATOR, approval, to, value, data ); } function approveToken( address token, address to, uint amount, bool forceUseQuota ) external onlyFromWalletOrOwnerWhenUnlocked { wallet.approveToken( priceOracle, token, to, amount, forceUseQuota ); } function approveTokenWA( Approval calldata approval, address token, address to, uint amount ) external returns (bytes32 approvedHash) { approvedHash = wallet.approveTokenWA( DOMAIN_SEPARATOR, approval, token, to, amount ); } function approveThenCallContract( address token, address to, uint amount, uint value, bytes calldata data, bool forceUseQuota ) external onlyFromWalletOrOwnerWhenUnlocked returns (bytes memory) { return wallet.approveThenCallContract( priceOracle, token, to, amount, value, data, forceUseQuota ); } function approveThenCallContractWA( Approval calldata approval, address token, address to, uint amount, uint value, bytes calldata data ) external returns (bytes32 approvedHash, bytes memory returnData) { (approvedHash, returnData) = wallet.approveThenCallContractWA( DOMAIN_SEPARATOR, approval, token, to, amount, value, data ); } // ERC165 function supportsInterface( bytes4 interfaceId ) external pure override returns (bool) { return interfaceId == type(ERC1271).interfaceId || interfaceId == type(IERC165).interfaceId || interfaceId == type(IERC721Receiver).interfaceId || interfaceId == type(IERC1155Receiver).interfaceId; } }
// SPDX-License-Identifier: GPL-2.0-or-later // Copyright 2017 Loopring Technology Limited. pragma solidity ^0.7.0; pragma experimental ABIEncoderV2; import "../../lib/EIP712.sol"; import "../../lib/SignatureUtil.sol"; import "./GuardianLib.sol"; import "./WalletData.sol"; /// @title ApprovalLib /// @dev Utility library for better handling of signed wallet requests. /// This library must be deployed and linked to other modules. /// /// @author Daniel Wang - <[email protected]> library ApprovalLib { using SignatureUtil for bytes32; function verifyApproval( Wallet storage wallet, bytes32 domainSeparator, SigRequirement sigRequirement, Approval memory approval, bytes memory encodedRequest ) internal returns (bytes32 approvedHash) { require(address(this) == approval.wallet, "INVALID_WALLET"); require(block.timestamp <= approval.validUntil, "EXPIRED_SIGNED_REQUEST"); approvedHash = EIP712.hashPacked(domainSeparator, keccak256(encodedRequest)); // Save hash to prevent replay attacks require(!wallet.hashes[approvedHash], "HASH_EXIST"); wallet.hashes[approvedHash] = true; require( approvedHash.verifySignatures(approval.signers, approval.signatures), "INVALID_SIGNATURES" ); require( GuardianLib.requireMajority( wallet, approval.signers, sigRequirement ), "PERMISSION_DENIED" ); } }
// SPDX-License-Identifier: GPL-2.0-or-later // Copyright 2017 Loopring Technology Limited. pragma solidity ^0.7.0; pragma experimental ABIEncoderV2; import "../../lib/SignatureUtil.sol"; import "./WalletData.sol"; /// @title ERC1271Lib /// @author Brecht Devos - <[email protected]> library ERC1271Lib { using SignatureUtil for bytes32; // Note that we allow chained wallet ownership: // Wallet1 owned by Wallet2, Wallet2 owned by Wallet3, ..., WaleltN owned by an EOA. // The verificaiton of Wallet1's signature will succeed if the final EOA's signature is // valid. function isValidSignature( Wallet storage wallet, bytes4 ERC1271_MAGICVALUE, bytes32 signHash, bytes memory signature ) public view returns (bytes4 magicValue) { if (wallet.locked) { return 0; } if (signHash.verifySignature(wallet.owner, signature)) { return ERC1271_MAGICVALUE; } else { return 0; } } }
// SPDX-License-Identifier: GPL-2.0-or-later // Copyright 2017 Loopring Technology Limited. pragma solidity ^0.7.0; pragma experimental ABIEncoderV2; import "../../thirdparty/SafeERC20.sol"; import "../../lib/ERC20.sol"; import "../../lib/MathUint.sol"; import "../../lib/AddressUtil.sol"; import "../../iface/PriceOracle.sol"; import "../../thirdparty/BytesUtil.sol"; import "./WhitelistLib.sol"; import "./QuotaLib.sol"; import "./ApprovalLib.sol"; /// @title ERC20Lib /// @author Brecht Devos - <[email protected]> /// @author Daniel Wang - <[email protected]> library ERC20Lib { using AddressUtil for address; using BytesUtil for bytes; using MathUint for uint; using WhitelistLib for Wallet; using QuotaLib for Wallet; using ApprovalLib for Wallet; using SafeERC20 for ERC20; event Transfered (address token, address to, uint amount, bytes logdata); event Approved (address token, address spender, uint amount); event ContractCalled (address to, uint value, bytes data); bytes32 public constant TRANSFER_TOKEN_TYPEHASH = keccak256( "transferToken(address wallet,uint256 validUntil,address token,address to,uint256 amount,bytes logdata)" ); bytes32 public constant APPROVE_TOKEN_TYPEHASH = keccak256( "approveToken(address wallet,uint256 validUntil,address token,address to,uint256 amount)" ); bytes32 public constant CALL_CONTRACT_TYPEHASH = keccak256( "callContract(address wallet,uint256 validUntil,address to,uint256 value,bytes data)" ); bytes32 public constant APPROVE_THEN_CALL_CONTRACT_TYPEHASH = keccak256( "approveThenCallContract(address wallet,uint256 validUntil,address token,address to,uint256 amount,uint256 value,bytes data)" ); function transferToken( Wallet storage wallet, PriceOracle priceOracle, address token, address to, uint amount, bytes calldata logdata, bool forceUseQuota ) external { if (forceUseQuota || !wallet.isAddressWhitelisted(to)) { wallet.checkAndAddToSpent(priceOracle, token, amount); } _transferWithEvent(token, to, amount, logdata); } function transferTokenWA( Wallet storage wallet, bytes32 domainSeparator, Approval calldata approval, address token, address to, uint amount, bytes calldata logdata ) external returns (bytes32 approvedHash) { approvedHash = wallet.verifyApproval( domainSeparator, SigRequirement.MAJORITY_OWNER_REQUIRED, approval, abi.encode( TRANSFER_TOKEN_TYPEHASH, approval.wallet, approval.validUntil, token, to, amount, keccak256(logdata) ) ); _transferWithEvent(token, to, amount, logdata); } function callContract( Wallet storage wallet, PriceOracle priceOracle, address to, uint value, bytes calldata data, bool forceUseQuota ) external returns (bytes memory returnData) { if (forceUseQuota || !wallet.isAddressWhitelisted(to)) { wallet.checkAndAddToSpent(priceOracle, address(0), value); } return _callContractInternal(to, value, data, priceOracle); } function callContractWA( Wallet storage wallet, bytes32 domainSeparator, Approval calldata approval, address to, uint value, bytes calldata data ) external returns (bytes32 approvedHash, bytes memory returnData) { approvedHash = wallet.verifyApproval( domainSeparator, SigRequirement.MAJORITY_OWNER_REQUIRED, approval, abi.encode( CALL_CONTRACT_TYPEHASH, approval.wallet, approval.validUntil, to, value, keccak256(data) ) ); returnData = _callContractInternal(to, value, data, PriceOracle(0)); } function approveToken( Wallet storage wallet, PriceOracle priceOracle, address token, address to, uint amount, bool forceUseQuota ) external { uint additionalAllowance = _approveInternal(token, to, amount); if (forceUseQuota || !wallet.isAddressWhitelisted(to)) { wallet.checkAndAddToSpent(priceOracle, token, additionalAllowance); } } function approveTokenWA( Wallet storage wallet, bytes32 domainSeparator, Approval calldata approval, address token, address to, uint amount ) external returns (bytes32 approvedHash) { approvedHash = wallet.verifyApproval( domainSeparator, SigRequirement.MAJORITY_OWNER_REQUIRED, approval, abi.encode( APPROVE_TOKEN_TYPEHASH, approval.wallet, approval.validUntil, token, to, amount ) ); _approveInternal(token, to, amount); } function approveThenCallContract( Wallet storage wallet, PriceOracle priceOracle, address token, address to, uint amount, uint value, bytes calldata data, bool forceUseQuota ) external returns (bytes memory returnData) { uint additionalAllowance = _approveInternal(token, to, amount); if (forceUseQuota || !wallet.isAddressWhitelisted(to)) { wallet.checkAndAddToSpent(priceOracle, token, additionalAllowance); wallet.checkAndAddToSpent(priceOracle, address(0), value); } return _callContractInternal(to, value, data, priceOracle); } function approveThenCallContractWA( Wallet storage wallet, bytes32 domainSeparator, Approval calldata approval, address token, address to, uint amount, uint value, bytes calldata data ) external returns (bytes32 approvedHash, bytes memory returnData) { approvedHash = wallet.verifyApproval( domainSeparator, SigRequirement.MAJORITY_OWNER_REQUIRED, approval, abi.encode( APPROVE_THEN_CALL_CONTRACT_TYPEHASH, approval.wallet, approval.validUntil, token, to, amount, value, keccak256(data) ) ); _approveInternal(token, to, amount); returnData = _callContractInternal(to, value, data, PriceOracle(0)); } function transfer( address token, address to, uint amount ) public { if (token == address(0)) { to.sendETHAndVerify(amount, gasleft()); } else { ERC20(token).safeTransfer(to, amount); } } // --- Internal functions --- function _transferWithEvent( address token, address to, uint amount, bytes calldata logdata ) private { transfer(token, to, amount); emit Transfered(token, to, amount, logdata); } function _approveInternal( address token, address spender, uint amount ) private returns (uint additionalAllowance) { // Current allowance uint allowance = ERC20(token).allowance(address(this), spender); if (amount != allowance) { // First reset the approved amount if needed if (allowance > 0) { ERC20(token).safeApprove(spender, 0); } // Now approve the requested amount ERC20(token).safeApprove(spender, amount); } // If we increased the allowance, calculate by how much if (amount > allowance) { additionalAllowance = amount.sub(allowance); } emit Approved(token, spender, amount); } function _callContractInternal( address to, uint value, bytes calldata txData, PriceOracle priceOracle ) private returns (bytes memory returnData) { require(to != address(this), "SELF_CALL_DISALLOWED"); if (priceOracle != PriceOracle(0)) { if (txData.length >= 4) { bytes4 methodId = txData.toBytes4(0); // bytes4(keccak256("transfer(address,uint256)")) = 0xa9059cbb // bytes4(keccak256("approve(address,uint256)")) = 0x095ea7b3 if (methodId == bytes4(0xa9059cbb) || methodId == bytes4(0x095ea7b3)) { // Disallow general calls to token contracts (for tokens that have price data // so the quota is actually used). require(priceOracle.tokenValue(to, 1e18) == 0, "CALL_DISALLOWED"); } } } bool success; (success, returnData) = to.call{value: value}(txData); require(success, "CALL_FAILED"); emit ContractCalled(to, value, txData); } }
// SPDX-License-Identifier: GPL-2.0-or-later // Copyright 2017 Loopring Technology Limited. pragma solidity ^0.7.0; pragma experimental ABIEncoderV2; import "./WalletData.sol"; import "./ApprovalLib.sol"; import "../../lib/SignatureUtil.sol"; import "../../thirdparty/SafeCast.sol"; /// @title GuardianModule /// @author Brecht Devos - <[email protected]> /// @author Daniel Wang - <[email protected]> library GuardianLib { using AddressUtil for address; using SafeCast for uint; using SignatureUtil for bytes32; using ApprovalLib for Wallet; uint public constant MAX_GUARDIANS = 10; uint public constant GUARDIAN_PENDING_PERIOD = 3 days; bytes32 public constant ADD_GUARDIAN_TYPEHASH = keccak256( "addGuardian(address wallet,uint256 validUntil,address guardian)" ); bytes32 public constant REMOVE_GUARDIAN_TYPEHASH = keccak256( "removeGuardian(address wallet,uint256 validUntil,address guardian)" ); bytes32 public constant RESET_GUARDIANS_TYPEHASH = keccak256( "resetGuardians(address wallet,uint256 validUntil,address[] guardians)" ); event GuardianAdded (address guardian, uint effectiveTime); event GuardianRemoved (address guardian, uint effectiveTime); function addGuardiansImmediately( Wallet storage wallet, address[] memory _guardians ) external { address guardian = address(0); for (uint i = 0; i < _guardians.length; i++) { require(_guardians[i] > guardian, "INVALID_ORDERING"); guardian = _guardians[i]; _addGuardian(wallet, guardian, 0, true); } } function addGuardian( Wallet storage wallet, address guardian ) external { _addGuardian(wallet, guardian, GUARDIAN_PENDING_PERIOD, false); } function addGuardianWA( Wallet storage wallet, bytes32 domainSeparator, Approval calldata approval, address guardian ) external returns (bytes32 approvedHash) { approvedHash = wallet.verifyApproval( domainSeparator, SigRequirement.MAJORITY_OWNER_REQUIRED, approval, abi.encode( ADD_GUARDIAN_TYPEHASH, approval.wallet, approval.validUntil, guardian ) ); _addGuardian(wallet, guardian, 0, true); } function removeGuardian( Wallet storage wallet, address guardian ) external { _removeGuardian(wallet, guardian, GUARDIAN_PENDING_PERIOD, false); } function removeGuardianWA( Wallet storage wallet, bytes32 domainSeparator, Approval calldata approval, address guardian ) external returns (bytes32 approvedHash) { approvedHash = wallet.verifyApproval( domainSeparator, SigRequirement.MAJORITY_OWNER_REQUIRED, approval, abi.encode( REMOVE_GUARDIAN_TYPEHASH, approval.wallet, approval.validUntil, guardian ) ); _removeGuardian(wallet, guardian, 0, true); } function resetGuardians( Wallet storage wallet, address[] calldata newGuardians ) external { Guardian[] memory allGuardians = guardians(wallet, true); for (uint i = 0; i < allGuardians.length; i++) { _removeGuardian(wallet, allGuardians[i].addr, GUARDIAN_PENDING_PERIOD, false); } for (uint j = 0; j < newGuardians.length; j++) { _addGuardian(wallet, newGuardians[j], GUARDIAN_PENDING_PERIOD, false); } } function resetGuardiansWA( Wallet storage wallet, bytes32 domainSeparator, Approval calldata approval, address[] calldata newGuardians ) external returns (bytes32 approvedHash) { approvedHash = wallet.verifyApproval( domainSeparator, SigRequirement.MAJORITY_OWNER_REQUIRED, approval, abi.encode( RESET_GUARDIANS_TYPEHASH, approval.wallet, approval.validUntil, keccak256(abi.encodePacked(newGuardians)) ) ); removeAllGuardians(wallet); for (uint i = 0; i < newGuardians.length; i++) { _addGuardian(wallet, newGuardians[i], 0, true); } } function requireMajority( Wallet storage wallet, address[] memory signers, SigRequirement requirement ) internal view returns (bool) { // We always need at least one signer if (signers.length == 0) { return false; } // Calculate total group sizes Guardian[] memory allGuardians = guardians(wallet, false); require(allGuardians.length > 0, "NO_GUARDIANS"); address lastSigner; bool walletOwnerSigned = false; address owner = wallet.owner; for (uint i = 0; i < signers.length; i++) { // Check for duplicates require(signers[i] > lastSigner, "INVALID_SIGNERS_ORDER"); lastSigner = signers[i]; if (signers[i] == owner) { walletOwnerSigned = true; } else { bool _isGuardian = false; for (uint j = 0; j < allGuardians.length; j++) { if (allGuardians[j].addr == signers[i]) { _isGuardian = true; break; } } require(_isGuardian, "SIGNER_NOT_GUARDIAN"); } } if (requirement == SigRequirement.OWNER_OR_ANY_GUARDIAN) { return signers.length == 1; } else if (requirement == SigRequirement.ANY_GUARDIAN) { require(!walletOwnerSigned, "WALLET_OWNER_SIGNATURE_NOT_ALLOWED"); return signers.length == 1; } // Check owner requirements if (requirement == SigRequirement.MAJORITY_OWNER_REQUIRED) { require(walletOwnerSigned, "WALLET_OWNER_SIGNATURE_REQUIRED"); } else if (requirement == SigRequirement.MAJORITY_OWNER_NOT_ALLOWED) { require(!walletOwnerSigned, "WALLET_OWNER_SIGNATURE_NOT_ALLOWED"); } uint numExtendedSigners = allGuardians.length; if (walletOwnerSigned) { numExtendedSigners += 1; require(signers.length > 1, "NO_GUARDIAN_SIGNED_BESIDES_OWNER"); } return signers.length >= (numExtendedSigners >> 1) + 1; } function isGuardian( Wallet storage wallet, address addr, bool includePendingAddition ) public view returns (bool) { Guardian memory g = _getGuardian(wallet, addr); return _isActiveOrPendingAddition(g, includePendingAddition); } function guardians( Wallet storage wallet, bool includePendingAddition ) public view returns (Guardian[] memory _guardians) { _guardians = new Guardian[](wallet.guardians.length); uint index = 0; for (uint i = 0; i < wallet.guardians.length; i++) { Guardian memory g = wallet.guardians[i]; if (_isActiveOrPendingAddition(g, includePendingAddition)) { _guardians[index] = g; index++; } } assembly { mstore(_guardians, index) } } function numGuardians( Wallet storage wallet, bool includePendingAddition ) public view returns (uint count) { for (uint i = 0; i < wallet.guardians.length; i++) { Guardian memory g = wallet.guardians[i]; if (_isActiveOrPendingAddition(g, includePendingAddition)) { count++; } } } function removeAllGuardians( Wallet storage wallet ) internal { uint size = wallet.guardians.length; if (size == 0) return; for (uint i = 0; i < wallet.guardians.length; i++) { delete wallet.guardianIdx[wallet.guardians[i].addr]; } delete wallet.guardians; } function cancelPendingGuardians(Wallet storage wallet) internal { bool cancelled = false; for (uint i = 0; i < wallet.guardians.length; i++) { Guardian memory g = wallet.guardians[i]; if (_isPendingAddition(g)) { wallet.guardians[i].status = uint8(GuardianStatus.REMOVE); wallet.guardians[i].timestamp = 0; cancelled = true; } if (_isPendingRemoval(g)) { wallet.guardians[i].status = uint8(GuardianStatus.ADD); wallet.guardians[i].timestamp = 0; cancelled = true; } } _cleanRemovedGuardians(wallet, true); } function storeGuardian( Wallet storage wallet, address addr, uint validSince, bool alwaysOverride ) internal returns (uint) { require(validSince >= block.timestamp, "INVALID_VALID_SINCE"); require(addr != address(0), "ZERO_ADDRESS"); require(addr != address(this), "INVALID_ADDRESS"); uint pos = wallet.guardianIdx[addr]; if (pos == 0) { // Add the new guardian Guardian memory _g = Guardian( addr, uint8(GuardianStatus.ADD), validSince.toUint64() ); wallet.guardians.push(_g); wallet.guardianIdx[addr] = wallet.guardians.length; _cleanRemovedGuardians(wallet, false); return validSince; } Guardian memory g = wallet.guardians[pos - 1]; if (_isRemoved(g)) { wallet.guardians[pos - 1].status = uint8(GuardianStatus.ADD); wallet.guardians[pos - 1].timestamp = validSince.toUint64(); return validSince; } if (_isPendingRemoval(g)) { wallet.guardians[pos - 1].status = uint8(GuardianStatus.ADD); wallet.guardians[pos - 1].timestamp = 0; return 0; } if (_isPendingAddition(g)) { if (!alwaysOverride) return g.timestamp; wallet.guardians[pos - 1].timestamp = validSince.toUint64(); return validSince; } require(_isAdded(g), "UNEXPECTED_RESULT"); return 0; } function deleteGuardian( Wallet storage wallet, address addr, uint validUntil, bool alwaysOverride ) internal returns (uint) { require(validUntil >= block.timestamp, "INVALID_VALID_UNTIL"); require(addr != address(0), "ZERO_ADDRESS"); uint pos = wallet.guardianIdx[addr]; require(pos > 0, "GUARDIAN_NOT_EXISTS"); Guardian memory g = wallet.guardians[pos - 1]; if (_isAdded(g)) { wallet.guardians[pos - 1].status = uint8(GuardianStatus.REMOVE); wallet.guardians[pos - 1].timestamp = validUntil.toUint64(); return validUntil; } if (_isPendingAddition(g)) { wallet.guardians[pos - 1].status = uint8(GuardianStatus.REMOVE); wallet.guardians[pos - 1].timestamp = 0; return 0; } if (_isPendingRemoval(g)) { if (!alwaysOverride) return g.timestamp; wallet.guardians[pos - 1].timestamp = validUntil.toUint64(); return validUntil; } require(_isRemoved(g), "UNEXPECTED_RESULT"); return 0; } // --- Internal functions --- function _addGuardian( Wallet storage wallet, address guardian, uint pendingPeriod, bool alwaysOverride ) internal { uint _numGuardians = numGuardians(wallet, true); require(_numGuardians < MAX_GUARDIANS, "TOO_MANY_GUARDIANS"); require(guardian != wallet.owner, "GUARDIAN_CAN_NOT_BE_OWNER"); uint validSince = block.timestamp + 1; if (_numGuardians >= 2) { validSince = block.timestamp + pendingPeriod; } validSince = storeGuardian(wallet, guardian, validSince, alwaysOverride); emit GuardianAdded(guardian, validSince); } function _removeGuardian( Wallet storage wallet, address guardian, uint pendingPeriod, bool alwaysOverride ) private { uint validUntil = block.timestamp + pendingPeriod; validUntil = deleteGuardian(wallet, guardian, validUntil, alwaysOverride); emit GuardianRemoved(guardian, validUntil); } function _getGuardian( Wallet storage wallet, address addr ) private view returns (Guardian memory guardian) { uint pos = wallet.guardianIdx[addr]; if (pos > 0) { guardian = wallet.guardians[pos - 1]; } } function _isAdded(Guardian memory guardian) private view returns (bool) { return guardian.status == uint8(GuardianStatus.ADD) && guardian.timestamp <= block.timestamp; } function _isPendingAddition(Guardian memory guardian) private view returns (bool) { return guardian.status == uint8(GuardianStatus.ADD) && guardian.timestamp > block.timestamp; } function _isRemoved(Guardian memory guardian) private view returns (bool) { return guardian.status == uint8(GuardianStatus.REMOVE) && guardian.timestamp <= block.timestamp; } function _isPendingRemoval(Guardian memory guardian) private view returns (bool) { return guardian.status == uint8(GuardianStatus.REMOVE) && guardian.timestamp > block.timestamp; } function _isActive(Guardian memory guardian) private view returns (bool) { return _isAdded(guardian) || _isPendingRemoval(guardian); } function _isActiveOrPendingAddition( Guardian memory guardian, bool includePendingAddition ) private view returns (bool) { return _isActive(guardian) || includePendingAddition && _isPendingAddition(guardian); } function _cleanRemovedGuardians( Wallet storage wallet, bool force ) private { uint count = wallet.guardians.length; if (!force && count < 10) return; for (int i = int(count) - 1; i >= 0; i--) { Guardian memory g = wallet.guardians[uint(i)]; if (_isRemoved(g)) { Guardian memory lastGuardian = wallet.guardians[wallet.guardians.length - 1]; if (g.addr != lastGuardian.addr) { wallet.guardians[uint(i)] = lastGuardian; wallet.guardianIdx[lastGuardian.addr] = uint(i) + 1; } wallet.guardians.pop(); delete wallet.guardianIdx[g.addr]; } } } }
// SPDX-License-Identifier: GPL-2.0-or-later // Copyright 2017 Loopring Technology Limited. pragma solidity ^0.7.0; pragma experimental ABIEncoderV2; import "./WalletData.sol"; import "./GuardianLib.sol"; import "./LockLib.sol"; import "./Utils.sol"; /// @title InheritanceLib /// @author Brecht Devos - <[email protected]> library InheritanceLib { using GuardianLib for Wallet; using InheritanceLib for Wallet; using LockLib for Wallet; using Utils for address; // The minimal number of guardians for recovery and locking. uint public constant TOUCH_GRACE_PERIOD = 30 days; event Inherited( address inheritor, address newOwner ); event InheritorChanged( address inheritor, uint32 waitingPeriod ); function touchLastActiveWhenRequired(Wallet storage wallet) internal { if (wallet.inheritor != address(0) && block.timestamp > wallet.lastActive + TOUCH_GRACE_PERIOD) { wallet.lastActive = uint64(block.timestamp); } } function setInheritor( Wallet storage wallet, address inheritor, uint32 waitingPeriod ) internal { if (inheritor == address(0)) { require(waitingPeriod == 0, "INVALID_WAITING_PERIOD"); } else { require(waitingPeriod >= TOUCH_GRACE_PERIOD, "INVALID_WAITING_PERIOD"); } require(inheritor != address(this), "INVALID_ARGS"); wallet.inheritor = inheritor; wallet.inheritWaitingPeriod = waitingPeriod; wallet.lastActive = uint64(block.timestamp); } function inherit( Wallet storage wallet, address newOwner, bool removeGuardians ) external { require(wallet.inheritor == msg.sender, "UNAUTHORIZED"); require(wallet.owner != newOwner, "IS_WALLET_OWNER"); require(newOwner.isValidWalletOwner(), "INVALID_NEW_WALLET_OWNER"); require(uint(wallet.lastActive) + uint(wallet.inheritWaitingPeriod) < block.timestamp, "TOO_EARLY"); if (removeGuardians) { wallet.removeAllGuardians(); } wallet.setInheritor(address(0), 0); wallet.setLock(address(this), false); wallet.owner = newOwner; emit Inherited(wallet.inheritor, newOwner); } }
// SPDX-License-Identifier: GPL-2.0-or-later // Copyright 2017 Loopring Technology Limited. pragma solidity ^0.7.0; pragma experimental ABIEncoderV2; import "./ApprovalLib.sol"; import "./WalletData.sol"; import "./GuardianLib.sol"; /// @title LockLib /// @author Brecht Devos - <[email protected]> library LockLib { using GuardianLib for Wallet; using ApprovalLib for Wallet; event WalletLocked ( address by, bool locked ); bytes32 public constant LOCK_TYPEHASH = keccak256( "lock(address wallet,uint256 validUntil)" ); bytes32 public constant UNLOCK_TYPEHASH = keccak256( "unlock(address wallet,uint256 validUntil)" ); function lock(Wallet storage wallet) public { require( msg.sender == address(this) || msg.sender == wallet.owner || wallet.isGuardian(msg.sender, false), "NOT_FROM_WALLET_OR_OWNER_OR_GUARDIAN" ); setLock(wallet, msg.sender, true); } function unlock( Wallet storage wallet, bytes32 domainSeparator, Approval calldata approval ) public returns (bytes32 approvedHash) { approvedHash = wallet.verifyApproval( domainSeparator, SigRequirement.MAJORITY_OWNER_REQUIRED, approval, abi.encode( UNLOCK_TYPEHASH, approval.wallet, approval.validUntil ) ); setLock(wallet, msg.sender, false); } function setLock( Wallet storage wallet, address by, bool locked ) internal { wallet.locked = locked; emit WalletLocked(by, locked); } }
// SPDX-License-Identifier: GPL-2.0-or-later // Copyright 2017 Loopring Technology Limited. pragma solidity ^0.7.0; pragma experimental ABIEncoderV2; import "../../lib/AddressUtil.sol"; import "../../lib/EIP712.sol"; import "../../lib/ERC20.sol"; import "../../lib/MathUint.sol"; import "../../lib/SignatureUtil.sol"; import "../../thirdparty/BytesUtil.sol"; import "./WalletData.sol"; import "./ERC20Lib.sol"; import "./QuotaLib.sol"; import "../SmartWallet.sol"; /// @title MetaTxLib /// @dev A module to support wallet meta-transactions. library MetaTxLib { using AddressUtil for address; using BytesUtil for bytes; using MathUint for uint; using SignatureUtil for bytes32; using QuotaLib for Wallet; using ERC20Lib for Wallet; bytes32 public constant META_TX_TYPEHASH = keccak256( "MetaTx(address to,uint256 nonce,address gasToken,uint256 gasPrice,uint256 gasLimit,uint256 gasOverhead,address feeRecipient,bytes data,bytes32 approvedHash)" ); event MetaTxExecuted( uint nonce, bytes32 approvedHash, bytes32 metaTxHash, bool success, uint gasUsed ); struct MetaTx { address to; uint nonce; address gasToken; uint gasPrice; uint gasLimit; uint gasOverhead; address feeRecipient; bool requiresSuccess; bytes data; bytes signature; } function validateMetaTx( Wallet storage wallet, bytes32 DOMAIN_SEPARATOR, MetaTx memory metaTx, bool success, bytes memory returnData ) public view returns (bytes32) { // If this is a dataless meta-tx the user only signs the function selector, // not the full function calldata. bytes memory data = metaTx.nonce == 0 ? metaTx.data.slice(0, 4) : metaTx.data; // Extracted the approved hash for dataless transactions // The approved hash always needs to be the first value returned by the called function // If the call failed we cannot deduce the approved hash so throw a nice // error message here instead of failing in the signature check. require(success || metaTx.nonce != 0, "APPROVED_HASH_UNKNOWN"); bytes32 approvedHash = metaTx.nonce == 0 ? returnData.toBytes32(0) : bytes32(0); bytes32 encodedHash = keccak256( abi.encode( META_TX_TYPEHASH, metaTx.to, metaTx.nonce, metaTx.gasToken, metaTx.gasPrice, metaTx.gasLimit, metaTx.gasOverhead, metaTx.feeRecipient, metaTx.requiresSuccess, keccak256(data), approvedHash ) ); bytes32 metaTxHash = EIP712.hashPacked(DOMAIN_SEPARATOR, encodedHash); require( metaTxHash.verifySignature(wallet.owner, metaTx.signature), "METATX_INVALID_SIGNATURE" ); return metaTxHash; } function executeMetaTx( Wallet storage wallet, bytes32 DOMAIN_SEPARATOR, PriceOracle priceOracle, MetaTx memory metaTx ) public returns (bool success) { uint gasLeft = gasleft(); require(gasLeft >= (metaTx.gasLimit.mul(64) / 63), "OPERATOR_INSUFFICIENT_GAS"); require(msg.sender != address(this), "RECURSIVE_METATXS_DISALLOWED"); // Only self calls allowed for now require(metaTx.to == address(this)); // Update the nonce before the call to protect against reentrancy require(isNonceValid(wallet, metaTx), "INVALID_NONCE"); if (metaTx.nonce != 0) { wallet.nonce = metaTx.nonce; } // Do the actual call bytes memory returnData; (success, returnData) = metaTx.to.call{gas: metaTx.gasLimit}(metaTx.data); // These checks are done afterwards to use the latest state post meta-tx call require(!wallet.locked, "WALLET_LOCKED"); bytes32 metaTxHash = validateMetaTx( wallet, DOMAIN_SEPARATOR, metaTx, success, returnData ); uint gasUsed = gasLeft - gasleft() + metaTx.gasOverhead; // Reimburse if (metaTx.gasPrice > 0 && (!metaTx.requiresSuccess || success)) { uint gasToReimburse = gasUsed <= metaTx.gasLimit ? gasUsed : metaTx.gasLimit; uint gasCost = gasToReimburse.mul(metaTx.gasPrice); wallet.checkAndAddToSpent( priceOracle, metaTx.gasToken, gasCost ); ERC20Lib.transfer(metaTx.gasToken, metaTx.feeRecipient, gasCost); } emit MetaTxExecuted( metaTx.nonce, metaTx.nonce == 0 ? returnData.toBytes32(0) : bytes32(0), metaTxHash, success, gasUsed ); } function selfBatchCall( Wallet storage /*wallet*/, bytes[] calldata data ) public { for (uint i = 0; i < data.length; i++) { (bool success, ) = address(this).call(data[i]); require(success, "BATCHED_CALL_FAILED"); } } function isNonceValid( Wallet storage wallet, MetaTx memory metaTx ) public view returns (bool) { return (metaTx.nonce > wallet.nonce && (metaTx.nonce >> 128) <= block.number) || isDataless(metaTx); } function isDataless( MetaTx memory metaTx ) public pure returns (bool) { // We don't require any data in the meta tx when // - the meta-tx has no nonce // - the meta-tx needs to be successful // - a function is called that requires a majority of guardians and fails when replayed bytes4 methodId = metaTx.data.toBytes4(0); return metaTx.nonce == 0 && metaTx.requiresSuccess && (methodId == SmartWallet.changeMasterCopy.selector || methodId == SmartWallet.addGuardianWA.selector || methodId == SmartWallet.removeGuardianWA.selector || methodId == SmartWallet.resetGuardiansWA.selector || methodId == SmartWallet.unlock.selector || methodId == SmartWallet.changeDailyQuotaWA.selector || methodId == SmartWallet.recover.selector || methodId == SmartWallet.addToWhitelistWA.selector || methodId == SmartWallet.transferTokenWA.selector || methodId == SmartWallet.callContractWA.selector || methodId == SmartWallet.approveTokenWA.selector || methodId == SmartWallet.approveThenCallContractWA.selector); } }
// SPDX-License-Identifier: GPL-2.0-or-later // Copyright 2017 Loopring Technology Limited. pragma solidity ^0.7.0; pragma experimental ABIEncoderV2; import "./ApprovalLib.sol"; import "./WalletData.sol"; import "../../iface/PriceOracle.sol"; import "../../lib/MathUint.sol"; import "../../thirdparty/SafeCast.sol"; /// @title QuotaLib /// @dev This store maintains daily spending quota for each wallet. /// A rolling daily limit is used. library QuotaLib { using MathUint for uint; using SafeCast for uint; using ApprovalLib for Wallet; uint128 public constant MAX_QUOTA = uint128(-1); uint public constant QUOTA_PENDING_PERIOD = 1 days; bytes32 public constant CHANGE_DAILY_QUOTE_TYPEHASH = keccak256( "changeDailyQuota(address wallet,uint256 validUntil,uint256 newQuota)" ); event QuotaScheduled( address wallet, uint pendingQuota, uint64 pendingUntil ); function changeDailyQuota( Wallet storage wallet, uint newQuota ) public { setQuota(wallet, newQuota, block.timestamp.add(QUOTA_PENDING_PERIOD)); } function changeDailyQuotaWA( Wallet storage wallet, bytes32 domainSeparator, Approval calldata approval, uint newQuota ) public returns (bytes32 approvedHash) { approvedHash = wallet.verifyApproval( domainSeparator, SigRequirement.MAJORITY_OWNER_REQUIRED, approval, abi.encode( CHANGE_DAILY_QUOTE_TYPEHASH, approval.wallet, approval.validUntil, newQuota ) ); setQuota(wallet, newQuota, 0); } function checkAndAddToSpent( Wallet storage wallet, PriceOracle priceOracle, address token, uint amount ) internal { Quota memory q = wallet.quota; uint available = _availableQuota(q); if (available != MAX_QUOTA) { uint value = (token == address(0)) ? amount : ((address(priceOracle) == address(0)) ? 0 : priceOracle.tokenValue(token, amount)); if (value > 0) { require(available >= value, "QUOTA_EXCEEDED"); _addToSpent(wallet, q, value); } } } // 0 for newQuota indicates unlimited quota, or daily quota is disabled. function setQuota( Wallet storage wallet, uint newQuota, uint effectiveTime ) internal { require(newQuota <= MAX_QUOTA, "INVALID_VALUE"); if (newQuota == MAX_QUOTA) { newQuota = 0; } uint __currentQuota = currentQuota(wallet); // Always allow the quota to be changed immediately when the quota doesn't increase if ((__currentQuota >= newQuota && newQuota != 0) || __currentQuota == 0) { effectiveTime = 0; } Quota storage quota = wallet.quota; quota.currentQuota = __currentQuota.toUint128(); quota.pendingQuota = newQuota.toUint128(); quota.pendingUntil = effectiveTime.toUint64(); emit QuotaScheduled( address(this), newQuota, quota.pendingUntil ); } // Returns 0 to indiciate unlimited quota function currentQuota(Wallet storage wallet) internal view returns (uint) { return _currentQuota(wallet.quota); } // Returns 0 to indiciate unlimited quota function pendingQuota(Wallet storage wallet) internal view returns ( uint __pendingQuota, uint __pendingUntil ) { return _pendingQuota(wallet.quota); } function spentQuota(Wallet storage wallet) internal view returns (uint) { return _spentQuota(wallet.quota); } function availableQuota(Wallet storage wallet) internal view returns (uint) { return _availableQuota(wallet.quota); } function hasEnoughQuota( Wallet storage wallet, uint requiredAmount ) internal view returns (bool) { return _hasEnoughQuota(wallet.quota, requiredAmount); } // --- Internal functions --- function _currentQuota(Quota memory q) private view returns (uint) { return q.pendingUntil <= block.timestamp ? q.pendingQuota : q.currentQuota; } function _pendingQuota(Quota memory q) private view returns ( uint __pendingQuota, uint __pendingUntil ) { if (q.pendingUntil > 0 && q.pendingUntil > block.timestamp) { __pendingQuota = q.pendingQuota; __pendingUntil = q.pendingUntil; } } function _spentQuota(Quota memory q) private view returns (uint) { uint timeSinceLastSpent = block.timestamp.sub(q.spentTimestamp); if (timeSinceLastSpent < 1 days) { return uint(q.spentAmount).sub(timeSinceLastSpent.mul(q.spentAmount) / 1 days); } else { return 0; } } function _availableQuota(Quota memory q) private view returns (uint) { uint quota = _currentQuota(q); if (quota == 0) { return MAX_QUOTA; } uint spent = _spentQuota(q); return quota > spent ? quota - spent : 0; } function _hasEnoughQuota( Quota memory q, uint requiredAmount ) private view returns (bool) { return _availableQuota(q) >= requiredAmount; } function _addToSpent( Wallet storage wallet, Quota memory q, uint amount ) private { Quota storage s = wallet.quota; s.spentAmount = _spentQuota(q).add(amount).toUint128(); s.spentTimestamp = uint64(block.timestamp); } }
// SPDX-License-Identifier: GPL-2.0-or-later // Copyright 2017 Loopring Technology Limited. pragma solidity ^0.7.0; pragma experimental ABIEncoderV2; import "./ApprovalLib.sol"; import "./WalletData.sol"; import "./GuardianLib.sol"; import "./LockLib.sol"; import "./Utils.sol"; /// @title RecoverLib /// @author Brecht Devos - <[email protected]> library RecoverLib { using GuardianLib for Wallet; using LockLib for Wallet; using ApprovalLib for Wallet; using Utils for address; event Recovered(address newOwner); bytes32 public constant RECOVER_TYPEHASH = keccak256( "recover(address wallet,uint256 validUntil,address newOwner,address[] newGuardians)" ); /// @dev Recover a wallet by setting a new owner and guardians. /// @param approval The approval. /// @param newOwner The new owner address to set. /// @param newGuardians The new guardians addresses to set. function recover( Wallet storage wallet, bytes32 domainSeparator, Approval calldata approval, address newOwner, address[] calldata newGuardians ) external returns (bytes32 approvedHash) { require(wallet.owner != newOwner, "IS_SAME_OWNER"); require(newOwner.isValidWalletOwner(), "INVALID_NEW_WALLET_OWNER"); approvedHash = wallet.verifyApproval( domainSeparator, SigRequirement.MAJORITY_OWNER_NOT_ALLOWED, approval, abi.encode( RECOVER_TYPEHASH, approval.wallet, approval.validUntil, newOwner, keccak256(abi.encodePacked(newGuardians)) ) ); wallet.owner = newOwner; wallet.setLock(address(this), false); if (newGuardians.length > 0) { for (uint i = 0; i < newGuardians.length; i++) { require(newGuardians[i] != newOwner, "INVALID_NEW_WALLET_GUARDIAN"); } wallet.removeAllGuardians(); wallet.addGuardiansImmediately(newGuardians); } else { if (wallet.isGuardian(newOwner, true)) { wallet.deleteGuardian(newOwner, block.timestamp, true); } wallet.cancelPendingGuardians(); } emit Recovered(newOwner); } }
// SPDX-License-Identifier: GPL-2.0-or-later // Copyright 2017 Loopring Technology Limited. pragma solidity ^0.7.0; pragma experimental ABIEncoderV2; import "./ApprovalLib.sol"; import "./WalletData.sol"; /// @title UpgradeLib /// @author Brecht Devos - <[email protected]> library UpgradeLib { using ApprovalLib for Wallet; event ChangedMasterCopy (address masterCopy); bytes32 public constant CHANGE_MASTER_COPY_TYPEHASH = keccak256( "changeMasterCopy(address wallet,uint256 validUntil,address masterCopy)" ); function changeMasterCopy( Wallet storage wallet, bytes32 domainSeparator, Approval calldata approval, address newMasterCopy ) external returns (bytes32 approvedHash) { require(newMasterCopy != address(0), "INVALID_MASTER_COPY"); approvedHash = wallet.verifyApproval( domainSeparator, SigRequirement.MAJORITY_OWNER_REQUIRED, approval, abi.encode( CHANGE_MASTER_COPY_TYPEHASH, approval.wallet, approval.validUntil, newMasterCopy ) ); emit ChangedMasterCopy(newMasterCopy); } }
// SPDX-License-Identifier: GPL-2.0-or-later // Copyright 2017 Loopring Technology Limited. pragma solidity ^0.7.0; pragma experimental ABIEncoderV2; import "./WalletData.sol"; import "../../lib/AddressUtil.sol"; /// @title Utils /// @author Brecht Devos - <[email protected]> library Utils { using AddressUtil for address; function isValidWalletOwner(address addr) view internal returns (bool) { return addr != address(0) && !addr.isContract(); } }
// SPDX-License-Identifier: GPL-2.0-or-later // Copyright 2017 Loopring Technology Limited. pragma solidity ^0.7.0; pragma experimental ABIEncoderV2; enum SigRequirement { MAJORITY_OWNER_NOT_ALLOWED, MAJORITY_OWNER_ALLOWED, MAJORITY_OWNER_REQUIRED, OWNER_OR_ANY_GUARDIAN, ANY_GUARDIAN } struct Approval { address[] signers; bytes[] signatures; uint validUntil; address wallet; } // Optimized to fit into 64 bytes (2 slots) struct Quota { uint128 currentQuota; uint128 pendingQuota; uint128 spentAmount; uint64 spentTimestamp; uint64 pendingUntil; } enum GuardianStatus { REMOVE, // Being removed or removed after validUntil timestamp ADD // Being added or added after validSince timestamp. } // Optimized to fit into 32 bytes (1 slot) struct Guardian { address addr; uint8 status; uint64 timestamp; // validSince if status = ADD; validUntil if adding = REMOVE; } struct Wallet { address owner; uint64 creationTimestamp; // relayer => nonce uint nonce; // hash => consumed mapping (bytes32 => bool) hashes; bool locked; Guardian[] guardians; mapping (address => uint) guardianIdx; address inheritor; uint32 inheritWaitingPeriod; uint64 lastActive; // the latest timestamp the owner is considered to be active Quota quota; // whitelisted address => effective timestamp mapping (address => uint) whitelisted; }
// SPDX-License-Identifier: GPL-2.0-or-later // Copyright 2017 Loopring Technology Limited. pragma solidity ^0.7.0; pragma experimental ABIEncoderV2; import "./ApprovalLib.sol"; import "./WalletData.sol"; import "../../lib/MathUint.sol"; /// @title WhitelistLib /// @dev This store maintains a wallet's whitelisted addresses. library WhitelistLib { using MathUint for uint; using WhitelistLib for Wallet; using ApprovalLib for Wallet; uint public constant WHITELIST_PENDING_PERIOD = 1 days; bytes32 public constant ADD_TO_WHITELIST_TYPEHASH = keccak256( "addToWhitelist(address wallet,uint256 validUntil,address addr)" ); event Whitelisted( address addr, bool whitelisted, uint effectiveTime ); function addToWhitelist( Wallet storage wallet, address addr ) external { wallet._addToWhitelist( addr, block.timestamp.add(WHITELIST_PENDING_PERIOD) ); } function addToWhitelistWA( Wallet storage wallet, bytes32 domainSeparator, Approval calldata approval, address addr ) external returns (bytes32 approvedHash) { approvedHash = wallet.verifyApproval( domainSeparator, SigRequirement.MAJORITY_OWNER_REQUIRED, approval, abi.encode( ADD_TO_WHITELIST_TYPEHASH, approval.wallet, approval.validUntil, addr ) ); wallet._addToWhitelist( addr, block.timestamp ); } function removeFromWhitelist( Wallet storage wallet, address addr ) external { wallet._removeFromWhitelist(addr); } function isAddressWhitelisted( Wallet storage wallet, address addr ) internal view returns (bool) { uint effectiveTime = wallet.whitelisted[addr]; return effectiveTime > 0 && effectiveTime <= block.timestamp; } // --- Internal functions --- function _addToWhitelist( Wallet storage wallet, address addr, uint effectiveTime ) internal { require(wallet.whitelisted[addr] == 0, "ADDRESS_ALREADY_WHITELISTED"); uint effective = effectiveTime >= block.timestamp ? effectiveTime : block.timestamp; wallet.whitelisted[addr] = effective; emit Whitelisted(addr, true, effective); } function _removeFromWhitelist( Wallet storage wallet, address addr ) internal { delete wallet.whitelisted[addr]; emit Whitelisted(addr, false, 0); } }
// SPDX-License-Identifier: GPL-2.0-or-later // Copyright 2017 Loopring Technology Limited. pragma solidity ^0.7.0; /// @title Loopring SmartWallet V2 interface /// @author Brecht Devos - <[email protected]> abstract contract ILoopringWalletV2 { /// @dev Initializes the smart wallet. /// @param owner The wallet owner address. /// @param guardians The initial wallet guardians. /// @param quota The initial wallet quota. /// @param inheritor The inheritor of the wallet. /// @param feeRecipient The address receiving the fee for creating the wallet. /// @param feeToken The token to use for the fee payment. /// @param feeAmount The amount of tokens paid to the fee recipient. function initialize( address owner, address[] calldata guardians, uint quota, address inheritor, address feeRecipient, address feeToken, uint feeAmount ) external virtual; /// @dev Returns the timestamp the wallet was created. /// @return The timestamp the wallet was created. function getCreationTimestamp() public view virtual returns (uint64); /// @dev Returns the current wallet owner. /// @return The current wallet owner. function getOwner() public view virtual returns (address); }
// SPDX-License-Identifier: GPL-2.0-or-later // Copyright 2017 Loopring Technology Limited. pragma solidity ^0.7.0; /// @title PriceOracle interface PriceOracle { // @dev Return's the token's value in ETH function tokenValue(address token, uint amount) external view returns (uint value); }
// SPDX-License-Identifier: GPL-2.0-or-later // Copyright 2017 Loopring Technology Limited. pragma solidity ^0.7.0; /// @title Utility Functions for addresses /// @author Daniel Wang - <[email protected]> /// @author Brecht Devos - <[email protected]> library AddressUtil { using AddressUtil for *; function isContract( address addr ) internal view returns (bool) { // According to EIP-1052, 0x0 is the value returned for not-yet created accounts // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned // for accounts without code, i.e. `keccak256('')` bytes32 codehash; // solhint-disable-next-line no-inline-assembly assembly { codehash := extcodehash(addr) } return (codehash != 0x0 && codehash != 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470); } function toPayable( address addr ) internal pure returns (address payable) { return payable(addr); } // Works like address.send but with a customizable gas limit // Make sure your code is safe for reentrancy when using this function! function sendETH( address to, uint amount, uint gasLimit ) internal returns (bool success) { if (amount == 0) { return true; } address payable recipient = to.toPayable(); /* solium-disable-next-line */ (success,) = recipient.call{value: amount, gas: gasLimit}(""); } // Works like address.transfer but with a customizable gas limit // Make sure your code is safe for reentrancy when using this function! function sendETHAndVerify( address to, uint amount, uint gasLimit ) internal returns (bool success) { success = to.sendETH(amount, gasLimit); require(success, "TRANSFER_FAILURE"); } // Works like call but is slightly more efficient when data // needs to be copied from memory to do the call. function fastCall( address to, uint gasLimit, uint value, bytes memory data ) internal returns (bool success, bytes memory returnData) { if (to != address(0)) { assembly { // Do the call success := call(gasLimit, to, value, add(data, 32), mload(data), 0, 0) // Copy the return data let size := returndatasize() returnData := mload(0x40) mstore(returnData, size) returndatacopy(add(returnData, 32), 0, size) // Update free memory pointer mstore(0x40, add(returnData, add(32, size))) } } } // Like fastCall, but throws when the call is unsuccessful. function fastCallAndVerify( address to, uint gasLimit, uint value, bytes memory data ) internal returns (bytes memory returnData) { bool success; (success, returnData) = fastCall(to, gasLimit, value, data); if (!success) { assembly { revert(add(returnData, 32), mload(returnData)) } } } }
// SPDX-License-Identifier: Apache-2.0 // Copyright 2017 Loopring Technology Limited. pragma solidity ^0.7.0; pragma experimental ABIEncoderV2; library EIP712 { struct Domain { string name; string version; address verifyingContract; } bytes32 constant internal EIP712_DOMAIN_TYPEHASH = keccak256( "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" ); string constant internal EIP191_HEADER = "\x19\x01"; function hash(Domain memory domain) internal pure returns (bytes32) { uint _chainid; assembly { _chainid := chainid() } return keccak256( abi.encode( EIP712_DOMAIN_TYPEHASH, keccak256(bytes(domain.name)), keccak256(bytes(domain.version)), _chainid, domain.verifyingContract ) ); } function hashPacked( bytes32 domainSeparator, bytes32 dataHash ) internal pure returns (bytes32) { return keccak256( abi.encodePacked( EIP191_HEADER, domainSeparator, dataHash ) ); } }
// SPDX-License-Identifier: GPL-2.0-or-later // Copyright 2017 Loopring Technology Limited. pragma solidity ^0.7.0; abstract contract ERC1271 { // bytes4(keccak256("isValidSignature(bytes32,bytes)") bytes4 constant internal ERC1271_MAGICVALUE = 0x1626ba7e; function isValidSignature( bytes32 _hash, bytes memory _signature) public view virtual returns (bytes4 magicValue); }
// SPDX-License-Identifier: GPL-2.0-or-later // Copyright 2017 Loopring Technology Limited. pragma solidity ^0.7.0; /// @title ERC20 Token Interface /// @dev see https://github.com/ethereum/EIPs/issues/20 /// @author Daniel Wang - <[email protected]> abstract contract ERC20 { function totalSupply() public view virtual returns (uint); function balanceOf( address who ) public view virtual returns (uint); function allowance( address owner, address spender ) public view virtual returns (uint); function transfer( address to, uint value ) public virtual returns (bool); function transferFrom( address from, address to, uint value ) public virtual returns (bool); function approve( address spender, uint value ) public virtual returns (bool); }
// SPDX-License-Identifier: GPL-2.0-or-later // Copyright 2017 Loopring Technology Limited. pragma solidity ^0.7.0; /// @title Utility Functions for uint /// @author Daniel Wang - <[email protected]> library MathUint { function mul( uint a, uint b ) internal pure returns (uint c) { c = a * b; require(a == 0 || c / a == b, "MUL_OVERFLOW"); } function sub( uint a, uint b ) internal pure returns (uint) { require(b <= a, "SUB_UNDERFLOW"); return a - b; } function add( uint a, uint b ) internal pure returns (uint c) { c = a + b; require(c >= a, "ADD_OVERFLOW"); } }
// SPDX-License-Identifier: GPL-2.0-or-later // Copyright 2017 Loopring Technology Limited. pragma solidity ^0.7.0; /// @title ReentrancyGuard /// @author Brecht Devos - <[email protected]> /// @dev Exposes a modifier that guards a function against reentrancy /// Changing the value of the same storage value multiple times in a transaction /// is cheap (starting from Istanbul) so there is no need to minimize /// the number of times the value is changed contract ReentrancyGuard { //The default value must be 0 in order to work behind a proxy. uint private _guardValue; modifier nonReentrant() { require(_guardValue == 0, "REENTRANCY"); _guardValue = 1; _; _guardValue = 0; } }
// SPDX-License-Identifier: GPL-2.0-or-later // Copyright 2017 Loopring Technology Limited. pragma solidity ^0.7.0; pragma experimental ABIEncoderV2; import "../thirdparty/BytesUtil.sol"; import "./AddressUtil.sol"; import "./ERC1271.sol"; import "./MathUint.sol"; /// @title SignatureUtil /// @author Daniel Wang - <[email protected]> /// @dev This method supports multihash standard. Each signature's last byte indicates /// the signature's type. library SignatureUtil { using BytesUtil for bytes; using MathUint for uint; using AddressUtil for address; enum SignatureType { ILLEGAL, INVALID, EIP_712, ETH_SIGN, WALLET // deprecated } bytes4 constant internal ERC1271_MAGICVALUE = 0x1626ba7e; function verifySignatures( bytes32 signHash, address[] memory signers, bytes[] memory signatures ) internal view returns (bool) { require(signers.length == signatures.length, "BAD_SIGNATURE_DATA"); address lastSigner; for (uint i = 0; i < signers.length; i++) { require(signers[i] > lastSigner, "INVALID_SIGNERS_ORDER"); lastSigner = signers[i]; if (!verifySignature(signHash, signers[i], signatures[i])) { return false; } } return true; } function verifySignature( bytes32 signHash, address signer, bytes memory signature ) internal view returns (bool) { if (signer == address(0)) { return false; } return signer.isContract()? verifyERC1271Signature(signHash, signer, signature): verifyEOASignature(signHash, signer, signature); } function recoverECDSASigner( bytes32 signHash, bytes memory signature ) internal pure returns (address) { if (signature.length != 65) { return address(0); } bytes32 r; bytes32 s; uint8 v; // 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 assembly { r := mload(add(signature, 0x20)) s := mload(add(signature, 0x40)) v := and(mload(add(signature, 0x41)), 0xff) } // See https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/cryptography/ECDSA.sol if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) { return address(0); } if (v == 27 || v == 28) { return ecrecover(signHash, v, r, s); } else { return address(0); } } function verifyEOASignature( bytes32 signHash, address signer, bytes memory signature ) private pure returns (bool success) { if (signer == address(0)) { return false; } require(signature.length == 65 || signature.length == 66, "INVALID_SIGNATURE_LENGTH"); bool trimmed = false; if (signature.length == 66) { // Strip off the last byte of the signature by updating the length assembly { mstore(signature, 65) } trimmed = true; } success = (signer == recoverECDSASigner(signHash, signature)); if (!success) { bytes32 hash = keccak256( abi.encodePacked("\x19Ethereum Signed Message:\n32", signHash) ); success = (signer == recoverECDSASigner(hash, signature)); } if (trimmed) { // Restore the signature length assembly { mstore(signature, 66) } } } function verifyERC1271Signature( bytes32 signHash, address signer, bytes memory signature ) private view returns (bool) { bytes memory callData = abi.encodeWithSelector( ERC1271.isValidSignature.selector, signHash, signature ); (bool success, bytes memory result) = signer.staticcall(callData); return ( success && result.length == 32 && result.toBytes4(0) == ERC1271_MAGICVALUE ); } }
// SPDX-License-Identifier: MIT // Token from: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/90ed1af972299070f51bf4665a85da56ac4d355e/contracts/utils/Address.sol pragma solidity >=0.6.2 <0.8.0; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize, which returns 0 for contracts in // construction, since the code is only stored at the end of the // constructor execution. uint256 size; // solhint-disable-next-line no-inline-assembly assembly { size := extcodesize(account) } return size > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); // solhint-disable-next-line avoid-low-level-calls, avoid-call-value (bool success, ) = recipient.call{ value: amount }(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain`call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCall(target, data, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) { //require(address(this).balance >= value, "Address: insufficient balance for call"); require(isContract(target), "Address: call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.call{ value: value }(data); return _verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) { require(isContract(target), "Address: static call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.staticcall(data); return _verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.3._ */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall(target, data, "Address: low-level delegate call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a delegate call. * * _Available since v3.3._ */ function functionDelegateCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) { require(isContract(target), "Address: delegate call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.delegatecall(data); return _verifyCallResult(success, returndata, errorMessage); } function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) { if (success) { return returndata; } else { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly // solhint-disable-next-line no-inline-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } }
// SPDX-License-Identifier: UNLICENSED // Taken from https://github.com/GNSPS/solidity-bytes-utils/blob/master/contracts/BytesLib.sol pragma solidity ^0.7.0; library BytesUtil { function slice( bytes memory _bytes, uint _start, uint _length ) internal pure returns (bytes memory) { require(_bytes.length >= (_start + _length)); bytes memory tempBytes; assembly { switch iszero(_length) case 0 { // Get a location of some free memory and store it in tempBytes as // Solidity does for memory variables. tempBytes := mload(0x40) // The first word of the slice result is potentially a partial // word read from the original array. To read it, we calculate // the length of that partial word and start copying that many // bytes into the array. The first word we copy will start with // data we don't care about, but the last `lengthmod` bytes will // land at the beginning of the contents of the new array. When // we're done copying, we overwrite the full first word with // the actual length of the slice. let lengthmod := and(_length, 31) // The multiplication in the next line is necessary // because when slicing multiples of 32 bytes (lengthmod == 0) // the following copy loop was copying the origin's length // and then ending prematurely not copying everything it should. let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod))) let end := add(mc, _length) for { // The multiplication in the next line has the same exact purpose // as the one above. let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start) } lt(mc, end) { mc := add(mc, 0x20) cc := add(cc, 0x20) } { mstore(mc, mload(cc)) } mstore(tempBytes, _length) //update free-memory pointer //allocating the array padded to 32 bytes like the compiler does now mstore(0x40, and(add(mc, 31), not(31))) } //if we want a zero-length slice let's just return a zero-length array default { tempBytes := mload(0x40) mstore(0x40, add(tempBytes, 0x20)) } } return tempBytes; } function toAddress(bytes memory _bytes, uint _start) internal pure returns (address) { require(_bytes.length >= (_start + 20)); address tempAddress; assembly { tempAddress := div(mload(add(add(_bytes, 0x20), _start)), 0x1000000000000000000000000) } return tempAddress; } function toUint8(bytes memory _bytes, uint _start) internal pure returns (uint8) { require(_bytes.length >= (_start + 1)); uint8 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x1), _start)) } return tempUint; } function toUint16(bytes memory _bytes, uint _start) internal pure returns (uint16) { require(_bytes.length >= (_start + 2)); uint16 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x2), _start)) } return tempUint; } function toUint24(bytes memory _bytes, uint _start) internal pure returns (uint24) { require(_bytes.length >= (_start + 3)); uint24 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x3), _start)) } return tempUint; } function toUint32(bytes memory _bytes, uint _start) internal pure returns (uint32) { require(_bytes.length >= (_start + 4)); uint32 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x4), _start)) } return tempUint; } function toUint64(bytes memory _bytes, uint _start) internal pure returns (uint64) { require(_bytes.length >= (_start + 8)); uint64 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x8), _start)) } return tempUint; } function toUint96(bytes memory _bytes, uint _start) internal pure returns (uint96) { require(_bytes.length >= (_start + 12)); uint96 tempUint; assembly { tempUint := mload(add(add(_bytes, 0xc), _start)) } return tempUint; } function toUint128(bytes memory _bytes, uint _start) internal pure returns (uint128) { require(_bytes.length >= (_start + 16)); uint128 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x10), _start)) } return tempUint; } function toUint(bytes memory _bytes, uint _start) internal pure returns (uint256) { require(_bytes.length >= (_start + 32)); uint256 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x20), _start)) } return tempUint; } function toBytes4(bytes memory _bytes, uint _start) internal pure returns (bytes4) { require(_bytes.length >= (_start + 4)); bytes4 tempBytes4; assembly { tempBytes4 := mload(add(add(_bytes, 0x20), _start)) } return tempBytes4; } function toBytes32(bytes memory _bytes, uint _start) internal pure returns (bytes32) { require(_bytes.length >= (_start + 32)); bytes32 tempBytes32; assembly { tempBytes32 := mload(add(add(_bytes, 0x20), _start)) } return tempBytes32; } function fastSHA256( bytes memory data ) internal view returns (bytes32) { bytes32[] memory result = new bytes32[](1); bool success; assembly { let ptr := add(data, 32) success := staticcall(sub(gas(), 2000), 2, ptr, mload(data), add(result, 32), 32) } require(success, "SHA256_FAILED"); return result[0]; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.7.0; import "./IERC1155Receiver.sol"; /** * @dev _Available since v3.1._ */ contract ERC1155Holder is IERC1155Receiver { function onERC1155Received(address, address, uint256, uint256, bytes memory) public virtual override pure returns (bytes4) { return this.onERC1155Received.selector; } function onERC1155BatchReceived(address, address, uint256[] memory, uint256[] memory, bytes memory) public virtual override pure returns (bytes4) { return this.onERC1155BatchReceived.selector; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.7.0; /** * _Available since v3.1._ */ interface IERC1155Receiver { /** @dev Handles the receipt of a single ERC1155 token type. This function is called at the end of a `safeTransferFrom` after the balance has been updated. To accept the transfer, this must return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` (i.e. 0xf23a6e61, or its own function selector). @param operator The address which initiated the transfer (i.e. msg.sender) @param from The address which previously owned the token @param id The ID of the token being transferred @param value The amount of tokens being transferred @param data Additional data with no specified format @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` if transfer is allowed */ function onERC1155Received( address operator, address from, uint256 id, uint256 value, bytes calldata data ) external returns(bytes4); /** @dev Handles the receipt of a multiple ERC1155 token types. This function is called at the end of a `safeBatchTransferFrom` after the balances have been updated. To accept the transfer(s), this must return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` (i.e. 0xbc197c81, or its own function selector). @param operator The address which initiated the batch transfer (i.e. msg.sender) @param from The address which previously owned the token @param ids An array containing ids of each token being transferred (order and length must match values array) @param values An array containing amounts of each token being transferred (order and length must match ids array) @param data Additional data with no specified format @return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` if transfer is allowed */ function onERC1155BatchReceived( address operator, address from, uint256[] calldata ids, uint256[] calldata values, bytes calldata data ) external returns(bytes4); }
// SPDX-License-Identifier: MIT pragma solidity ^0.7.0; /** * @dev Interface of the ERC165 standard, as defined in the * https://eips.ethereum.org/EIPS/eip-165[EIP]. * * Implementers can declare support of contract interfaces, which can then be * queried by others ({ERC165Checker}). * * For an implementation, see {ERC165}. */ interface IERC165 { /** * @dev Returns true if this contract implements the interface defined by * `interfaceId`. See the corresponding * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] * to learn more about how these ids are created. * * This function call must use less than 30 000 gas. */ function supportsInterface(bytes4 interfaceId) external view returns (bool); }
// SPDX-License-Identifier: MIT pragma solidity ^0.7.0; import "./IERC721Receiver.sol"; /** * @dev Implementation of the {IERC721Receiver} interface. * * Accepts all token transfers. * Make sure the contract is able to use its token with {IERC721-safeTransferFrom}, {IERC721-approve} or {IERC721-setApprovalForAll}. */ contract ERC721Holder is IERC721Receiver { /** * @dev See {IERC721Receiver-onERC721Received}. * * Always returns `IERC721Receiver.onERC721Received.selector`. */ function onERC721Received(address, address, uint256, bytes memory) public virtual override pure returns (bytes4) { return this.onERC721Received.selector; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.7.0; /** * @title ERC721 token receiver interface * @dev Interface for any contract that wants to support safeTransfers * from ERC721 asset contracts. */ interface IERC721Receiver { /** * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom} * by `operator` from `from`, this function is called. * * It must return its Solidity selector to confirm the token transfer. * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted. * * The selector can be obtained in Solidity with `IERC721.onERC721Received.selector`. */ function onERC721Received(address operator, address from, uint256 tokenId, bytes calldata data) external returns (bytes4); }
// SPDX-License-Identifier: MIT // Taken from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/SafeCast.sol pragma solidity ^0.7.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 uint96 from uint256, reverting on * overflow (when the input is greater than largest uint96). * * Counterpart to Solidity's `uint96` operator. * * Requirements: * * - input must fit into 96 bits */ function toUint96(uint256 value) internal pure returns (uint96) { require(value < 2**96, "SafeCast: value doesn\'t fit in 96 bits"); return uint96(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 uint40 from uint256, reverting on * overflow (when the input is greater than largest uint40). * * Counterpart to Solidity's `uint32` operator. * * Requirements: * * - input must fit into 40 bits */ function toUint40(uint256 value) internal pure returns (uint40) { require(value < 2**40, "SafeCast: value doesn\'t fit in 40 bits"); return uint40(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); } }
// SPDX-License-Identifier: MIT // Taken from: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/utils/SafeERC20.sol pragma solidity >=0.6.0 <0.8.0; import "./Address.sol"; import "../lib/ERC20.sol"; import "../lib/MathUint.sol"; /** * @title SafeERC20 * @dev Wrappers around ERC20 operations that throw on failure (when the token * contract returns false). Tokens that return no value (and instead revert or * throw on failure) are also supported, non-reverting calls are assumed to be * successful. * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ library SafeERC20 { using MathUint for uint256; using Address for address; function safeTransfer(ERC20 token, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); } function safeTransferFrom(ERC20 token, address from, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); } /** * @dev Deprecated. This function has issues similar to the ones found in * {IERC20-approve}, and its usage is discouraged. * * Whenever possible, use {safeIncreaseAllowance} and * {safeDecreaseAllowance} instead. */ function safeApprove(ERC20 token, address spender, uint256 value) internal { // safeApprove should only be called when setting an initial allowance, // or when resetting it to zero. To increase and decrease it, use // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' // solhint-disable-next-line max-line-length require((value == 0) || (token.allowance(address(this), spender) == 0), "SafeERC20: approve from non-zero to non-zero allowance" ); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); } function safeIncreaseAllowance(ERC20 token, address spender, uint256 value) internal { uint256 newAllowance = token.allowance(address(this), spender).add(value); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } function safeDecreaseAllowance(ERC20 token, address spender, uint256 value) internal { uint256 newAllowance = token.allowance(address(this), spender).sub(value); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function _callOptionalReturn(ERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that // the target address contains contract code and also asserts for success in the low-level call. bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed"); if (returndata.length > 0) { // Return data is optional // solhint-disable-next-line max-line-length require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } } }
{ "optimizer": { "enabled": true, "runs": 100000 }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "libraries": { "contracts/base/libwallet/ERC1271Lib.sol": { "ERC1271Lib": "0x18cfe847d4ca31bcdb4a3a23d5a99c313a41e424" }, "contracts/base/libwallet/ERC20Lib.sol": { "ERC20Lib": "0xa9abc45a98ac6f5bd44944babb425ef480abd3fe" }, "contracts/base/libwallet/GuardianLib.sol": { "GuardianLib": "0x9b1270fe4d22d9051b8d5dceb7b32f6a8ab4dfcf" }, "contracts/base/libwallet/InheritanceLib.sol": { "InheritanceLib": "0xf38605b5cc6324215adc11607d52db0d1093adb7" }, "contracts/base/libwallet/LockLib.sol": { "LockLib": "0xc369c7f2d7dd35023a74c6ffe09affcb09e50583" }, "contracts/base/libwallet/MetaTxLib.sol": { "MetaTxLib": "0xf5d3ec68826e4774d214c6a515f85bebc7c07874" }, "contracts/base/libwallet/QuotaLib.sol": { "QuotaLib": "0x5f0594d3e3e279d63bb73de95578b0f4b59e80d4" }, "contracts/base/libwallet/RecoverLib.sol": { "RecoverLib": "0x84ade1d4f8c4e35027cbcfa73e91139f7600fd16" }, "contracts/base/libwallet/UpgradeLib.sol": { "UpgradeLib": "0xb4de0305d9e8670b9f045c5045af03a121fab5ad" }, "contracts/base/libwallet/WhitelistLib.sol": { "WhitelistLib": "0x6456ab7259bfc51cc20609a8a733ffb49e97a4d8" } } }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"internalType":"contract PriceOracle","name":"_priceOracle","type":"address"},{"internalType":"address","name":"_blankOwner","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"guardian","type":"address"}],"name":"addGuardian","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address[]","name":"signers","type":"address[]"},{"internalType":"bytes[]","name":"signatures","type":"bytes[]"},{"internalType":"uint256","name":"validUntil","type":"uint256"},{"internalType":"address","name":"wallet","type":"address"}],"internalType":"struct Approval","name":"approval","type":"tuple"},{"internalType":"address","name":"guardian","type":"address"}],"name":"addGuardianWA","outputs":[{"internalType":"bytes32","name":"approvedHash","type":"bytes32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"addToWhitelist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address[]","name":"signers","type":"address[]"},{"internalType":"bytes[]","name":"signatures","type":"bytes[]"},{"internalType":"uint256","name":"validUntil","type":"uint256"},{"internalType":"address","name":"wallet","type":"address"}],"internalType":"struct Approval","name":"approval","type":"tuple"},{"internalType":"address","name":"addr","type":"address"}],"name":"addToWhitelistWA","outputs":[{"internalType":"bytes32","name":"approvedHash","type":"bytes32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"},{"internalType":"bool","name":"forceUseQuota","type":"bool"}],"name":"approveThenCallContract","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address[]","name":"signers","type":"address[]"},{"internalType":"bytes[]","name":"signatures","type":"bytes[]"},{"internalType":"uint256","name":"validUntil","type":"uint256"},{"internalType":"address","name":"wallet","type":"address"}],"internalType":"struct Approval","name":"approval","type":"tuple"},{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"approveThenCallContractWA","outputs":[{"internalType":"bytes32","name":"approvedHash","type":"bytes32"},{"internalType":"bytes","name":"returnData","type":"bytes"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bool","name":"forceUseQuota","type":"bool"}],"name":"approveToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address[]","name":"signers","type":"address[]"},{"internalType":"bytes[]","name":"signatures","type":"bytes[]"},{"internalType":"uint256","name":"validUntil","type":"uint256"},{"internalType":"address","name":"wallet","type":"address"}],"internalType":"struct Approval","name":"approval","type":"tuple"},{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approveTokenWA","outputs":[{"internalType":"bytes32","name":"approvedHash","type":"bytes32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"blankOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"},{"internalType":"bool","name":"forceUseQuota","type":"bool"}],"name":"callContract","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address[]","name":"signers","type":"address[]"},{"internalType":"bytes[]","name":"signatures","type":"bytes[]"},{"internalType":"uint256","name":"validUntil","type":"uint256"},{"internalType":"address","name":"wallet","type":"address"}],"internalType":"struct Approval","name":"approval","type":"tuple"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"callContractWA","outputs":[{"internalType":"bytes32","name":"approvedHash","type":"bytes32"},{"internalType":"bytes","name":"returnData","type":"bytes"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newQuota","type":"uint256"}],"name":"changeDailyQuota","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address[]","name":"signers","type":"address[]"},{"internalType":"bytes[]","name":"signatures","type":"bytes[]"},{"internalType":"uint256","name":"validUntil","type":"uint256"},{"internalType":"address","name":"wallet","type":"address"}],"internalType":"struct Approval","name":"approval","type":"tuple"},{"internalType":"uint256","name":"newQuota","type":"uint256"}],"name":"changeDailyQuotaWA","outputs":[{"internalType":"bytes32","name":"approvedHash","type":"bytes32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address[]","name":"signers","type":"address[]"},{"internalType":"bytes[]","name":"signatures","type":"bytes[]"},{"internalType":"uint256","name":"validUntil","type":"uint256"},{"internalType":"address","name":"wallet","type":"address"}],"internalType":"struct Approval","name":"approval","type":"tuple"},{"internalType":"address","name":"newMasterCopy","type":"address"}],"name":"changeMasterCopy","outputs":[{"internalType":"bytes32","name":"approvedHash","type":"bytes32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"address","name":"gasToken","type":"address"},{"internalType":"uint256","name":"gasPrice","type":"uint256"},{"internalType":"uint256","name":"gasLimit","type":"uint256"},{"internalType":"uint256","name":"gasOverhead","type":"uint256"},{"internalType":"address","name":"feeRecipient","type":"address"},{"internalType":"bool","name":"requiresSuccess","type":"bool"},{"internalType":"bytes","name":"data","type":"bytes"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"executeMetaTx","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getCreationTimestamp","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"includePendingAddition","type":"bool"}],"name":"getGuardians","outputs":[{"components":[{"internalType":"address","name":"addr","type":"address"},{"internalType":"uint8","name":"status","type":"uint8"},{"internalType":"uint64","name":"timestamp","type":"uint64"}],"internalType":"struct Guardian[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getMasterCopy","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"getWhitelistEffectiveTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"},{"internalType":"bool","name":"removeGuardians","type":"bool"}],"name":"inherit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address[]","name":"guardians","type":"address[]"},{"internalType":"uint256","name":"quota","type":"uint256"},{"internalType":"address","name":"inheritor","type":"address"},{"internalType":"address","name":"feeRecipient","type":"address"},{"internalType":"address","name":"feeToken","type":"address"},{"internalType":"uint256","name":"feeAmount","type":"uint256"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"},{"internalType":"bool","name":"includePendingAddition","type":"bool"}],"name":"isGuardian","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"signHash","type":"bytes32"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"isValidSignature","outputs":[{"internalType":"bytes4","name":"magicValue","type":"bytes4"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"isWhitelisted","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lock","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256[]","name":"","type":"uint256[]"},{"internalType":"uint256[]","name":"","type":"uint256[]"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC1155BatchReceived","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC1155Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC721Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"priceOracle","outputs":[{"internalType":"contract PriceOracle","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address[]","name":"signers","type":"address[]"},{"internalType":"bytes[]","name":"signatures","type":"bytes[]"},{"internalType":"uint256","name":"validUntil","type":"uint256"},{"internalType":"address","name":"wallet","type":"address"}],"internalType":"struct Approval","name":"approval","type":"tuple"},{"internalType":"address","name":"newOwner","type":"address"},{"internalType":"address[]","name":"newGuardians","type":"address[]"}],"name":"recover","outputs":[{"internalType":"bytes32","name":"approvedHash","type":"bytes32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"removeFromWhitelist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"guardian","type":"address"}],"name":"removeGuardian","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address[]","name":"signers","type":"address[]"},{"internalType":"bytes[]","name":"signatures","type":"bytes[]"},{"internalType":"uint256","name":"validUntil","type":"uint256"},{"internalType":"address","name":"wallet","type":"address"}],"internalType":"struct Approval","name":"approval","type":"tuple"},{"internalType":"address","name":"guardian","type":"address"}],"name":"removeGuardianWA","outputs":[{"internalType":"bytes32","name":"approvedHash","type":"bytes32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"newGuardians","type":"address[]"}],"name":"resetGuardians","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address[]","name":"signers","type":"address[]"},{"internalType":"bytes[]","name":"signatures","type":"bytes[]"},{"internalType":"uint256","name":"validUntil","type":"uint256"},{"internalType":"address","name":"wallet","type":"address"}],"internalType":"struct Approval","name":"approval","type":"tuple"},{"internalType":"address[]","name":"newGuardians","type":"address[]"}],"name":"resetGuardiansWA","outputs":[{"internalType":"bytes32","name":"approvedHash","type":"bytes32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes[]","name":"data","type":"bytes[]"}],"name":"selfBatchCall","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"inheritor","type":"address"},{"internalType":"uint32","name":"waitingPeriod","type":"uint32"}],"name":"setInheritor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes","name":"logdata","type":"bytes"},{"internalType":"bool","name":"forceUseQuota","type":"bool"}],"name":"transferToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address[]","name":"signers","type":"address[]"},{"internalType":"bytes[]","name":"signatures","type":"bytes[]"},{"internalType":"uint256","name":"validUntil","type":"uint256"},{"internalType":"address","name":"wallet","type":"address"}],"internalType":"struct Approval","name":"approval","type":"tuple"},{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes","name":"logdata","type":"bytes"}],"name":"transferTokenWA","outputs":[{"internalType":"bytes32","name":"approvedHash","type":"bytes32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address[]","name":"signers","type":"address[]"},{"internalType":"bytes[]","name":"signatures","type":"bytes[]"},{"internalType":"uint256","name":"validUntil","type":"uint256"},{"internalType":"address","name":"wallet","type":"address"}],"internalType":"struct Approval","name":"approval","type":"tuple"}],"name":"unlock","outputs":[{"internalType":"bytes32","name":"approvedHash","type":"bytes32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"wallet","outputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint64","name":"creationTimestamp","type":"uint64"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"bool","name":"locked","type":"bool"},{"internalType":"address","name":"inheritor","type":"address"},{"internalType":"uint32","name":"inheritWaitingPeriod","type":"uint32"},{"internalType":"uint64","name":"lastActive","type":"uint64"},{"components":[{"internalType":"uint128","name":"currentQuota","type":"uint128"},{"internalType":"uint128","name":"pendingQuota","type":"uint128"},{"internalType":"uint128","name":"spentAmount","type":"uint128"},{"internalType":"uint64","name":"spentTimestamp","type":"uint64"},{"internalType":"uint64","name":"pendingUntil","type":"uint64"}],"internalType":"struct Quota","name":"quota","type":"tuple"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]
Deployed Bytecode
0x6080604052600436106102f65760003560e01c80639a792d461161018f578063c274298f116100e1578063e9f8db041161008a578063f2fde38b11610064578063f2fde38b1461087d578063f83d08ba1461089d578063ff06ff56146108b2576102fd565b8063e9f8db041461081d578063f1b43ae41461083d578063f23a6e611461085d576102fd565b8063e43252d7116100bb578063e43252d7146107bd578063e4c3d6b2146107dd578063e913d278146107fd576102fd565b8063c274298f14610766578063c948bb8a14610788578063d6bb65c21461079d576102fd565b8063ab0b90f811610143578063ba324ac61161011d578063ba324ac614610706578063bb64cabe14610726578063bc197c8114610746576102fd565b8063ab0b90f8146106a6578063b6aa057e146106c6578063b9806d99146106e6576102fd565b8063a1d7876f11610174578063a1d7876f14610646578063a526d83b14610666578063a79c930c14610686576102fd565b80639a792d46146106065780639b5ab34314610626576102fd565b806331ea76b11161024857806371404156116101fc5780638ab1d681116101d65780638ab1d681146105a65780638f22edf3146105c65780638fbbddc4146105e6576102fd565b8063714041561461054f5780637be5fdc314610571578063893d20e814610591576102fd565b80633af32abf1161022d5780633af32abf146104e657806351953ea614610506578063521eb27314610526576102fd565b806331ea76b1146104a35780633644e515146104d1576102fd565b8063150b7a02116102aa5780632630c12f116102845780632630c12f1461044157806326e1c978146104565780632e9feb7914610476576102fd565b8063150b7a02146103d45780631626ba7e1461040157806324db3d0214610421576102fd565b80630206b440116102db5780630206b4401461036557806304e80e901461039257806309779927146103b4576102fd565b806301a44c251461030257806301ffc9a714610338576102fd565b366102fd57005b600080fd5b34801561030e57600080fd5b5061032261031d366004613d36565b6108d2565b60405161032f91906141e5565b60405180910390f35b34801561034457600080fd5b506103586103533660046139af565b6109a6565b60405161032f91906141da565b34801561037157600080fd5b506103856103803660046138df565b610ad9565b60405161032f919061416b565b34801561039e57600080fd5b506103a7610ba1565b60405161032f919061402b565b3480156103c057600080fd5b506103586103cf366004613628565b610bbd565b3480156103e057600080fd5b506103f46103ef3660046133f1565b610d7a565b60405161032f9190614240565b34801561040d57600080fd5b506103f461041c36600461392f565b610da3565b34801561042d57600080fd5b5061032261043c366004613a4d565b610e74565b34801561044d57600080fd5b506103a7610f3e565b34801561046257600080fd5b50610322610471366004613a92565b610f62565b34801561048257600080fd5b506104966104913660046136ff565b611039565b60405161032f919061426d565b3480156104af57600080fd5b506104c36104be366004613b94565b6111b0565b60405161032f929190614227565b3480156104dd57600080fd5b506103226112b9565b3480156104f257600080fd5b50610358610501366004613256565b6112dd565b34801561051257600080fd5b50610322610521366004613d9c565b6112ea565b34801561053257600080fd5b5061053b611364565b60405161032f9897969594939291906140b5565b34801561055b57600080fd5b5061056f61056a366004613256565b611447565b005b34801561057d57600080fd5b5061032261058c366004613a4d565b611540565b34801561059d57600080fd5b506103a7611656565b3480156105b257600080fd5b5061056f6105c1366004613256565b611672565b3480156105d257600080fd5b5061056f6105e13660046137a6565b611738565b3480156105f257600080fd5b50610322610601366004613a4d565b611834565b34801561061257600080fd5b50610322610621366004613c38565b6118ae565b34801561063257600080fd5b5061056f6106413660046135f0565b61192c565b34801561065257600080fd5b5061049661066136600461345b565b611981565b34801561067257600080fd5b5061056f610681366004613256565b611af5565b34801561069257600080fd5b506103226106a1366004613a1a565b611bbb565b3480156106b257600080fd5b506103586106c13660046135f0565b611c83565b3480156106d257600080fd5b506103226106e1366004613a4d565b611d2b565b3480156106f257600080fd5b5061056f61070136600461336e565b611da5565b34801561071257600080fd5b5061056f610721366004613771565b611ecf565b34801561073257600080fd5b50610322610741366004613afa565b611f52565b34801561075257600080fd5b506103f4610761366004613272565b61202f565b34801561077257600080fd5b5061077b612059565b60405161032f9190614a9e565b34801561079457600080fd5b506103a7612081565b3480156107a957600080fd5b5061056f6107b8366004613550565b6120a5565b3480156107c957600080fd5b5061056f6107d8366004613256565b612399565b3480156107e957600080fd5b5061056f6107f83660046137a6565b61245f565b34801561080957600080fd5b50610322610818366004613256565b612527565b34801561082957600080fd5b506104c3610838366004613cb0565b61254f565b34801561084957600080fd5b5061056f610858366004613ddf565b612652565b34801561086957600080fd5b506103f46108783660046134e9565b612718565b34801561088957600080fd5b5061056f610898366004613256565b612742565b3480156108a957600080fd5b5061056f61288c565b3480156108be57600080fd5b5061056f6108cd36600461331c565b61290f565b6040517f3140f8cf000000000000000000000000000000000000000000000000000000008152600090739b1270fe4d22d9051b8d5dceb7b32f6a8ab4dfcf90633140f8cf9061094e906001907f6eb62ababf4da0f65013bf6c6df74fa46ecbd0a164b398cb17b05350d84e253790899089908990600401614864565b60206040518083038186803b15801561096657600080fd5b505af415801561097a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061099e9190613917565b949350505050565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f1626ba7e000000000000000000000000000000000000000000000000000000001480610a3957507fffffffff0000000000000000000000000000000000000000000000000000000082167f01ffc9a700000000000000000000000000000000000000000000000000000000145b80610a8557507fffffffff0000000000000000000000000000000000000000000000000000000082167f150b7a0200000000000000000000000000000000000000000000000000000000145b80610ad157507fffffffff0000000000000000000000000000000000000000000000000000000082167f4e2312e000000000000000000000000000000000000000000000000000000000145b90505b919050565b6040517f18d9ad71000000000000000000000000000000000000000000000000000000008152606090739b1270fe4d22d9051b8d5dceb7b32f6a8ab4dfcf906318d9ad7190610b2f906001908690600401614a8e565b60006040518083038186803b158015610b4757600080fd5b505af4158015610b5b573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201604052610ad191908101906137e6565b60005473ffffffffffffffffffffffffffffffffffffffff1690565b6000806040518061014001604052808e73ffffffffffffffffffffffffffffffffffffffff1681526020018d81526020018c73ffffffffffffffffffffffffffffffffffffffff1681526020018b81526020018a81526020018981526020018873ffffffffffffffffffffffffffffffffffffffff168152602001871515815260200186868080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152505050908252506020018490526040517f531020c200000000000000000000000000000000000000000000000000000000815290915073f5d3ec68826e4774d214c6a515f85bebc7c078749063531020c290610d19906001907f6eb62ababf4da0f65013bf6c6df74fa46ecbd0a164b398cb17b05350d84e2537907f000000000000000000000000b124190942976431d8181fbe183e44584253da6890879060040161450a565b60206040518083038186803b158015610d3157600080fd5b505af4158015610d45573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d6991906138fb565b9d9c50505050505050505050505050565b7f150b7a0200000000000000000000000000000000000000000000000000000000949350505050565b6040517f7d631ea10000000000000000000000000000000000000000000000000000000081526000907318cfe847d4ca31bcdb4a3a23d5a99c313a41e42490637d631ea190610e1d906001907f1626ba7e0000000000000000000000000000000000000000000000000000000090889088906004016148c6565b60206040518083038186803b158015610e3557600080fd5b505af4158015610e49573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e6d91906139cb565b9392505050565b6040517f912fce54000000000000000000000000000000000000000000000000000000008152600090739b1270fe4d22d9051b8d5dceb7b32f6a8ab4dfcf9063912fce5490610eee906001907f6eb62ababf4da0f65013bf6c6df74fa46ecbd0a164b398cb17b05350d84e2537908890889060040161462d565b60206040518083038186803b158015610f0657600080fd5b505af4158015610f1a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e6d9190613917565b7f000000000000000000000000b124190942976431d8181fbe183e44584253da6881565b6040517fe2e2492700000000000000000000000000000000000000000000000000000000815260009073a9abc45a98ac6f5bd44944babb425ef480abd3fe9063e2e2492790610fe0906001907f6eb62ababf4da0f65013bf6c6df74fa46ecbd0a164b398cb17b05350d84e2537908a908a908a908a90600401614673565b60206040518083038186803b158015610ff857600080fd5b505af415801561100c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110309190613917565b95945050505050565b60603330148061106e575060015473ffffffffffffffffffffffffffffffffffffffff163314801561106e575060045460ff16155b6110ad576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016110a490614382565b60405180910390fd5b6110b76001612aa0565b6040517ffc55b67900000000000000000000000000000000000000000000000000000000815273a9abc45a98ac6f5bd44944babb425ef480abd3fe9063fc55b67990611134906001907f000000000000000000000000b124190942976431d8181fbe183e44584253da68908b908b908b908b908b90600401614a24565b60006040518083038186803b15801561114c57600080fd5b505af4158015611160573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682016040526111a691908101906139e7565b9695505050505050565b6040517fd77c3b5d00000000000000000000000000000000000000000000000000000000815260009060609073a9abc45a98ac6f5bd44944babb425ef480abd3fe9063d77c3b5d90611237906001907f6eb62ababf4da0f65013bf6c6df74fa46ecbd0a164b398cb17b05350d84e2537908e908e908e908e908e908e908e90600401614730565b60006040518083038186803b15801561124f57600080fd5b505af4158015611263573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682016040526112a99190810190613974565b909a909950975050505050505050565b7f6eb62ababf4da0f65013bf6c6df74fa46ecbd0a164b398cb17b05350d84e253781565b6000610ad1600183612b4a565b6040517f2d5d461a000000000000000000000000000000000000000000000000000000008152600090735f0594d3e3e279d63bb73de95578b0f4b59e80d490632d5d461a90610eee906001907f6eb62ababf4da0f65013bf6c6df74fa46ecbd0a164b398cb17b05350d84e25379088908890600401614896565b6001546002546004546007546040805160a0810182526008546fffffffffffffffffffffffffffffffff808216835270010000000000000000000000000000000091829004811660208401526009549081169383019390935267ffffffffffffffff908304811660608301527801000000000000000000000000000000000000000000000000928390048116608083015273ffffffffffffffffffffffffffffffffffffffff8088169774010000000000000000000000000000000000000000908190048316979660ff16959182169490820463ffffffff169391049091169088565b3330148061147a575060015473ffffffffffffffffffffffffffffffffffffffff163314801561147a575060045460ff16155b6114b0576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016110a490614382565b6114ba6001612aa0565b6040517fee3da3e3000000000000000000000000000000000000000000000000000000008152739b1270fe4d22d9051b8d5dceb7b32f6a8ab4dfcf9063ee3da3e39061150d906001908590600401614484565b60006040518083038186803b15801561152557600080fd5b505af4158015611539573d6000803e3d6000fd5b5050505050565b6040517fec08e14400000000000000000000000000000000000000000000000000000000815260009073b4de0305d9e8670b9f045c5045af03a121fab5ad9063ec08e144906115ba906001907f6eb62ababf4da0f65013bf6c6df74fa46ecbd0a164b398cb17b05350d84e2537908890889060040161462d565b60206040518083038186803b1580156115d257600080fd5b505af41580156115e6573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061160a9190613917565b600080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff949094169390931790925550919050565b60015473ffffffffffffffffffffffffffffffffffffffff1690565b333014806116a5575060015473ffffffffffffffffffffffffffffffffffffffff16331480156116a5575060045460ff16155b6116db576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016110a490614382565b6116e56001612aa0565b6040517f9509eea5000000000000000000000000000000000000000000000000000000008152736456ab7259bfc51cc20609a8a733ffb49e97a4d890639509eea59061150d906001908590600401614484565b3330148061176b575060015473ffffffffffffffffffffffffffffffffffffffff163314801561176b575060045460ff16155b6117a1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016110a490614382565b6117ab6001612aa0565b6040517f28c80935000000000000000000000000000000000000000000000000000000008152739b1270fe4d22d9051b8d5dceb7b32f6a8ab4dfcf906328c809359061180090600190869086906004016144d6565b60006040518083038186803b15801561181857600080fd5b505af415801561182c573d6000803e3d6000fd5b505050505050565b6040517f1b8b0ce9000000000000000000000000000000000000000000000000000000008152600090736456ab7259bfc51cc20609a8a733ffb49e97a4d890631b8b0ce990610eee906001907f6eb62ababf4da0f65013bf6c6df74fa46ecbd0a164b398cb17b05350d84e2537908890889060040161462d565b6040517fc089037e0000000000000000000000000000000000000000000000000000000081526000907384ade1d4f8c4e35027cbcfa73e91139f7600fd169063c089037e90610fe0906001907f6eb62ababf4da0f65013bf6c6df74fa46ecbd0a164b398cb17b05350d84e2537908a908a908a908a906004016147a7565b6040517f91fc12c600000000000000000000000000000000000000000000000000000000815273f38605b5cc6324215adc11607d52db0d1093adb7906391fc12c69061180090600190869086906004016144a8565b6060333014806119b6575060015473ffffffffffffffffffffffffffffffffffffffff16331480156119b6575060045460ff16155b6119ec576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016110a490614382565b6119f66001612aa0565b6040517f2b84eece00000000000000000000000000000000000000000000000000000000815273a9abc45a98ac6f5bd44944babb425ef480abd3fe90632b84eece90611a77906001907f000000000000000000000000b124190942976431d8181fbe183e44584253da68908d908d908d908d908d908d908d906004016149b6565b60006040518083038186803b158015611a8f57600080fd5b505af4158015611aa3573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201604052611ae991908101906139e7565b98975050505050505050565b33301480611b28575060015473ffffffffffffffffffffffffffffffffffffffff1633148015611b28575060045460ff16155b611b5e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016110a490614382565b611b686001612aa0565b6040517fb9d8df14000000000000000000000000000000000000000000000000000000008152739b1270fe4d22d9051b8d5dceb7b32f6a8ab4dfcf9063b9d8df149061150d906001908590600401614484565b6040517ffbcfc53c00000000000000000000000000000000000000000000000000000000815260009073c369c7f2d7dd35023a74c6ffe09affcb09e505839063fbcfc53c90611c33906001907f6eb62ababf4da0f65013bf6c6df74fa46ecbd0a164b398cb17b05350d84e253790879060040161460e565b60206040518083038186803b158015611c4b57600080fd5b505af4158015611c5f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ad19190613917565b6040517fa643b107000000000000000000000000000000000000000000000000000000008152600090739b1270fe4d22d9051b8d5dceb7b32f6a8ab4dfcf9063a643b10790611cdb90600190879087906004016144a8565b60206040518083038186803b158015611cf357600080fd5b505af4158015611d07573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e6d91906138fb565b6040517f9668d94c000000000000000000000000000000000000000000000000000000008152600090739b1270fe4d22d9051b8d5dceb7b32f6a8ab4dfcf90639668d94c90610eee906001907f6eb62ababf4da0f65013bf6c6df74fa46ecbd0a164b398cb17b05350d84e2537908890889060040161462d565b33301480611dd8575060015473ffffffffffffffffffffffffffffffffffffffff1633148015611dd8575060045460ff16155b611e0e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016110a490614382565b611e186001612aa0565b6040517fc5354f8b00000000000000000000000000000000000000000000000000000000815273a9abc45a98ac6f5bd44944babb425ef480abd3fe9063c5354f8b90611e97906001907f000000000000000000000000b124190942976431d8181fbe183e44584253da68908b908b908b908b908b908b90600401614951565b60006040518083038186803b158015611eaf57600080fd5b505af4158015611ec3573d6000803e3d6000fd5b50505050505050505050565b33301480611f02575060015473ffffffffffffffffffffffffffffffffffffffff1633148015611f02575060045460ff16155b611f38576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016110a490614382565b611f426001612aa0565b611f4e60018383612b86565b5050565b6040517faed4047900000000000000000000000000000000000000000000000000000000815260009073a9abc45a98ac6f5bd44944babb425ef480abd3fe9063aed4047990611fd4906001907f6eb62ababf4da0f65013bf6c6df74fa46ecbd0a164b398cb17b05350d84e2537908c908c908c908c908c908c906004016146c4565b60206040518083038186803b158015611fec57600080fd5b505af4158015612000573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120249190613917565b979650505050505050565b7fbc197c810000000000000000000000000000000000000000000000000000000095945050505050565b60015474010000000000000000000000000000000000000000900467ffffffffffffffff1690565b7f000000000000000000000000dd2a08a1c1a28c1a571e098914ca10f2877d9c9781565b60005474010000000000000000000000000000000000000000900460ff16156120fa576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016110a490614325565b60015473ffffffffffffffffffffffffffffffffffffffff161561214a576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016110a4906142ee565b73ffffffffffffffffffffffffffffffffffffffff8816612197576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016110a490614416565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff8a16177fffffffff0000000000000000ffffffffffffffffffffffffffffffffffffffff16740100000000000000000000000000000000000000004267ffffffffffffffff16021781556040517fb4f212fa000000000000000000000000000000000000000000000000000000008152739b1270fe4d22d9051b8d5dceb7b32f6a8ab4dfcf9163b4f212fa9161226f91908b908b906004016144d6565b60006040518083038186803b15801561228757600080fd5b505af415801561229b573d6000803e3d6000fd5b50505050846000146122b4576122b46001866000612d4b565b73ffffffffffffffffffffffffffffffffffffffff8416156122e0576122e06001856301e13380612b86565b73ffffffffffffffffffffffffffffffffffffffff8316158015906123055750600081115b1561238f576040517fbeabacc800000000000000000000000000000000000000000000000000000000815273a9abc45a98ac6f5bd44944babb425ef480abd3fe9063beabacc89061235e9085908790869060040161404c565b60006040518083038186803b15801561237657600080fd5b505af415801561238a573d6000803e3d6000fd5b505050505b5050505050505050565b333014806123cc575060015473ffffffffffffffffffffffffffffffffffffffff16331480156123cc575060045460ff16155b612402576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016110a490614382565b61240c6001612aa0565b6040517ff42a9d41000000000000000000000000000000000000000000000000000000008152736456ab7259bfc51cc20609a8a733ffb49e97a4d89063f42a9d419061150d906001908590600401614484565b33301480612492575060015473ffffffffffffffffffffffffffffffffffffffff1633148015612492575060045460ff16155b6124c8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016110a490614382565b6124d26001612aa0565b6040517f8b83659600000000000000000000000000000000000000000000000000000000815273f5d3ec68826e4774d214c6a515f85bebc7c0787490638b8365969061180090600190869086906004016144f0565b73ffffffffffffffffffffffffffffffffffffffff166000908152600a602052604090205490565b6040517ff9fa74e600000000000000000000000000000000000000000000000000000000815260009060609073a9abc45a98ac6f5bd44944babb425ef480abd3fe9063f9fa74e6906125d2906001907f6eb62ababf4da0f65013bf6c6df74fa46ecbd0a164b398cb17b05350d84e2537908c908c908c908c908c90600401614802565b60006040518083038186803b1580156125ea57600080fd5b505af41580156125fe573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682016040526126449190810190613974565b909890975095505050505050565b33301480612685575060015473ffffffffffffffffffffffffffffffffffffffff1633148015612685575060045460ff16155b6126bb576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016110a490614382565b6126c56001612aa0565b6040517fe522f168000000000000000000000000000000000000000000000000000000008152735f0594d3e3e279d63bb73de95578b0f4b59e80d49063e522f1689061150d906001908590600401614a80565b7ff23a6e610000000000000000000000000000000000000000000000000000000095945050505050565b3373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000dd2a08a1c1a28c1a571e098914ca10f2877d9c97161480156127c257506001547f000000000000000000000000dd2a08a1c1a28c1a571e098914ca10f2877d9c9773ffffffffffffffffffffffffffffffffffffffff9081169116145b6127f8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016110a4906143df565b73ffffffffffffffffffffffffffffffffffffffff8116612845576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016110a490614416565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b6040517f686d7c3700000000000000000000000000000000000000000000000000000000815273c369c7f2d7dd35023a74c6ffe09affcb09e505839063686d7c37906128dd906001906004016141e5565b60006040518083038186803b1580156128f557600080fd5b505af4158015612909573d6000803e3d6000fd5b50505050565b33301480612942575060015473ffffffffffffffffffffffffffffffffffffffff1633148015612942575060045460ff16155b612978576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016110a490614382565b6129826001612aa0565b6040517f3b39d10d00000000000000000000000000000000000000000000000000000000815273a9abc45a98ac6f5bd44944babb425ef480abd3fe90633b39d10d906129fd906001907f000000000000000000000000b124190942976431d8181fbe183e44584253da6890899089908990899060040161490d565b60006040518083038186803b158015612a1557600080fd5b505af415801561238f573d6000803e3d6000fd5b6000804690507f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f836000015180519060200120846020015180519060200120838660400151604051602001612a829594939291906141ee565b60405160208183030381529060405280519060200120915050919050565b600681015473ffffffffffffffffffffffffffffffffffffffff1615801590612af85750600681015467ffffffffffffffff78010000000000000000000000000000000000000000000000009091041662278d000142115b15612b475760068101805477ffffffffffffffffffffffffffffffffffffffffffffffff1678010000000000000000000000000000000000000000000000004267ffffffffffffffff16021790555b50565b73ffffffffffffffffffffffffffffffffffffffff81166000908152600983016020526040812054801580159061099e57504210159392505050565b73ffffffffffffffffffffffffffffffffffffffff8216612be45763ffffffff811615612bdf576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016110a4906142b7565b612c27565b62278d008163ffffffff161015612c27576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016110a4906142b7565b73ffffffffffffffffffffffffffffffffffffffff8216301415612c77576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016110a490614280565b6006909201805467ffffffffffffffff421678010000000000000000000000000000000000000000000000000277ffffffffffffffffffffffffffffffffffffffffffffffff63ffffffff90951674010000000000000000000000000000000000000000027fffffffffffffffff00000000ffffffffffffffffffffffffffffffffffffffff73ffffffffffffffffffffffffffffffffffffffff9095167fffffffffffffffffffffffff000000000000000000000000000000000000000090931692909217939093161792909216179055565b6fffffffffffffffffffffffffffffffff821115612d95576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016110a49061444d565b6fffffffffffffffffffffffffffffffff821415612db257600091505b6000612dbd84612f01565b9050828110158015612dce57508215155b80612dd7575080155b15612de157600091505b60078401612dee82612f96565b81547fffffffffffffffffffffffffffffffff00000000000000000000000000000000166fffffffffffffffffffffffffffffffff91909116178155612e3384612f96565b81546fffffffffffffffffffffffffffffffff918216700100000000000000000000000000000000029116178155612e6a83613005565b60018201805477ffffffffffffffffffffffffffffffffffffffffffffffff16780100000000000000000000000000000000000000000000000067ffffffffffffffff938416810291909117918290556040517ff4109b774ae6f56e72fe6cb9396e75bc9017595ac0797f852334dbd0db2fa5b693612ef29330938a9391049091169061407d565b60405180910390a15050505050565b6040805160a08101825260078301546fffffffffffffffffffffffffffffffff8082168352700100000000000000000000000000000000918290048116602084015260088501549081169383019390935267ffffffffffffffff9083048116606083015278010000000000000000000000000000000000000000000000009092049091166080820152600090610ad190613068565b60007001000000000000000000000000000000008210613001576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526027815260200180614c266027913960400191505060405180910390fd5b5090565b6000680100000000000000008210613001576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526026815260200180614c4d6026913960400191505060405180910390fd5b600042826080015167ffffffffffffffff16111561308757815161308d565b81602001515b6fffffffffffffffffffffffffffffffff1692915050565b8035610ad481614bc7565b60008083601f8401126130c1578182fd5b50813567ffffffffffffffff8111156130d8578182fd5b60208301915083602080830285010111156130f257600080fd5b9250929050565b600082601f830112613109578081fd5b8135602061311e61311983614ad7565b614ab3565b828152818101908583018385028701840188101561313a578586fd5b855b858110156131585781358452928401929084019060010161313c565b5090979650505050505050565b8035610ad481614be9565b60008083601f840112613181578182fd5b50813567ffffffffffffffff811115613198578182fd5b6020830191508360208285010111156130f257600080fd5b600082601f8301126131c0578081fd5b81356131ce61311982614af5565b8181528460208386010111156131e2578283fd5b816020850160208301379081016020019190915292915050565b600082601f83011261320c578081fd5b815161321a61311982614af5565b81815284602083860101111561322e578283fd5b61099e826020830160208701614b9b565b600060808284031215613250578081fd5b50919050565b600060208284031215613267578081fd5b8135610e6d81614bc7565b600080600080600060a08688031215613289578081fd5b853561329481614bc7565b945060208601356132a481614bc7565b9350604086013567ffffffffffffffff808211156132c0578283fd5b6132cc89838a016130f9565b945060608801359150808211156132e1578283fd5b6132ed89838a016130f9565b93506080880135915080821115613302578283fd5b5061330f888289016131b0565b9150509295509295909350565b60008060008060808587031215613331578182fd5b843561333c81614bc7565b9350602085013561334c81614bc7565b925060408501359150606085013561336381614be9565b939692955090935050565b60008060008060008060a08789031215613386578384fd5b863561339181614bc7565b955060208701356133a181614bc7565b945060408701359350606087013567ffffffffffffffff8111156133c3578182fd5b6133cf89828a01613170565b90945092505060808701356133e381614be9565b809150509295509295509295565b60008060008060808587031215613406578182fd5b843561341181614bc7565b9350602085013561342181614bc7565b925060408501359150606085013567ffffffffffffffff811115613443578182fd5b61344f878288016131b0565b91505092959194509250565b600080600080600080600060c0888a031215613475578485fd5b873561348081614bc7565b9650602088013561349081614bc7565b95506040880135945060608801359350608088013567ffffffffffffffff8111156134b9578182fd5b6134c58a828b01613170565b90945092505060a08801356134d981614be9565b8091505092959891949750929550565b600080600080600060a08688031215613500578283fd5b853561350b81614bc7565b9450602086013561351b81614bc7565b93506040860135925060608601359150608086013567ffffffffffffffff811115613544578182fd5b61330f888289016131b0565b60008060008060008060008060e0898b03121561356b578182fd5b883561357681614bc7565b9750602089013567ffffffffffffffff811115613591578283fd5b61359d8b828c016130b0565b9098509650506040890135945060608901356135b881614bc7565b935060808901356135c881614bc7565b925060a08901356135d881614bc7565b8092505060c089013590509295985092959890939650565b60008060408385031215613602578182fd5b823561360d81614bc7565b9150602083013561361d81614be9565b809150509250929050565b60008060008060008060008060008060006101408c8e031215613649578485fd5b6136528c6130a5565b9a5060208c0135995061366760408d016130a5565b985060608c0135975060808c0135965060a08c0135955061368a60c08d016130a5565b945061369860e08d01613165565b935067ffffffffffffffff806101008e013511156136b4578384fd5b6136c58e6101008f01358f01613170565b90945092506101208d01358110156136db578182fd5b506136ed8d6101208e01358e016131b0565b90509295989b509295989b9093969950565b600080600080600060808688031215613716578283fd5b853561372181614bc7565b945060208601359350604086013567ffffffffffffffff811115613743578384fd5b61374f88828901613170565b909450925050606086013561376381614be9565b809150509295509295909350565b60008060408385031215613783578182fd5b823561378e81614bc7565b9150602083013563ffffffff8116811461361d578182fd5b600080602083850312156137b8578182fd5b823567ffffffffffffffff8111156137ce578283fd5b6137da858286016130b0565b90969095509350505050565b600060208083850312156137f8578182fd5b825167ffffffffffffffff8082111561380f578384fd5b818501915085601f830112613822578384fd5b815161383061311982614ad7565b818152848101908486016060808502870188018b101561384e578889fd5b8896505b848710156138d05780828c031215613868578889fd5b60408051828101818110898211171561387d57fe5b8252835161388a81614bc7565b8152838a015160ff8116811461389e578b8cfd5b818b01528382015188811681146138b3578b8cfd5b918101919091528452600196909601959287019290810190613852565b50909998505050505050505050565b6000602082840312156138f0578081fd5b8135610e6d81614be9565b60006020828403121561390c578081fd5b8151610e6d81614be9565b600060208284031215613928578081fd5b5051919050565b60008060408385031215613941578182fd5b82359150602083013567ffffffffffffffff81111561395e578182fd5b61396a858286016131b0565b9150509250929050565b60008060408385031215613986578182fd5b82519150602083015167ffffffffffffffff8111156139a3578182fd5b61396a858286016131fc565b6000602082840312156139c0578081fd5b8135610e6d81614bf7565b6000602082840312156139dc578081fd5b8151610e6d81614bf7565b6000602082840312156139f8578081fd5b815167ffffffffffffffff811115613a0e578182fd5b61099e848285016131fc565b600060208284031215613a2b578081fd5b813567ffffffffffffffff811115613a41578182fd5b61099e8482850161323f565b60008060408385031215613a5f578182fd5b823567ffffffffffffffff811115613a75578283fd5b613a818582860161323f565b925050602083013561361d81614bc7565b60008060008060808587031215613aa7578182fd5b843567ffffffffffffffff811115613abd578283fd5b613ac98782880161323f565b9450506020850135613ada81614bc7565b92506040850135613aea81614bc7565b9396929550929360600135925050565b60008060008060008060a08789031215613b12578384fd5b863567ffffffffffffffff80821115613b29578586fd5b613b358a838b0161323f565b975060208901359150613b4782614bc7565b909550604088013590613b5982614bc7565b9094506060880135935060808801359080821115613b75578384fd5b50613b8289828a01613170565b979a9699509497509295939492505050565b600080600080600080600060c0888a031215613bae578081fd5b873567ffffffffffffffff80821115613bc5578283fd5b613bd18b838c0161323f565b985060208a01359150613be382614bc7565b909650604089013590613bf582614bc7565b909550606089013594506080890135935060a08901359080821115613c18578283fd5b50613c258a828b01613170565b989b979a50959850939692959293505050565b60008060008060608587031215613c4d578182fd5b843567ffffffffffffffff80821115613c64578384fd5b613c708883890161323f565b955060208701359150613c8282614bc7565b90935060408601359080821115613c97578384fd5b50613ca4878288016130b0565b95989497509550505050565b600080600080600060808688031215613cc7578283fd5b853567ffffffffffffffff80821115613cde578485fd5b613cea89838a0161323f565b965060208801359150613cfc82614bc7565b9094506040870135935060608701359080821115613d18578283fd5b50613d2588828901613170565b969995985093965092949392505050565b600080600060408486031215613d4a578081fd5b833567ffffffffffffffff80821115613d61578283fd5b613d6d8783880161323f565b94506020860135915080821115613d82578283fd5b50613d8f868287016130b0565b9497909650939450505050565b60008060408385031215613dae578182fd5b823567ffffffffffffffff811115613dc4578283fd5b613dd08582860161323f565b95602094909401359450505050565b600060208284031215613df0578081fd5b5035919050565b73ffffffffffffffffffffffffffffffffffffffff169052565b60008284526020808501945082825b85811015613e5b578135613e3381614bc7565b73ffffffffffffffffffffffffffffffffffffffff1687529582019590820190600101613e20565b509495945050505050565b818352602080840193600091908185020181018584845b87811015613f0257828403895281357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1883603018112613ebb578687fd5b8701803567ffffffffffffffff811115613ed3578788fd5b803603891315613ee1578788fd5b613eee8682898501613f15565b9a87019a9550505090840190600101613e7d565b5091979650505050505050565b15159052565b600082845282826020860137806020848601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f85011685010190509392505050565b60008151808452613f75816020860160208601614b9b565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6000613fb38283614b35565b60808552613fc5608086018284613e11565b915050613fd56020840184614b35565b8583036020870152613fe8838284613e66565b9250505060408301356040850152606083013561400481614bc7565b73ffffffffffffffffffffffffffffffffffffffff81166060860152508091505092915050565b73ffffffffffffffffffffffffffffffffffffffff91909116815260200190565b73ffffffffffffffffffffffffffffffffffffffff9384168152919092166020820152604081019190915260600190565b73ffffffffffffffffffffffffffffffffffffffff939093168352602083019190915267ffffffffffffffff16604082015260600190565b73ffffffffffffffffffffffffffffffffffffffff988916815267ffffffffffffffff978816602080830191909152604080830198909852951515606080830191909152949098166080808a019190915263ffffffff9390931660a089015290861660c088015280516fffffffffffffffffffffffffffffffff90811660e08901529381015184166101008801529384015190921661012086015282015183166101408501520151166101608201526101800190565b602080825282518282018190526000919060409081850190868401855b82811015613f02578151805173ffffffffffffffffffffffffffffffffffffffff1685528681015160ff168786015285015167ffffffffffffffff168585015260609093019290850190600101614188565b901515815260200190565b90815260200190565b94855260208501939093526040840191909152606083015273ffffffffffffffffffffffffffffffffffffffff16608082015260a00190565b60008382526040602083015261099e6040830184613f5d565b7fffffffff0000000000000000000000000000000000000000000000000000000091909116815260200190565b600060208252610e6d6020830184613f5d565b6020808252600c908201527f494e56414c49445f415247530000000000000000000000000000000000000000604082015260600190565b60208082526016908201527f494e56414c49445f57414954494e475f504552494f4400000000000000000000604082015260600190565b60208082526013908201527f494e495449414c495a45445f414c524541445900000000000000000000000000604082015260600190565b60208082526025908201527f444953414c4c4f5745445f4f4e5f494d504c454d454e544154494f4e5f434f4e60408201527f5452414354000000000000000000000000000000000000000000000000000000606082015260800190565b60208082526029908201527f4e4f545f46524f4d5f57414c4c45545f4f525f4f574e45525f4f525f57414c4c60408201527f45545f4c4f434b45440000000000000000000000000000000000000000000000606082015260800190565b60208082526018908201527f4e4f545f414c4c4f5745445f544f5f5345545f4f574e45520000000000000000604082015260600190565b6020808252600d908201527f494e56414c49445f4f574e455200000000000000000000000000000000000000604082015260600190565b6020808252600d908201527f494e56414c49445f56414c554500000000000000000000000000000000000000604082015260600190565b91825273ffffffffffffffffffffffffffffffffffffffff16602082015260400190565b92835273ffffffffffffffffffffffffffffffffffffffff9190911660208301521515604082015260600190565b600084825260406020830152611030604083018486613e11565b600084825260406020830152611030604083018486613e66565b600085825284602083015273ffffffffffffffffffffffffffffffffffffffff8416604083015260806060830152614546608083018451613df7565b602083015160a0830152604083015161456260c0840182613df7565b50606083015160e08301526080830151610100818185015260a08501519150610120828186015260c0860151925061014061459f81870185613df7565b60e087015193506145b4610160870185613f0f565b82870151935080610180870152506145d06101c0860184613f5d565b9250808601519150507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80848303016101a0850152611ae98282613f5d565b6000848252836020830152606060408301526110306060830184613fa7565b60008582528460208301526080604083015261464c6080830185613fa7565b905073ffffffffffffffffffffffffffffffffffffffff8316606083015295945050505050565b600087825286602083015260c0604083015261469260c0830187613fa7565b73ffffffffffffffffffffffffffffffffffffffff958616606084015293909416608082015260a00152949350505050565b600089825288602083015260e060408301526146e360e0830189613fa7565b73ffffffffffffffffffffffffffffffffffffffff88811660608501528716608084015260a0830186905282810360c0840152614721818587613f15565b9b9a5050505050505050505050565b60006101008b83528a60208401528060408401526147508184018b613fa7565b905073ffffffffffffffffffffffffffffffffffffffff808a1660608501528089166080850152508660a08401528560c084015282810360e0840152614797818587613f15565b9c9b505050505050505050505050565b600087825286602083015260a060408301526147c660a0830187613fa7565b73ffffffffffffffffffffffffffffffffffffffff8616606084015282810360808401526147f5818587613e11565b9998505050505050505050565b600088825287602083015260c0604083015261482160c0830188613fa7565b73ffffffffffffffffffffffffffffffffffffffff8716606084015285608084015282810360a0840152614856818587613f15565b9a9950505050505050505050565b6000868252856020830152608060408301526148836080830186613fa7565b8281036060840152611ae9818587613e11565b6000858252846020830152608060408301526148b56080830185613fa7565b905082606083015295945050505050565b60008582527fffffffff0000000000000000000000000000000000000000000000000000000085166020830152836040830152608060608301526111a66080830184613f5d565b95865273ffffffffffffffffffffffffffffffffffffffff94851660208701529284166040860152921660608401526080830191909152151560a082015260c00190565b600089825273ffffffffffffffffffffffffffffffffffffffff808a166020840152808916604084015280881660608401525085608083015260e060a083015261499f60e083018587613f15565b905082151560c08301529998505050505050505050565b60006101008b835273ffffffffffffffffffffffffffffffffffffffff808c166020850152808b166040850152808a166060850152508760808401528660a08401528060c0840152614a0b8184018688613f15565b91505082151560e08301529a9950505050505050505050565b600088825273ffffffffffffffffffffffffffffffffffffffff808916602084015280881660408401525085606083015260c06080830152614a6a60c083018587613f15565b905082151560a083015298975050505050505050565b918252602082015260400190565b9182521515602082015260400190565b67ffffffffffffffff91909116815260200190565b60405181810167ffffffffffffffff81118282101715614acf57fe5b604052919050565b600067ffffffffffffffff821115614aeb57fe5b5060209081020190565b600067ffffffffffffffff821115614b0957fe5b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112614b69578283fd5b830160208101925035905067ffffffffffffffff811115614b8957600080fd5b6020810236038313156130f257600080fd5b60005b83811015614bb6578181015183820152602001614b9e565b838111156129095750506000910152565b73ffffffffffffffffffffffffffffffffffffffff81168114612b4757600080fd5b8015158114612b4757600080fd5b7fffffffff0000000000000000000000000000000000000000000000000000000081168114612b4757600080fdfe53616665436173743a2076616c756520646f65736e27742066697420696e20313238206269747353616665436173743a2076616c756520646f65736e27742066697420696e2036342062697473a26469706673582212202ca99f5e7e4d1b0a6eb705ed669ff26c31649dc87a12623ed9419436363c4f2264736f6c63430007060033
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 27 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.