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
8 changes: 7 additions & 1 deletion Anchor.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ wallet = "test/svm/keys/localnet-wallet.json"

[scripts]
### SvmSpoke scripts:
test = "anchor run generateExternalTypes && yarn run ts-mocha -p ./tsconfig.json -t 1000000 test/svm/**/*.ts"
# Pass the glob to Mocha; shell expansion can otherwise select only files in fixture subdirectories.
test = "anchor run generateExternalTypes && yarn run ts-mocha -p ./tsconfig.json -t 1000000 \"test/svm/**/*.ts\""
queryEvents = "NODE_NO_WARNINGS=1 yarn run ts-node ./scripts/svm/queryEvents.ts"
queryEventsV2 = "NODE_NO_WARNINGS=1 yarn run ts-node ./scripts/svm/queryEventsV2.ts"
initialize = "NODE_NO_WARNINGS=1 yarn run ts-node ./scripts/svm/initialize.ts"
Expand Down Expand Up @@ -76,6 +77,11 @@ upgradeable = true
[test.validator]
url = "https://api.mainnet-beta.solana.com"

# Pre-upgrade RequestedSlowFill account; used to verify fast-fill compatibility after slow-fill retirement.
[[test.validator.account]]
address = "Gd2seG3bKUfb7R1CJYFrjxVNVMLAwrX4euc9bvmLAGea"
filename = "test/svm/accounts/legacy_requested_slow_fill.json"

### Forked Circle MessageTransmitterV2 Program
[[test.validator.clone]]
address = "CCTPV2Sm4AdWt5296sk4P66VBZ7bEhcARwFaaS9YPbeC"
Expand Down
46 changes: 45 additions & 1 deletion programs/svm-spoke/V5_ADAPTER_SPEC.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,9 @@ V5 fills (legacy fills continue to store their relayer), binding permissionless
rent without an account-layout migration. V5 fills emit the existing `FilledRelay` schema and derive the relay hash
from the supplied standard `RelayData` and the configured SVM chain ID. Adapter mode uses no callback message; the
relay witness remains exactly `V5_MAGIC_PREFIX || step_id`. As on EVM, V5-tagged relays are quarantined
from the slow-fill lifecycle, so their fill status can only transition directly from an uninitialized PDA to `Filled`.
from legacy fill handling, so their fill status can only transition directly from an uninitialized PDA to `Filled`.
Slow-fill request and execution entrypoints are retired for all relays. Existing legacy requested accounts and
historical event slots remain compatible as described in [historical compatibility](#historical-compatibility).

Token-2022 mint extensions fail closed. Wire version 1 permits only mint-close authority and metadata/group pointer
or data extensions. Transfer fees remain excluded until debit/delivery delta semantics are defined; transfer hooks,
Expand All @@ -160,6 +162,34 @@ semantics are explicitly reviewed and validator-tested. This gate covers mint ex
relevant to this path, such as source CPI guard or destination memo requirements, fail the token transfer rather than
altering accounting.

## Historical compatibility

`request_slow_fill` and `execute_slow_relay_leaf` are absent from dispatch, the public IDL, and generated clients.
Their historical selectors fail with `InstructionFallbackNotFound` (101) before account validation.
`FillStatus` retains `Unfilled = 0`, `RequestedSlowFill = 1`, and `Filled = 2`; no supported instruction creates
`RequestedSlowFill`. Account layouts and enum positions must remain stable for existing accounts.

An existing requested relay may still receive a legacy fast fill before its deadline, subject to the normal
legacy fill checks. Success records `Filled`, updates the rent recipient to the submitting relayer, and emits
`ReplacedSlowFill`; replay fails and transaction failure rolls back the transfer and status change. V5-tagged
relays require the V5 adapter and create a new status account directly as `Filled`.

Historical `RequestedSlowFill` and `FilledRelay` events remain decodable. `FillType` retains `FastFill = 0`,
`ReplacedSlowFill = 1`, and `SlowFill = 2`; `SlowFill` is historical only.
Retired slow-fill error slots remain reserved so later Common error assignments do not shift.
Indexers must check transaction success before accepting events.

`RootBundle` retains both roots and its refund-claim bitmap. The two-root admin payload still accepts nonzero
`slow_relay_root`: HubPool shares that root across chains, so it may contain other destinations' slow fills.
Rejecting it would also block the accompanying refund root. Solana stores and emits it but cannot execute it.
No account or two-root admin-message migration is required. Historical instruction-parameter buffers remain
closable by their creator through `close_instruction_params` without decoding the retired parameter types.

After the recorded deadline, anyone may call `close_fill_pda`; rent goes only to the recorded recipient.
Closing the PDA reclaims rent, not the deposit. Unfilled expired deposits follow the dataworker-driven origin-chain
refund process; there is no destination slow-fill fallback. `scripts/svm/closeRelayerPdas.ts` discovers only
`FilledRelay` events, so never-filled requests need separate discovery before submitting the permissionless close.

## Enabled source-deposit behavior

After authenticating the live Gateway dispatch PDA, Deposit mode strictly decodes branch-specific JIT data, resolves
Expand Down Expand Up @@ -212,3 +242,17 @@ authenticate all JIT variants of an aggregate production route. The canonical re
single-fill template. `fixtures/v5_gateway_path.json` additionally pins Borsh consumption-tape bytes, path hashes,
sorted sibling roots and witnesses across TypeScript, Rust and Solidity. Its placeholder keys are hashing fixtures,
not deployed token accounts. See [the lane guide](../../test/svm-gateway/README.md) for execution and companion docs.

## Deployment sequencing

Before deploying slow-fill retirement as part of the Lite-chain upgrade, verify the active dataworker and
configuration exclude Solana slow fills. The SDK excludes slow fills to/from Lite chains and requires token
equivalence through pool-rebalance routes. Lite-chain classification uses each deposit's quote timestamp;
today's classification does not establish that older deposits were excluded.

Reconcile older requests and bundles, including funded slow-fill leaves, pending return liabilities, and vault
balances. Settle remaining obligations before removing their execution/return paths, or use a separately reviewed
recovery procedure. The combined upgrade rejects nonzero `amount_to_return` and removes
`bridge_tokens_to_hub_pool`, so ordinary return processing cannot recover residual Solana funds afterward.
An origin-chain expiry refund does not itself return excess Solana vault funds. Compatibility tests do not
establish that the live in-flight window is empty.
2 changes: 2 additions & 0 deletions programs/svm-spoke/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@ pub enum CommonError {
InvalidFillDeadline,
#[msg("Caller is not the exclusive relayer and exclusivity deadline has not passed!")]
NotExclusiveRelayer,
// Historical slow-fill exclusivity error; no longer raised.
#[msg("The Deposit is still within the exclusivity window!")]
NoSlowFillsInExclusivityWindow,
#[msg("The relay has already been filled!")]
RelayFilled,
// Historical slow-fill request error; no longer raised.
#[msg("Slow fill requires status of Unfilled!")]
InvalidSlowFillRequest,
#[msg("The fill deadline has passed!")]
Expand Down
4 changes: 3 additions & 1 deletion programs/svm-spoke/src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,9 @@ pub struct FundsDeposited {
#[derive(AnchorSerialize, AnchorDeserialize, Clone, PartialEq)]
pub enum FillType {
FastFill,
// A fast fill replacing a pre-upgrade slow-fill request.
ReplacedSlowFill,
// Historical event decoding only. Never remove or reorder these variants.
SlowFill,
}

Expand Down Expand Up @@ -92,7 +94,7 @@ pub struct V5FillFloatWithdrawn {
pub amount: u64,
}

// Slow fill events
// Historical event decoding only; no instruction emits new slow-fill requests.
#[event]
pub struct RequestedSlowFill {
pub input_token: Pubkey,
Expand Down
2 changes: 0 additions & 2 deletions programs/svm-spoke/src/instructions/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ mod fill;
mod handle_receive_message;
mod instruction_params;
mod refund_claims;
mod slow_fill;
mod v5_adapter;
mod v5_fill_status;

Expand All @@ -18,6 +17,5 @@ pub use fill::*;
pub use handle_receive_message::*;
pub use instruction_params::*;
pub use refund_claims::*;
pub use slow_fill::*;
pub use v5_adapter::*;
pub use v5_fill_status::*;
Loading
Loading