Skip to content
Draft
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
52 changes: 52 additions & 0 deletions src/lib/abi/polymeroracle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,32 @@ export const POLYMER_ORACLE_ABI = [
outputs: [],
stateMutability: "nonpayable"
},
{
type: "function",
name: "receiveSolanaMessage",
inputs: [
{
name: "proofs",
type: "bytes[]",
internalType: "bytes[]"
}
],
outputs: [],
stateMutability: "nonpayable"
},
{
type: "function",
name: "receiveSolanaMessage",
inputs: [
{
name: "proof",
type: "bytes",
internalType: "bytes"
}
],
outputs: [],
stateMutability: "nonpayable"
},
{
type: "event",
name: "OutputProven",
Expand Down Expand Up @@ -124,6 +150,11 @@ export const POLYMER_ORACLE_ABI = [
name: "ContextOutOfRange",
inputs: []
},
{
type: "error",
name: "InvalidSolanaMessage",
inputs: []
},
{
type: "error",
name: "NotDivisible",
Expand All @@ -145,6 +176,27 @@ export const POLYMER_ORACLE_ABI = [
name: "NotProven",
inputs: []
},
{
type: "error",
name: "NotSolanaMessage",
inputs: []
},
{
type: "error",
name: "SolanaProgramIdMismatch",
inputs: [
{
name: "returnedProgramId",
type: "bytes32",
internalType: "bytes32"
},
{
name: "messageProgramId",
type: "bytes32",
internalType: "bytes32"
}
]
},
{
type: "error",
name: "WrongEventSignature",
Expand Down
22 changes: 11 additions & 11 deletions src/lib/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,18 +109,18 @@ export const WORMHOLE_ORACLE: Partial<Record<number, `0x${string}`>> = {
[base.id]: "0x0000000000000000000000000000000000000000"
};
export const POLYMER_ORACLE: Partial<Record<number, `0x${string}`>> = {
[ethereum.id]: "0x0000003E06000007A224AeE90052fA6bb46d43C9",
[arbitrum.id]: "0x0000003E06000007A224AeE90052fA6bb46d43C9",
[base.id]: "0x0000003E06000007A224AeE90052fA6bb46d43C9",
[megaeth.id]: "0x0000003E06000007A224AeE90052fA6bb46d43C9",
[katana.id]: "0x0000003E06000007A224AeE90052fA6bb46d43C9",
[polygon.id]: "0x0000003E06000007A224AeE90052fA6bb46d43C9",
[bsc.id]: "0x0000003E06000007A224AeE90052fA6bb46d43C9",
[ethereum.id]: "0xe68b03ff7277fbe1a9c5d1f9c5ec30a220e3cb36",
[arbitrum.id]: "0xe68b03ff7277fbe1a9c5d1f9c5ec30a220e3cb36",
[base.id]: "0xe68b03ff7277fbe1a9c5d1f9c5ec30a220e3cb36",
[megaeth.id]: "0xe68b03ff7277fbe1a9c5d1f9c5ec30a220e3cb36",
[katana.id]: "0xe68b03ff7277fbe1a9c5d1f9c5ec30a220e3cb36",
[polygon.id]: "0xe68b03ff7277fbe1a9c5d1f9c5ec30a220e3cb36",
[bsc.id]: "0xe68b03ff7277fbe1a9c5d1f9c5ec30a220e3cb36",
// testnet
[sepolia.id]: "0x00d5b500ECa100F7cdeDC800eC631Aca00BaAC00",
[baseSepolia.id]: "0x00d5b500ECa100F7cdeDC800eC631Aca00BaAC00",
[arbitrumSepolia.id]: "0x00d5b500ECa100F7cdeDC800eC631Aca00BaAC00",
[optimismSepolia.id]: "0x00d5b500ECa100F7cdeDC800eC631Aca00BaAC00",
[sepolia.id]: "0xe68b03ff7277fbe1a9c5d1f9c5ec30a220e3cb36",
[baseSepolia.id]: "0xe68b03ff7277fbe1a9c5d1f9c5ec30a220e3cb36",
[arbitrumSepolia.id]: "0xe68b03ff7277fbe1a9c5d1f9c5ec30a220e3cb36",
[optimismSepolia.id]: "0xe68b03ff7277fbe1a9c5d1f9c5ec30a220e3cb36",
[solanaDevnet.id]: SOLANA_PDAS.devnet.POLYMER_ORACLE,
[solanaMainnet.id]: SOLANA_PDAS.mainnet.POLYMER_ORACLE
};
Expand Down
12 changes: 11 additions & 1 deletion src/lib/libraries/coreDeps.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import {
COIN_FILLER,
INPUT_SETTLER_COMPACT_LIFI,
INPUT_SETTLER_ESCROW_LIFI,
MULTICHAIN_INPUT_SETTLER_COMPACT,
MULTICHAIN_INPUT_SETTLER_ESCROW,
POLYMER_ORACLE,
SOLANA_INPUT_SETTLER_ESCROW,
SOLANA_PDAS,
WORMHOLE_ORACLE
} from "$lib/config";
import { solanaAddressToBytes32 } from "$lib/utils/solana";
import type { IntentDeps, OrderContainerValidationDeps } from "@lifi/intent";

function isNonZeroAddress(value: string | undefined): value is `0x${string}` {
Expand All @@ -25,7 +29,13 @@ export const intentDeps: IntentDeps = {
};

export const orderValidationDeps: OrderContainerValidationDeps = {
inputSettlers: [INPUT_SETTLER_COMPACT_LIFI, MULTICHAIN_INPUT_SETTLER_COMPACT],
inputSettlers: [
INPUT_SETTLER_COMPACT_LIFI,
INPUT_SETTLER_ESCROW_LIFI,
MULTICHAIN_INPUT_SETTLER_COMPACT,
MULTICHAIN_INPUT_SETTLER_ESCROW,
solanaAddressToBytes32(SOLANA_INPUT_SETTLER_ESCROW)
],
allowedInputOracles({ chainId, sameChainFill }) {
const key = Number(chainId);
if (!Number.isFinite(key)) return undefined;
Expand Down
78 changes: 65 additions & 13 deletions src/lib/libraries/flowProgress.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,14 @@ import { getOutputHash, encodeMandateOutput } from "@lifi/intent";
import { addressToBytes32, bytes32ToAddress } from "@lifi/intent";
import { orderToIntent } from "@lifi/intent";
import { getOrFetchRpc } from "$lib/libraries/rpcCache";
import { deriveAttestationPda } from "$lib/libraries/solanaValidateLib";
import type { MandateOutput, OrderContainer } from "@lifi/intent";
import {
deriveAttestationPda,
encodeCommonPayload,
encodeFillDescription
} from "$lib/libraries/solanaValidateLib";
import { isSolanaSubmittedFillRecord } from "$lib/libraries/solanaFillLib";
import { deriveOrderContextPda } from "$lib/libraries/solanaFinaliseLib";
import type { MandateOutput, OrderContainer, SolanaStandardOrder } from "@lifi/intent";
import store from "$lib/state.svelte";

const PROGRESS_TTL_MS = 30_000;
Expand All @@ -41,12 +47,15 @@ export function getOutputStorageKey(output: MandateOutput) {
});
}

function isValidHash(hash: string | undefined): hash is `0x${string}` {
return !!hash && hash.startsWith("0x") && hash.length === 66;
function hasFillReference(hash: string | undefined): hash is string {
return typeof hash === "string" && hash.length > 0;
}

async function isOutputFilled(orderId: `0x${string}`, output: MandateOutput) {
const outputKey = getOutputStorageKey(output);
if (isSolanaChain(output.chainId)) {
return Boolean(store.fillTransactions[outputKey]);
}
return getOrFetchRpc(
`progress:filled:${orderId}:${outputKey}`,
async () => {
Expand All @@ -69,30 +78,58 @@ async function isOutputValidatedOnChain(
inputChain: bigint,
orderContainer: OrderContainer,
output: MandateOutput,
fillTransactionHash: `0x${string}`
fillTransactionHash: string
) {
const outputKey = getOutputStorageKey(output);
const cachedReceipt = store.getTransactionReceipt(output.chainId, fillTransactionHash);
if (isSolanaChain(output.chainId)) {
const record = store.getTransactionReceipt(output.chainId, fillTransactionHash);
if (!isSolanaSubmittedFillRecord(record)) return false;
const outputHash = keccak256(
encodeFillDescription(
record.solverBytes32,
orderId,
record.fillTimestamp,
encodeCommonPayload(output)
)
);
return getOrFetchRpc(
`progress:solana-proven-evm:${orderId}:${inputChain.toString()}:${outputKey}:${fillTransactionHash}`,
async () => {
const sourceChainClient = getClient(inputChain);
return sourceChainClient.readContract({
address: orderContainer.order.inputOracle,
abi: POLYMER_ORACLE_ABI,
functionName: "isProven",
args: [output.chainId, output.oracle, output.settler, outputHash]
});
},
{ ttlMs: PROGRESS_TTL_MS }
);
}

const evmFillTransactionHash = fillTransactionHash as `0x${string}`;
const cachedReceipt = store.getTransactionReceipt(output.chainId, evmFillTransactionHash);
const receipt = (
cachedReceipt
? cachedReceipt
: await getOrFetchRpc(
`progress:receipt:${output.chainId.toString()}:${fillTransactionHash}`,
`progress:receipt:${output.chainId.toString()}:${evmFillTransactionHash}`,
async () => {
const outputClient = getClient(output.chainId);
return outputClient.getTransactionReceipt({
hash: fillTransactionHash
hash: evmFillTransactionHash
});
},
{ ttlMs: PROGRESS_TTL_MS }
)
) as {
blockHash: `0x${string}`;
from: `0x${string}`;
logs: unknown[];
};
if (!cachedReceipt) {
store
.saveTransactionReceipt(output.chainId, fillTransactionHash, receipt)
.saveTransactionReceipt(output.chainId, evmFillTransactionHash, receipt)
.catch((error) => console.warn("saveTransactionReceipt error", error));
}

Expand All @@ -104,7 +141,7 @@ async function isOutputValidatedOnChain(
const logs = parseEventLogs({
abi: COIN_FILLER_ABI,
eventName: "OutputFilled",
logs: (receipt as unknown as { logs: any[] }).logs
logs: receipt.logs as never
});
const expectedHash = hashStruct({
types: compactTypes,
Expand Down Expand Up @@ -178,10 +215,25 @@ async function isOutputValidatedOnChain(

async function isInputChainFinalised(chainId: bigint, container: OrderContainer) {
const { order, inputSettler } = container;
const inputChainClient = getClient(chainId);
const intent = orderToIntent(container);
const orderId = intent.orderId();

if (isSolanaChain(chainId)) {
return getOrFetchRpc(
`progress:finalised:solana:${orderId}:${chainId.toString()}`,
async () => {
const { PublicKey } = await import("@solana/web3.js");
const conn = getSolanaConnection(chainId);
const pdaBase58 = await deriveOrderContextPda(order as SolanaStandardOrder);
const info = await conn.getAccountInfo(new PublicKey(pdaBase58));
return info === null;
},
{ ttlMs: PROGRESS_TTL_MS }
);
}

const inputChainClient = getClient(chainId);

if (
inputSettler === INPUT_SETTLER_ESCROW_LIFI ||
inputSettler === MULTICHAIN_INPUT_SETTLER_ESCROW
Expand Down Expand Up @@ -238,7 +290,7 @@ async function isInputChainFinalised(chainId: bigint, container: OrderContainer)

export async function getOrderProgressChecks(
orderContainer: OrderContainer,
fillTransactions: Record<string, `0x${string}`>
fillTransactions: Record<string, string>
): Promise<FlowCheckState> {
try {
const intent = orderToIntent(orderContainer);
Expand All @@ -257,7 +309,7 @@ export async function getOrderProgressChecks(
inputChains.flatMap((inputChain) =>
outputs.map(async (output) => {
const fillHash = fillTransactions[getOutputStorageKey(output)];
if (!isValidHash(fillHash)) return false;
if (!hasFillReference(fillHash)) return false;
return isOutputValidatedOnChain(orderId, inputChain, orderContainer, output, fillHash);
})
)
Expand Down
2 changes: 2 additions & 0 deletions src/lib/libraries/intentFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,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 @@ -72,6 +73,7 @@ function toCoreCreateIntentOptions(opts: AppCreateIntentOptions): CreateIntentOp
outputTokens: opts.outputTokens.map(toCoreTokenContext),
verifier: opts.verifier,
account,
outputRecipient: opts.outputRecipient,
lock: {
type: "escrow"
}
Expand Down
Loading
Loading