UNPKG

@lit-protocol/vincent-scaffold-sdk

Version:

Shared build configuration and utilities for Vincent abilities and policies

129 lines (128 loc) 5.93 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.sponsoredGasContractCall = void 0; const infra_1 = require("@account-kit/infra"); const litActionSmartSigner_1 = require("./litActionSmartSigner"); const smart_contracts_1 = require("@account-kit/smart-contracts"); const getAlchemyChainConfig_1 = require("../../../la-helpers/getAlchemyChainConfig"); /** * Handler function for making contract calls * This function handles the preparation, signing, and sending of contract transactions * * @param pkpPublicKey - The PKP public key for transaction signing * @param pkpEthAddress - The ethereum address derived from PKP * @param abi - The ABI of the contract function * @param contractAddress - The contract address * @param functionName - The name of the function to call * @param args - The arguments to pass to the function * @param overrides - Optional transaction overrides (value) * @param chainId - Optional chain ID (defaults to yellowstoneConfig.id) * @param eip7702AlchemyApiKey - The Alchemy API key for gas sponsorship * @param eip7702AlchemyPolicyId - The Alchemy policy ID for gas sponsorship * @returns The UserOperation hash. You must use the alchemy smartAccountClient.waitForUserOperationTransaction() to convert the userOp into a txHash. */ const sponsoredGasContractCall = async ({ pkpPublicKey, abi, contractAddress, functionName, args, overrides = {}, chainId, eip7702AlchemyApiKey, eip7702AlchemyPolicyId, }) => { // Step 1: Encode function data using ethers Interface const iface = new ethers.utils.Interface(abi); const encodedData = iface.encodeFunctionData(functionName, args); console.log("Encoded data:", encodedData); if (!eip7702AlchemyApiKey || !eip7702AlchemyPolicyId) { throw new Error("EIP7702 Alchemy API key and policy ID are required when using Alchemy for gas sponsorship"); } if (!chainId) { throw new Error("Chain ID is required when using Alchemy for gas sponsorship"); } // Convert value override if exists to BigNumber const txValue = overrides.value ? BigInt(overrides.value.toString()) : 0n; // Create LitActionsSmartSigner for EIP-7702 const litSigner = new litActionSmartSigner_1.LitActionsSmartSigner({ pkpPublicKey, chainId, }); // Get the Alchemy chain configuration const alchemyChain = (0, getAlchemyChainConfig_1.getAlchemyChainConfig)(chainId); // Create the Smart Account Client with EIP-7702 mode const smartAccountClient = await (0, smart_contracts_1.createModularAccountV2Client)({ mode: "7702", transport: (0, infra_1.alchemy)({ apiKey: eip7702AlchemyApiKey }), chain: alchemyChain, signer: litSigner, policyId: eip7702AlchemyPolicyId, }); console.log("Smart account client created"); // Prepare the user operation const userOperation = { target: contractAddress, value: txValue, data: encodedData, }; console.log("User operation prepared", userOperation); // Build the user operation const uoStructResponse = await Lit.Actions.runOnce({ waitForResponse: true, name: "buildUserOperation", }, async () => { try { const uoStruct = await smartAccountClient.buildUserOperation({ uo: userOperation, account: smartAccountClient.account, }); // Properly serialize BigInt with a "type" tag return JSON.stringify(uoStruct, (_, v) => typeof v === "bigint" ? { type: "BigInt", value: v.toString() } : v); } catch (e) { console.log("Failed to build user operation, error below"); console.log(e); console.log(e.stack); return ""; } }); if (uoStructResponse === "") { throw new Error("Failed to build user operation"); } // Custom reviver to convert {type: "BigInt", value: "..."} back to BigInt const uoStruct = JSON.parse(uoStructResponse, (_, v) => { if (v && typeof v === "object" && v.type === "BigInt" && typeof v.value === "string") { return BigInt(v.value); } return v; }); console.log("User operation built, starting signing...", uoStruct); // sign the actual user operation with the PKP. // this must be done outside a runOnce call, because all the nodes must initiate a signature for it to be valid const signedUserOperation = await smartAccountClient.signUserOperation({ account: smartAccountClient.account, uoStruct, }); console.log("User operation signed", signedUserOperation); // getting the entry point from the smart account client so we can send the user operation const entryPoint = smartAccountClient.account.getEntryPoint(); // console.log("Entry point", entryPoint); // send the user operation with EIP-7702 delegation in a runOnce // so that we don't submit it more than once const uoHash = await Lit.Actions.runOnce({ waitForResponse: true, name: "sendWithAlchemy", }, async () => { try { // Send the user operation with EIP-7702 delegation const userOpResult = await smartAccountClient.sendRawUserOperation(signedUserOperation, entryPoint.address); console.log(`[@lit-protocol/vincent-ability-morpho/executeOperationWithGasSponsorship] User operation sent`, { userOpHash: userOpResult }); return userOpResult; } catch (e) { console.log("Failed to send user operation, error below"); console.log(e); console.log(e.stack); return ""; } }); if (uoHash === "") { throw new Error("Failed to send user operation"); } return uoHash; }; exports.sponsoredGasContractCall = sponsoredGasContractCall;