UNPKG

@biconomy/permission-context-builder

Version:

ERC-7715 Permission Context Builder utility package.

106 lines 5.95 kB
import { EXECUTE_SINGLE, SmartSessionMode } from "./types/general.js"; import { encodeAbiParameters, encodePacked, keccak256, parseAbiParameters, toBytes, toFunctionSelector } from "viem"; import { ethers } from "ethers"; import { decodeDIDToPublicKey, encodeSigners, KEY_TYPES } from "./utils/methods.js"; import { SignerType } from "./types/signers.js"; import { publicKeyToAddress } from "viem/accounts"; import { parsePublicKey } from "webauthn-p256"; import { mockValidator, multiKeySignerAddress, smartSessionAddress, timeFramePolicyAddress } from "./utils/constants.js"; export const getContext = async (walletClient, { smartAccountAddress, permissions, expiry, signer }) => { if (walletClient.chain === undefined) { throw new Error("Chain is undefined"); } const chainId = walletClient.chain?.id; // Convert the address to a BigInt (equivalent to uint160 in Solidity) const uint160Address = BigInt(smartSessionAddress[chainId]); // Shift left by 32 bits to get the uint192 value const nonceKey = uint160Address << BigInt(32); let signers = []; // if singer type if multiKeySigner if (signer.type === "keys") { const publicKeys = signer.data.ids.map((id) => decodeDIDToPublicKey(id)); publicKeys.forEach(key => { if (key.keyType === KEY_TYPES.secp256k1) { const eoaAddress = publicKeyToAddress(key.key); const signer = { type: SignerType.EOA, data: eoaAddress }; signers.push(signer); } if (key.keyType === KEY_TYPES.secp256r1) { const passkeyPublicKey = parsePublicKey(key.key); const signer = { type: SignerType.PASSKEY, data: encodeAbiParameters(parseAbiParameters('uint256, uint256'), [passkeyPublicKey.x, passkeyPublicKey.y]) }; signers.push(signer); } }); } // const encodedSignersInitData = encodeSigners const signerId = keccak256(encodeAbiParameters(parseAbiParameters("address, bytes"), [multiKeySignerAddress[chainId], encodeSigners(signers)])); const actions = permissions.map(permission => ({ actionId: keccak256(encodePacked(["address", "bytes4"], [permission.data.target, toFunctionSelector(permission.data.functionName)])), actionPolicies: [{ policy: timeFramePolicyAddress[chainId], initData: encodePacked(["uint128", "uint128"], [BigInt(expiry), BigInt(0)]) }] // hardcoded for demo })); const enableData = await _prepareMockEnableData({ smartAccountAddress, signers, walletClient, userOpPolicies: [], // @todo for demo, no userOpPolicies actions, erc1271Policies: [], // @todo for demo, no erc1271Policies }); const context = encodePacked(['uint192', 'bytes', 'bytes32', 'bytes'], [ nonceKey, // 192 bits, 24 bytes EXECUTE_SINGLE, // execution mode, 32 bytes signerId, // 32 bytes encodeEnableSessions(enableData) ]); return context; }; export const encodeEnableSessions = (enableData) => { return ethers.utils.defaultAbiCoder.encode([ 'tuple(address isigner, bytes isignerInitData, tuple(address policy, bytes initData)[] userOpPolicies, tuple(address policy, bytes initData)[] erc1271Policies, tuple(bytes32 actionId, tuple(address policy, bytes initData)[] actionPolicies)[] actions, bytes permissionEnableSig)' ], [{ isigner: enableData.isigner, // Assuming ISigner has an 'address' property isignerInitData: enableData.isignerInitData, userOpPolicies: enableData.userOpPolicies, erc1271Policies: enableData.erc1271Policies, actions: enableData.actions.map(action => ({ actionId: action.actionId, actionPolicies: action.actionPolicies })), permissionEnableSig: enableData.permissionEnableSig }]); }; export const _prepareMockEnableData = async ({ smartAccountAddress, signers, walletClient, userOpPolicies, actions, erc1271Policies, }) => { if (walletClient.account === undefined) { throw new Error("Account is undefined"); } if (walletClient.chain === undefined) { throw new Error("Chain is undefined"); } const chainId = walletClient.chain?.id; // Construct enableData let enableData = { isigner: multiKeySignerAddress[chainId], // Example ISigner address isignerInitData: toBytes(encodeSigners(signers)), userOpPolicies, erc1271Policies, actions, permissionEnableSig: toBytes("") }; const digest = await getDigest(walletClient, smartAccountAddress, enableData, SmartSessionMode.UNSAFE_ENABLE); enableData.permissionEnableSig = toBytes(encodePacked(['address', 'bytes'], [mockValidator[chainId], await walletClient.signMessage({ account: walletClient.account, message: digest })])); return enableData; }; export const getDigest = async (walletClient, smartAccountAddress, enableData, mode) => { if (walletClient.chain === undefined) { throw new Error("Chain is undefined"); } const chainId = walletClient.chain?.id; const abi = [ "function getDigest(address isigner, address account, tuple(address isigner, bytes isignerInitData, tuple(address policy, bytes initData)[] userOpPolicies, tuple(address policy, bytes initData)[] erc1271Policies, tuple(bytes32 actionId, tuple(address policy, bytes initData)[] actionPolicies)[] actions, bytes permissionEnableSig) data, uint8 mode) view returns (bytes32)" ]; const provider = new ethers.providers.JsonRpcProvider(walletClient.chain.rpcUrls.default.http[0]); const contract = new ethers.Contract(smartSessionAddress[chainId], abi, provider); const digest = await contract.getDigest(multiKeySignerAddress[chainId], smartAccountAddress, enableData, mode); return digest; }; //# sourceMappingURL=contextBuilder.js.map