SPB Git

Repository Public

build-evm.ts 2.3 KB 38 lines typescript

1import { encodeFunctionData, erc20Abi, serializeTransaction, type TransactionSerializableEIP1559 } from 'viem';2import { baseUnits } from './amounts.ts';3import { normalizeAddress } from './address.ts';4import { getAsset, getNetwork } from './networks.ts';5import type { Intent } from './intent.ts';67/** Rebuild the exact unsigned EIP-1559 transaction from an intent. Only native + allowlisted ERC-20 transfer. */8export function buildEvmTx(i: Intent): TransactionSerializableEIP1559 {9  const n = getNetwork(i.network);10  if (n.family !== 'evm' || !i.evm) throw new Error('not an EVM intent');11  if (i.swap) throw new Error('this is a swap intent, not a transfer');12  if (i.bridge) throw new Error('this is a bridge intent, not a transfer');13  if (i.xswap) throw new Error('this is a cross-chain swap intent, not a transfer');14  if (i.chainRef !== n.chainRef) throw new Error('chain id mismatch');15  const asset = getAsset(i.network, i.asset); // throws when token is not allowlisted16  if (asset.decimals !== i.decimals || asset.symbol !== i.symbol) throw new Error('asset metadata mismatch');17  const to = normalizeAddress(i.network, i.to) as `0x${string}`;18  if (to !== i.to) throw new Error('destination not normalized');19  const amount = baseUnits(i.amount);20  if (amount <= 0n) throw new Error('amount must be positive');21  const gas = baseUnits(i.evm.gasLimit), maxFeePerGas = baseUnits(i.evm.maxFeePerGas), tip = baseUnits(i.evm.maxPriorityFeePerGas);22  if (tip > maxFeePerGas) throw new Error('priority fee above max fee');23  if (gas * maxFeePerGas > baseUnits(i.maxFee)) throw new Error('fee exceeds approved maximum');24  const common = { type: 'eip1559' as const, chainId: Number(n.chainRef), nonce: i.evm.nonce, gas, maxFeePerGas, maxPriorityFeePerGas: tip };25  if (asset.id === 'native') {26    if (gas !== 21000n) throw new Error('native transfer must use 21000 gas (EOA destinations only)');27    return { ...common, to, value: amount };28  }29  if (gas > 200_000n) throw new Error('gas limit too high for ERC-20 transfer');30  if (to.toLowerCase() === asset.id.toLowerCase()) throw new Error('destination is the token contract');31  return {32    ...common, to: asset.id as `0x${string}`, value: 0n,33    data: encodeFunctionData({ abi: erc20Abi, functionName: 'transfer', args: [to, amount] }),34  };35}3637export const serializeEvmUnsigned = (i: Intent) => serializeTransaction(buildEvmTx(i));38
Files main
Menu

Theme