UNPKG

@biconomy/abstractjs

Version:

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

334 lines 14.7 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.signPermitQuote = exports.formatSignedPermitQuotePayload = exports.prepareSignablePermitQuotePayload = exports.getEIP712DomainType = exports.isDefaultEIP712DomainWithSalt = exports.isDefaultEIP712Domain = exports.isLegacyEIP712Domain = void 0; const viem_1 = require("viem"); const actions_1 = require("viem/actions"); const getVersion_1 = require("../../../account/utils/getVersion.js"); const constants_1 = require("../../../constants/index.js"); const TokenWithPermitAbi_1 = require("../../../constants/abi/TokenWithPermitAbi.js"); const PERMIT_PREFIX = "0x177eee02"; const isLegacyEIP712Domain = (name, version, verifyingContract, salt, domainSeparator) => { const DOMAIN_TYPEHASH = (0, viem_1.keccak256)((0, viem_1.encodePacked)(["string"], [ "EIP712Domain(string name,string version,address verifyingContract,bytes32 salt)" ])); const nameHash = (0, viem_1.keccak256)((0, viem_1.encodePacked)(["string"], [name])); const versionHash = (0, viem_1.keccak256)((0, viem_1.encodePacked)(["string"], [version])); const DOMAIN_SEPARATOR = (0, viem_1.keccak256)((0, viem_1.encodeAbiParameters)([ { type: "bytes32" }, { type: "bytes32" }, { type: "bytes32" }, { type: "address" }, { type: "bytes32" } ], [DOMAIN_TYPEHASH, nameHash, versionHash, verifyingContract, salt])); return domainSeparator.toLowerCase() === DOMAIN_SEPARATOR.toLowerCase(); }; exports.isLegacyEIP712Domain = isLegacyEIP712Domain; const isDefaultEIP712Domain = (name, version, chainId, verifyingContract, domainSeparator) => { const DOMAIN_TYPEHASH = (0, viem_1.keccak256)((0, viem_1.encodePacked)(["string"], [ "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" ])); const nameHash = (0, viem_1.keccak256)((0, viem_1.encodePacked)(["string"], [name])); const versionHash = (0, viem_1.keccak256)((0, viem_1.encodePacked)(["string"], [version])); const DOMAIN_SEPARATOR = (0, viem_1.keccak256)((0, viem_1.encodeAbiParameters)([ { type: "bytes32" }, { type: "bytes32" }, { type: "bytes32" }, { type: "uint256" }, { type: "address" } ], [ DOMAIN_TYPEHASH, nameHash, versionHash, BigInt(chainId), verifyingContract ])); return domainSeparator.toLowerCase() === DOMAIN_SEPARATOR.toLowerCase(); }; exports.isDefaultEIP712Domain = isDefaultEIP712Domain; const isDefaultEIP712DomainWithSalt = (name, version, chainId, verifyingContract, salt, domainSeparator) => { const DOMAIN_TYPEHASH = (0, viem_1.keccak256)((0, viem_1.encodePacked)(["string"], [ "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract,bytes32 salt)" ])); const nameHash = (0, viem_1.keccak256)((0, viem_1.encodePacked)(["string"], [name])); const versionHash = (0, viem_1.keccak256)((0, viem_1.encodePacked)(["string"], [version])); const DOMAIN_SEPARATOR = (0, viem_1.keccak256)((0, viem_1.encodeAbiParameters)([ { type: "bytes32" }, { type: "bytes32" }, { type: "bytes32" }, { type: "uint256" }, { type: "address" }, { type: "bytes32" } ], [ DOMAIN_TYPEHASH, nameHash, versionHash, BigInt(chainId), verifyingContract, salt ])); return domainSeparator.toLowerCase() === DOMAIN_SEPARATOR.toLowerCase(); }; exports.isDefaultEIP712DomainWithSalt = isDefaultEIP712DomainWithSalt; const getEIP712DomainType = (name, version, chainId, verifyingContract, salt, domainSeparator) => { try { const isValid = (0, exports.isLegacyEIP712Domain)(name, version, verifyingContract, salt, domainSeparator); if (isValid) { return "legacy"; } } catch { } try { const isValid = (0, exports.isDefaultEIP712Domain)(name, version, chainId, verifyingContract, domainSeparator); if (isValid) { return "default"; } } catch { } try { const isValid = (0, exports.isDefaultEIP712DomainWithSalt)(name, version, chainId, verifyingContract, salt, domainSeparator); if (isValid) { return "default-with-salt"; } } catch { } return "invalid"; }; exports.getEIP712DomainType = getEIP712DomainType; const prepareSignablePermitQuotePayload = async (quoteParams, owner, spender, publicClient) => { const { quote, trigger } = quoteParams; if (trigger.call) { throw new Error("Custom triggers are not supported for permit quotes"); } if (!trigger.amount) throw new Error("Amount is required to sign a permit quote"); if (trigger.approvalAmount && trigger.amount !== undefined && trigger.approvalAmount < trigger.amount) { throw new Error(`Approval amount must be bigger or equal with the amount from the trigger (triggerAmount: ${trigger.amount} amount: ${trigger.approvalAmount})`); } const amount = trigger.approvalAmount ?? trigger.amount; try { const values = await (0, actions_1.multicall)(publicClient, { contracts: [ { address: trigger.tokenAddress, abi: TokenWithPermitAbi_1.TokenWithPermitAbi, functionName: "nonces", args: [owner] }, { address: trigger.tokenAddress, abi: TokenWithPermitAbi_1.TokenWithPermitAbi, functionName: "name" }, { address: trigger.tokenAddress, abi: TokenWithPermitAbi_1.TokenWithPermitAbi, functionName: "version" }, { address: trigger.tokenAddress, abi: TokenWithPermitAbi_1.TokenWithPermitAbi, functionName: "DOMAIN_SEPARATOR" }, { address: trigger.tokenAddress, abi: TokenWithPermitAbi_1.TokenWithPermitAbi, functionName: "eip712Domain" } ] }); const [nonce, name, version, domainSeparator, eip712Domain] = values.map((value, i) => { const key = [ "nonce", "name", "version", "domainSeparator", "eip712Domain" ][i]; if (value.status === "success") { return value.result; } if (value.status === "failure") { if (key === "nonce") { throw new Error("Permit signing failed: Token does not implement nonces(). This function is required for EIP-2612 compliance."); } if (key === "domainSeparator") { throw new Error("Permit signing failed: Token does not implement DOMAIN_SEPARATOR(). This function is required for EIP-712 domain separation."); } if (key === "name" || key === "version") { return undefined; } if (key === "eip712Domain") { return []; } } return undefined; }); const [, name_, version_, chainId_, verifyingContract_, salt_] = eip712Domain; const defaultVersion = "1"; if (version?.length >= 0 && version_?.length >= 0) { if (version !== version_) console.warn("Warning: Mismatch between token version() and eip712Domain().version. This may cause permit signature verification to fail."); } if (name?.length >= 0 && name_?.length >= 0) { if (name !== name_) console.warn("Warning: Mismatch between token name() and eip712Domain().name. This may cause permit signature verification to fail."); } if (name === undefined && name_ === undefined) { throw new Error("Permit signing failed: Token name is missing. Neither name() nor eip712Domain().name is available."); } const permitChainId = chainId_ !== undefined ? Number(chainId_) : trigger.chainId; const permitValues = { name: name_ ?? name, version: version_ ?? version ?? defaultVersion, chainId: permitChainId, verifyingContract: verifyingContract_ ?? trigger.tokenAddress, salt: salt_ ?? (0, viem_1.toHex)(permitChainId, { size: 32 }), domainSeparator }; const eip712DomainType = (0, exports.getEIP712DomainType)(permitValues.name, permitValues.version, permitValues.chainId, permitValues.verifyingContract, permitValues.salt, permitValues.domainSeparator); const getDomain = (eip712DomainType) => { switch (eip712DomainType) { case "default": { return { name: permitValues.name, version: permitValues.version, chainId: permitValues.chainId, verifyingContract: permitValues.verifyingContract }; } case "default-with-salt": { return { name: permitValues.name, version: permitValues.version, chainId: permitValues.chainId, verifyingContract: permitValues.verifyingContract, salt: permitValues.salt }; } case "legacy": { return { name: permitValues.name, version: permitValues.version, verifyingContract: permitValues.verifyingContract, salt: permitValues.salt }; } default: throw new Error("Permit signing failed: Domain separator mismatch, please double check the token's permit functionality"); } }; const signablePermitQuotePayload = { domain: getDomain(eip712DomainType), types: { Permit: [ { name: "owner", type: "address" }, { name: "spender", type: "address" }, { name: "value", type: "uint256" }, { name: "nonce", type: "uint256" }, { name: "deadline", type: "uint256" } ] }, primaryType: "Permit", message: { owner: owner, spender: spender, value: amount, nonce, deadline: BigInt(quote.hash) } }; const metadata = { nonce, name: permitValues.name, version: permitValues.version, domainSeparator: permitValues.domainSeparator, owner, spender, amount }; return { signablePayload: signablePermitQuotePayload, metadata }; } catch (error) { const errorMessage = error.message || "Permit signing failed"; console.warn(errorMessage); console.info("Permit signing failed, fallback to onchain mode"); return { fallbackToOnchainMode: true }; } }; exports.prepareSignablePermitQuotePayload = prepareSignablePermitQuotePayload; const formatSignedPermitQuotePayload = (meeVersions, quoteParams, metadata, signature) => { const { quote, trigger } = quoteParams; let encodedSignature; if ((0, getVersion_1.versionIsAtLeast)(meeVersions[0].version.version, constants_1.MEEVersion.V3_0_0)) { encodedSignature = (0, viem_1.encodeAbiParameters)([ { name: "token", type: "address" }, { name: "owner", type: "address" }, { name: "spender", type: "address" }, { name: "domainSeparator", type: "bytes32" }, { name: "permitTypehash", type: "bytes32" }, { name: "amount", type: "uint256" }, { name: "chainId", type: "uint256" }, { name: "nonce", type: "uint256" }, { name: "signature", type: "bytes" } ], [ trigger.tokenAddress, metadata.owner, metadata.spender, metadata.domainSeparator, constants_1.PERMIT_TYPEHASH, metadata.amount, BigInt(trigger.chainId), metadata.nonce, signature ]); } else { const sigComponents = (0, viem_1.parseSignature)(signature); encodedSignature = (0, viem_1.encodeAbiParameters)([ { name: "token", type: "address" }, { name: "spender", type: "address" }, { name: "domainSeparator", type: "bytes32" }, { name: "permitTypehash", type: "bytes32" }, { name: "amount", type: "uint256" }, { name: "chainId", type: "uint256" }, { name: "nonce", type: "uint256" }, { name: "v", type: "uint256" }, { name: "r", type: "bytes32" }, { name: "s", type: "bytes32" } ], [ trigger.tokenAddress, metadata.spender, metadata.domainSeparator, constants_1.PERMIT_TYPEHASH, metadata.amount, BigInt(trigger.chainId), metadata.nonce, sigComponents.v, sigComponents.r, sigComponents.s ]); } return { ...quote, signature: (0, viem_1.concatHex)([PERMIT_PREFIX, encodedSignature]) }; }; exports.formatSignedPermitQuotePayload = formatSignedPermitQuotePayload; const signPermitQuote = async (parameters) => { const { owner, spender, walletClient, meeVersions } = parameters.account; const { fallbackToOnchainMode, signablePayload, metadata } = await (0, exports.prepareSignablePermitQuotePayload)(parameters.fusionQuote, owner, spender, walletClient); if (fallbackToOnchainMode) { return { fallbackToOnchainMode }; } const signature = await walletClient.signTypedData({ ...signablePayload, account: walletClient.account }); const signedPermitQuotePayload = (0, exports.formatSignedPermitQuotePayload)(meeVersions, parameters.fusionQuote, metadata, signature); return { signedPermitQuotePayload }; }; exports.signPermitQuote = signPermitQuote; exports.default = exports.signPermitQuote; //# sourceMappingURL=signPermitQuote.js.map