@lifi/sdk
Version:
LI.FI Any-to-Any Cross-Chain-Swap SDK
318 lines • 11.8 kB
JavaScript
;
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