Forkchoice Ethereum Mainnet

Address Contract Partially Verified

Address 0x9a52283276A0ec8740DF50bF01B28A80D880eaf2
Balance 0 ETH
Nonce 1
Code Size 6539 bytes
Indexed Transactions 0 (1 on-chain, 1.5% indexed)
External Etherscan · Sourcify

Contract Bytecode

6539 bytes
Copy Bytecode
0x6080604052600436101561001257600080fd5b6000803560e01c908163244d6cb2146100aa57508063309bfb76146100a55780636d61fe70146100a05780637129edce1461009b5780638a91b0e314610096578063d60b347f14610091578063d8ed2b3c1461008c578063ecd05961146100875763ffbb39f81461008257600080fd5b6107d3565b6107b3565b610755565b6106f6565b610670565b61046f565b6101d7565b61012b565b346100e55760203660031901126100e5576040906004356100ca816100e8565b6001600160a01b031681526020818152919020546080908152f35b80fd5b6001600160a01b038116036100f957565b600080fd5b9181601f840112156100f9578235916001600160401b0383116100f957602083818601950101116100f957565b346100f95760803660031901126100f9576101476024356100e8565b6064356001600160401b0381116100f9576101669036906004016100fe565b5050600435600090815260016020908152604080832033845290915290205460ff1660038110156101a4576001036100f95760405160008152602090f35b610735565b60206003198201126100f957600435906001600160401b0382116100f9576101d3916004016100fe565b9091565b6101e0366101a9565b906020916020116100f95790610248602083359361023a61022b610224610211886000526001602052604060002090565b3360009081526020919091526040902090565b5460ff1690565b6102348161074b565b15610849565b018035019060208201913590565b90916000925b8284106102b2576102956102888661027133916000526001602052604060002090565b9060018060a01b0316600052602052604060002090565b805460ff19166001179055565b3360009081526020819052604090206102ae8154610d08565b9055005b6102c56102c0858584610a7f565b610aa1565b6102da836102d4878786610a7f565b01610996565b946040916102f3836102ed848988610a7f565b01610ab6565b83516001600160f81b031990921686830190815260609890981b6001600160601b03191660018901526001600160e01b03191660158801529586601982010396610345601f19988981018352826109d9565b5190209661035e610357338a84610a19565b5115610acb565b6000975b61036d838887610a7f565b61037c60809182810190610b17565b90508a101561040a576001998a91899088878460066103ba6103b5836103af6103a6878b8a610a7f565b89810190610b17565b9061096f565b610b60565b6103c381610b56565b036103d6575b5050505050019850610362565b6103af87946103ef6103a694610400986103f897610a7f565b90810190610b17565b905014610b6a565b88388887846103c9565b50600194985061045f610467939492988a61045361044561043b898d6060610433838389610a7f565b013595610a7f565b6080810190610b17565b9096519687938c8501610c01565b039081018452836109d9565b339088611930565b01929361024e565b604060031981813601126100f95760049081356024356001600160401b0381116100f9576101208185019382360301126100f957606461051d91016104e36104c06104ba83876108cf565b90610850565b63e9ae5c5360e01b916001600160e01b0319916104dc91610901565b1614610849565b6105146105026104fc6104f684886108cf565b9061085e565b906108b2565b90818060081b918160301b9160501b90565b505050936108cf565b506024810135019183602484019301359060ff60f81b809116801560001461057e5750509061055261055c9261056094611908565b92909193336115d4565b1590565b61057057505b5160008152602090f35b905163016754b960e61b8152fd5b600160f81b8103610619575050509061059e908035019060208201913590565b60009291925b8181106105b5575050505050610566565b6105fd61055c6105ce6105c984868961096f565b610996565b6105e56105dc85878a61096f565b8a8101906108cf565b9060206105f387898c61096f565b013592883361127e565b610609576001016105a4565b855163016754b960e61b81528590fd5b03610660579161055c9161064d848061064761064161063b610656998861086f565b9061092d565b60601c90565b9461087d565b92909133610ec1565b6105705750610566565b84516339d2eb5560e01b81528490fd5b610679366101a9565b6020116100f95735600081815260016020908152604080832033845290915281205490919060ff1660038110156101a4576001036106f2578152600160209081526040808320336000908152908352818120805460ff19166002179055918290529020805480156106ed5760001901905580f35b610cf2565b5080fd5b346100f95760203660031901126100f957600435610713816100e8565b60018060a01b0316600052600060205260206040600020541515604051908152f35b634e487b7160e01b600052602160045260246000fd5b600311156101a457565b346100f95760403660031901126100f95760ff61079e602435610777816100e8565b600435600052600160205260406000209060018060a01b0316600052602052604060002090565b541660405160038210156101a4576020918152f35b346100f95760203660031901126100f95760206040516005600435148152f35b346100f95760603660031901126100f9576107fe6044356107f3816100e8565b602435600435610a19565b6040805190602080835283519182602085015260005b8381106108365784604081866000838284010152601f80199101168101030190f35b8581018301518582018301528201610814565b156100f957565b906004116100f95790600490565b906024116100f95760040190602090565b906014116100f95790601490565b90929192836014116100f95783116100f957601401916013190190565b909392938483116100f95784116100f9578101920390565b3590602081106108c0575090565b6000199060200360031b1b1690565b903590601e19813603018212156100f957018035906001600160401b0382116100f9576020019181360383136100f957565b6001600160e01b0319903581811693926004811061091e57505050565b60040360031b82901b16169150565b6001600160601b0319903581811693926014811061094a57505050565b60140360031b82901b16169150565b634e487b7160e01b600052603260045260246000fd5b91908110156109915760051b81013590605e19813603018212156100f9570190565b610959565b356109a0816100e8565b90565b634e487b7160e01b600052604160045260246000fd5b606081019081106001600160401b038211176109d457604052565b6109a3565b90601f801991011681019081106001600160401b038211176109d457604052565b9190604051926020840152604083015260408252610a17826109b9565b565b610a4191610a29919493946109fa565b805160209182012090936001600160a01b03166109fa565b80519083012060405181548082529093918183019083805b838110610a6c5750505050830101604052565b8060051c83015481890152018490610a59565b91908110156109915760051b81013590609e19813603018212156100f9570190565b356001600160f81b0319811681036100f95790565b356001600160e01b0319811681036100f95790565b15610ad257565b60405162461bcd60e51b815260206004820152601860248201527f6475706c6963617465207065726d697373696f6e4861736800000000000000006044820152606490fd5b903590601e19813603018212156100f957018035906001600160401b0382116100f957602001918160051b360383136100f957565b600711156100f957565b600711156101a457565b356109a081610b4c565b15610b7157565b60405162461bcd60e51b815260206004820152602d60248201527f6f6e6c79204f6e654f6620636f6e646974696f6e2063616e2068617665206d7560448201526c6c7469706c6520706172616d7360981b6064820152608490fd5b6001600160401b038116036100f957565b81835290916001600160fb1b0383116100f95760209260051b809284830137010190565b90926040936040830190835281602091604083860152526060926060810160059660608560051b84010197876000945b878610610c45575050505050505050505090565b90919293949596979899605f198282030186528a35605e19843603018112156100f95783018035610c7581610b4c565b60078110156101a45782528881013590610c8e82610bcc565b6001600160401b038092168a84015286810135601e19823603018112156100f9570189813591019181116100f95780861b360382136100f957610cde8a9283928e86818c60019901520191610bdd565b9c0196019601949897969593929190610c31565b634e487b7160e01b600052601160045260246000fd5b60001981146106ed5760010190565b6001600160401b0381116109d45760051b60200190565b6040929181810384136100f957815193602091828401516001600160401b03948582116100f957019181601f840112156100f957825194610d6e86610d17565b95610d7b835197886109d9565b808752858701928660059260051b870101958587116100f957878101945b878610610dac5750505050505050505090565b85518581116100f957820160609081601f19828b0301126100f957845191610dd3836109b9565b8b820151610de081610b4c565b835285820151610def81610bcc565b8c840152810151908782116100f9570188603f820112156100f9578a81015190610e1882610d17565b91610e25875193846109d9565b808352868d840191891b830101918b83116100f95790878e959396949201905b868210610e6057505083945086820152815201950194610d99565b815181528e959182019101610e45565b8051156109915760200190565b80518210156109915760209160051b010190565b906001600160401b038092166004019182116106ed57565b9060206001600160401b03809316019182116106ed57565b90929084611265576000905b604080516001600160f81b0319602080830191825260609790971b6001600160601b03191660218301526001600160e01b031985166035830152919690939091610f3690849086603981015b0396610f2d601f19988981018352826109d9565b51902084610a19565b9384511561120d575b505050508051156111fc5780602080610f5d93518301019101610d2e565b905060005b81518110156111f157610f758183610e7d565b51610fc86104fc868301610fb4610faf610f9c610fa1610f9c85516001600160401b031690565b610e91565b93516001600160401b031690565b610ea9565b6001600160401b0380911691168a8861089a565b8151610fd381610b56565b610fdc81610b56565b15806111db575b15610ff5575050505050505050600090565b81519061100182610b56565b61100a82610b56565b6001809214806111c5575b1561102857505050505050505050600090565b6002835161103581610b56565b61103e81610b56565b14806111af575b1561105857505050505050505050600090565b6003835161106581610b56565b61106e81610b56565b148061119a575b1561108857505050505050505050600090565b6004835161109581610b56565b61109e81610b56565b1480611185575b156110b857505050505050505050600090565b600583516110c581610b56565b6110ce81610b56565b1480611170575b156110e857505050505050505050600090565b600683516110f581610b56565b6110fe81610b56565b1461110f575b505050600101610f62565b600092880183835b611138575b505050501561112d57388080611104565b505050505050600090565b8151805182101561116a578161114d91610e7d565b51831461115c57830183611117565b50505090503880808061111c565b5061111c565b5061117d88840151610e70565b5181146110d5565b5061119288840151610e70565b5181116110a5565b506111a788840151610e70565b518110611075565b506111bc88840151610e70565b51811015611045565b506111d288840151610e70565b51811115611015565b506111e887830151610e70565b51811415610fe3565b505050505050600190565b8351631c49f4d160e01b8152600490fd5b87516001600160f81b031960208201908152600060218301526001600160e01b0319909316603582015261125c9550906112539082603981015b039081018352826109d9565b51902090610a19565b38808080610f3f565b6112786112728684610850565b90610901565b90610ecd565b93949093919291856115c1576000905b6040958651926112d98360209886610f19856020830193849091601992600083526bffffffffffffffffffffffff199060601b16600183015263ffffffff60e01b1660158201520190565b93845115611583575b50505050805115611572578060208061130093518301019101610d2e565b91116115615760005b81518110156111f15761131c8183610e7d565b516113436104fc868301610fb4610faf610f9c610fa1610f9c85516001600160401b031690565b815161134e81610b56565b61135781610b56565b158061154b575b15611370575050505050505050600090565b81519061137c82610b56565b61138582610b56565b600180921480611535575b156113a357505050505050505050600090565b600283516113b081610b56565b6113b981610b56565b148061151f575b156113d357505050505050505050600090565b600383516113e081610b56565b6113e981610b56565b148061150a575b1561140357505050505050505050600090565b6004835161141081610b56565b61141981610b56565b14806114f5575b1561143357505050505050505050600090565b6005835161144081610b56565b61144981610b56565b14806114e0575b1561146357505050505050505050600090565b6006835161147081610b56565b61147981610b56565b1461148a575b505050600101611309565b600092880183835b6114a8575b505050501561112d5738808061147f565b815180518210156114da57816114bd91610e7d565b5183146114cc57830183611492565b505050905038808080611497565b50611497565b506114ed88840151610e70565b518114611450565b5061150288840151610e70565b518111611420565b5061151788840151610e70565b5181106113f0565b5061152c88840151610e70565b518110156113c0565b5061154288840151610e70565b51811115611390565b5061155887830151610e70565b5181141561135e565b8351631ed604b560e21b8152600490fd5b8451631c49f4d160e01b8152600490fd5b6115b8945061125389519182611247602082019586601991600082526000600183015263ffffffff60e01b1660158201520190565b388080806112e2565b6115ce6112728785610850565b9061128e565b93949093919291856118f5576000905b60409586519261162f8360209886610f19856020830193849091601992600083526bffffffffffffffffffffffff199060601b16600183015263ffffffff60e01b1660158201520190565b938451156118b7575b50505050805115611572578060208061165693518301019101610d2e565b91116115615760005b81518110156111f1576116728183610e7d565b516116996104fc868301610fb4610faf610f9c610fa1610f9c85516001600160401b031690565b81516116a481610b56565b6116ad81610b56565b15806118a1575b156116c6575050505050505050600090565b8151906116d282610b56565b6116db82610b56565b60018092148061188b575b156116f957505050505050505050600090565b6002835161170681610b56565b61170f81610b56565b1480611875575b1561172957505050505050505050600090565b6003835161173681610b56565b61173f81610b56565b1480611860575b1561175957505050505050505050600090565b6004835161176681610b56565b61176f81610b56565b148061184b575b1561178957505050505050505050600090565b6005835161179681610b56565b61179f81610b56565b1480611836575b156117b957505050505050505050600090565b600683516117c681610b56565b6117cf81610b56565b146117e0575b50505060010161165f565b600092880183835b6117fe575b505050501561112d573880806117d5565b81518051821015611830578161181391610e7d565b518314611822578301836117e8565b5050509050388080806117ed565b506117ed565b5061184388840151610e70565b5181146117a6565b5061185888840151610e70565b518111611776565b5061186d88840151610e70565b518110611746565b5061188288840151610e70565b51811015611716565b5061189888840151610e70565b518111156116e6565b506118ae87830151610e70565b518114156116b4565b6118ec945061125389519182611247602082019586601991600082526000600183015263ffffffff60e01b1660158201520190565b38808080611638565b6119026112728785610850565b906115e4565b90806014116100f957813560601c92816034116100f957601483013592603401916033190190565b61195891611940919594956109fa565b805160209182012090926001600160a01b03166109fa565b805160209182012084518082558201915b82811061197857505050509050565b8084918701518160051c8401550161196956

Verified Source Code Partial Match

Compiler: v0.8.24+commit.e11b9ed9 EVM: paris Optimization: Yes (200 runs)
CallPolicy.sol 229 lines
pragma solidity ^0.8.0;

import "kernel/sdk/moduleBase/PolicyBase.sol";
import "kernel/utils/ExecLib.sol";
import {IERC7579Account} from "kernel/interfaces/IERC7579Account.sol";

struct Permission {
    CallType callType; // calltype can be CALLTYPE_SINGLE/CALLTYPE_DELEGATECALL
    address target;
    bytes4 selector;
    uint256 valueLimit;
    ParamRule[] rules;
}

struct ParamRule {
    ParamCondition condition;
    uint64 offset;
    bytes32[] params;
}

enum ParamCondition {
    EQUAL,
    GREATER_THAN,
    LESS_THAN,
    GREATER_THAN_OR_EQUAL,
    LESS_THAN_OR_EQUAL,
    NOT_EQUAL,
    ONE_OF
}

enum Status {
    NA,
    Live,
    Deprecated
}

contract CallPolicy is PolicyBase {
    error InvalidCallType();
    error InvalidCallData();
    error CallViolatesParamRule();
    error CallViolatesValueRule();

    mapping(address => uint256) public usedIds;
    mapping(bytes32 id => mapping(address => Status)) public status;
    //mapping(bytes32 id => mapping(bytes32 permissionHash => mapping(address => bytes))) public encodedPermissions;

    function isInitialized(address wallet) external view override returns (bool) {
        return usedIds[wallet] > 0;
    }

    function setPermission(bytes32 _id, bytes32 _permissionHash, address _owner, bytes memory _permission) internal {
        bytes32 slot = keccak256(bytes.concat(bytes32(uint256(uint160(_owner))), keccak256(bytes.concat(_id,_permissionHash))));

        uint256 length = _permission.length;
        assembly {
            // store length on first slot, as usual
            sstore(slot, length)
            for { let cursor := 0x20 } lt(cursor, add(length, 0x20)) { cursor := add(cursor, 0x20) } {
                sstore(add(slot, div(cursor, 0x20)), mload(add(_permission, cursor))) // slot + cursor/32
            }
        }
    }

    function encodedPermissions(bytes32 _id, bytes32 _permissionHash, address _owner) public view returns(bytes memory encoded) {
        bytes32 slot = keccak256(bytes.concat(bytes32(uint256(uint160(_owner))), keccak256(bytes.concat(_id,_permissionHash))));
        uint256 length;
        assembly {
            encoded := mload(0x40)
            length := sload(slot)
            mstore(encoded, length)
            for { let cursor := 0x20 } lt(cursor, add(length, 0x20)) { cursor := add(cursor, 0x20) } {
                mstore(add(encoded, cursor), sload(add(slot, div(cursor, 0x20))))
            }
            mstore(0x40, add(encoded, add(length, 0x20)))
        }
    }

    function checkUserOpPolicy(bytes32 id, PackedUserOperation calldata userOp)
        external
        payable
        override
        returns (uint256)
    {
        require(bytes4(userOp.callData[0:4]) == IERC7579Account.execute.selector);
        ExecMode mode = ExecMode.wrap(bytes32(userOp.callData[4:36]));
        (CallType callType, ExecType execType,,) = ExecLib.decode(mode);
        bytes calldata executionCallData = userOp.callData; // Cache calldata here
        assembly {
            executionCallData.offset :=
                add(add(executionCallData.offset, 0x24), calldataload(add(executionCallData.offset, 0x24)))
            executionCallData.length := calldataload(sub(executionCallData.offset, 0x20))
        }
        if (callType == CALLTYPE_SINGLE) {
            (address target, uint256 value, bytes calldata callData) = ExecLib.decodeSingle(executionCallData);
            bool permissionPass = _checkPermission(msg.sender, id, CALLTYPE_SINGLE, target, callData, value);
            if (!permissionPass) {
                revert CallViolatesParamRule();
            }
        } else if (callType == CALLTYPE_BATCH) {
            Execution[] calldata exec = ExecLib.decodeBatch(executionCallData);
            for (uint256 i = 0; i < exec.length; i++) {
                bool permissionPass =
                    _checkPermission(msg.sender, id, CALLTYPE_SINGLE, exec[i].target, exec[i].callData, exec[i].value);
                if (!permissionPass) {
                    revert CallViolatesParamRule();
                }
            }
        } else if (callType == CALLTYPE_DELEGATECALL) {
            address target = address(bytes20(executionCallData[0:20]));
            bytes calldata callData = executionCallData[20:];
            bool permissionPass = _checkPermission(msg.sender, id, CALLTYPE_DELEGATECALL, target, callData, 0);
            if (!permissionPass) {
                revert CallViolatesParamRule();
            }
        } else {
            revert InvalidCallType();
        }
    }

    function _checkPermission(
        address wallet,
        bytes32 id,
        CallType callType,
        address target,
        bytes calldata data,
        uint256 value
    ) internal returns (bool) {
        bytes4 _data = data.length == 0 ? bytes4(0x0) : bytes4(data[0:4]);
        bytes32 permissionHash = keccak256(abi.encodePacked(callType, target, _data));
        bytes memory encodedPermission = encodedPermissions(id, permissionHash, wallet);

        // try to find the permission with zero address which means ANY target address
        // e.g. allow to call `approve` function of `ANY` ERC20 token contracts
        if (encodedPermission.length == 0) {
            bytes32 permissionHashWithZeroAddress = keccak256(abi.encodePacked(callType, address(0), _data));
            encodedPermission = encodedPermissions(id,permissionHashWithZeroAddress,wallet);
        }

        // if still no permission found, then the call is not allowed
        if (encodedPermission.length == 0) {
            revert InvalidCallData();
        }

        (uint256 allowedValue, ParamRule[] memory rules) = abi.decode(encodedPermission, (uint256, ParamRule[]));

        if (value > allowedValue) {
            revert CallViolatesValueRule();
        }
        for (uint256 i = 0; i < rules.length; i++) {
            ParamRule memory rule = rules[i];
            bytes32 param = bytes32(data[4 + rule.offset:4 + rule.offset + 32]);
            // only ONE_OF condition can have multiple params
            if (rule.condition == ParamCondition.EQUAL && param != rule.params[0]) {
                return false;
            } else if (rule.condition == ParamCondition.GREATER_THAN && param <= rule.params[0]) {
                return false;
            } else if (rule.condition == ParamCondition.LESS_THAN && param >= rule.params[0]) {
                return false;
            } else if (rule.condition == ParamCondition.GREATER_THAN_OR_EQUAL && param < rule.params[0]) {
                return false;
            } else if (rule.condition == ParamCondition.LESS_THAN_OR_EQUAL && param > rule.params[0]) {
                return false;
            } else if (rule.condition == ParamCondition.NOT_EQUAL && param == rule.params[0]) {
                return false;
            } else if (rule.condition == ParamCondition.ONE_OF) {
                bool oneOfStatus = false;
                for (uint256 j = 0; j < rule.params.length; j++) {
                    if (param == rule.params[j]) {
                        oneOfStatus = true;
                        break;
                    }
                }
                if (!oneOfStatus) {
                    return false;
                }
            }
        }
        return true;
    }

    function checkSignaturePolicy(bytes32 id, address sender, bytes32 hash, bytes calldata sig)
        external
        view
        override
        returns (uint256)
    {
        require(status[id][msg.sender] == Status.Live);
        return 0;
    }

    function _parsePermission(bytes calldata _sig) internal pure returns (Permission[] calldata permissions) {
        assembly {
            permissions.offset := add(add(_sig.offset, 32), calldataload(_sig.offset))
            permissions.length := calldataload(sub(permissions.offset, 32))
        }
    }

    function _policyOninstall(bytes32 id, bytes calldata _data) internal override {
        require(status[id][msg.sender] == Status.NA);
        Permission[] calldata permissions = _parsePermission(_data);
        for (uint256 i = 0; i < permissions.length; i++) {
            // check if the permissionHash is unique
            bytes32 permissionHash =
                keccak256(abi.encodePacked(permissions[i].callType, permissions[i].target, permissions[i].selector));
            require(encodedPermissions(id, permissionHash, msg.sender).length == 0, "duplicate permissionHash");

            // check if the params length is correct
            for (uint256 j = 0; j < permissions[i].rules.length; j++) {
                if (permissions[i].rules[j].condition != ParamCondition.ONE_OF) {
                    require(permissions[i].rules[j].params.length == 1, "only OneOf condition can have multiple params");
                }
            }

            setPermission(
                id,permissionHash,msg.sender,
                abi.encode(permissions[i].valueLimit, permissions[i].rules)
            );
        }
        status[id][msg.sender] = Status.Live;
        usedIds[msg.sender]++;
    }

    function _policyOnUninstall(bytes32 id, bytes calldata _data) internal override {
        require(status[id][msg.sender] == Status.Live);
        //delete encodedPermissions[id][msg.sender];
        status[id][msg.sender] = Status.Deprecated;
        usedIds[msg.sender]--;
    }
}
Types.sol 106 lines
pragma solidity ^0.8.23;

// Custom type for improved developer experience
type ExecMode is bytes32;

type CallType is bytes1;

type ExecType is bytes1;

type ExecModeSelector is bytes4;

type ExecModePayload is bytes22;

using {eqModeSelector as ==} for ExecModeSelector global;
using {eqCallType as ==} for CallType global;
using {eqExecType as ==} for ExecType global;

function eqCallType(CallType a, CallType b) pure returns (bool) {
    return CallType.unwrap(a) == CallType.unwrap(b);
}

function eqExecType(ExecType a, ExecType b) pure returns (bool) {
    return ExecType.unwrap(a) == ExecType.unwrap(b);
}

function eqModeSelector(ExecModeSelector a, ExecModeSelector b) pure returns (bool) {
    return ExecModeSelector.unwrap(a) == ExecModeSelector.unwrap(b);
}

type ValidationMode is bytes1;

type ValidationId is bytes21;

type ValidationType is bytes1;

type PermissionId is bytes4;

type PolicyData is bytes22; // 2bytes for flag on skip, 20 bytes for validator address

type PassFlag is bytes2;

using {vModeEqual as ==} for ValidationMode global;
using {vTypeEqual as ==} for ValidationType global;
using {vIdentifierEqual as ==} for ValidationId global;
using {vModeNotEqual as !=} for ValidationMode global;
using {vTypeNotEqual as !=} for ValidationType global;
using {vIdentifierNotEqual as !=} for ValidationId global;

// nonce = uint192(key) + nonce
// key = mode + (vtype + validationDataWithoutType) + 2bytes parallelNonceKey
// key = 0x00 + 0x00 + 0x000 .. 00 + 0x0000
// key = 0x00 + 0x01 + 0x1234...ff + 0x0000
// key = 0x00 + 0x02 + ( ) + 0x000

function vModeEqual(ValidationMode a, ValidationMode b) pure returns (bool) {
    return ValidationMode.unwrap(a) == ValidationMode.unwrap(b);
}

function vModeNotEqual(ValidationMode a, ValidationMode b) pure returns (bool) {
    return ValidationMode.unwrap(a) != ValidationMode.unwrap(b);
}

function vTypeEqual(ValidationType a, ValidationType b) pure returns (bool) {
    return ValidationType.unwrap(a) == ValidationType.unwrap(b);
}

function vTypeNotEqual(ValidationType a, ValidationType b) pure returns (bool) {
    return ValidationType.unwrap(a) != ValidationType.unwrap(b);
}

function vIdentifierEqual(ValidationId a, ValidationId b) pure returns (bool) {
    return ValidationId.unwrap(a) == ValidationId.unwrap(b);
}

function vIdentifierNotEqual(ValidationId a, ValidationId b) pure returns (bool) {
    return ValidationId.unwrap(a) != ValidationId.unwrap(b);
}

type ValidationData is uint256;

type ValidAfter is uint48;

type ValidUntil is uint48;

function getValidationResult(ValidationData validationData) pure returns (address result) {
    assembly {
        result := validationData
    }
}

function packValidationData(ValidAfter validAfter, ValidUntil validUntil) pure returns (uint256) {
    return uint256(ValidAfter.unwrap(validAfter)) << 208 | uint256(ValidUntil.unwrap(validUntil)) << 160;
}

function parseValidationData(uint256 validationData)
    pure
    returns (ValidAfter validAfter, ValidUntil validUntil, address result)
{
    assembly {
        result := validationData
        validUntil := and(shr(160, validationData), 0xffffffffffff)
        switch iszero(validUntil)
        case 1 { validUntil := 0xffffffffffff }
        validAfter := shr(208, validationData)
    }
}
Structs.sol 8 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

struct Execution {
    address target;
    uint256 value;
    bytes callData;
}
ExecLib.sol 209 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

import {ExecMode, CallType, ExecType, ExecModeSelector, ExecModePayload} from "../types/Types.sol";
import {
    CALLTYPE_SINGLE,
    CALLTYPE_BATCH,
    EXECTYPE_DEFAULT,
    EXEC_MODE_DEFAULT,
    EXECTYPE_TRY,
    CALLTYPE_DELEGATECALL
} from "../types/Constants.sol";
import {Execution} from "../types/Structs.sol";

/**
 * @dev ExecLib is a helper library for execution
 */
library ExecLib {
    error ExecutionFailed();

    event TryExecuteUnsuccessful(uint256 batchExecutionindex, bytes result);

    function _execute(ExecMode execMode, bytes calldata executionCalldata)
        internal
        returns (bytes[] memory returnData)
    {
        (CallType callType, ExecType execType,,) = decode(execMode);

        // check if calltype is batch or single
        if (callType == CALLTYPE_BATCH) {
            // destructure executionCallData according to batched exec
            Execution[] calldata executions = decodeBatch(executionCalldata);
            // check if execType is revert or try
            if (execType == EXECTYPE_DEFAULT) returnData = _execute(executions);
            else if (execType == EXECTYPE_TRY) returnData = _tryExecute(executions);
            else revert("Unsupported");
        } else if (callType == CALLTYPE_SINGLE) {
            // destructure executionCallData according to single exec
            (address target, uint256 value, bytes calldata callData) = decodeSingle(executionCalldata);
            returnData = new bytes[](1);
            bool success;
            // check if execType is revert or try
            if (execType == EXECTYPE_DEFAULT) {
                returnData[0] = _execute(target, value, callData);
            } else if (execType == EXECTYPE_TRY) {
                (success, returnData[0]) = _tryExecute(target, value, callData);
                if (!success) emit TryExecuteUnsuccessful(0, returnData[0]);
            } else {
                revert("Unsupported");
            }
        } else if (callType == CALLTYPE_DELEGATECALL) {
            address delegate = address(bytes20(executionCalldata[0:20]));
            bytes calldata callData = executionCalldata[20:];
            _executeDelegatecall(delegate, callData);
        } else {
            revert("Unsupported");
        }
    }

    function _execute(Execution[] calldata executions) internal returns (bytes[] memory result) {
        uint256 length = executions.length;
        result = new bytes[](length);

        for (uint256 i; i < length; i++) {
            Execution calldata _exec = executions[i];
            result[i] = _execute(_exec.target, _exec.value, _exec.callData);
        }
    }

    function _tryExecute(Execution[] calldata executions) internal returns (bytes[] memory result) {
        uint256 length = executions.length;
        result = new bytes[](length);

        for (uint256 i; i < length; i++) {
            Execution calldata _exec = executions[i];
            bool success;
            (success, result[i]) = _tryExecute(_exec.target, _exec.value, _exec.callData);
            if (!success) emit TryExecuteUnsuccessful(i, result[i]);
        }
    }

    function _execute(address target, uint256 value, bytes calldata callData) internal returns (bytes memory result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := mload(0x40)
            calldatacopy(result, callData.offset, callData.length)
            if iszero(call(gas(), target, value, result, callData.length, codesize(), 0x00)) {
                // Bubble up the revert if the call reverts.
                returndatacopy(result, 0x00, returndatasize())
                revert(result, returndatasize())
            }
            mstore(result, returndatasize()) // Store the length.
            let o := add(result, 0x20)
            returndatacopy(o, 0x00, returndatasize()) // Copy the returndata.
            mstore(0x40, add(o, returndatasize())) // Allocate the memory.
        }
    }

    function _tryExecute(address target, uint256 value, bytes calldata callData)
        internal
        returns (bool success, bytes memory result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            result := mload(0x40)
            calldatacopy(result, callData.offset, callData.length)
            success := call(gas(), target, value, result, callData.length, codesize(), 0x00)
            mstore(result, returndatasize()) // Store the length.
            let o := add(result, 0x20)
            returndatacopy(o, 0x00, returndatasize()) // Copy the returndata.
            mstore(0x40, add(o, returndatasize())) // Allocate the memory.
        }
    }

    /// @dev Execute a delegatecall with `delegate` on this account.
    function _executeDelegatecall(address delegate, bytes calldata callData)
        internal
        returns (bool success, bytes memory result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            result := mload(0x40)
            calldatacopy(result, callData.offset, callData.length)
            // Forwards the `data` to `delegate` via delegatecall.
            success := delegatecall(gas(), delegate, result, callData.length, codesize(), 0x00)
            mstore(result, returndatasize()) // Store the length.
            let o := add(result, 0x20)
            returndatacopy(o, 0x00, returndatasize()) // Copy the returndata.
            mstore(0x40, add(o, returndatasize())) // Allocate the memory.
        }
    }

    function decode(ExecMode mode)
        internal
        pure
        returns (CallType _calltype, ExecType _execType, ExecModeSelector _modeSelector, ExecModePayload _modePayload)
    {
        assembly {
            _calltype := mode
            _execType := shl(8, mode)
            _modeSelector := shl(48, mode)
            _modePayload := shl(80, mode)
        }
    }

    function encode(CallType callType, ExecType execType, ExecModeSelector mode, ExecModePayload payload)
        internal
        pure
        returns (ExecMode)
    {
        return ExecMode.wrap(
            bytes32(abi.encodePacked(callType, execType, bytes4(0), ExecModeSelector.unwrap(mode), payload))
        );
    }

    function encodeSimpleBatch() internal pure returns (ExecMode mode) {
        mode = encode(CALLTYPE_BATCH, EXECTYPE_DEFAULT, EXEC_MODE_DEFAULT, ExecModePayload.wrap(0x00));
    }

    function encodeSimpleSingle() internal pure returns (ExecMode mode) {
        mode = encode(CALLTYPE_SINGLE, EXECTYPE_DEFAULT, EXEC_MODE_DEFAULT, ExecModePayload.wrap(0x00));
    }

    function getCallType(ExecMode mode) internal pure returns (CallType calltype) {
        assembly {
            calltype := mode
        }
    }

    function decodeBatch(bytes calldata callData) internal pure returns (Execution[] calldata executionBatch) {
        /*
         * Batch Call Calldata Layout
         * Offset (in bytes)    | Length (in bytes) | Contents
         * 0x0                  | 0x4               | bytes4 function selector
        *  0x4                  | -                 |
        abi.encode(IERC7579Execution.Execution[])
         */
        // solhint-disable-next-line no-inline-assembly
        assembly ("memory-safe") {
            let dataPointer := add(callData.offset, calldataload(callData.offset))

            // Extract the ERC7579 Executions
            executionBatch.offset := add(dataPointer, 32)
            executionBatch.length := calldataload(dataPointer)
        }
    }

    function encodeBatch(Execution[] memory executions) internal pure returns (bytes memory callData) {
        callData = abi.encode(executions);
    }

    function decodeSingle(bytes calldata executionCalldata)
        internal
        pure
        returns (address target, uint256 value, bytes calldata callData)
    {
        target = address(bytes20(executionCalldata[0:20]));
        value = uint256(bytes32(executionCalldata[20:52]));
        callData = executionCalldata[52:];
    }

    function encodeSingle(address target, uint256 value, bytes memory callData)
        internal
        pure
        returns (bytes memory userOpCalldata)
    {
        userOpCalldata = abi.encodePacked(target, value, callData);
    }
}
Constants.sol 43 lines
pragma solidity ^0.8.0;

import {CallType, ExecType, ExecModeSelector} from "./Types.sol";
import {PassFlag, ValidationMode, ValidationType} from "./Types.sol";
import {ValidationData} from "./Types.sol";
// Default CallType

CallType constant CALLTYPE_SINGLE = CallType.wrap(0x00);
// Batched CallType
CallType constant CALLTYPE_BATCH = CallType.wrap(0x01);
// @dev Implementing delegatecall is OPTIONAL!
// implement delegatecall with extreme care.
CallType constant CALLTYPE_DELEGATECALL = CallType.wrap(0xFF);

// @dev default behavior is to revert on failure
// To allow very simple accounts to use mode encoding, the default behavior is to revert on failure
// Since this is value 0x00, no additional encoding is required for simple accounts
ExecType constant EXECTYPE_DEFAULT = ExecType.wrap(0x00);
// @dev account may elect to change execution behavior. For example "try exec" / "allow fail"
ExecType constant EXECTYPE_TRY = ExecType.wrap(0x01);

ExecModeSelector constant EXEC_MODE_DEFAULT = ExecModeSelector.wrap(bytes4(0x00000000));

PassFlag constant SKIP_USEROP = PassFlag.wrap(0x0001);
PassFlag constant SKIP_SIGNATURE = PassFlag.wrap(0x0002);

// FLAG
ValidationMode constant VALIDATION_MODE_DEFAULT = ValidationMode.wrap(0x00);
ValidationMode constant VALIDATION_MODE_ENABLE = ValidationMode.wrap(0x01);
ValidationMode constant VALIDATION_MODE_INSTALL = ValidationMode.wrap(0x02);

// TYPES, ENUM
ValidationType constant VALIDATION_TYPE_SUDO = ValidationType.wrap(0x00);
ValidationType constant VALIDATION_TYPE_VALIDATOR = ValidationType.wrap(0x01);
ValidationType constant VALIDATION_TYPE_PERMISSION = ValidationType.wrap(0x02);

// ERC4337 constants
uint256 constant SIG_VALIDATION_FAILED_UINT = 1;
ValidationData constant SIG_VALIDATION_FAILED = ValidationData.wrap(SIG_VALIDATION_FAILED_UINT);

// ERC-1271 constants
bytes4 constant ERC1271_MAGICVALUE = 0x1626ba7e;
bytes4 constant ERC1271_INVALID = 0xffffffff;
PolicyBase.sol 38 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IPolicy} from "../../interfaces/IERC7579Modules.sol";
import {PackedUserOperation} from "../../interfaces/PackedUserOperation.sol";

abstract contract PolicyBase is IPolicy {
    function onInstall(bytes calldata data) external payable {
        bytes32 id = bytes32(data[0:32]);
        bytes calldata _data = data[32:];
        _policyOninstall(id, _data);
    }

    function onUninstall(bytes calldata data) external payable {
        bytes32 id = bytes32(data[0:32]);
        bytes calldata _data = data[32:];
        _policyOnUninstall(id, _data);
    }

    function isModuleType(uint256 id) external pure returns (bool) {
        return id == 5;
    }

    function isInitialized(address) external view virtual returns (bool); // TODO : not sure if this is the right way to do it
    function checkUserOpPolicy(bytes32 id, PackedUserOperation calldata userOp)
        external
        payable
        virtual
        returns (uint256);
    function checkSignaturePolicy(bytes32 id, address sender, bytes32 hash, bytes calldata sig)
        external
        view
        virtual
        returns (uint256);

    function _policyOninstall(bytes32 id, bytes calldata _data) internal virtual;
    function _policyOnUninstall(bytes32 id, bytes calldata _data) internal virtual;
}
IERC7579Account.sol 109 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;

import {CallType, ExecType, ExecMode} from "../utils/ExecLib.sol";
import {PackedUserOperation} from "./PackedUserOperation.sol";

struct Execution {
    address target;
    uint256 value;
    bytes callData;
}

interface IERC7579Account {
    event ModuleInstalled(uint256 moduleTypeId, address module);
    event ModuleUninstalled(uint256 moduleTypeId, address module);

    /**
     * @dev Executes a transaction on behalf of the account.
     *         This function is intended to be called by ERC-4337 EntryPoint.sol
     * @dev Ensure adequate authorization control: i.e. onlyEntryPointOrSelf
     *
     * @dev MSA MUST implement this function signature.
     * If a mode is requested that is not supported by the Account, it MUST revert
     * @param mode The encoded execution mode of the transaction. See ModeLib.sol for details
     * @param executionCalldata The encoded execution call data
     */
    function execute(ExecMode mode, bytes calldata executionCalldata) external payable;

    /**
     * @dev Executes a transaction on behalf of the account.
     *         This function is intended to be called by Executor Modules
     * @dev Ensure adequate authorization control: i.e. onlyExecutorModule
     *
     * @dev MSA MUST implement this function signature.
     * If a mode is requested that is not supported by the Account, it MUST revert
     * @param mode The encoded execution mode of the transaction. See ModeLib.sol for details
     * @param executionCalldata The encoded execution call data
     */
    function executeFromExecutor(ExecMode mode, bytes calldata executionCalldata)
        external
        payable
        returns (bytes[] memory returnData);

    /**
     * @dev ERC-1271 isValidSignature
     *         This function is intended to be used to validate a smart account signature
     * and may forward the call to a validator module
     *
     * @param hash The hash of the data that is signed
     * @param data The data that is signed
     */
    function isValidSignature(bytes32 hash, bytes calldata data) external view returns (bytes4);

    /**
     * @dev installs a Module of a certain type on the smart account
     * @dev Implement Authorization control of your chosing
     * @param moduleTypeId the module type ID according the ERC-7579 spec
     * @param module the module address
     * @param initData arbitrary data that may be required on the module during `onInstall`
     * initialization.
     */
    function installModule(uint256 moduleTypeId, address module, bytes calldata initData) external payable;

    /**
     * @dev uninstalls a Module of a certain type on the smart account
     * @dev Implement Authorization control of your chosing
     * @param moduleTypeId the module type ID according the ERC-7579 spec
     * @param module the module address
     * @param deInitData arbitrary data that may be required on the module during `onUninstall`
     * de-initialization.
     */
    function uninstallModule(uint256 moduleTypeId, address module, bytes calldata deInitData) external payable;

    /**
     * Function to check if the account supports a certain CallType or ExecType (see ModeLib.sol)
     * @param encodedMode the encoded mode
     */
    function supportsExecutionMode(ExecMode encodedMode) external view returns (bool);

    /**
     * Function to check if the account supports installation of a certain module type Id
     * @param moduleTypeId the module type ID according the ERC-7579 spec
     */
    function supportsModule(uint256 moduleTypeId) external view returns (bool);

    /**
     * Function to check if the account has a certain module installed
     * @param moduleTypeId the module type ID according the ERC-7579 spec
     *      Note: keep in mind that some contracts can be multiple module types at the same time. It
     *            thus may be necessary to query multiple module types
     * @param module the module address
     * @param additionalContext additional context data that the smart account may interpret to
     *                          identifiy conditions under which the module is installed.
     *                          usually this is not necessary, but for some special hooks that
     *                          are stored in mappings, this param might be needed
     */
    function isModuleInstalled(uint256 moduleTypeId, address module, bytes calldata additionalContext)
        external
        view
        returns (bool);

    /**
     * @dev Returns the account id of the smart account
     * @return accountImplementationId the account id of the smart account
     * the accountId should be structured like so:
     *        "vendorname.accountname.semver"
     */
    function accountId() external view returns (string memory accountImplementationId);
}
IERC7579Modules.sol 108 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;

import {PackedUserOperation} from "./PackedUserOperation.sol";

uint256 constant VALIDATION_SUCCESS = 0;
uint256 constant VALIDATION_FAILED = 1;

uint256 constant MODULE_TYPE_VALIDATOR = 1;
uint256 constant MODULE_TYPE_EXECUTOR = 2;
uint256 constant MODULE_TYPE_FALLBACK = 3;
uint256 constant MODULE_TYPE_HOOK = 4;
uint256 constant MODULE_TYPE_POLICY = 5;
uint256 constant MODULE_TYPE_SIGNER = 6;
uint256 constant MODULE_TYPE_ACTION = 7;

interface IModule {
    error AlreadyInitialized(address smartAccount);
    error NotInitialized(address smartAccount);

    /**
     * @dev This function is called by the smart account during installation of the module
     * @param data arbitrary data that may be required on the module during `onInstall`
     * initialization
     *
     * MUST revert on error (i.e. if module is already enabled)
     */
    function onInstall(bytes calldata data) external payable;

    /**
     * @dev This function is called by the smart account during uninstallation of the module
     * @param data arbitrary data that may be required on the module during `onUninstall`
     * de-initialization
     *
     * MUST revert on error
     */
    function onUninstall(bytes calldata data) external payable;

    /**
     * @dev Returns boolean value if module is a certain type
     * @param moduleTypeId the module type ID according the ERC-7579 spec
     *
     * MUST return true if the module is of the given type and false otherwise
     */
    function isModuleType(uint256 moduleTypeId) external view returns (bool);

    /**
     * @dev Returns if the module was already initialized for a provided smartaccount
     */
    function isInitialized(address smartAccount) external view returns (bool);
}

interface IValidator is IModule {
    error InvalidTargetAddress(address target);

    /**
     * @dev Validates a transaction on behalf of the account.
     *         This function is intended to be called by the MSA during the ERC-4337 validaton phase
     *         Note: solely relying on bytes32 hash and signature is not suffcient for some
     * validation implementations (i.e. SessionKeys often need access to userOp.calldata)
     * @param userOp The user operation to be validated. The userOp MUST NOT contain any metadata.
     * The MSA MUST clean up the userOp before sending it to the validator.
     * @param userOpHash The hash of the user operation to be validated
     * @return return value according to ERC-4337
     */
    function validateUserOp(PackedUserOperation calldata userOp, bytes32 userOpHash)
        external
        payable
        returns (uint256);

    /**
     * Validator can be used for ERC-1271 validation
     */
    function isValidSignatureWithSender(address sender, bytes32 hash, bytes calldata data)
        external
        view
        returns (bytes4);
}

interface IExecutor is IModule {}

interface IHook is IModule {
    function preCheck(address msgSender, bytes calldata msgData) external payable returns (bytes memory hookData);
    function postCheck(bytes calldata hookData) external payable returns (bool success);
}

interface IFallback is IModule {}

interface IPolicy is IModule {
    function checkUserOpPolicy(bytes32 id, PackedUserOperation calldata userOp) external payable returns (uint256);
    function checkSignaturePolicy(bytes32 id, address sender, bytes32 hash, bytes calldata sig)
        external
        view
        returns (uint256);
}

interface ISigner is IModule {
    function checkUserOpSignature(bytes32 id, PackedUserOperation calldata userOp, bytes32 userOpHash)
        external
        payable
        returns (uint256);
    function checkSignature(bytes32 id, address sender, bytes32 hash, bytes calldata sig)
        external
        view
        returns (bytes4);
}

interface IAction is IModule {}
PackedUserOperation.sol 28 lines
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.5;

/**
 * User Operation struct
 * @param sender                - The sender account of this request.
 * @param nonce                 - Unique value the sender uses to verify it is not a replay.
 * @param initCode              - If set, the account contract will be created by this constructor/
 * @param callData              - The method call to execute on this account.
 * @param accountGasLimits      - Packed gas limits for validateUserOp and gas limit passed to the callData method call.
 * @param preVerificationGas    - Gas not calculated by the handleOps method, but added to the gas paid.
 *                                Covers batch overhead.
 * @param gasFees                - packed gas fields maxFeePerGas and maxPriorityFeePerGas - Same as EIP-1559 gas parameter.
 * @param paymasterAndData      - If set, this field holds the paymaster address, verification gas limit, postOp gas limit and paymaster-specific extra data
 *                                The paymaster will pay for the transaction instead of the sender.
 * @param signature             - Sender-verified signature over the entire request, the EntryPoint address and the chain ID.
 */
struct PackedUserOperation {
    address sender;
    uint256 nonce;
    bytes initCode;
    bytes callData;
    bytes32 accountGasLimits;
    uint256 preVerificationGas;
    bytes32 gasFees; //maxPriorityFee and maxFeePerGas;
    bytes paymasterAndData;
    bytes signature;
}

Read Contract

checkSignaturePolicy 0x309bfb76 → uint256
encodedPermissions 0xffbb39f8 → bytes
isInitialized 0xd60b347f → bool
isModuleType 0xecd05961 → bool
status 0xd8ed2b3c → uint8
usedIds 0x244d6cb2 → uint256

Write Contract 3 functions

These functions modify contract state and require a wallet transaction to execute.

checkUserOpPolicy 0xd0832c39
bytes32 id
tuple userOp
returns: uint256
onInstall 0x6d61fe70
bytes data
onUninstall 0x8a91b0e3
bytes data

Recent Transactions

This address has 1 on-chain transactions, but only 1.5% of the chain is indexed. Transactions will appear as indexing progresses. View on Etherscan →