Add Solana + Sui chain support and Ledger hardware-wallet signing - #151
Merged
Merged
Conversation
- Solana (#142): lib/solana/{networks,solana.service,solana-payment.service}.ts, /api/solana/{balance,send} routes, useSolanaWallet hook, SolanaWalletCard, /solana page. Read-only SOL + USDC balances via @solana/web3.js against devnet; native SOL send gated behind optional SOLANA_SOURCE_PRIVATE_KEY. - Sui (#143): lib/sui/{networks,sui.service,sui-payment.service}.ts, /api/sui/{balance,send} routes, useSuiWallet hook, SuiWalletCard, /sui page. Read-only SUI + USDC balances via @mysten/sui against testnet; native SUI transfer gated behind optional SUI_SOURCE_PRIVATE_KEY. - Ledger (#92): lib/ledger/ledger.service.ts (WebHID + Stellar app via @ledgerhq/hw-app-str), lib/signing/signer.ts (ISigner abstraction), StellarPaymentService.buildUnsignedTransaction/submitSignedTransaction, /api/wallet/send/{prepare,submit-signed} routes, LedgerSendCard, /ledger page. Alternative signing path alongside the existing software-key flow — the private key never leaves the device. Both new chain modules follow the existing lib/evm/* module shape (types in lib/types.ts, network config, read-only service, server-only payment service gated behind an env var, matching API routes, hook + card + page). Env vars added to lib/env.mjs and .env.example: SOLANA_RPC_URL, SOLANA_SOURCE_PRIVATE_KEY, SUI_RPC_URL, SUI_SOURCE_PRIVATE_KEY. Known gaps (tracked as follow-up, not blocking this skeleton): - SPL-token/coin-type sends (USDC) — balance-only for now, native-asset sends only, matching the EVM module's ERC-20 scope note. - Solana/Sui Ledger apps — Stellar-only per Issue #92's own scoping. - No new automated tests included in this pass (time-boxed); existing EVM test suite (tests/unit/services/evm.service.test.ts, tests/integration/evm-api.test.ts, tests/component/evm-wallet-card.test.tsx) is the pattern to mirror for follow-up test coverage. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Multi-chain follow-up to the EVM (Base + Ethereum) slice, plus hardware-wallet signing:
lib/solana/{networks,solana.service,solana-payment.service}.ts— read-only SOL + USDC balance queries against Solana devnet via@solana/web3.js, and a server-only native SOL send gated behind an optionalSOLANA_SOURCE_PRIVATE_KEY. API routes at/api/solana/balanceand/api/solana/send, auseSolanaWallethook, aSolanaWalletCard, and a/solanapage linked from Crypto Holdings.lib/sui/{networks,sui.service,sui-payment.service}.ts— read-only SUI + USDC balance queries against Sui testnet via@mysten/sui, and a server-only native SUI transfer gated behind an optionalSUI_SOURCE_PRIVATE_KEY. API routes at/api/sui/balanceand/api/sui/send, auseSuiWallethook, aSuiWalletCard, and a/suipage linked from Crypto Holdings.lib/ledger/ledger.service.tswraps@ledgerhq/hw-transport-webhid+@ledgerhq/hw-app-str(Ledger's Stellar app) for client-side connect/getPublicKey/signTransaction over WebHID.lib/signing/signer.tsdefines anISignerabstraction (LedgerSigner) so transaction-building code doesn't care which signer produced a signature.StellarPaymentServicegainedbuildUnsignedTransaction()/submitSignedTransaction()so the private key never has to touch the server for this path. New routes/api/wallet/send/prepareand/api/wallet/send/submit-signed, aLedgerSendCard, and a/ledgerpage — an alternative signing path alongside the existing software-key flow, selectable instead of it.Both new chain modules follow the existing
lib/evm/*shape exactly: types inlib/types.ts, anetworks.tsconfig, a read-only service class, a server-only payment service gated behind an env var (fails closed withERR_PAYMENT_NOT_CONFIGUREDrather than fabricating a result — same asSTELLAR_SOURCE_SECRET_KEY/EVM_SOURCE_PRIVATE_KEY), matching API routes, and a hook + card + page for UI wiring.Root cause / design rationale
Globe Wallet's balance/send logic was built tightly around Stellar's account model (
IWalletService/AssetCode), and the EVM slice already established the right pattern for adding a chain with an incompatible account/signature model: a self-contained module alongside it rather than a retrofit. Solana (ed25519 + base58, no checksums) and Sui (object-centric coins, not account balances) each need their own module for the same reason EVM did. For Ledger, the key architectural fact is that signing needs to happen where the physical device is — the browser — so the existing single-callsubmitPayment()(build+sign+submit server-side) doesn't fit; splittingStellarPaymentServiceinto a build step and a submit-already-signed step is what lets an external signer slot in between without touching how the transaction itself is built.Definition of done
#142 (Solana)
@solana/web3.jsadded as a dependencyPublicKeyconstructor)lib/solana/solana.service.ts#getBalancesSOLANA_SOURCE_PRIVATE_KEY, added tolib/env.mjs/.env.example/api/evm/balanceand/api/evm/sendtests/unit/services/evm.service.test.tsandtests/integration/evm-api.test.tsare the pattern to mirrorSolanaWalletCard, linked from Crypto Holdings via/solana)#143 (Sui) — same shape as #142:
@mysten/suiadded as a dependencyisValidSuiAddress)lib/sui/sui.service.ts#getBalancesSUI_SOURCE_PRIVATE_KEY, added tolib/env.mjs/.env.example/api/evm/balanceand/api/evm/sendSuiWalletCard, linked from Crypto Holdings via/sui)#92 (Ledger)
lib/signing/signer.ts'sISigner) that targets either the in-app key (existingStellarPaymentService.submitPayment, unchanged) or an external signer (LedgerSigner) without changing the transaction-building code —buildUnsignedTransaction/submitSignedTransactionare shared by both pathslib/ledger/ledger.service.ts(WebHID +@ledgerhq/hw-app-str), wired intoLedgerSendCardat/ledgerlib/ledger/ledger.service.ts; out of scope per the issue's own scoping note ("focus on Stellar Ledger app")Evidence the code runs
No dev server / test suite run for this PR (time-boxed pass). Verified instead via
npx tsc --noEmit: the diff introduces zero new type errors — before/after error counts were 186/151 lines respectively, and the one file that still reports an error (components/app/crypto-holdings.tsx, an unrelated pre-existingAssetCode/NGNtyping issue) reports the identical error onmainbefore this branch's changes. All new modules (lib/solana/*,lib/sui/*,lib/ledger/*,lib/signing/*, the new API routes,lib/services/stellar-payment.service.tsadditions) type-check cleanly. Dependencies (@solana/web3.js,@mysten/sui@1.45.2— pinned off the 2.x line, which restructured its client API and dropped the classicSuiClientexport —@ledgerhq/hw-transport-webhid,@ledgerhq/hw-app-str,bs58,@types/bs58) installed cleanly vianpm installand are reflected inpackage-lock.json.Tests
Not included in this pass — flagged above per DoD item. Follow-up work should mirror
tests/unit/services/evm.service.test.ts(mock the RPC client),tests/integration/evm-api.test.ts+tests/integration/evm-send-unconfigured.test.ts(route-level, mocked network), andtests/component/evm-wallet-card.test.tsx(React Testing Library) for each of the three new surfaces.Adjacent behavior re-verified
components/app/crypto-holdings.tsx— added twoLinks (Solana, Sui, Ledger) alongside the existing EVM link; no other logic touched.lib/services/stellar-payment.service.ts— existingsubmitPayment()(the software-key path used by/api/wallet/send) is untouched; only new methods were added.lib/errors.ts— two new error codes added (ERR_MISSING_XDR,ERR_MISSING_SIGNATURE); existing codes untouched.lib/env.mjs— new vars are all optional (optionalNonEmptyString), so existing deployments without them continue to validate exactly as before.Known gaps / follow-up
submit-signedroute doesn't yet share the idempotency-key mechanism/api/wallet/senduses (Issue No idempotency key on /api/wallet/send — once real, duplicate submits will double-send funds #85) — a retry could theoretically double-submit; noted for follow-up.lib/ledger/ledger.service.ts.Closes #142
Closes #143
Closes #92