Skip to content

feat: support Solana standard orders#3

Open
Asem-Abdelhady wants to merge 16 commits intomainfrom
asem/V2-109/solana-orders
Open

feat: support Solana standard orders#3
Asem-Abdelhady wants to merge 16 commits intomainfrom
asem/V2-109/solana-orders

Conversation

@Asem-Abdelhady
Copy link
Copy Markdown

@Asem-Abdelhady Asem-Abdelhady commented Mar 9, 2026

Summary

  • Adds StandardSolana order type — a single-input variant of StandardOrder for Solana input settlers
  • Adds Borsh serialization (borshEncodeSolanaOrder) and order ID derivation (computeStandardSolanaId) matching the common::types::StandardOrder layout in catalyst-intent-svm
  • Routes EVM→Solana output tokens to the correct output settler PDA in buildMandateOutputs, falling back to COIN_FILLER for EVM outputs
  • Adds outputRecipient option to CreateIntentOptions to support a different recipient than the connected wallet
  • Extends CoreToken with chainNameSpace: "eip155" | "solana" (CAIP namespace) for output routing

Design notes

  • StandardSolana enforces a single input — the Solana program only supports one SPL token input per order. standardOrderToSolanaOrder throws if called with zero or more than one input.
  • Input amount is encoded as u64 (Solana stores SPL amounts as u64); output amount is bytes32 to match the EVM ABI layout.
  • Mainnet and testnet settler addresses are undefined with TODO comments — inputSettlerForSolana and buildMandateOutputs throw for undeployed chains.

Testing

  • src/intent/solanaStandard.spec.ts — full coverage: Borsh encoding golden value, u32 overflow guards, mutation tests, conversion helpers, StandardSolanaIntent class
  • src/intent/helpers/output-encoding.spec.ts — Solana PDA routing, unsupported chain rejection, EVM regression coverage
  • src/intent/helpers/shared.spec.tsinputSettlerForSolana happy path and error paths
  • Run all tests: bun test

@Asem-Abdelhady Asem-Abdelhady requested a review from reednaa March 9, 2026 21:55
Copy link
Copy Markdown
Member

@reednaa reednaa left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few nits. Ideally we simplify the library rather than add to it.

@Asem-Abdelhady Asem-Abdelhady requested a review from reednaa March 11, 2026 10:17
Copy link
Copy Markdown
Member

@reednaa reednaa left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The added files does not have test coverage unlike all other files in this repository.

Provide coverage of solanaStandard.spec.ts.

Here are a variety of agent findings:

src/intent/solanaStandard.ts

  • The amount field in mandateInputSchema uses "u64" but borshEncodeSolanaOrder passes BigInt(order.input.amount) — this is fine for
    u64, but the amount in mandateOutputSchema uses bytes32 (big-endian 32-byte encoding), which is inconsistent. Verify this matches
    the SVM program's actual layout.
  • The standardOrderToSolanaOrder conversion only uses the first input (order.inputs[0]), silently dropping all others. A
    StandardOrder can have multiple inputs — should this throw if inputs.length > 1 rather than silently truncating?
  • borshEncodeSolanaOrder is exported but not re-exported from src/intent/index.ts. Intentional or oversight?

src/intent/helpers/output-encoding.ts

  • outputSettler is now computed per-output via getSettler?.(token.chainId) ?? COIN_FILLER — this is a meaningful behavioral change
    for all EVM orders too (previously settled globally). Ensure no regression for existing EVM paths.

src/helpers/convert.ts

  • The early-return guard for already-bytes32 addresses is reasonable, but a Solana address (base58 string) would fail silently or
    produce wrong output if passed here. The comment says "Accept only EVM addresses here" but there's no enforcement — could confuse
    callers


export function addressToBytes32(address: `0x${string}`): `0x${string}` {
if (address.length === 66) return address; // already bytes32 with 0x prefix
if (address.length === 64) return `0x${address}`; // already bytes32 without 0x
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this properly catch 64 length addresses prefixed with 0x?

I would rather use something like: 0x${address.replace("0x", "").padStart("0", 64)}

Copy link
Copy Markdown
Author

@Asem-Abdelhady Asem-Abdelhady Mar 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah i think we should remove if (address.length === 64) also we don't need to check the address length in address.length !== 40 for EVM since we are already constraining the template with address: 0x${string} in the parameters.

asOrder(): TOrder;
inputChains(): bigint[];
orderId(): `0x${string}`;
compactClaimHash(): `0x${string}`;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is required. Add a handler for EVM vs Solana and then provide compactClaimHash if chain is identified as EVM.

Asem-Abdelhady and others added 7 commits March 16, 2026 21:58
The PR accidentally swapped compact↔escrow settler assignments. Restores
the original: compact/single→COMPACT_LIFI, compact/multi→MULTICHAIN_COMPACT,
escrow/single→ESCROW_LIFI, escrow/multi→MULTICHAIN_ESCROW.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@Asem-Abdelhady Asem-Abdelhady requested a review from reednaa April 1, 2026 12:57
Comment on lines +22 to +24
export const SOLANA_MAINNET_CHAIN_ID = 1151111081099710n;
export const SOLANA_TESTNET_CHAIN_ID = 1151111081099711n;
export const SOLANA_DEVNET_CHAIN_ID = 1151111081099712n;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to consider renaming these to just SOLANA_MAINNET, SOLANA_TESTNET, etc?

Comment on lines +4 to +13
export type SolanaStandardOrder = {
user: `0x${string}`;
nonce: bigint;
originChainId: bigint;
expires: number;
fillDeadline: number;
inputOracle: `0x${string}`;
input: MandateInput;
outputs: MandateOutput[];
};
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really hate this type. It does not feel welcome. Why can we not use StandardOrder?

Then when we use it, we just cast it? The alternative is using a compatibility layer between EVM and Solana then moving that into StandardOrder?

export type StandardEVM = {
  user: `0x${string}`;
  nonce: bigint;
  originChainId: bigint;
  expires: number;
  fillDeadline: number;
  inputOracle: `0x${string}`;
  inputs: [bigint, bigint][];
  outputs: MandateOutput[];
};
export type StandardSolana = {
  user: `0x${string}`;
  nonce: bigint;
  originChainId: bigint;
  expires: number;
  fillDeadline: number;
  inputOracle: `0x${string}`;
  inputs: [bigint, bigint][1];
  outputs: MandateOutput[];
};
export type StandardIntent = StandardEVM | StandardSolana;

The only other alternative is to use an abstract representation of the intent for longer.

@Asem-Abdelhady Asem-Abdelhady changed the title Asem/v2 109/solana orders feat: support Solana standard orders Apr 2, 2026
@Asem-Abdelhady Asem-Abdelhady requested a review from reednaa April 3, 2026 11:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants