Skip to content

feat(frontend): send imported Solana balances to the user's own wallet - #13651

Draft
sbpublic wants to merge 3 commits into
feat/frontend/ic-seed-phrase-import-evmfrom
feat/frontend/ic-seed-phrase-import-sol
Draft

feat(frontend): send imported Solana balances to the user's own wallet#13651
sbpublic wants to merge 3 commits into
feat/frontend/ic-seed-phrase-import-evmfrom
feat/frontend/ic-seed-phrase-import-sol

Conversation

@sbpublic

@sbpublic sbpublic commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator

Motivation

PR4 moves EVM balances of an imported wallet; this does the same for Solana — native SOL and SPL tokens.

Like the EVM addresses, the imported wallet's Solana address is a threshold key rooted in its own helper canister, so only that canister can sign for it. We ask it via sign_sol and broadcast ourselves.

Stacked on #13650; review that first. This PR targets its branch.

Note

Draft. Not exercised against a funded Solana account — sign_sol has not been called with a real transaction message, and the SPL path (which may create a destination token account) is untested end-to-end. One small real send would validate it.

Changes

  • Add the Solana send path for the import. sign_sol is raw Ed25519 over the message bytes — exactly what a TransactionPartialSigner produces — so the imported signer drops into OISY's own sendSol in place of the default.
  • sendSol gains one optional signerOverride parameter (backward-compatible; its three existing callers are unchanged). Everything else is reused: building the transfer, creating the destination token account when the OISY side does not yet hold that token, broadcast, and confirm.
  • Route Solana rows on the import page to it.
  • Update PRODUCT.md and the spec.

Two design points:

  • Fee shape mirrors EVM. The fee is paid in SOL from the imported account, so a native send moves balance - fee and an SPL send needs SOL present. Rather than duplicate EVM's gating, this generalises it: isPlugFeeChain covers EVM and Solana, isPlugGasToken covers the tokens that need a native coin for their fee (ERC-20/ERC-4626 and SPL), and the "needs gas" message names the right coin — BNB, ETH or SOL. The tested EVM behaviour rides on the same predicates, so the EVM tests are the regression guard for the generalisation.
  • SPL rent is left to the network. An SPL transfer to an OISY account that doesn't hold the token yet must create its token account (~0.002 SOL rent), paid by the imported account. Estimating that up front is fiddly; the base-fee check gates the obvious case and the network rejects an underfunded transaction cleanly, surfacing an error toast.

The Solana destination is the user's own OISY Solana address, mirroring the EVM path.

Tests

  • plug-sol.services.spec.ts (4 cases): native reserves the fee and injects the imported signer; native below the fee refuses without sending; SPL sends the full balance; SPL with no SOL refuses without sending.
  • PlugImportAccount.spec.ts (+3 cases): native SOL and funded-SPL rows offer an action; an SPL row with no SOL is blocked and names SOL.
  • OISY's own sol-send.services.spec.ts still passes unchanged — the signerOverride addition did not disturb the default path.

Local gates, all green:

Gate Result
npm run format clean
npm run lint -- --max-warnings 0 (whole repo) clean
npm run check 5893 files, 0 errors
npm run check:tests 7123 files, 0 errors
targeted vitest 97 passed (whole feature)

Not run locally: the full suite (unrelated localStorage/jsdom failures on main) and test-coverage. compare-sizes will fail as across the stack; the increase is known and accepted.


🤖 Generated with Claude Code

sbpublic and others added 3 commits August 5, 2026 11:31
Native SOL and SPL tokens are signed by the imported wallet's own canister
and broadcast by OISY.

Solana signing is raw Ed25519 over the message bytes, which is exactly
what a TransactionPartialSigner produces — so the imported signer drops
into OISY's own sendSol in place of the default. sendSol gains one optional
signerOverride parameter, backward-compatible for its existing callers, and
everything else is reused: building the transfer, creating the destination
token account when the OISY side does not hold that token yet, broadcast,
and confirm.

The fee is paid in SOL from the imported account, so a native send moves
balance minus fee and an SPL send needs SOL present. SPL sufficiency beyond
the base fee — the token-account rent — is left to the network to reject,
rather than estimated up front.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Generalises the EVM-specific send gating to a shared fee-chain concept, so
Solana reuses it rather than duplicating: isPlugFeeChain covers EVM and
Solana, isPlugGasToken covers the tokens that need a native coin for their
fee (ERC-20/ERC-4626 and SPL), and the "needs gas" reason names the native
coin — BNB, ETH or SOL — accordingly.

The Solana destination is the user's own OISY Solana address, mirroring the
EVM path's use of the OISY EVM address.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Notes that sign_sol fits OISY's TransactionPartialSigner directly, that
sendSol is reused via an injected signer, and that the fee gating is shared
with EVM. BTC remains the one unbuilt chain, with its broadcast path still
an open question.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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.

1 participant