UNPKG

@lifi/composer-sdk

Version:

Public Composer SDK for building and submitting flows

96 lines (83 loc) 2.97 kB
import type { ComposeCompileRequest, Flow } from '@lifi/compose-spec'; import { createComposeSdk, materialisers, resources } from '../index.js'; import type { Address } from '../types.js'; import { BASE_URL } from './config.js'; const WETH = '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2'; const USDC = '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48'; export interface InvariantChecksInput { readonly owner: Address; readonly spender: Address; readonly amount: `${bigint}`; readonly minAmountOut: `${bigint}`; } /** * Swap WETH → USDC via LI.FI, then layer two of the newly-exposed invariant * ops on the result. * * Demonstrates: * - `invariant.numeric` asserting the swap output is at least a constant * threshold (`amountOut >= minAmountOut`) — a constant-threshold check that * needs no second on-chain value to compare against. * - `invariant.allowanceAtLeast` asserting the execution proxy still permits * `spender` to move at least the swapped USDC on its behalf. * - Binding a swap's `amountOut` resource handle into both a `uint256` numeric * slot and a `resource` allowance slot. * * Both ops compile to on-chain `AssertRawInvariant` instructions; the run * reverts if either assertion is violated. */ export const buildInvariantChecks = ({ owner, spender, amount, minAmountOut, }: InvariantChecksInput): { flow: Flow; request: ComposeCompileRequest; } => { const sdk = createComposeSdk({ baseUrl: BASE_URL }); // One WETH resource input plus a scalar spender address. const builder = sdk.flow(1, { name: 'invariant-checks', inputs: { amountIn: resources.erc20(WETH, 1), spender: 'address', }, }); // Swap WETH → USDC via LI.FI. const swapOutputs = builder.lifi.swap('swap', { bind: { amountIn: builder.inputs.amountIn }, config: { resourceOut: resources.erc20(USDC, 1), slippage: 0.03, }, }); // Assert the swap produced at least `minAmountOut` USDC. The threshold is a // configured constant, so this is `invariant.numeric` rather than a // handle-vs-handle compare. builder.invariant.numeric('min-out', { bind: { value: swapOutputs.amountOut }, config: { op: 'gte', threshold: minAmountOut }, }); // Assert the execution proxy still grants `spender` an allowance of at least // the swapped USDC amount. builder.invariant.allowanceAtLeast('check-allowance', { bind: { minimumAmount: swapOutputs.amountOut, owner: builder.context.executionAddress, spender: builder.inputs.spender, }, }); const flow = builder.build(); // directDeposit marks amountIn as a fixed amount of WETH pre-deposited into // the VM; the spender address is supplied as a plain scalar input. const request = sdk.request(flow, { signer: owner, inputs: { amountIn: materialisers.directDeposit({ amount }), spender, }, sweepTo: builder.context.sender, }); return { flow, request }; };