UNPKG

@hyperlane-xyz/sdk

Version:

The official SDK for the Hyperlane Network

223 lines 7.93 kB
import { concat, decodeAbiParameters, encodeAbiParameters, encodeFunctionData, encodePacked, keccak256, toHex, zeroAddress, } from 'viem'; /** * Sentinel value: resolves to the contract's token balance (ERC20) or * native balance (ETH) at execution time. Matches QuotedCalls.CONTRACT_BALANCE. */ export const CONTRACT_BALANCE = 2n ** 255n; // ============ SignedQuote ABI tuple ============ // Matches: abi.decode(input, (address, SignedQuote, bytes, bytes32)) // SignedQuote = (bytes context, bytes data, uint48 issuedAt, uint48 expiry, bytes32 salt, address submitter) const signedQuoteTuple = { type: 'tuple', components: [ { name: 'context', type: 'bytes' }, { name: 'data', type: 'bytes' }, { name: 'issuedAt', type: 'uint48' }, { name: 'expiry', type: 'uint48' }, { name: 'salt', type: 'bytes32' }, { name: 'submitter', type: 'address' }, ], }; // ============ Permit2 PermitSingle ABI tuple ============ // Matches: abi.decode(input, (IAllowanceTransfer.PermitSingle, bytes)) const permitDetailsTuple = { type: 'tuple', components: [ { name: 'token', type: 'address' }, { name: 'amount', type: 'uint160' }, { name: 'expiration', type: 'uint48' }, { name: 'nonce', type: 'uint48' }, ], }; const permitSingleTuple = { type: 'tuple', components: [ { name: 'details', ...permitDetailsTuple }, { name: 'spender', type: 'address' }, { name: 'sigDeadline', type: 'uint256' }, ], }; // ============ Salt scoping ============ /** Matches QuotedCalls._scopeSalt: keccak256(abi.encodePacked(caller, salt)) */ export function computeScopedSalt(caller, clientSalt) { return keccak256(encodePacked(['address', 'bytes32'], [caller, clientSalt])); } // ============ Per-command input encoders ============ /** Encode SUBMIT_QUOTE input: abi.encode(address quoter, SignedQuote quote, bytes signature, bytes32 clientSalt) */ export function encodeSubmitQuoteInput(cmd, clientSalt) { return encodeAbiParameters([ { type: 'address' }, signedQuoteTuple, { type: 'bytes' }, { type: 'bytes32' }, ], [ cmd.quoter, { context: cmd.quote.context, data: cmd.quote.data, issuedAt: cmd.quote.issuedAt, expiry: cmd.quote.expiry, salt: cmd.quote.salt, submitter: cmd.quote.submitter, }, cmd.signature, clientSalt, ]); } /** Encode PERMIT2_PERMIT input: abi.encode(IAllowanceTransfer.PermitSingle, bytes signature) */ export function encodePermit2PermitInput(permit2Data) { const { permitSingle, signature } = permit2Data; return encodeAbiParameters([permitSingleTuple, { type: 'bytes' }], [ { details: { token: permitSingle.details.token, amount: permitSingle.details.amount, expiration: permitSingle.details.expiration, nonce: permitSingle.details.nonce, }, spender: permitSingle.spender, sigDeadline: BigInt(permitSingle.sigDeadline), }, signature, ]); } /** Encode PERMIT2_TRANSFER_FROM input: abi.encode(address token, uint160 amount) */ export function encodePermit2TransferFromInput(token, amount) { return encodeAbiParameters([{ type: 'address' }, { type: 'uint160' }], [token, amount]); } /** Encode TRANSFER_FROM input: abi.encode(address token, uint256 amount) */ export function encodeTransferFromInput(token, amount) { return encodeAbiParameters([{ type: 'address' }, { type: 'uint256' }], [token, amount]); } /** Encode TRANSFER_REMOTE input: abi.encode(address warpRoute, uint32 destination, bytes32 recipient, uint256 amount, uint256 value, address token, uint256 approval) */ export function encodeTransferRemoteInput(params) { return encodeAbiParameters([ { type: 'address' }, { type: 'uint32' }, { type: 'bytes32' }, { type: 'uint256' }, { type: 'uint256' }, { type: 'address' }, { type: 'uint256' }, ], [ params.warpRoute, params.destination, params.recipient, params.amount, params.value, params.token, params.approval, ]); } /** Encode TRANSFER_REMOTE_TO input: abi.encode(address router, uint32 destination, bytes32 recipient, uint256 amount, bytes32 targetRouter, uint256 value, address token, uint256 approval) */ export function encodeTransferRemoteToInput(params) { return encodeAbiParameters([ { type: 'address' }, { type: 'uint32' }, { type: 'bytes32' }, { type: 'uint256' }, { type: 'bytes32' }, { type: 'uint256' }, { type: 'address' }, { type: 'uint256' }, ], [ params.router, params.destination, params.recipient, params.amount, params.targetRouter, params.value, params.token, params.approval, ]); } /** Encode SWEEP input: abi.encode(address token) */ export function encodeSweepInput(token) { return encodeAbiParameters([{ type: 'address' }], [token]); } // ============ Execute calldata ============ // ============ ABI definitions ============ const quoteTuple = { type: 'tuple', components: [ { name: 'token', type: 'address' }, { name: 'amount', type: 'uint256' }, ], }; export const quotedCallsAbi = [ { name: 'execute', type: 'function', inputs: [ { name: 'commands', type: 'bytes' }, { name: 'inputs', type: 'bytes[]' }, ], outputs: [], stateMutability: 'payable', }, { name: 'quoteExecute', type: 'function', inputs: [ { name: 'commands', type: 'bytes' }, { name: 'inputs', type: 'bytes[]' }, ], outputs: [ { name: 'results', type: 'tuple[][]', components: quoteTuple.components, }, ], stateMutability: 'nonpayable', }, ]; // ============ Encode helpers ============ function encodeCommandsBytes(commands) { return concat(commands.map((c) => toHex(c, { size: 1 }))); } /** Encode QuotedCalls.execute(commands, inputs) calldata */ export function encodeExecuteCalldata(commands, inputs) { return encodeFunctionData({ abi: quotedCallsAbi, functionName: 'execute', args: [encodeCommandsBytes(commands), inputs], }); } /** Encode QuotedCalls.quoteExecute(commands, inputs) calldata */ export function encodeQuoteExecuteCalldata(commands, inputs) { return encodeFunctionData({ abi: quotedCallsAbi, functionName: 'quoteExecute', args: [encodeCommandsBytes(commands), inputs], }); } /** Decode the return value of quoteExecute into Quote[][] */ export function decodeQuoteExecuteResult(data) { const [results] = decodeAbiParameters([ { type: 'tuple[][]', components: quoteTuple.components, }, ], data); return results.map((perCommand) => perCommand.map((q) => ({ token: q.token, amount: q.amount }))); } /** Sum Quote[][] into totals per token address (normalized to lowercase) */ export function sumQuotesByToken(results) { const totals = new Map(); for (const perCommand of results) { for (const q of perCommand) { const key = q.token.toLowerCase(); totals.set(key, (totals.get(key) ?? 0n) + q.amount); } } return totals; } /** Extract native (address(0)) and ERC20 totals from quote results */ export function extractQuoteTotals(results) { const all = sumQuotesByToken(results); const nativeValue = all.get(zeroAddress) ?? 0n; all.delete(zeroAddress); return { nativeValue, tokenTotals: all }; } //# sourceMappingURL=codec.js.map