UNPKG

@biconomy/abstractjs

Version:

SDK for Biconomy integration with support for account abstraction, smart accounts, ERC-4337.

181 lines 7.89 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.getMockSafeSigner = exports.signSafeQuote = exports.validateSafeDeployment = exports.formatSignedSafeQuotePayload = exports.computeSafeDomainSeparator = exports.SAFE_SA_PREFIX = void 0; exports.getDataToPrepareSafeTransaction = getDataToPrepareSafeTransaction; const types_kit_1 = require("@safe-global/types-kit"); const viem_1 = require("viem"); const accounts_1 = require("viem/accounts"); const ForwarderAbi_1 = require("../../../constants/abi/ForwarderAbi.js"); exports.SAFE_SA_PREFIX = "0x177eee04"; const SAFE_DOMAIN_TYPEHASH = "0x47e79534a245952e8b16893a336b85a3d9ea9fa8c573f3d803afb92a79469218"; const computeSafeDomainSeparator = (safeAddress, chainId) => { return (0, viem_1.keccak256)((0, viem_1.encodeAbiParameters)([ { type: "bytes32" }, { type: "uint256" }, { type: "address" } ], [SAFE_DOMAIN_TYPEHASH, BigInt(chainId), safeAddress])); }; exports.computeSafeDomainSeparator = computeSafeDomainSeparator; const formatSignedSafeQuotePayload = (safeAccount, quoteParams, safeTxData, signatures) => { const { quote } = quoteParams; console.log("safeAccount:", safeAccount); const encodedSafeTxnData = (0, viem_1.encodeAbiParameters)([ { name: "safeAccount", type: "address" }, { name: "safeTxnData", type: "tuple", components: [ { name: "ogDomainSeparator", type: "bytes32" }, { name: "to", type: "address" }, { name: "value", type: "uint256" }, { name: "data", type: "bytes" }, { name: "operation", type: "uint8" }, { name: "safeTxGas", type: "uint256" }, { name: "baseGas", type: "uint256" }, { name: "gasPrice", type: "uint256" }, { name: "gasToken", type: "address" }, { name: "refundReceiver", type: "address" }, { name: "nonce", type: "uint256" }, { name: "signatures", type: "bytes" } ] } ], [ safeAccount, { ogDomainSeparator: safeTxData.ogDomainSeparator, to: safeTxData.to, value: safeTxData.value, data: safeTxData.data, operation: safeTxData.operation, safeTxGas: safeTxData.safeTxGas, baseGas: safeTxData.baseGas, gasPrice: safeTxData.gasPrice, gasToken: safeTxData.gasToken, refundReceiver: safeTxData.refundReceiver, nonce: safeTxData.nonce, signatures } ]); return { ...quote, signature: (0, viem_1.concatHex)([exports.SAFE_SA_PREFIX, encodedSafeTxnData]) }; }; exports.formatSignedSafeQuotePayload = formatSignedSafeQuotePayload; const getInvolvedChainIds = (quote) => { const { userOps, paymentInfo } = quote; const isSponsored = paymentInfo.sponsored === true; const relevantUserOps = isSponsored ? userOps.slice(1) : userOps; const chainIds = relevantUserOps.map((op) => Number(op.chainId)); return [...new Set(chainIds)]; }; const validateSafeDeployment = async (client, quote, safeAddress) => { const chainIds = getInvolvedChainIds(quote); const deploymentChecks = await Promise.all(chainIds.map(async (chainId) => { const deployment = client.account.deploymentOn(chainId, false); if (!deployment) { return { chainId, deployed: false, reason: "no deployment config" }; } const publicClient = (0, viem_1.createPublicClient)({ chain: deployment.client.chain, transport: (0, viem_1.http)() }); const code = await publicClient.getCode({ address: safeAddress }); const isDeployed = code !== undefined && code !== "0x"; return { chainId, deployed: isDeployed }; })); const undeployedChains = deploymentChecks.filter((check) => !check.deployed); if (undeployedChains.length > 0) { const chainList = undeployedChains.map((c) => c.chainId).join(", "); throw new Error(`Safe at ${safeAddress} is not deployed on chains: ${chainList}. Safe must be deployed on all chains involved in the supertransaction.`); } }; exports.validateSafeDeployment = validateSafeDeployment; const signSafeQuote = async (client, parameters) => { const { fusionQuote, signedSafeTxn } = parameters; await (0, exports.validateSafeDeployment)(client, fusionQuote.quote, parameters.safeAccount); const txnData = signedSafeTxn.data; const signatures = signedSafeTxn.encodedSignatures(); const ogDomainSeparator = (0, exports.computeSafeDomainSeparator)(parameters.safeAccount, fusionQuote.trigger.chainId); const safeTxData = { ogDomainSeparator, to: txnData.to, value: BigInt(txnData.value), data: txnData.data, operation: txnData.operation, safeTxGas: BigInt(txnData.safeTxGas), baseGas: BigInt(txnData.baseGas), gasPrice: BigInt(txnData.gasPrice), gasToken: txnData.gasToken, refundReceiver: txnData.refundReceiver, nonce: BigInt(txnData.nonce), signatures: signatures }; return (0, exports.formatSignedSafeQuotePayload)(parameters.safeAccount, parameters.fusionQuote, safeTxData, signatures); }; exports.signSafeQuote = signSafeQuote; function getDataToPrepareSafeTransaction(client, quoteParams, companionAccount) { const { quote, trigger } = quoteParams; if (trigger.call) { throw new Error("Custom triggers are not supported for Safe fusion transactions"); } if (!trigger.amount) { throw new Error("Amount is required to sign a Safe quote"); } const account = companionAccount ?? client.account; const spender = account.addressOn(trigger.chainId, true); const recipient = trigger.recipientAddress || spender; const { version } = account.deploymentOn(trigger.chainId, true); const ethForwarderAddress = version.ethForwarderAddress; const amount = trigger.approvalAmount ?? trigger.amount; let to; let value; let calldata; if (trigger.tokenAddress === viem_1.zeroAddress) { const forwardCalldata = (0, viem_1.encodeFunctionData)({ abi: ForwarderAbi_1.ForwarderAbi, functionName: "forward", args: [recipient] }); to = ethForwarderAddress; value = amount; calldata = forwardCalldata; } else { const approveCalldata = (0, viem_1.encodeFunctionData)({ abi: viem_1.erc20Abi, functionName: "approve", args: [spender, amount] }); to = trigger.tokenAddress; value = 0n; calldata = approveCalldata; } const dataWithHash = (0, viem_1.concatHex)([calldata, quote.hash]); return { to, value: value.toString(), data: dataWithHash, operation: types_kit_1.OperationType.Call }; } const getMockSafeSigner = (safeAddress) => { return (0, accounts_1.toAccount)({ address: safeAddress, async signMessage(_) { throw new Error("signMessage is not supported for Safe-owned signer. " + "Use protocol-kit to sign Safe transactions instead."); }, async signTransaction(_) { throw new Error("signTransaction is not supported for Safe-owned signer. " + "Use protocol-kit to sign Safe transactions instead."); }, async signTypedData(_) { throw new Error("signTypedData is not supported for Safe-owned signer. " + "Use protocol-kit to sign Safe transactions instead."); } }); }; exports.getMockSafeSigner = getMockSafeSigner; exports.default = exports.signSafeQuote; //# sourceMappingURL=signSafeQuote.js.map