Skip to content
Merged
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
12 changes: 12 additions & 0 deletions .claude/settings.local.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"permissions": {
"allow": [
"Bash(bun run format:*)",
"Bash(bun run lint:*)",
"Bash(bunx eslint:*)",
"Bash(bun run check:*)",
"Bash(bun run:*)",
"Bash(bun install:*)"
]
}
}
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
14 changes: 5 additions & 9 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 @@ -49,7 +49,7 @@
"@base-org/account": "^2.5.1",
"@coinbase/wallet-sdk": "^4.3.6",
"@electric-sql/pglite": "^0.3.15",
"@lifi/intent": "0.0.3-alpha.1",
"@lifi/intent": "0.0.4",
"@metamask/sdk": "^0.34.0",
"@safe-global/safe-apps-provider": "~0.18.6",
"@safe-global/safe-apps-sdk": "^9.1.0",
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
30 changes: 26 additions & 4 deletions src/lib/libraries/intentFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,26 @@ 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
]);

const SAME_CHAIN_DURATION_SECONDS = 10 * 60; // 10 minutes
const SAME_CHAIN_EXCLUSIVITY_SECONDS = 12 * 3; // 36 seconds

Expand Down Expand Up @@ -55,12 +67,14 @@ function applyExclusivityOverride(
}

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 All @@ -75,6 +89,7 @@ function toCoreCreateIntentOptions(opts: AppCreateIntentOptions): CreateIntentOp
outputTokens: opts.outputTokens.map(toCoreTokenContext),
verifier: opts.verifier,
account,
outputRecipient: opts.outputRecipient,
lock: {
type: "compact",
resetPeriod: opts.lock.resetPeriod,
Expand All @@ -89,6 +104,7 @@ function toCoreCreateIntentOptions(opts: AppCreateIntentOptions): CreateIntentOp
outputTokens: opts.outputTokens.map(toCoreTokenContext),
verifier: opts.verifier,
account,
outputRecipient: opts.outputRecipient,
lock: {
type: "escrow"
}
Expand Down Expand Up @@ -161,6 +177,8 @@ export class IntentFactory {
applySameChainTimings(intentInstance);
const sameChain = intentInstance.isSameChain();
const intent = intentInstance.order();
if (intent instanceof StandardSolanaIntent)
throw new Error("Compact signing is not supported for Solana intents.");
applyExclusivityOverride(intent, opts.exclusiveFor, sameChain);

const sponsorSignature = await signIntentCompact(intent, account(), this.walletClient);
Expand Down Expand Up @@ -203,6 +221,8 @@ export class IntentFactory {
applySameChainTimings(intentInstance2);
const sameChain2 = intentInstance2.isSameChain();
const intent = intentInstance2.singlechain();
if (intent instanceof StandardSolanaIntent)
throw new Error("Compact deposit and register is not supported for Solana intents.");
applyExclusivityOverride(intent, opts.exclusiveFor, sameChain2);

if (this.preHook) await this.preHook(inputTokens[0].token.chainId);
Expand Down Expand Up @@ -242,6 +262,8 @@ export class IntentFactory {
applySameChainTimings(intentInstance3);
const sameChain3 = intentInstance3.isSameChain();
const intent = intentInstance3.order();
if (intent instanceof StandardSolanaIntent)
throw new Error("openEscrowIntent is not supported for Solana intents.");
applyExclusivityOverride(intent, opts.exclusiveFor, sameChain3);

const inputChain = inputTokens[0].token.chainId;
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 @@ -110,7 +110,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
27 changes: 18 additions & 9 deletions src/lib/screens/IssueIntent.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,16 @@
const resolveExclusiveFor = (value: string): `0x${string}` | undefined =>
isAddress(value, { strict: false }) ? value : undefined;

const resolveRecipient = (value: string): `0x${string}` | undefined =>
isAddress(value, { strict: false }) ? value : undefined;

const intentOptions = $derived.by(
(): AppCreateIntentOptions => ({
exclusiveFor: resolveExclusiveFor(store.exclusiveFor),
inputTokens: store.inputTokens,
outputTokens: store.outputTokens,
verifier: store.verifier,
outputRecipient: resolveRecipient(store.recipient),
lock:
store.intentType === "compact"
? {
Expand Down Expand Up @@ -293,6 +297,19 @@

<SectionCard compact>
<div class="flex flex-col gap-2">
<div class="flex min-w-0 items-center gap-1">
<span class="text-[11px] font-semibold whitespace-nowrap text-gray-500">Recipient</span>
<FormControl
type="text"
size="sm"
className="flex-1"
placeholder="0x... (optional)"
state={store.recipient.length > 0 && !resolveRecipient(store.recipient)
? "error"
: "default"}
bind:value={store.recipient}
/>
</div>
<div class="flex items-center gap-1">
<span class="text-[11px] font-semibold text-gray-500">Verifier</span>
{#if sameChain}
Expand Down Expand Up @@ -330,15 +347,7 @@
</SectionCard>

<div class="mt-2 flex justify-center">
{#if !true}
<button
type="button"
class="h-8 rounded border border-gray-200 bg-white px-3 text-sm font-semibold text-gray-400"
disabled
>
Input must be exactly raw 100 USDC
</button>
{:else if !allowanceCheck}
{#if !allowanceCheck}
<AwaitButton buttonFunction={approveFunction}>
{#snippet name()}
Set allowance
Expand Down
Loading
Loading