Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ jobs:
run: bun install --frozen-lockfile
- name: Run checks
run: bun run check
env:
PRIVATE_POLYMER_MAINNET_ZONE_API_KEY: ${{ secrets.PRIVATE_POLYMER_MAINNET_ZONE_API_KEY }}
PRIVATE_POLYMER_TESTNET_ZONE_API_KEY: ${{ secrets.PRIVATE_POLYMER_TESTNET_ZONE_API_KEY }}
PUBLIC_WALLET_CONNECT_PROJECT_ID: ${{ secrets.PUBLIC_WALLET_CONNECT_PROJECT_ID }}
- name: Run unit tests
run: bun run test:unit
- name: Upload coverage artifact
Expand Down
6 changes: 4 additions & 2 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@
"vite": "^7.1.1"
},
"dependencies": {
"@lifi/intent": "0.0.3-alpha.1",
"@electric-sql/pglite": "^0.3.15",
"@lifi/intent": "0.0.4",
"@metamask/sdk": "^0.34.0",
"@sveltejs/adapter-cloudflare": "^7.0.3",
"@wagmi/connectors": "^7.2.1",
Expand Down
6 changes: 3 additions & 3 deletions src/lib/libraries/flowProgress.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { hashStruct, keccak256 } from "viem";
import { compactTypes } from "@lifi/intent";
import { getOutputHash, encodeMandateOutput } from "@lifi/intent";
import { addressToBytes32, bytes32ToAddress } from "@lifi/intent";
import { orderToIntent } from "@lifi/intent";
import { containerToIntent } from "$lib/utils/intent";
import { getOrFetchRpc } from "$lib/libraries/rpcCache";
import type { MandateOutput, OrderContainer } from "@lifi/intent";
import store from "$lib/state.svelte";
Expand Down Expand Up @@ -128,7 +128,7 @@ async function isOutputValidatedOnChain(
async function isInputChainFinalised(chainId: bigint, container: OrderContainer) {
const { order, inputSettler } = container;
const inputChainClient = getClient(chainId);
const intent = orderToIntent(container);
const intent = containerToIntent(container);
const orderId = intent.orderId();

if (
Expand Down Expand Up @@ -185,7 +185,7 @@ export async function getOrderProgressChecks(
fillTransactions: Record<string, `0x${string}`>
): Promise<FlowCheckState> {
try {
const intent = orderToIntent(orderContainer);
const intent = containerToIntent(orderContainer);
const orderId = intent.orderId();
const inputChains = intent.inputChains();
const outputs = orderContainer.order.outputs;
Expand Down
2 changes: 1 addition & 1 deletion src/lib/libraries/intentExecution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {
import { compact_type_hash } from "@lifi/intent";
import { addressToBytes32 } from "@lifi/intent";
import { signMultichainCompact, signStandardCompact } from "@lifi/intent";
import { MultichainOrderIntent, StandardOrderIntent } from "@lifi/intent";
import { MultichainOrderIntent, StandardEVMIntent as StandardOrderIntent } from "@lifi/intent";
import type { NoSignature, Signature } from "@lifi/intent";
import type { TypedDataSigner } from "@lifi/intent";
import { switchWalletChain } from "$lib/utils/walletClientRuntime";
Expand Down
28 changes: 24 additions & 4 deletions src/lib/libraries/intentFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,35 @@ import type {
Signature,
StandardOrder
} from "@lifi/intent";
import {
Intent,
IntentApi,
StandardSolanaIntent,
SOLANA_MAINNET_CHAIN_ID,
SOLANA_TESTNET_CHAIN_ID,
SOLANA_DEVNET_CHAIN_ID
} from "@lifi/intent";
import type { AppCreateIntentOptions, AppTokenContext } from "$lib/appTypes";
import { ERC20_ABI } from "$lib/abi/erc20";
import { Intent } from "@lifi/intent";
import { IntentApi } from "@lifi/intent";
import { store } from "$lib/state.svelte";
import { depositAndRegisterCompact, openEscrowIntent, signIntentCompact } from "./intentExecution";
import { intentDeps } from "./coreDeps";

const SOLANA_CHAIN_IDS = new Set([
SOLANA_MAINNET_CHAIN_ID,
SOLANA_TESTNET_CHAIN_ID,
SOLANA_DEVNET_CHAIN_ID
]);

function toCoreTokenContext(input: AppTokenContext): TokenContext {
const chainId = BigInt(input.token.chainId);
return {
token: {
address: input.token.address,
name: input.token.name,
chainId: BigInt(input.token.chainId),
decimals: input.token.decimals
chainId,
decimals: input.token.decimals,
chainNamespace: SOLANA_CHAIN_IDS.has(chainId) ? "solana" : "eip155"
},
amount: input.amount
};
Expand Down Expand Up @@ -127,6 +141,8 @@ export class IntentFactory {
const inputChain = inputTokens[0].token.chainId;
if (this.preHook) await this.preHook(inputChain);
const intent = new Intent(toCoreCreateIntentOptions(opts), intentDeps).order();
if (intent instanceof StandardSolanaIntent)
throw new Error("Compact signing is not supported for Solana intents.");

const sponsorSignature = await signIntentCompact(intent, account(), this.walletClient);

Expand Down Expand Up @@ -165,6 +181,8 @@ export class IntentFactory {
return async () => {
const { inputTokens, account } = opts;
const intent = new Intent(toCoreCreateIntentOptions(opts), intentDeps).singlechain();
if (intent instanceof StandardSolanaIntent)
throw new Error("Compact deposit and register is not supported for Solana intents.");

if (this.preHook) await this.preHook(inputTokens[0].token.chainId);

Expand Down Expand Up @@ -200,6 +218,8 @@ export class IntentFactory {
return async () => {
const { inputTokens, account } = opts;
const intent = new Intent(toCoreCreateIntentOptions(opts), intentDeps).order();
if (intent instanceof StandardSolanaIntent)
throw new Error("openEscrowIntent is not supported for Solana intents.");

const inputChain = inputTokens[0].token.chainId;
if (this.preHook) await this.preHook(inputChain);
Expand Down
4 changes: 2 additions & 2 deletions src/lib/libraries/intentList.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import {
MULTICHAIN_INPUT_SETTLER_ESCROW,
MULTICHAIN_INPUT_SETTLER_COMPACT
} from "../config";
import { orderToIntent } from "@lifi/intent";
import { bytes32ToAddress, idToToken } from "@lifi/intent";
import { containerToIntent } from "$lib/utils/intent";
import type { OrderContainer, StandardOrder, MultichainOrder } from "@lifi/intent";
import { validateOrderContainerWithReason } from "@lifi/intent";
import { orderValidationDeps } from "./coreDeps";
Expand Down Expand Up @@ -201,7 +201,7 @@ function getContextDetails(orderContainer: OrderContainer): ContextDetails {

export function buildBaseIntentRow(orderContainer: OrderContainer): BaseIntentRow {
const order = orderContainer.order;
const orderId = orderToIntent(orderContainer).orderId();
const orderId = containerToIntent(orderContainer).orderId();
const inputChipsRaw = getInputs(order);
const outputChipsRaw = getOutputs(order);
const chainScope = getChainScope(order);
Expand Down
13 changes: 6 additions & 7 deletions src/lib/libraries/solver.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { BYTES32_ZERO, COIN_FILLER, getChain, getClient, getOracle, type WC } from "$lib/config";
import { hashStruct, maxUint256, parseEventLogs } from "viem";
import type { MandateOutput, OrderContainer } from "@lifi/intent";
import { addressToBytes32, bytes32ToAddress } from "@lifi/intent";
import { addressToBytes32, bytes32ToAddress, StandardSolanaIntent } from "@lifi/intent";
import axios from "axios";
import { POLYMER_ORACLE_ABI } from "$lib/abi/polymeroracle";
import { COIN_FILLER_ABI } from "$lib/abi/outputsettler";
import { ERC20_ABI } from "$lib/abi/erc20";
import { orderToIntent } from "@lifi/intent";
import { containerToIntent } from "$lib/utils/intent";
import { compactTypes } from "@lifi/intent";
import store from "$lib/state.svelte";
import { finaliseIntent } from "./intentExecution";
Expand Down Expand Up @@ -66,7 +66,7 @@ export class Solver {
orderContainer: { order, inputSettler },
outputs
} = args;
const orderId = orderToIntent({ order, inputSettler }).orderId();
const orderId = containerToIntent(args.orderContainer).orderId();

const outputChainId = Number(outputs[0].chainId);
const outputChain = getChain(outputChainId);
Expand Down Expand Up @@ -310,10 +310,9 @@ export class Solver {
const { preHook, postHook, account } = opts;
const { orderContainer, fillTransactionHashes, sourceChainId } = args;
const { order, inputSettler } = orderContainer;
const intent = orderToIntent({
inputSettler,
order
});
const intent = containerToIntent(orderContainer);
if (intent instanceof StandardSolanaIntent)
throw new Error("Finalise is not supported for Solana input intents.");
if (fillTransactionHashes.length !== order.outputs.length) {
throw new Error(
`Fill transaction hash count (${fillTransactionHashes.length}) does not match output count (${order.outputs.length}).`
Expand Down
4 changes: 2 additions & 2 deletions src/lib/screens/FillIntent.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
import ChainActionRow from "$lib/components/ui/ChainActionRow.svelte";
import TokenAmountChip from "$lib/components/ui/TokenAmountChip.svelte";
import store from "$lib/state.svelte";
import { orderToIntent } from "@lifi/intent";
import { containerToIntent } from "$lib/utils/intent";
import { compactTypes } from "@lifi/intent";
import { hashStruct } from "viem";

Expand Down Expand Up @@ -77,7 +77,7 @@
$effect(() => {
refreshValidation;

const orderId = orderToIntent(orderContainer).orderId();
const orderId = containerToIntent(orderContainer).orderId();
if (autoScrolledOrderId === orderId) return;

const outputs = sortOutputsByChain(orderContainer).flatMap(([, chainOutputs]) => chainOutputs);
Expand Down
6 changes: 3 additions & 3 deletions src/lib/screens/Finalise.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
import { SETTLER_ESCROW_ABI } from "$lib/abi/escrow";
import { idToToken } from "@lifi/intent";
import store from "$lib/state.svelte";
import { orderToIntent } from "@lifi/intent";
import { containerToIntent } from "$lib/utils/intent";
import { hashStruct } from "viem";
import { compactTypes } from "@lifi/intent";

Expand All @@ -41,7 +41,7 @@
let refreshClaimed = $state(0);
let claimedByChain = $state<Record<string, boolean>>({});
let claimStatusRun = 0;
const inputChains = $derived(orderToIntent(orderContainer).inputChains());
const inputChains = $derived(containerToIntent(orderContainer).inputChains());
const getInputsForChain = (container: OrderContainer, inputChain: bigint): [bigint, bigint][] => {
const { order } = container;
if ("originChainId" in order) {
Expand Down Expand Up @@ -89,7 +89,7 @@
const { order, inputSettler } = container;
const inputChainClient = getClient(chainId);

const intent = orderToIntent(container);
const intent = containerToIntent(container);
const orderId = intent.orderId();
// Determine the order type.
if (
Expand Down
4 changes: 2 additions & 2 deletions src/lib/screens/IntentList.svelte
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script lang="ts">
import { onDestroy } from "svelte";
import { tick } from "svelte";
import { orderToIntent } from "@lifi/intent";
import { containerToIntent } from "$lib/utils/intent";
import IntentListDetailRow from "$lib/components/IntentListDetailRow.svelte";
import {
buildBaseIntentRow,
Expand Down Expand Up @@ -104,7 +104,7 @@
);

const selectedOrderId = $derived(
selectedOrder ? orderToIntent(selectedOrder).orderId() : undefined
selectedOrder ? containerToIntent(selectedOrder).orderId() : undefined
);
</script>

Expand Down
6 changes: 3 additions & 3 deletions src/lib/screens/ReceiveMessage.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
import ChainActionRow from "$lib/components/ui/ChainActionRow.svelte";
import TokenAmountChip from "$lib/components/ui/TokenAmountChip.svelte";
import store from "$lib/state.svelte";
import { orderToIntent } from "@lifi/intent";
import { containerToIntent } from "$lib/utils/intent";
import { compactTypes } from "@lifi/intent";

// This script needs to be updated to be able to fetch the associated events of fills. Currently, this presents an issue since it can only fill single outputs.
Expand Down Expand Up @@ -110,7 +110,7 @@
$effect(() => {
refreshValidation;

const intent = orderToIntent(orderContainer);
const intent = containerToIntent(orderContainer);
const orderId = intent.orderId();
if (autoScrolledOrderId === orderId) return;

Expand Down Expand Up @@ -167,7 +167,7 @@
description="Click on each output and wait until they turn green. Polymer does not support batch validation. Continue to the right."
>
<div class="space-y-2">
{#each orderToIntent(orderContainer).inputChains() as inputChain}
{#each containerToIntent(orderContainer).inputChains() as inputChain}
<SectionCard compact>
<ChainActionRow chainLabel={getChainName(inputChain)}>
{#snippet action()}
Expand Down
6 changes: 3 additions & 3 deletions src/lib/state.svelte.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import {
transactionReceipts as transactionReceiptsTable
} from "./schema";
import { and, eq } from "drizzle-orm";
import { orderToIntent } from "@lifi/intent";
import { containerToIntent } from "./utils/intent";
import { getOrFetchRpc, invalidateRpcPrefix } from "./libraries/rpcCache";
import {
getCurrentConnection,
Expand All @@ -49,7 +49,7 @@ class Store {
async saveOrderToDb(order: OrderContainer) {
if (!browser) return;
if (!db) await initDb();
const orderId = orderToIntent(order).orderId();
const orderId = containerToIntent(order).orderId();
const now = Math.floor(Date.now() / 1000);
const id =
(order as any).id ?? (typeof crypto !== "undefined" ? crypto.randomUUID() : String(now));
Expand Down Expand Up @@ -89,7 +89,7 @@ class Store {
console.warn("saveOrderToDb db write failed", { orderId, error });
}
}
const idx = this.orders.findIndex((o) => orderToIntent(o).orderId() === orderId);
const idx = this.orders.findIndex((o) => containerToIntent(o).orderId() === orderId);
if (idx >= 0) this.orders[idx] = order;
else this.orders.push(order);
}
Expand Down
29 changes: 29 additions & 0 deletions src/lib/utils/intent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import {
orderToIntent,
SOLANA_MAINNET_CHAIN_ID,
SOLANA_TESTNET_CHAIN_ID,
SOLANA_DEVNET_CHAIN_ID,
StandardEVMIntent,
StandardSolanaIntent,
MultichainOrderIntent
} from "@lifi/intent";
import type { OrderContainer } from "@lifi/intent";

const SOLANA_CHAIN_IDS = new Set([
SOLANA_MAINNET_CHAIN_ID,
SOLANA_TESTNET_CHAIN_ID,
SOLANA_DEVNET_CHAIN_ID
]);

export function containerToIntent(
container: OrderContainer
): StandardEVMIntent | StandardSolanaIntent | MultichainOrderIntent {
const { inputSettler, order } = container;
if (!("originChainId" in order)) {
return orderToIntent({ namespace: "eip155", inputSettler, order });
}
if (SOLANA_CHAIN_IDS.has(order.originChainId)) {
return orderToIntent({ namespace: "solana", inputSettler, order });
}
return orderToIntent({ namespace: "eip155", inputSettler, order });
}
14 changes: 7 additions & 7 deletions src/routes/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
import ConnectWallet from "$lib/screens/ConnectWallet.svelte";
import FlowStepTracker from "$lib/components/ui/FlowStepTracker.svelte";
import store from "$lib/state.svelte";
import { orderToIntent } from "@lifi/intent";
import { containerToIntent } from "$lib/utils/intent";

// Fix bigint so we can json serialize it:
(BigInt.prototype as any).toJSON = function () {
Expand Down Expand Up @@ -67,8 +67,8 @@
const orderContainer = { ...order, allocatorSignature, sponsorSignature };

// Deduplicate: only add if not already present
const orderId = orderToIntent(orderContainer).orderId();
const alreadyExists = store.orders.some((o) => orderToIntent(o).orderId() === orderId);
const orderId = containerToIntent(orderContainer).orderId();
const alreadyExists = store.orders.some((o) => containerToIntent(o).orderId() === orderId);
if (alreadyExists) return;

store.orders.push(orderContainer);
Expand Down Expand Up @@ -100,18 +100,18 @@
let scrollStepProgress = $state(0);
async function importOrderById(orderId: `0x${string}`): Promise<"inserted" | "updated"> {
const importedOrder = await intentApi.getOrderByOnChainOrderId(orderId);
const importedOrderId = orderToIntent(importedOrder).orderId();
const importedOrderId = containerToIntent(importedOrder).orderId();
const existingIndex = store.orders.findIndex(
(o) => orderToIntent(o).orderId() === importedOrderId
(o) => containerToIntent(o).orderId() === importedOrderId
);
await store.saveOrderToDb(importedOrder);
selectedOrder =
store.orders.find((o) => orderToIntent(o).orderId() === importedOrderId) ?? importedOrder;
store.orders.find((o) => containerToIntent(o).orderId() === importedOrderId) ?? importedOrder;
return existingIndex >= 0 ? "updated" : "inserted";
}
async function deleteOrderById(orderId: `0x${string}`): Promise<void> {
await store.deleteOrderFromDb(orderId);
if (selectedOrder && orderToIntent(selectedOrder).orderId() === orderId) {
if (selectedOrder && containerToIntent(selectedOrder).orderId() === orderId) {
selectedOrder = undefined;
}
}
Expand Down
Loading