@biconomy/permission-context-builder
Version:
ERC-7715 Permission Context Builder utility package.
106 lines • 5.95 kB
JavaScript
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