@biconomy/abstractjs
Version:
SDK for Biconomy integration with support for account abstraction, smart accounts, ERC-4337.
334 lines • 14.7 kB
JavaScript
;
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