UNPKG

@lifi/sdk

Version:

LI.FI Any-to-Any Cross-Chain-Swap SDK

318 lines 11.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.getNativePermit = void 0; const viem_1 = require("viem"); const actions_1 = require("viem/actions"); const abi_js_1 = require("../abi.js"); const getActionWithFallback_js_1 = require("../getActionWithFallback.js"); const utils_js_1 = require("../utils.js"); const constants_js_1 = require("./constants.js"); const EIP7702_DELEGATION_BLOCKLIST = [ '36d3CBD83961868398d056EfBf50f5CE15528c0D', ]; function makeDomainSeparator({ name, version, chainId, verifyingContract, withSalt = false, }) { const nameHash = (0, viem_1.keccak256)((0, viem_1.toBytes)(name)); const versionHash = (0, viem_1.keccak256)((0, viem_1.toBytes)(version)); const encoded = withSalt ? (0, viem_1.encodeAbiParameters)((0, viem_1.parseAbiParameters)('bytes32, bytes32, bytes32, address, bytes32'), [ constants_js_1.EIP712_DOMAIN_TYPEHASH_WITH_SALT, nameHash, versionHash, verifyingContract, (0, viem_1.pad)((0, viem_1.toHex)(chainId), { size: 32 }), ]) : (0, viem_1.encodeAbiParameters)((0, viem_1.parseAbiParameters)('bytes32, bytes32, bytes32, uint256, address'), [ constants_js_1.EIP712_DOMAIN_TYPEHASH, nameHash, versionHash, BigInt(chainId), verifyingContract, ]); return (0, viem_1.keccak256)(encoded); } function validateDomainSeparator({ name, version, chainId, verifyingContract, domainSeparator, }) { if (!name || !domainSeparator) { return { isValid: false, domain: {}, }; } for (const withSalt of [false, true]) { const computedDS = makeDomainSeparator({ name, version, chainId, verifyingContract, withSalt, }); if (domainSeparator.toLowerCase() === computedDS.toLowerCase()) { return { isValid: true, domain: withSalt ? { name, version, verifyingContract, salt: (0, viem_1.pad)((0, viem_1.toHex)(chainId), { size: 32 }), } : { name, version, chainId, verifyingContract, }, }; } } return { isValid: false, domain: {}, }; } const canAccountUseNativePermits = async (client) => { try { const accountCode = await (0, getActionWithFallback_js_1.getActionWithFallback)(client, actions_1.getCode, 'getCode', { address: client.account.address, }); if (!accountCode || accountCode === '0x') { return true; } if ((0, utils_js_1.isDelegationDesignatorCode)(accountCode)) { const codeLC = accountCode.toLowerCase(); const isBlocked = EIP7702_DELEGATION_BLOCKLIST.some((blockedAddress) => codeLC.includes(blockedAddress.toLowerCase())); if (isBlocked) { return false; } return true; } return false; } catch { return false; } }; const getEIP712DomainData = async (client, chainId, tokenAddress) => { try { const multicallAddress = await (0, utils_js_1.getMulticallAddress)(chainId); const contractCalls = [ { address: tokenAddress, abi: abi_js_1.eip2612Abi, functionName: 'eip712Domain', }, { address: tokenAddress, abi: abi_js_1.eip2612Abi, functionName: 'nonces', args: [client.account.address], }, ]; if (multicallAddress) { try { const [eip712DomainResult, noncesResult] = await (0, getActionWithFallback_js_1.getActionWithFallback)(client, actions_1.multicall, 'multicall', { contracts: contractCalls, multicallAddress, }); if (eip712DomainResult.status !== 'success' || noncesResult.status !== 'success' || !eip712DomainResult.result || noncesResult.result === undefined) { throw new Error('EIP-5267 multicall failed'); } const [, name, version, tokenChainId, verifyingContract, salt] = eip712DomainResult.result; if (Number(tokenChainId) !== chainId || verifyingContract.toLowerCase() !== tokenAddress.toLowerCase()) { return undefined; } const hasSalt = salt !== viem_1.zeroHash; const domain = hasSalt ? { name, version, verifyingContract: tokenAddress, salt, } : { name, version, chainId, verifyingContract: tokenAddress, }; return { name, version, domain, permitTypehash: undefined, nonce: noncesResult.result, }; } catch { } } const [eip712DomainResult, noncesResult] = (await Promise.allSettled(contractCalls.map((call) => (0, getActionWithFallback_js_1.getActionWithFallback)(client, actions_1.readContract, 'readContract', call)))); if (eip712DomainResult.status !== 'fulfilled' || noncesResult.status !== 'fulfilled') { return undefined; } const [, name, version, tokenChainId, verifyingContract, salt] = eip712DomainResult.value; if (Number(tokenChainId) !== chainId || verifyingContract.toLowerCase() !== tokenAddress.toLowerCase()) { return undefined; } const hasSalt = salt !== viem_1.zeroHash; const domain = hasSalt ? { name, version, verifyingContract: tokenAddress, salt, } : { name, version, chainId, verifyingContract: tokenAddress, }; return { name, version, domain, permitTypehash: undefined, nonce: noncesResult.value, }; } catch { return undefined; } }; const getContractData = async (client, chainId, tokenAddress) => { try { const eip5267Data = await getEIP712DomainData(client, chainId, tokenAddress); if (eip5267Data) { return eip5267Data; } const multicallAddress = await (0, utils_js_1.getMulticallAddress)(chainId); const contractCalls = [ { address: tokenAddress, abi: abi_js_1.eip2612Abi, functionName: 'name', }, { address: tokenAddress, abi: abi_js_1.eip2612Abi, functionName: 'DOMAIN_SEPARATOR', }, { address: tokenAddress, abi: abi_js_1.eip2612Abi, functionName: 'PERMIT_TYPEHASH', }, { address: tokenAddress, abi: abi_js_1.eip2612Abi, functionName: 'nonces', args: [client.account.address], }, { address: tokenAddress, abi: abi_js_1.eip2612Abi, functionName: 'version', }, ]; if (multicallAddress) { try { const [nameResult, domainSeparatorResult, permitTypehashResult, noncesResult, versionResult,] = await (0, getActionWithFallback_js_1.getActionWithFallback)(client, actions_1.multicall, 'multicall', { contracts: contractCalls, multicallAddress, }); if (nameResult.status !== 'success' || domainSeparatorResult.status !== 'success' || noncesResult.status !== 'success' || !nameResult.result || !domainSeparatorResult.result || noncesResult.result === undefined) { throw new Error('Multicall failed'); } const { isValid, domain } = validateDomainSeparator({ name: nameResult.result, version: versionResult.result ?? '1', chainId, verifyingContract: tokenAddress, domainSeparator: domainSeparatorResult.result, }); if (!isValid) { return undefined; } return { name: nameResult.result, domain, permitTypehash: permitTypehashResult.result, nonce: noncesResult.result, version: versionResult.result ?? '1', }; } catch { } } const [nameResult, domainSeparatorResult, permitTypehashResult, noncesResult, versionResult,] = (await Promise.allSettled(contractCalls.map((call) => (0, getActionWithFallback_js_1.getActionWithFallback)(client, actions_1.readContract, 'readContract', call)))); if (nameResult.status !== 'fulfilled' || domainSeparatorResult.status !== 'fulfilled' || noncesResult.status !== 'fulfilled') { return undefined; } const name = nameResult.value; const version = versionResult.status === 'fulfilled' ? versionResult.value : '1'; const { isValid, domain } = validateDomainSeparator({ name, version, chainId, verifyingContract: tokenAddress, domainSeparator: domainSeparatorResult.value, }); if (!isValid) { return undefined; } return { name, domain, permitTypehash: permitTypehashResult.status === 'fulfilled' ? permitTypehashResult.value : undefined, nonce: noncesResult.value, version, }; } catch { return undefined; } }; const getNativePermit = async (client, { chainId, tokenAddress, spenderAddress, amount }) => { const canUsePermits = await canAccountUseNativePermits(client); if (!canUsePermits) { return undefined; } const contractData = await getContractData(client, chainId, tokenAddress); if (!contractData) { return undefined; } if (contractData.permitTypehash === constants_js_1.DAI_LIKE_PERMIT_TYPEHASH) { return undefined; } const deadline = BigInt(Math.floor(Date.now() / 1000) + 30 * 60).toString(); const message = { owner: client.account.address, spender: spenderAddress, value: amount.toString(), nonce: contractData.nonce.toString(), deadline, }; return { primaryType: 'Permit', domain: contractData.domain, types: constants_js_1.eip2612Types, message, }; }; exports.getNativePermit = getNativePermit; //# sourceMappingURL=getNativePermit.js.map