Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
d4f6fa4
feat(svm): retire V4 deposit and fill entrypoints
Reinis-FRP Sep 24, 2026
dd5999e
fix(svm): clean up retired V4 code warnings
Reinis-FRP Sep 24, 2026
0d84f28
test(svm): pin frozen legacy deposit counter
Reinis-FRP Sep 24, 2026
344d06c
docs(svm): clarify retained legacy fill test coverage
Reinis-FRP Sep 24, 2026
e75f4a0
fix(svm): preserve public V5 wire codecs in generated IDL
Reinis-FRP Sep 24, 2026
cd947ae
docs(svm): update Gateway guide for V4 retirement
Reinis-FRP Sep 24, 2026
03494de
fix(svm): remove unused V4 errors and message guards
Reinis-FRP Sep 24, 2026
0d00999
docs(svm): restore retirement guide line wrapping
Reinis-FRP Sep 24, 2026
959858d
fix(svm): align refund helper and script account types
Reinis-FRP Sep 24, 2026
d51f744
fix(svm): remove retired V4 delegate client helpers
Reinis-FRP Sep 24, 2026
d8ccf4e
test(svm): port retired V4 coverage to V5 Gateway flows
Reinis-FRP Sep 24, 2026
8b8a980
fix(svm): invalidate artifact caches on build helper changes
Reinis-FRP Sep 25, 2026
b2c336c
docs(svm): distinguish CI and manual V5 regression coverage
Reinis-FRP Sep 25, 2026
bcd8745
fix(svm): export complete V5 payload schemas in IDL
Reinis-FRP Sep 25, 2026
e8295ac
docs(svm): pin retired test coverage to fixed snapshot
Reinis-FRP Sep 25, 2026
50d9a94
test(svm): clarify V5 deposit input nesting
Reinis-FRP Sep 25, 2026
06bee97
refactor(svm): retire legacy deposit ID endpoint
Reinis-FRP Sep 25, 2026
de964dc
test(svm): drop stale retained-endpoint assertion
droplet-rl Sep 25, 2026
e28a6d6
test(svm): typecheck TypeScript tests in CI
Reinis-FRP Sep 25, 2026
d193ab0
refactor(svm): mark reserved slow-fill errors as retired
Reinis-FRP Sep 25, 2026
c7169b6
fix(svm): confirm refund parameter writes before execution
Reinis-FRP Sep 25, 2026
77ffbeb
build(svm): fail on stack-overflow diagnostics
Reinis-FRP Sep 25, 2026
80e5ce9
test(svm): run shared codec vectors after artifact generation
Reinis-FRP Sep 29, 2026
a514edf
docs(svm): clarify paused V5 cutover prerequisites
Reinis-FRP Sep 29, 2026
c8990cc
fix(svm): reject stale verified build artifacts
Reinis-FRP Sep 30, 2026
e7b52c9
docs(svm): clarify unsupported V5 devnet deployment
Reinis-FRP Sep 30, 2026
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: 2 additions & 2 deletions .github/actions/cache-svm-artifacts/action.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Reusable action to cache SVM artifacts if the source code and dependencies have not changed.
# Reusable action to cache SVM artifacts if source code, dependencies, and build tooling have not changed.
name: Cache SVM Artifacts
inputs:
type:
Expand Down Expand Up @@ -26,7 +26,7 @@ runs:
# The job that generates the artifacts is responsible for archiving them to the cache tarball. This avoids any
# conflicts with other caching actions that might have cleaned some of cached contents.
path: svm-${{ inputs.type }}.tar
key: svm-${{ inputs.type }}-${{ runner.os }}-node-${{ steps.resolved-node.outputs.version }}-${{ hashFiles('yarn.lock', 'Cargo.lock', 'programs/**/Cargo.toml', 'programs/**/Xargo.toml', 'programs/**/*.rs') }}
key: svm-${{ inputs.type }}-${{ runner.os }}-node-${{ steps.resolved-node.outputs.version }}-${{ hashFiles('yarn.lock', 'Cargo.lock', 'programs/**/Cargo.toml', 'programs/**/Xargo.toml', 'programs/**/*.rs', 'scripts/svm/buildHelpers/**') }}
- name: Unpack restored SVM artifacts
if: steps.svm-artifacts-cache.outputs.cache-hit == 'true'
shell: bash
Expand Down
15 changes: 9 additions & 6 deletions .github/workflows/pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,8 @@ jobs:
run: yarn lint-rust
- name: Test rust
run: cargo test -p svm-spoke --lib
- name: Test Gateway hash vectors (no private dependency)
run: yarn test-svm-gateway-vectors
- name: Test SBF build diagnostic guard
run: yarn test-svm-build-guard
- name: Regenerate constants.json
run: yarn generate-constants-json && yarn prettier --write generated/constants.json
- name: Regenerate deployed-addresses
Expand Down Expand Up @@ -132,7 +132,7 @@ jobs:
- name: Setup Anchor & Solana
uses: ./.github/actions/setup-solana-anchor
with:
verify_version: 0.4.3
verify_version: 0.5.1
node_version: ${{ env.NODE_VERSION }}
- name: Check which paths changed
uses: dorny/paths-filter@v3
Expand All @@ -149,6 +149,12 @@ jobs:
uses: actions/download-artifact@v4
with:
name: svm-artifacts-${{ runner.os }}-node-${{ env.NODE_VERSION }}
- name: Generate test-feature IDLs
run: yarn generate-svm-test-idls
- name: Test Gateway hash vectors (no private dependency)
run: yarn test-svm-gateway-vectors
- name: Typecheck TypeScript tests
run: yarn typecheck-tests
# Cargo cache would only apply to dependencies that are not reused in the verified docker build, hence we need to
# manually cache the verified build binaries if the program source code has not changed.
- name: Cache verified test build binaries
Expand All @@ -165,9 +171,6 @@ jobs:
- name: Archive verified test build (for caching)
if: steps.verified-test-build-cache.outputs.cache-hit != 'true'
run: tar -cf svm-verified-test-binaries.tar target/deploy
- name: Generate test-feature IDLs
if: steps.filter.outputs.svm == 'true'
run: yarn generate-svm-test-idls
- name: Test verified SVM build
if: steps.filter.outputs.svm == 'true'
run: anchor test --skip-build
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ jobs:
- name: Setup Anchor & Solana
uses: ./.github/actions/setup-solana-anchor
with:
verify_version: 0.4.3
verify_version: 0.5.1
node_version: ${{ env.NODE_VERSION }}

- name: Create verified production build
Expand All @@ -143,7 +143,7 @@ jobs:
- name: Setup Anchor & Solana
uses: ./.github/actions/setup-solana-anchor
with:
verify_version: 0.4.3
verify_version: 0.5.1
node_version: ${{ env.NODE_VERSION }}

- name: Create verified test build
Expand Down
7 changes: 2 additions & 5 deletions Anchor.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ test = "8tsEfDSiE4WUMf97oyyyasLAvWwjeRZb2GByh4w7HckA"
sponsored_cctp_src_periphery = "FsD4uqAyXiAcQapVTokNvdz1ZC5hMHjY6a93nYmEqZQS"

[programs.devnet]
# V5 svm_spoke and its supporting offchain infrastructure are not configured on public devnet.
multicall_handler = "Fk1RpqsfeWt8KnFCTW9NQVdVxYvxuqjGn6iPB9wrmM8h"
svm_spoke = "JAZWcGrpSWNPTBj8QtJ9UyQqhJCDhG9GJkDeMf5NQBiq"
sponsored_cctp_src_periphery = "CPr4bRvkVKcSCLyrQpkZrRrwGzQeVAXutFU8WupuBLXq"

[programs.mainnet]
Expand All @@ -38,10 +38,8 @@ initialize = "NODE_NO_WARNINGS=1 yarn run ts-node ./scripts/svm/initialize.ts"
queryState = "NODE_NO_WARNINGS=1 yarn run ts-node ./scripts/svm/queryState.ts"
createVault = "NODE_NO_WARNINGS=1 yarn run ts-node ./scripts/svm/createVault.ts"
queryVault = "NODE_NO_WARNINGS=1 yarn run ts-node ./scripts/svm/queryVault.ts"
simpleDeposit = "NODE_NO_WARNINGS=1 yarn run ts-node ./scripts/svm/simpleDeposit.ts"
queryDeposits = "NODE_NO_WARNINGS=1 yarn run ts-node ./scripts/svm/queryDeposits.ts"
queryFills = "NODE_NO_WARNINGS=1 yarn run ts-node ./scripts/svm/queryFills.ts"
simpleFill = "NODE_NO_WARNINGS=1 yarn run ts-node ./scripts/svm/simpleFill.ts"
simpleFakeRelayerRepayment = "NODE_NO_WARNINGS=1 yarn run ts-node ./scripts/svm/simpleFakeRelayerRepayment.ts"
closeRelayerPdas = "NODE_NO_WARNINGS=1 yarn run ts-node ./scripts/svm/closeRelayerPdas.ts"
closeDataWorkerLookUpTables = "NODE_NO_WARNINGS=1 yarn run ts-node ./scripts/svm/closeDataWorkerLookUpTables.ts"
Expand All @@ -54,7 +52,6 @@ addressToPublicKey = "NODE_NO_WARNINGS=1 yarn run ts-node ./scripts/svm/addressT
publicKeyToAddress = "NODE_NO_WARNINGS=1 yarn run ts-node ./scripts/svm/publicKeyToAddress.ts"
findFillStatusPdaFromEvent = "NODE_NO_WARNINGS=1 yarn run ts-node ./scripts/svm/findFillStatusPdaFromEvent.ts"
findFillStatusFromFillStatusPda = "NODE_NO_WARNINGS=1 yarn run ts-node ./scripts/svm/findFillStatusFromFillStatusPda.ts"
nativeDeposit = "NODE_NO_WARNINGS=1 yarn run ts-node ./scripts/svm/nativeDeposit.ts"
squadsIdlUpgrade = "NODE_NO_WARNINGS=1 yarn run ts-node ./scripts/svm/squadsIdlUpgrade.ts"

### SponsoredCctpSrc scripts:
Expand All @@ -77,7 +74,7 @@ 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.
# Pre-upgrade RequestedSlowFill account; used to verify historical decoding and expiry rent reclaim.
[[test.validator.account]]
address = "Gd2seG3bKUfb7R1CJYFrjxVNVMLAwrX4euc9bvmLAGea"
filename = "test/svm/accounts/legacy_requested_slow_fill.json"
Expand Down
20 changes: 19 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ EVM builds and tests use the Foundry version pinned in `.foundry-version`; CI in
`foundry-rs/foundry-toolchain`. Run `yarn pin-foundry` to switch your local toolchain to it (a no-op when it already
matches). Bump the pin by editing that file.

SVM local, verified, and real-Gateway builds fail on stack-overflow diagnostics even when the compiler exits
successfully. The shared guard is `scripts/svm/buildHelpers/runSbfBuild.sh`; run `yarn test-svm-build-guard` to test it
without a validator or Rust build. CI invalidates cached SVM builds when these build helpers change.

## Test

```shell
Expand All @@ -49,8 +53,13 @@ yarn test:report-gas # Run unit tests with gas reporting enabled
yarn test-evm # Only test EVM code
yarn test-svm # Only test SVM code (local toolchain build)
yarn test-svm-solana-verify # Only test SVM code (verified docker build)
yarn typecheck-tests # Typecheck all TypeScript tests without emitting files
```

The test typecheck uses `tsconfig.test.json` and requires generated clients and test-feature IDLs. Run
`yarn generate-svm-artifacts && yarn generate-svm-test-idls` first. CI runs this check after preparing these artifacts,
before the verified SVM build and runtime tests. The package build continues to use `tsconfig.json`.

## Lint

```shell
Expand Down Expand Up @@ -94,9 +103,18 @@ yarn forge-script-zksync script/016DeployZkSyncSpokePool.s.sol:DeployZkSyncSpoke

For CCTP V2 root/admin finalization, pause recovery and light-chain intent examples, see the [Solana operational scripts guide](scripts/svm/README.md).

V5 `svm_spoke` is configured for mainnet and local-validator testing. Its public-devnet deployment and supporting
offchain infrastructure are not configured, so `Anchor.toml` has no devnet Spoke entry. The devnet deployment examples
below require a separately configured deployment; the legacy devnet Spoke address is not a V5 target.

Before deploying for the first time make sure all program IDs in `lib.rs` and `Anchor.toml` are the same as listed when running `anchor keys list`. If not, update them to match the deployment keypairs under `target/deploy/` and commit the changes.

Make sure to use the verified docker binaries that can be built:
V5 PDA addresses and bumps are pinned to the configured Spoke and Gateway IDs to avoid runtime derivation costs.
If either program ID changes, update its dependent PDA constants and run `cargo test -p svm-spoke --lib` to check consistency.

Use `solana-verify` 0.5.1, matching CI, to build the verified Docker binaries. The build removes each
previous binary before compilation and rejects missing or empty output; failed builds must not be used for deployment.
Keep the stack-diagnostic guard enabled, since SBF compilers can report stack overflows with a successful exit status.

```shell
unset IS_TEST # Ensures the production build is used (not the test feature)
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@
"build-svm": "bash ./scripts/svm/buildHelpers/buildSvmLocalToolchain.sh",
"build-svm-solana-verify": "bash ./scripts/svm/buildHelpers/buildSolanaVerify.sh",
"build-ts": "rm -rf ./dist && tsc",
"typecheck-tests": "tsc -p tsconfig.test.json",
"test-svm-build-guard": "ts-mocha -p tsconfig.test.json -t 10000 test/svm/Scripts.SbfBuild.ts",
"export-idl": "mkdir -p dist/src/svm/assets/idl && cp src/svm/assets/idl/*.json dist/src/svm/assets/idl/",
"export-evm-artifacts": "sh scripts/exportEvmArtifacts.sh",
"prepublishOnly": "yarn build-ts && yarn export-idl && yarn export-evm-artifacts",
Expand Down
8 changes: 7 additions & 1 deletion programs/svm-spoke/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,25 @@ publish = false
crate-type = ["cdylib", "lib"]
name = "svm_spoke"

[[bin]]
name = "export_v5_types"
path = "idl/export_v5_types.rs"
required-features = ["idl-build"]

[features]
default = []
cpi = ["no-entrypoint"]
no-entrypoint = []
no-idl = []
no-log-ix-name = []
idl-build = ["anchor-lang/idl-build", "anchor-spl/idl-build"]
idl-build = ["anchor-lang/idl-build", "anchor-spl/idl-build", "dep:serde_json"]
test = []

[dependencies]
anchor-lang = { version = "0.31.1", features = ["init-if-needed","event-cpi"]}
anchor-spl = "0.31.1"
solana-security-txt = "1.1.1"
serde_json = { version = "1.0", optional = true }

[dev-dependencies]
hex = "0.4.3"
Expand Down
79 changes: 44 additions & 35 deletions programs/svm-spoke/ERROR_CODES.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
# Spoke runtime error-code migration

This release assigns distinct runtime error ranges to `svm_spoke`. **Existing `CommonError` codes are unchanged.**
`SvmError` moves from 6000–6018 to 7000–7018, and `CallDataError` moves from 6000–6006 to 8000–8006.
The callback rejection and V5 errors are new relative to
[`master` at `75d968e4`](https://github.com/across-protocol/contracts/blob/75d968e4e86c37ef12c01277345ea8ed2f550901/programs/svm-spoke/src/error.rs),
the comparison used in the tables below. A dash means the error did not exist in that baseline.

Earlier undeployed revisions of this stack used `V5Error` codes in the 7000 range. The finalized variants now use
9000–9015; the pre-release list also changed before this migration was finalized. They also used 6019 for the new
`LegacyFillMessageUnsupported`, now 7019. These earlier assignments were pre-release values.

The tables cover every current variant in [error.rs](src/error.rs) and the removed `AcrossPlusError` variants.
`SvmError` moves from 6000–6018 to 7000–7016 after unused variants are removed; `CallDataError` moves from
6000–6006 to 8000–8006.
The compatibility baseline is the deployed release
[`v5.0.12-beta.1` (`d8da3000`)](https://github.com/across-protocol/contracts/blob/d8da3000f1aba2593e712a9942a3249bf8e8205b/programs/svm-spoke/src/error.rs),
not intermediate PRs in this undeployed stack. V5 errors are new relative to that baseline. A dash means the
error did not exist in that deployed release.

Earlier undeployed revisions used different SVM/V5 assignments and introduced `V5FillOnly` and
`LegacyFillMessageUnsupported`. Both unused callback-rejection errors are now removed; neither existed in the
deployed baseline, so they have no deployed assignments to preserve.

The tables cover every declared variant in [error.rs](src/error.rs) and every removed deployed variant.
Enum membership does not imply reachability: the old `CommonError` slow-fill slots remain reserved as noted below.
Use the mapping for the program version being queried. Historical transaction errors retain the old codes;
do not relabel them using the new table. Legacy
numbers overlap across enums, so a number alone cannot identify a historical error. Runtime log names distinguish
Expand All @@ -26,9 +29,9 @@ mislabel errors. See [deployment sequencing](V5_ADAPTER_SPEC.md#deployment-seque
| `InvalidQuoteTimestamp` | 6000 | 6000 |
| `InvalidFillDeadline` | 6001 | 6001 |
| `NotExclusiveRelayer` | 6002 | 6002 |
| `NoSlowFillsInExclusivityWindow` | 6003 | 6003 |
| `RetiredNoSlowFillsInExclusivityWindow` | 6003 | 6003 |
| `RelayFilled` | 6004 | 6004 |
| `InvalidSlowFillRequest` | 6005 | 6005 |
| `RetiredInvalidSlowFillRequest` | 6005 | 6005 |
| `ExpiredFillDeadline` | 6006 | 6006 |
| `InvalidMerkleProof` | 6007 | 6007 |
| `InvalidChainId` | 6008 | 6008 |
Expand All @@ -39,36 +42,42 @@ mislabel errors. See [deployment sequencing](V5_ADAPTER_SPEC.md#deployment-seque
| `InsufficientSpokePoolBalanceToExecuteLeaf` | 6013 | 6013 |
| `InvalidExclusiveRelayer` | 6014 | 6014 |
| `InvalidOutputToken` | 6015 | 6015 |
| `V5FillOnly` | — | 6016 |

The two `Retired` placeholders were named `NoSlowFillsInExclusivityWindow` (6003) and `InvalidSlowFillRequest`
(6005) in the deployed release. Their numeric codes and messages are unchanged; the prefix explicitly marks
the Rust symbols, and generated names wherever exposed, as retired. Historical transaction logs keep the old names.
These slots must never be emitted, removed, or reused. Keeping them in place preserves every later live
`CommonError` assignment.

## SvmError

| Error | Before release | This release |
| ----------------------------------------------- | -------------- | ------------ |
| `NotOwner` | 6000 | 7000 |
| `InvalidRelayHash` | 6001 | 7001 |
| `CanOnlyCloseFillStatusPdaIfFillDeadlinePassed` | 6002 | 7002 |
| `NotRelayer` | 6003 | 7003 |
| `CannotSetCurrentTime` | 6004 | 7004 |
| `InvalidRemoteDomain` | 6005 | 7005 |
| `InvalidRemoteSender` | 6006 | 7006 |
| `InvalidMint` | 6007 | 7007 |
| `InvalidRelayHash` | 6001 | removed |
| `CanOnlyCloseFillStatusPdaIfFillDeadlinePassed` | 6002 | 7001 |
| `NotRelayer` | 6003 | 7002 |
| `CannotSetCurrentTime` | 6004 | 7003 |
| `InvalidRemoteDomain` | 6005 | 7004 |
| `InvalidRemoteSender` | 6006 | 7005 |
| `InvalidMint` | 6007 | 7006 |
| `ExceededPendingBridgeAmount` | 6008 | removed |
| `NonZeroAmountToReturn` | — | 7008 |
| `ParamsWriteOverflow` | 6009 | 7009 |
| `InvalidRefund` | 6010 | 7010 |
| `ZeroRefundClaim` | 6011 | 7011 |
| `NonZeroRefundClaim` | 6012 | 7012 |
| `InvalidClaimInitializer` | 6013 | 7013 |
| `InvalidRefundTokenAccount` | 6014 | 7014 |
| `InvalidProductionSeed` | 6015 | 7015 |
| `InvalidATACreationAccounts` | 6016 | 7016 |
| `InvalidDelegatePda` | 6017 | 7017 |
| `InconsistentOptionalParameters` | 6018 | 7018 |
| `LegacyFillMessageUnsupported` | — | 7019 |

The CCTP V2 migration removes `ExceededPendingBridgeAmount` and uses that enum slot for
`NonZeroAmountToReturn`, which rejects relayer refund leaves that return tokens to the HubPool.
| `NonZeroAmountToReturn` | — | 7007 |
| `ParamsWriteOverflow` | 6009 | 7008 |
| `InvalidRefund` | 6010 | 7009 |
| `ZeroRefundClaim` | 6011 | 7010 |
| `NonZeroRefundClaim` | 6012 | 7011 |
| `InvalidClaimInitializer` | 6013 | 7012 |
| `InvalidRefundTokenAccount` | 6014 | 7013 |
| `InvalidProductionSeed` | 6015 | 7014 |
| `InvalidATACreationAccounts` | 6016 | 7015 |
| `InvalidDelegatePda` | 6017 | 7016 |
| `InconsistentOptionalParameters` | 6018 | removed |

The CCTP V2 migration replaces `ExceededPendingBridgeAmount` with `NonZeroAmountToReturn`, which rejects
relayer refund leaves that return tokens to the HubPool. V4 entrypoint retirement removes `InvalidRelayHash`
and `InconsistentOptionalParameters`; neither has a remaining construction site. The final 7000–7016 mapping
includes these removals. Intermediate stack assignments were never deployed and are not compatibility constraints.

## CallDataError

Expand Down
Loading
Loading