Skip to content

feat: thorchain solana swapper integration#12036

Merged
gomesalexandre merged 14 commits intodevelopfrom
feat/thorchain-solana
Mar 2, 2026
Merged

feat: thorchain solana swapper integration#12036
gomesalexandre merged 14 commits intodevelopfrom
feat/thorchain-solana

Conversation

@gomesalexandre
Copy link
Contributor

@gomesalexandre gomesalexandre commented Feb 25, 2026

Description

Wires up SOL as a valid sell/buy asset in the THORChain swapper. SOL.SOL pool is confirmed live on THORChain mainnet (status: Available, trading_halted: false). Follows the Tron pattern: native SOL sent to vault with an SPL Memo Program instruction carrying the THORChain memo.

What this enables:

  • SOL → BTC, SOL → ETH, BTC → SOL, ETH → SOL etc. through THORChain
  • SOL appears in the THORChain swapper sell/buy dropdowns
  • Fees estimated at quote time using adapter.getFeeData with the memo instruction for accurate compute unit pricing
  • Forward-compat for SPL tokens (when THORChain adds SPL pools, no new code needed — asset map auto-generates, adapter handles ATA creation via tokenId)

What's in this PR:

  1. SolanaChainId type in @shapeshiftoss/types
  2. Solana input types in @shapeshiftoss/swapper
  3. Asset map generation script updated — yarn generate:tradable-asset-map now outputs SOL.SOL
  4. thorchain-utils/solana/ module: getThorTxData, getUnsignedSolanaTransaction, getSolanaTransactionFees
  5. getL1RateOrQuote.ts Solana case — replaces the 'Solana is not supported' stub
  6. ThorchainSwapper endpoints + execution wiring
  7. unchained-client Solana THORChain parseTx parser (Memo Program detection, base58 decode, thormaya delegation)

Issue (if applicable)

closes #12034

Risk

Low — new chain support, additive only, no existing swap flows touched. Solana adapter already exists and is battle-tested via Jupiter/Relay.

What protocols, transaction types, wallets or contract interactions might be affected by this PR?

THORChain native SOL swaps only. Inbound vault: GWYXY7c6SVMkuhmDq2LT1Hj6qGkDFCqmdZWJdCeHUgEN.

Testing

Engineering

# verify SOL.SOL in generated asset map
grep "SOL.SOL" packages/swapper/src/swappers/ThorchainSwapper/generated/generatedTradableAssetMap.json

# verify pool is live
curl https://thornode.ninerealms.com/thorchain/pool/SOL.SOL | jq '{status, trading_halted, balance_asset}'

# dev testing (connect a Solana wallet)
# 1. Navigate to the swap page
# 2. Select THORChain swapper
# 3. Sell: SOL, Buy: BTC (or any THORChain-supported asset)
# 4. Verify quote returns with fee estimate
# 5. Execute: tx should go to vault GWYXY7c6SVMkuhmDq2LT1Hj6qGkDFCqmdZWJdCeHUgEN with Memo instruction

Manual test pairs to verify:

  • SOL → BTC quote returns
  • BTC → SOL quote returns
  • SOL → ETH quote returns
  • SOL → USDC quote returns (longtail)
  • Fee estimate is non-null when wallet connected
  • Transaction executes with correct vault address and memo instruction

Operations

  • 🏁 My feature is behind a flag and doesn't require operations testing (yet)

SOL.SOL pool is live, no flag needed. Operations can test in staging with a small SOL → BTC swap.

Screenshots

SOL → RUNE quote

sol-to-rune-v3-quote

SOL → RUNE confirm

sol-to-rune-v3-confirm

SOL → RUNE result (1 Pending, swap being processed)

sol-to-rune-success-final

RUNE → SOL quote (reverse direction)

rune-to-sol

ETH → RUNE regression test

eth-to-rune-pending

ETH → USDC sanity check

eth-to-usdc-pending

All swaps pending

all-swaps-pending

Summary by CodeRabbit

  • New Features

    • Added Solana support for swaps, including building, signing, executing, and fee estimation for Solana transactions.
    • Transaction parsing now surfaces Solana trade metadata for clearer trade details.
  • Bug Fixes

    • Improved asset ID mapping to handle Solana assets and case-sensitive lookups to prevent incorrect asset resolution.

gomesalexandre and others added 6 commits February 25, 2026 23:25
getThorTxData, getUnsignedSolanaTransaction, getSolanaTransactionFees.
Follows tron/ pattern: vault lookup + SPL Memo Program instruction for
fee estimation, hdwallet SolanaTxInstruction for buildSendApiTransaction.
SPL token support forward-compat via tokenId passthrough.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Replaces the hard-reject stub with a full Memo Program fee estimation
case. Uses TransactionInstruction (web3.js) for getFeeData, returns
ThorUtxoOrCosmosTradeRateOrQuote routes. Also exports solana from
thorchain-utils/index.ts.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Adds getUnsignedSolanaTransaction, getSolanaTransactionFees to endpoints.ts
and executeSolanaTransaction to ThorchainSwapper.ts.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Detects SPL Memo Program instruction, base58-decodes data to UTF-8,
delegates to shared thormaya.Parser. Adds bs58 dependency.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 25, 2026

Important

Review skipped

Review was skipped due to path filters

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock

CodeRabbit blocks several paths by default. You can override this behavior by explicitly including those paths in the path filters. For example, including **/dist/** will override the default block on the dist directory, by removing the pattern from both the lists.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds Solana support across the ThorChain swapper: case-sensitive asset ID lookups, new Solana utilities (vault lookup, unsigned tx builder, fee estimator), Solana-aware rate/quote flow, swapper endpoints and execution method, parser support for THORChain memos, type additions, and asset map updates.

Changes

Cohort / File(s) Summary
Asset mapping helpers
packages/swapper/src/swappers/MayachainSwapper/utils/poolAssetHelpers/poolAssetHelpers.ts, packages/swapper/src/swappers/ThorchainSwapper/utils/poolAssetHelpers/poolAssetHelpers.ts
Make inverted asset-id lookups case-sensitive by removing .toLowerCase(); mapping and tests updated to include SOL.SOL.
Thorchain Solana utilities
packages/swapper/src/thorchain-utils/solana/*
New module with getThorTxData, getUnsignedSolanaTransaction, getSolanaTransactionFees — vault lookup, build unsigned Solana tx (memo + transfer), and fee estimation via Solana adapter.
Thorchain utils index & wiring
packages/swapper/src/thorchain-utils/index.ts, packages/swapper/src/thorchain-utils/getL1RateOrQuote.ts
Export solana namespace; replace hard-fail for Solana with per-route memo-enabled fee/quote generation using Solana chain adapter and memo instructions.
Swapper endpoints & executor
packages/swapper/src/swappers/ThorchainSwapper/endpoints.ts, packages/swapper/src/swappers/ThorchainSwapper/ThorchainSwapper.ts
Expose getUnsignedSolanaTransaction and getSolanaTransactionFees endpoints; add executeSolanaTransaction execution method on ThorchainSwapper.
Types & assets
packages/swapper/src/types.ts, packages/types/src/base.ts, scripts/generateTradableAssetMap/utils.ts
Add SolanaChainId and Solana-specific trade input types; add SOL chain to asset map generation and SPL token namespace handling.
Unchained Solana parser
packages/unchained-client/src/solana/parser/thorchain.ts, packages/unchained-client/src/solana/parser/types.ts, packages/unchained-client/src/solana/parser/index.ts
New THORChain memo parser: finds Memo program instruction, decodes base58 data to memo, delegates to thorchain parser; ParsedTx/TxSpecific extended with optional metadata data.
Unchained dependency
packages/unchained-client/package.json
Added bs58 dependency for base58 decoding of memo instruction data.
Tests
packages/swapper/src/swappers/ThorchainSwapper/utils/poolAssetHelpers/poolAssetHelpers.test.ts
Added tests covering SOL.SOLsolAssetId mapping.

Sequence Diagram

sequenceDiagram
    participant Client as Client
    participant Swapper as ThorchainSwapper
    participant Utils as Thorchain Utils (solana)
    participant Adapter as SolanaAdapter
    participant Node as THORNode

    Client->>Swapper: getUnsignedSolanaTransaction(tradeQuote)
    Swapper->>Utils: getUnsignedSolanaTransaction(args, swapperName)
    Utils->>Utils: validate quote & extract memo
    Utils->>Node: getThorTxData(sellAsset, config)
    Node-->>Utils: { vault }
    Utils->>Utils: build memo instruction + transfer instruction
    Utils->>Adapter: getFeeData({ instructions, memo, tokenId? })
    Adapter-->>Utils: { fast: { txFee, computeUnitLimit } }
    Utils->>Swapper: return SolanaSignTx (unsigned)
    Client->>Swapper: executeSolanaTransaction(signedTx)
    Swapper->>Adapter: signAndBroadcastTransaction(signedTx)
    Adapter-->>Swapper: broadcast result
    Swapper-->>Client: result
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • NeOMakinG

Poem

🐰
I nibble memos, hop to vaults with glee,
SOL in paw, I ferry swaps with ease,
A memo stitch, a lamport leap,
From chain to chain my carrots keep,
Hops of code, and swaps that please!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'feat: thorchain solana swapper integration' directly and clearly describes the main change: adding Solana support to the THORChain swapper end-to-end.
Linked Issues check ✅ Passed All primary coding objectives from #12034 are implemented: Solana types added, thorchain-utils/solana module with getThorTxData/getUnsignedSolanaTransaction/getSolanaTransactionFees created, getL1RateOrQuote.ts Solana stub replaced with working implementation, endpoints/ThorchainSwapper extended for Solana, unchained-client parser added, and asset map generation updated.
Out of Scope Changes check ✅ Passed All changes are directly related to Solana swapper integration objectives. Case-sensitivity fixes in poolAssetHelpers and bs58 dependency addition are supporting changes necessary for the feature; the ParsedTx augmentation with data field supports the Solana parser implementation.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/thorchain-solana

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@gomesalexandre gomesalexandre marked this pull request as ready for review February 26, 2026 01:08
@gomesalexandre gomesalexandre requested a review from a team as a code owner February 26, 2026 01:08
@gomesalexandre gomesalexandre marked this pull request as draft February 26, 2026 01:12
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (2)
packages/swapper/src/thorchain-utils/solana/getUnsignedSolanaTransaction.ts (1)

10-15: Extract shared Solana memo/instruction builders to avoid drift.

MEMO_PROGRAM_ID and memo/transfer instruction construction are duplicated across multiple thorchain Solana utilities. Consolidating these into one helper would reduce divergence between quote, fee, and execution paths.

Also applies to: 39-43, 45-57, 60-64

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/swapper/src/thorchain-utils/solana/getUnsignedSolanaTransaction.ts`
around lines 10 - 15, The constants MEMO_PROGRAM_ID and
COMPUTE_BUDGET_INSTRUCTION_OVERHEAD_CU and the logic that builds Solana memo and
transfer instructions (used in getUnsignedSolanaTransaction and other thorchain
Solana utilities) are duplicated; extract them into a single shared helper
module (e.g., solanaInstructionHelpers) that exports MEMO_PROGRAM_ID,
COMPUTE_BUDGET_INSTRUCTION_OVERHEAD_CU, and functions like
buildSolanaMemoInstruction(memo: string) and
buildSolanaTransferInstruction(params) and update getUnsignedSolanaTransaction
and the other utilities to import and use these shared symbols instead of
redefining the constants/instruction construction locally so all quote, fee, and
execution paths use the same implementations.
packages/unchained-client/src/solana/parser/thorchain.ts (1)

8-10: Prefer type over interface for ParserArgs.

Line 8 can be converted to a type alias to align with the project’s TS conventions.

Proposed fix
-export interface ParserArgs {
+export type ParserArgs = {
   midgardUrl: string
-}
+}

As per coding guidelines: "Prefer type over interface for type definitions in ShapeShift project".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/unchained-client/src/solana/parser/thorchain.ts` around lines 8 -
10, Change the exported ParserArgs declaration from an interface to a type alias
to follow project TS conventions: replace the "export interface ParserArgs {
midgardUrl: string }" declaration with a "export type ParserArgs = { midgardUrl:
string }" type alias, leaving the exported name ParserArgs and its property
midgardUrl unchanged so all usages of ParserArgs continue to work.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/swapper/src/thorchain-utils/getL1RateOrQuote.ts`:
- Around line 456-467: The fee estimation in getL1RateOrQuote uses
adapter.getFeeData with only a memoInstruction and an empty to:'' which
underestimates Solana fees; update the flow to call getThorTxData (the same
helper used in getSolanaTransactionFees.ts) to obtain the vault/transfer details
and construct the transfer instruction, then pass both the memoInstruction and
the transfer instruction(s) in chainSpecific.instructions to adapter.getFeeData
(instead of only memoInstruction) so fee estimation mirrors execution path;
locate the adapter.getFeeData call, the memoInstruction creation, and the
getThorTxData helper to implement this change.

In `@packages/swapper/src/thorchain-utils/solana/getSolanaTransactionFees.ts`:
- Around line 35-39: The lamports conversion in getSolanaTransactionFees.ts
currently uses Number(sellAmountIncludingProtocolFeesCryptoBaseUnit) when
building transferInstruction (SystemProgram.transfer), which loses precision for
values > Number.MAX_SAFE_INTEGER; modify the fee-estimation logic in
getSolanaTransactionFees (and mirror in getUnsignedSolanaTransaction if present)
to either convert using BigInt(sellAmountIncludingProtocolFeesCryptoBaseUnit) or
first guard with a safe-integer check (e.g., throw or clamp if >
Number.MAX_SAFE_INTEGER) before using Number, ensuring
transferInstruction.fromPubkey/toPubkey lamports are computed without silent
precision loss and keeping the original string value passed later to
buildSendApiTransaction unchanged.

In `@packages/unchained-client/src/solana/parser/thorchain.ts`:
- Around line 23-26: Wrap the memo decode and parse block in a try-catch so
invalid base58 or parsing errors don’t throw and break transaction processing:
guard the Buffer.from(base58.decode(memoInstruction.data)).toString('utf8') and
the call to this.parser.parse(memo, tx.txid) with a try { ... } catch (err) {
console.error(...) } and return undefined/null on error to mirror existing error
handling patterns; also change the exported ParserArgs declaration from an
interface to a type (rename `interface ParserArgs` to `type ParserArgs = { ...
}`) to conform to the TypeScript style guideline.

In `@scripts/generateTradableAssetMap/utils.ts`:
- Around line 79-80: The code lowercases assetReference unconditionally which
breaks case-sensitive Solana SPL mint addresses; update the logic so that when
the asset namespace is ASSET_NAMESPACE.splToken (or the chain is
KnownChainIds.SolanaMainnet) you do NOT call assetReference.toLowerCase(), but
keep lowercasing for other namespaces; locate the branch that returns
ASSET_NAMESPACE.splToken and the code that calls assetReference.toLowerCase()
and make the lowercase step conditional on namespace !==
ASSET_NAMESPACE.splToken (or chain !== KnownChainIds.SolanaMainnet).

---

Nitpick comments:
In `@packages/swapper/src/thorchain-utils/solana/getUnsignedSolanaTransaction.ts`:
- Around line 10-15: The constants MEMO_PROGRAM_ID and
COMPUTE_BUDGET_INSTRUCTION_OVERHEAD_CU and the logic that builds Solana memo and
transfer instructions (used in getUnsignedSolanaTransaction and other thorchain
Solana utilities) are duplicated; extract them into a single shared helper
module (e.g., solanaInstructionHelpers) that exports MEMO_PROGRAM_ID,
COMPUTE_BUDGET_INSTRUCTION_OVERHEAD_CU, and functions like
buildSolanaMemoInstruction(memo: string) and
buildSolanaTransferInstruction(params) and update getUnsignedSolanaTransaction
and the other utilities to import and use these shared symbols instead of
redefining the constants/instruction construction locally so all quote, fee, and
execution paths use the same implementations.

In `@packages/unchained-client/src/solana/parser/thorchain.ts`:
- Around line 8-10: Change the exported ParserArgs declaration from an interface
to a type alias to follow project TS conventions: replace the "export interface
ParserArgs { midgardUrl: string }" declaration with a "export type ParserArgs =
{ midgardUrl: string }" type alias, leaving the exported name ParserArgs and its
property midgardUrl unchanged so all usages of ParserArgs continue to work.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 6a4d0ee and 8bddb0b.

⛔ Files ignored due to path filters (23)
  • e2e/screenshots/thor_solana/all-swaps-pending.png is excluded by !**/*.png
  • e2e/screenshots/thor_solana/eth-to-rune-confirm.png is excluded by !**/*.png
  • e2e/screenshots/thor_solana/eth-to-rune-pending.png is excluded by !**/*.png
  • e2e/screenshots/thor_solana/eth-to-rune-quote.png is excluded by !**/*.png
  • e2e/screenshots/thor_solana/eth-to-usdc-confirm.png is excluded by !**/*.png
  • e2e/screenshots/thor_solana/eth-to-usdc-pending.png is excluded by !**/*.png
  • e2e/screenshots/thor_solana/eth-to-usdc-quote.png is excluded by !**/*.png
  • e2e/screenshots/thor_solana/rune-to-sol.png is excluded by !**/*.png
  • e2e/screenshots/thor_solana/sol-rune-v2-confirm.png is excluded by !**/*.png
  • e2e/screenshots/thor_solana/sol-rune-v2-quote.png is excluded by !**/*.png
  • e2e/screenshots/thor_solana/sol-rune-v2-success.png is excluded by !**/*.png
  • e2e/screenshots/thor_solana/sol-to-rune-confirm.png is excluded by !**/*.png
  • e2e/screenshots/thor_solana/sol-to-rune-failed-compute-budget.png is excluded by !**/*.png
  • e2e/screenshots/thor_solana/sol-to-rune-fixed-confirm.png is excluded by !**/*.png
  • e2e/screenshots/thor_solana/sol-to-rune-fixed-quote.png is excluded by !**/*.png
  • e2e/screenshots/thor_solana/sol-to-rune-quote-e2e.png is excluded by !**/*.png
  • e2e/screenshots/thor_solana/sol-to-rune-success.png is excluded by !**/*.png
  • e2e/screenshots/thor_solana/sol-to-rune-v3-confirm.png is excluded by !**/*.png
  • e2e/screenshots/thor_solana/sol-to-rune-v3-quote.png is excluded by !**/*.png
  • e2e/screenshots/thor_solana/sol-to-rune-v3-result.png is excluded by !**/*.png
  • e2e/screenshots/thor_solana/sol-to-rune.png is excluded by !**/*.png
  • packages/swapper/src/swappers/ThorchainSwapper/generated/generatedTradableAssetMap.json is excluded by !**/generated/**
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (18)
  • packages/swapper/src/swappers/MayachainSwapper/utils/poolAssetHelpers/poolAssetHelpers.ts
  • packages/swapper/src/swappers/ThorchainSwapper/ThorchainSwapper.ts
  • packages/swapper/src/swappers/ThorchainSwapper/endpoints.ts
  • packages/swapper/src/swappers/ThorchainSwapper/utils/poolAssetHelpers/poolAssetHelpers.test.ts
  • packages/swapper/src/swappers/ThorchainSwapper/utils/poolAssetHelpers/poolAssetHelpers.ts
  • packages/swapper/src/thorchain-utils/getL1RateOrQuote.ts
  • packages/swapper/src/thorchain-utils/index.ts
  • packages/swapper/src/thorchain-utils/solana/getSolanaTransactionFees.ts
  • packages/swapper/src/thorchain-utils/solana/getThorTxData.ts
  • packages/swapper/src/thorchain-utils/solana/getUnsignedSolanaTransaction.ts
  • packages/swapper/src/thorchain-utils/solana/index.ts
  • packages/swapper/src/types.ts
  • packages/types/src/base.ts
  • packages/unchained-client/package.json
  • packages/unchained-client/src/solana/parser/index.ts
  • packages/unchained-client/src/solana/parser/thorchain.ts
  • packages/unchained-client/src/solana/parser/types.ts
  • scripts/generateTradableAssetMap/utils.ts

- include transfer instruction in quote-time solana fee estimation (mirrors execution path)
- use bigint for lamport conversion to avoid precision loss above 9007 SOL
- wrap base58 decode in try-catch in unchained-client thorchain parser
- interface -> type for ParserArgs
- skip toLowerCase for spl token mint addresses in asset map generation

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
@gomesalexandre gomesalexandre marked this pull request as ready for review February 26, 2026 11:17
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/swapper/src/thorchain-utils/getL1RateOrQuote.ts`:
- Around line 470-477: The fee estimation call to adapter.getFeeData is missing
the Solana-specific tokenId in the chainSpecific payload, causing
under-estimated SPL-token fees; update the code that constructs chainSpecific
for the getFeeData call (the block passing to adapter.getFeeData in
getL1RateOrQuote) to include tokenId (use the tokenId available in the
quote/asset context) alongside from, instructions (memoInstruction,
transferInstruction) and ensure tokenId is forwarded so quote-time fees match
execution-time behavior.

In `@packages/swapper/src/thorchain-utils/solana/getUnsignedSolanaTransaction.ts`:
- Around line 51-58: The fee simulation call to adapter.getFeeData (the const {
fast } = await adapter.getFeeData(...)) omits chainSpecific.tokenId causing
mismatched estimates vs execution; update that getFeeData invocation to include
chainSpecific.tokenId (pass the same tokenId used later during transaction
construction) so the simulated fees reflect SPL-token routes exactly, ensuring
the tokenId value is forwarded into the chainSpecific object used for fee
estimation.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 8bddb0b and c8a5010.

⛔ Files ignored due to path filters (1)
  • e2e/screenshots/thor_solana/sol-to-rune-success-final.png is excluded by !**/*.png
📒 Files selected for processing (5)
  • packages/swapper/src/thorchain-utils/getL1RateOrQuote.ts
  • packages/swapper/src/thorchain-utils/solana/getSolanaTransactionFees.ts
  • packages/swapper/src/thorchain-utils/solana/getUnsignedSolanaTransaction.ts
  • packages/unchained-client/src/solana/parser/thorchain.ts
  • scripts/generateTradableAssetMap/utils.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/unchained-client/src/solana/parser/thorchain.ts
  • scripts/generateTradableAssetMap/utils.ts

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
packages/swapper/src/thorchain-utils/getL1RateOrQuote.ts (1)

452-460: Hoist THOR tx data lookup to avoid duplicated per-route network calls.

solana.getThorTxData() is invariant for this request and currently invoked once per route. Reusing a single promise keeps behavior the same while reducing duplicate I/O and failure fan-out.

♻️ Proposed refactor
     case CHAIN_NAMESPACE.Solana: {
       const adapter = assertGetSolanaChainAdapter(sellAsset.chainId)
       const sendAddress = (input as CommonTradeQuoteInput).sendAddress
+      const thorTxDataPromise = sendAddress
+        ? solana.getThorTxData({ sellAsset, config, swapperName })
+        : undefined

       const maybeRoutes = await Promise.allSettled(
         perRouteValues.map(async (route): Promise<T> => {
           const memo = getMemo(route)
           const protocolFees = getProtocolFees(route.quote)

           const feeData = await (async (): Promise<QuoteFeeData> => {
-            if (!sendAddress) return { networkFeeCryptoBaseUnit: undefined, protocolFees }
-            const { vault } = await solana.getThorTxData({ sellAsset, config, swapperName })
+            if (!thorTxDataPromise) return { networkFeeCryptoBaseUnit: undefined, protocolFees }
+            const { vault } = await thorTxDataPromise
             const memoInstruction = new TransactionInstruction({
               keys: [],
               programId: new PublicKey(SOLANA_MEMO_PROGRAM_ID),
               data: Buffer.from(memo, 'utf8'),
             })
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/swapper/src/thorchain-utils/getL1RateOrQuote.ts` around lines 452 -
460, The code calls solana.getThorTxData() inside the perRouteValues.map
handler, causing redundant identical network calls and failure fan-out; hoist a
single promise by calling solana.getThorTxData({ sellAsset, config, swapperName
}) once before building perRouteValues (or before Promise.allSettled) and reuse
that result (or its promise) inside the per-route async function where current
code references { vault } and memoInstruction, updating references in the
per-route block (used by maybeRoutes, getMemo, getProtocolFees, feeData) to use
the hoisted value instead of calling solana.getThorTxData repeatedly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@packages/swapper/src/thorchain-utils/getL1RateOrQuote.ts`:
- Around line 452-460: The code calls solana.getThorTxData() inside the
perRouteValues.map handler, causing redundant identical network calls and
failure fan-out; hoist a single promise by calling solana.getThorTxData({
sellAsset, config, swapperName }) once before building perRouteValues (or before
Promise.allSettled) and reuse that result (or its promise) inside the per-route
async function where current code references { vault } and memoInstruction,
updating references in the per-route block (used by maybeRoutes, getMemo,
getProtocolFees, feeData) to use the hoisted value instead of calling
solana.getThorTxData repeatedly.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between c8a5010 and 69c5264.

📒 Files selected for processing (2)
  • packages/swapper/src/thorchain-utils/getL1RateOrQuote.ts
  • packages/swapper/src/thorchain-utils/solana/getUnsignedSolanaTransaction.ts

Copy link
Collaborator

@NeOMakinG NeOMakinG left a comment

Choose a reason for hiding this comment

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

Code Review — PR #12036

Verdict: ✅ APPROVE

Summary: Clean THORChain Solana swapper integration following the established Tron pattern. Adds SOL as a valid sell/buy asset through THORChain, with proper case-sensitive base58 handling, SPL token forward-compatibility, and a Solana transaction parser for unchained-client.

Architecture

Follows the exact same structure as the Tron integration:

  • thorchain-utils/solana/ module with getThorTxData, getUnsignedSolanaTransaction, getSolanaTransactionFees
  • ThorchainSwapper.executeSolanaTransaction + endpoint wiring
  • Solana case in getL1RateOrQuote.ts replaces the 'Solana is not supported' stub
  • Asset map generation updated for SOL chain + SPL token namespace

What's Good

  1. Case-sensitive base58 fix — Removing .toLowerCase() from assetIdToThorPoolAssetIdMap and assetIdToMayaPoolAssetIdMap lookups is critical. Solana addresses are base58-encoded and case-sensitive. .toLowerCase() would corrupt them. Test added to verify.

  2. SPL token forward-compatgetTokenStandardFromChainId returns splToken for Solana, and assetReference skips .toLowerCase() for SPL tokens. When THORChain adds SPL pools, no new code needed.

  3. Compute budget overheadCOMPUTE_BUDGET_INSTRUCTION_OVERHEAD_CU = 300 accounts for the 2 ComputeBudgetProgram instructions (150 CU each) that aren't included in fee estimation. Good detail that prevents "compute budget exceeded" errors.

  4. Memo Program instruction — Uses MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr (Solana's Memo Program) to attach THORChain memos. This is the correct pattern — same as how THORChain's own Solana integration works.

  5. Transaction parserthorchain.ts decodes base58-encoded memo data from the Memo Program instruction, delegates to the shared thorchain parser. Error handling with console.error + graceful return undefined.

  6. Tests — Pool asset helpers tested for SOL.SOL ↔ solAssetId roundtrip, including case-sensitivity verification.

Minor Observations (Non-blocking)

  1. Memo/transfer instruction duplication across 3 files (getL1RateOrQuote, getSolanaTransactionFees, getUnsignedSolanaTransaction). This follows the same pattern as Tron — each file builds instructions independently. Could be extracted to a shared helper, but consistency with existing patterns is fine.

  2. allowanceContract: '0x0' in the Solana case — Solana doesn't have ERC-20 allowances. This is a typed-system placeholder consistent with UTXO/Cosmos paths.

  3. GetSolanaTradeQuoteInputBase and GetSolanaTradeQuoteInput are identical — follows Tron scaffolding pattern for when wallet-specific fields are added later.

Risk Assessment

Low risk — Additive only, no existing swap flows touched. SOL.SOL pool is live on THORChain mainnet. The .toLowerCase() removal is safe because EVM asset IDs in CAIP format already use lowercase hex addresses. CI passes including static analysis.

Browser Test

Swapper-level change — quote/execution logic lives in packages, not UI components. E2E screenshots in the PR demonstrate working SOL ↔ RUNE swaps. No additional browser test needed.

LGTM 👍

@gomesalexandre gomesalexandre enabled auto-merge (squash) March 2, 2026 10:16
@gomesalexandre gomesalexandre merged commit 7ee096e into develop Mar 2, 2026
4 checks passed
@gomesalexandre gomesalexandre deleted the feat/thorchain-solana branch March 2, 2026 10:45
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.

feat: thorchain solana swapper integration

2 participants