Skip to content

Menese Protocol Integration

Last updated: 2026-07-11


OhShii Launcher is an allowlisted partner on Menese’s Sovereign Send canister. This means:

  • Free address derivation — our vouched users don’t pay 45B cycles per getMyAddress()
  • No subscription required — we use the per-tx fee model, not the MeneseSDK subscription
  • 20% revenue share — of the 0.1% protocol fee on sendSol/signSend operations
  • Backend canister allowlisted — can call registerPartnerUsers() to vouch for users
Partner (OhShii)Non-partner
getMyAddress() costFree (vouched users)45B cycles (caller pays)
Subscription neededNoYes ($20-$323/mo) or cycles
Revenue share20% of 0.1% protocol feeNone
User registrationBackend calls registerPartnerUsers()Must use registerSession(developerKey)
depositSol() fee0% (pool’s own fee applies)0% (same)
sendSol() fee0.1% atomic0.1% atomic
  • Allowlisted backend canister: vil43-piaaa-aaaal-qsibq-cai (prod)
  • Partner fee-share address: BuqZagKKiRFUfxNFRKK7w3JymQgxuCy2rxFBdQTysMW4 (assigned by Menese)

CanisterIDRole
Sovereign Sendfxjsq-raaaa-aaaab-agdaa-caiSigning canister — 0.1% fee model, no subscription
ICP-SOL Swap Pool (chain-key)w2vjc-2yaaa-aaaab-ae6zq-caiSOL↔ICP swaps at minimal slippage. NOT an oracle — legacy CROSSCHAIN_ORACLE_* code symbols are kept for API stability
ckSOL mintercrmds-kqaaa-aaaaf-qf5aq-caimSOL minting/redemption
mSOL ledger (ICRC-2)2ykjj-eyaaa-aaaae-af4ma-caimSOL token
MeneseSDK (legacy)urs2a-ziaaa-aaaad-aembq-caiOld subscription model — used only for legacy recovery
ICP Ledgerryjl3-tyaaa-aaaaa-aaaba-caiICP token

flowchart TB
Browser["Browser (Frontend)"]
Browser --> Sovereign["Sovereign Send (fxjsq)"]
Browser --> Pool["ICP-SOL Swap Pool (w2vjc)"]
Browser --> Backend["OhShii Backend (prod: vil43)"]
Sovereign -->|signDeposit| Solana["Solana Network (threshold Schnorr)"]
Pool -->|swapIcpToSol| Solana
Backend -->|registerPartnerUsers| Sovereign

Flow 1: SOL → ICP (via Sovereign Send — sign-only path)

Section titled “Flow 1: SOL → ICP (via Sovereign Send — sign-only path)”

Status: Implemented — uses sign-only for reliable broadcasting. ICP arrives automatically in ~30-60s after Solana confirms.

sequenceDiagram
participant Wallet as User Wallet (Phantom)
participant Frontend as OhShii Frontend
participant Sovereign as Sovereign Send (fxjsq)
participant RPC as Solana RPC
participant Pool as ICP-SOL Pool (w2vjc)
Wallet->>Frontend: 1. Send SOL to derived address
Frontend->>RPC: 2. fetchBlockhash
Frontend->>Sovereign: 3. signDeposit(pool, lamports, blockhash)
Sovereign-->>Frontend: signedTxBase64
Frontend->>RPC: 4. broadcastSolTx(signedTxBase64)
RPC->>Pool: 5. Pool receives SOL, credits ICP
Pool-->>Frontend: 6. ICP arrives at user principal (~30-60s)

Frontend code path: sovereignSend.js → depositSolForIcp(lamports, identityOrAgent)

Why sign-only instead of autonomous depositSol:

  • The autonomous path (depositSol) uses canister HTTP outcalls to broadcast — these can fail silently due to stale blockhash or HTTP errors, returning ok while the SOL doesn’t actually move.
  • The sign-only path (signDeposit) lets our frontend fetch a fresh blockhash and broadcast directly via Solana RPC, which is faster (~0 cycles) and more reliable.
  • Multiple partner dApps have experienced the autonomous broadcast returning ok while the Solana transaction fails silently.

Broadcast honesty: broadcastSolTx tries the RPCs sequentially (first acceptance wins) and then polls getSignatureStatuses until the transaction is confirmed/finalized — RPC acceptance alone is never reported as success (with skipPreflight, a stale-blockhash tx is accepted and then silently dropped). The tx signature is always surfaced with a Solana explorer link, on failures too.

Key facts:

  • signDeposit has 0% Sovereign Send fee — the pool applies its own 0.15% LP fee
  • Min deposit: 0.05 SOL
  • Always reserve 935,880 lamports for Solana (890,880 rent-exempt minimum + 45,000 priority fee) via maxSendLamports() — a transfer that would drop the derived account below the rent-exempt minimum is rejected. (NOT 50,000.)
  • No second call needed after broadcast — ICP release is automatic
  • Cached users can deposit even if registerPartnerUsers is temporarily failing

Flow 2: ICP → SOL (via the chain-key swap pool)

Section titled “Flow 2: ICP → SOL (via the chain-key swap pool)”

Status: Implemented and working.

sequenceDiagram
participant User
participant Frontend as OhShii Frontend
participant Pool as ICP-SOL Swap Pool (w2vjc)
Note over Frontend: 1. Gateway fee (0.3% to backend)
Note over Frontend: 2. icrc2_approve (ICP_LEDGER, spender=pool)
Frontend->>Pool: 3. swapIcpToSol(e8s, solAddress)
Note over Pool: 4. Pool pulls ICP, signs SOL tx, broadcasts to Solana
Pool-->>User: 5. SOL arrives at Solana addr

Frontend code path: solSwap.js → swapIcpToSol(e8s, solAddress, identityOrAgent)

Key facts:

  • The swap pool pulls ICP via ICRC-2 transfer_from after the user’s icrc2_approve
  • OhShii charges a governance-configurable gateway fee (default 30 bps = 0.3%) in ICP. The fee is read live from the backend (get_solana_gateway_fee_config; the frontend falls back to 30 bps if the query fails) and set via admin_set_solana_gateway_fee (admin or ONS proposal, 1000 bps hard cap). It is collected on the backend canister’s default account (the DAO fee treasury)
  • The fee is charged only after the swap returns ok — a user whose swap fails never loses the fee. If the fee transfer itself fails after a successful swap, the swap still succeeds and the uncollected fee is logged (the DAO’s loss, never the user’s)
  • The pool has its own fee (0.15% for LPs + dynamic spread up to 0.3%)
  • ⚠️ The pool reserves are small (verified 2026-07-11: ~1,124 ICP / ~2.96 SOL); large swaps can hit reserve exhaustion — the UI reads getCanisterInfo and blocks amounts whose quoted output exceeds the current reserve

Flow 3: SOL → mSOL (via Sovereign Send sign-only + ckSOL minter)

Section titled “Flow 3: SOL → mSOL (via Sovereign Send sign-only + ckSOL minter)”

Status: ⚠️ CODE-COMPLETE, NOT EXPOSED, UNVERIFIED. depositSolForMsol now signs, broadcasts, confirms the tx on Solana (getSignatureStatuses polling) and then calls expectCkSolDeposit(txHash, lamports) so the minter verifies the deposit on-chain and mints. The flow is deliberately NOT reachable from any UI until the full round-trip (Flow 3 + Flow 4) is verified with a real deposit — whether Menese’s layer would also auto-mint remains an open question with Menese.

1. User sends SOL to derived address (same as Flow 1)
2. Frontend: fetchSolanaBlockhash() via RPC
3. Frontend: sovereignSend.signDeposit(CKSOL_MINTER, lamports, blockhash)
4. Frontend: broadcastSolTx(signedTxBase64) via Solana RPC
5. Frontend: cksol.expectCkSolDeposit(txHash, lamports) ← verifies on-chain
6. ckSOL minter mints mSOL to user's principal

Note: Unlike Flow 1, mSOL minting requires a second call (expectCkSolDeposit) to verify the Solana deposit. This is because the ckSOL canister verifies the tx on-chain before minting.

Status: ⚠️ CODE-COMPLETE, NOT EXPOSED, UNVERIFIED. redeemMsolToSol() now performs the required ICRC-2 approve on the mSOL ledger (spender = ckSOL minter, amount + fee headroom) before requestCkSolRedemption, and the broadcast is confirmation-polled. Still not user-reachable in the UI (no redeem button) and it must stay that way until the full mSOL round-trip is verified end-to-end with a real deposit/redemption.

1. Frontend: icrc2_approve(MSOL_LEDGER, spender=CKSOL_MINTER, amount)
2. Frontend: cksol.requestCkSolRedemption(lamports, solAddress, memo)
3. ckSOL burns mSOL, signs SOL transfer, returns signedTxBase64
4. Frontend: broadcasts signedTxBase64 to Solana via RPC
5. User receives SOL at their Solana address

Flow 5: mSOL → ICP (two-step, presented as one action)

Section titled “Flow 5: mSOL → ICP (two-step, presented as one action)”

Status: ⚠️ CODE-COMPLETE, NOT EXPOSED, UNVERIFIED — phase 1 is redeemMsolToSol() (Flow 4, approve now implemented). Inherits Flow 4’s status: not user-reachable and must not be exposed until the round-trip is verified.

Phase 1 — mSOL → SOL:
1. Approve mSOL for burning
2. Redeem via ckSOL → get signed SOL tx
3. Broadcast to Solana
4. Poll derived SOL address until balance arrives (~15-30s)
Phase 2 — SOL → ICP:
5. depositSol(ICP_SOL_SWAP, balance)
6. ICP arrives automatically (~30-60s)

Progress indicator: Approving → Redeeming → Broadcasting → Waiting for SOL → Converting to ICP → Complete

mSOL is a separate product, not an intermediate step in SOL↔ICP. Users can:

  • Convert SOL to mSOL to hold yield-bearing SOL (1-2.2% APY)
  • Convert mSOL back to SOL when they want native SOL
  • Convert mSOL to ICP via the two-step flow (mSOL → SOL → ICP)
  • Convert SOL directly to ICP without touching mSOL at all

Registration is lazy: it runs only when the user explicitly enters a SOL→ICP flow (the SOL→ICP mode on the Solana tab, or the Solana participation box on a DAO page) and the gateway is verifiably healthy — never eagerly on tab mount. This stops the partner map from growing with visitors who never deposit and keeps a disabled/paused gateway from generating any Menese traffic.

1. Frontend (on SOL→ICP intent): backend.register_menese_user()
2. Backend: sovereignSend.registerPartnerUsers([caller_principal])
3. User is now vouched — free getMyAddress() and signDeposit()
4. Frontend: sovereignSend.getMyAddress() → user's derived SOL address

The backend rate-limits registrations (Shield: per-caller + global caps, PoW under Defcon) and records each vouched principal in a stable working set.

admin_remove_menese_user(opt vec principal) (admin or ONS governance; ONS proposal “Revoke Menese Vouched Users”) calls Sovereign Send’s removePartnerUsers. Passing null/empty revokes every principal in the stable vouched working set in one call — the incident-time lever for de-authorizing abusive already-vouched principals without waiting on a governance vote mid-incident.

Known limitation: the working set only tracks principals vouched after the upgrade that introduced it (Sovereign Send has no enumeration API), so the bulk path cannot see pre-upgrade vouches — revoke those by passing the principals explicitly (the explicit path forwards them verbatim). Full historical coverage otherwise requires Menese (adminRemoveVouchedUser is Menese-key-only) — an open item with Menese. The backend also self-reports its partner state via the anonymous get_menese_partner_status query (last registration outcome, vouched-set size, cached fee-treasury address).

Graceful degradation when registration is unavailable

Section titled “Graceful degradation when registration is unavailable”

If registerPartnerUsers fails (allowlist reset, Sovereign Send maintenance, etc.):

  • Cached users (anyone who previously called getMyAddress): can still derive addresses and sign deposits. cachedUsers survives allowlist resets. These users are NOT blocked.
  • New users (never used the Solana tab before): cannot get a derived address. The UI shows a warning that the Solana Gateway is temporarily unavailable for new users.
  • ICP → SOL (swap-pool path): unaffected — does not use Sovereign Send.
  • The deposit button remains enabled for users who have an address loaded.
  • Registration is retried on next page load.

OperationFeePaid to
getMyAddress()Free (partner)
depositSol()0% Sovereign Send + pool’s 0.15% LP feePool LPs
sendSol()0.1% atomicMenese protocol
swapIcpToSol()0.15% LP fee + dynamic spread (max 0.3%)Pool LPs + swap pool
OperationFeeCollected by
ICP → SOLGovernance-configurable, default 0.3% (30 bps); hard cap 10%Backend canister (DAO fee treasury) — only after the swap succeeds
SOL → ICPNot yet implemented frontend-side

The fee is stored on-chain (SolanaGatewayFeeConfig, get_solana_gateway_fee_config query) and set by admin_set_solana_gateway_fee — admin command or ONS “Set Solana Gateway Fee” proposal. The frontend reads it live and falls back to 30 bps if the query fails (it never silently drops to 0).

⚠️ Collection is client-enforced and therefore best-effort by design: a user who rejects the fee transfer prompt after a successful swap (or runs a modified client) skips it — the same was true of every prior ordering, since the pool does not enforce our fee. Robust, non-skippable collection would require a pool-side fee split (Menese) or routing swaps through the backend; tracked as a design follow-up.

  • 20% of the 0.1% protocol fee on sendSol/signSend operations
  • Distributed in SOL on Solana, 7-day epochs, minimum 0.05 SOL
  • Partner fee-share address: BuqZagKKiRFUfxNFRKK7w3JymQgxuCy2rxFBdQTysMW4
  • Applies to sendSol/signSend only. OhShii’s conversion flows use fee-free signDeposit deposits and the ICP↔SOL swap pool — neither carries the 0.1% protocol fee — so OhShii usage generates no material fee-share. Consistent with this, the address holds only the Solana rent-exempt minimum (~0.0009 SOL, as of 2026-07-12).

Status: balance visibility SHIPPED. The SystemStatus page’s “Menese / Solana Gateway” section shows the SOL balance of both surfaced addresses (public keyless RPC getBalance) with explorer links. There is no movement/sweep tooling because OhShii’s flows do not accrue a fee-share (see below) — there is nothing to move.

  • Partner fee-share address BuqZagKKiRFUfxNFRKK7w3JymQgxuCy2rxFBdQTysMW4 — the address Menese assigns for the 20% share on sendSol/signSend. Read-only.
  • Menese protocol fee treasurygetFeeTreasuryAddress() on Sovereign Send returns a single global address (the sink for the 0.1% protocol fee), identical for every caller (verified 2026-07-12: anonymous, unrelated principals, and the OhShii backend all receive the same address). The backend fetches and caches it (admin_fetch_menese_treasury_address, admin/ONS-gated) so the status page can read it via the anonymous get_menese_partner_status query and show its balance. It is Menese-controlled, not derived from our principal.

The 20% partner fee-share is 20% of the 0.1% protocol fee on sendSol/signSend (peer-to-peer SOL transfers). OhShii’s Gateway does not use those operations: SOL→ICP goes through fee-free signDeposit, and ICP→SOL goes through the ICP↔SOL swap pool (the pool’s own LP fee, not the Sovereign Send protocol fee). OhShii usage therefore generates no material fee-share — consistent with the partner fee-share address holding only the rent-exempt minimum. Any share Menese does attribute is distributed Menese-side (SOL on Solana, 7-day epochs, min 0.05 SOL).


Partner status — how to verify per environment

Section titled “Partner status — how to verify per environment”

The live Sovereign Send canister exposes getPartnerUserCount : (principal) -> (nat) query (a public query). To confirm the currently-deployed backend is a working partner, query getPartnerUserCount(<active backend principal>) and treat > 0 as registered. This is environment-aware: it reflects whichever backend is actually deployed, so it is the correct signal after a dev↔prod switch (the switch scripts do not remap Menese IDs — they now print a non-fatal post-switch advisory that runs this exact check against the target backend). getShieldStatus() returns only aggregate counts and cannot prove membership of a specific principal.

This check is wired into the product in two places:

  • SystemStatus page — the “Menese / Solana Gateway” section resolves the active backend from the deployed environment, queries getPartnerUserCount(activeBackend) anonymously, and renders GREEN only when membership is proven AND the shield resolved AND the circuit breaker is off AND cycles are above the reserve AND the pool is unpaused (reachable-but-unproven renders amber, failures render red).
  • Backend self-report — the anonymous get_menese_partner_status query returns the last registration outcome, the stable vouched-set size, and the cached Menese protocol fee-treasury address as the cheap complement to the authoritative live query.

Derivation Shield v3 — Cycle Drain Protection

Section titled “Derivation Shield v3 — Cycle Drain Protection”

Ed25519 (Solana) doesn’t support hierarchical public key derivation like secp256k1/BIP-32. Unlike ckBTC (which derives child keys locally from a cached master public key), each Solana address derivation requires a threshold Schnorr signing call (~30B cycles).

  1. Circuit breaker — hard stop when canister cycles < 500B. Cached users keep working.
  2. Global throttle — max 20 new derivations per 60s sliding window.
  3. Caller allowlist — partner canister bypasses throttle (not circuit breaker).
  • Unknown callers: attach 45B cycles per derivation (30B cost + 15B profit)
  • Cached users (anyone who derived before): free, bypass all layers
  • Allowlisted partners + vouched users (us): free derivation

MethodTypeFeeDescription
getMyAddress()updatefree (partner)Get user’s derived SOL address
signSend(to, lamports, blockhash)update0.1% atomicSign SOL transfer — caller broadcasts
sendSol(to, lamports)update0.1% atomicAutonomous — avoid (stale blockhash bug)
signDeposit(target, lamports, blockhash, borrowParams?)update0%Sign-only deposit — RECOMMENDED
depositSol(target, lamports, borrowParams?)update0%Autonomous deposit — avoid (stale blockhash bug)

Partner management (called by allowlisted backend)

Section titled “Partner management (called by allowlisted backend)”
MethodDescription
registerPartnerUsers(Principal[])Vouch for users — max 50K per partner
removePartnerUsers(Principal[])Remove previously vouched users

Registered treasury targets for depositSol

Section titled “Registered treasury targets for depositSol”
TargetCanisterWhat you get
ICP-SOL Swapw2vjc-2yaaa-aaaab-ae6zq-caiICP at swap-pool rate
mSOL (ckSOL)crmds-kqaaa-aaaaf-qf5aq-caimSOL (yield-bearing wrapped SOL)
SOL Borrow V3p7teu-wyaaa-aaaab-afnvq-caiUSDC loan against SOL collateral

ComponentStatusNotes
ICP → SOL swapWorkingSwap-pool direct call; governance-configurable gateway fee charged only after a successful swap; reserve-exhaustion guard in the UI
Price/quote queriesWorkingSwap-pool query calls (both directions surfaced in the UI)
User registration (partner)WorkingBackend register_menese_user(), LAZY (on SOL→ICP intent, gateway-health-gated); graceful degradation for cached users
Partner revocationWorkingadmin_remove_menese_user (admin/ONS) → removePartnerUsers, bulk revoke from the stable vouched set
SOL address derivationWorkingSovereign Send getMyAddress() (cached users survive allowlist resets); lazy, gateway-health-gated
SOL balance checkWorkingVia PublicNode/dRPC/Ankr RPCs; visibility-gated 10s poll with failure backoff
SOL → ICP depositWorkingSign-only (signDeposit + frontend broadcast) with on-chain confirmation polling; tx signature + explorer link surfaced
Broadcast confirmationWorkingSequential RPC fallback + getSignatureStatuses polling; success reported only after confirmed/finalized
DAO-page Solana onrampWorkingSolanaDaoParticipationBox (SOL → ICP → LGE) on Active campaigns, fail-closed on gateway/pause/breaker/Guest-cap, hands off to the shared LGE purchase panel
Gateway health surfacingWorkingShared SolanaGatewayStatus (SolanaTab + DAO box) + SystemStatus “Menese / Solana Gateway” section (env-aware getPartnerUserCount, never-green discipline)
SOL → mSOL depositCode-complete, NOT exposed, unverifiedConfirmed broadcast + expectCkSolDeposit wired; must not be exposed until the round-trip is verified (auto-mint question open with Menese)
mSOL balance displayNot exposedHidden together with the whole mSOL surface until the round-trip is verified
mSOL → SOL redemptionCode-complete, NOT exposed, unverifiedICRC-2 approve implemented; no redeem UI until verified end-to-end
mSOL → ICP conversionCode-complete, NOT exposed, unverifiedDepends on the redemption above
Legacy SOL recoveryRemovedFunds recovered; the dormant urs2a autonomous-SDK code paths were deleted from solSwap.js
Gateway fee (DAO-managed)WorkingSolanaGatewayFeeConfig on-chain, admin_set_solana_gateway_fee + ONS proposal, frontend reads live with a safe 30 bps fallback
Solana treasury visibility (DAO)WorkingSystemStatus section: partner fee-share + Menese protocol fee-treasury balances via public RPC, explorer links
Solana treasury movement / auto-sweepNot applicableOhShii’s flows (signDeposit + swap pool) don’t carry the 0.1% protocol fee, so no fee-share accrues — nothing to sweep
SOL→ICP awaiting-credit verificationWorkingwaitForIcpCredit polls the user’s ICP balance after Solana confirmation; timeout escalates with signature + explorer link + depositId (Menese-side, non-custodial)
ICP→SOL live status pollingWorkingpollSwapUntilSettled surfaces the pool SwapRecord status (Pending/Refunded/Failed/Completed) in the LIVE flow; the error path re-attaches to a swap record the failed call may have created
Fault attributionWorkingEvery gateway error is tagged with one owner (OhShii-config / Menese / Dfinity-minter / ICPSwap / network-user) + the correct next action (utils/crosschainFaults.js)
Swap historyWorkingNewest-first (createdAt desc) + “Load more” pagination in SolanaTab
Dust displayWorkingRent-exempt-only residues render as “≈0 SOL” with tap-to-reveal exact + a precise why-note (0.00089088 rent + 0.000045 fee = 0.00093588 reserved); spendable math untouched
Treasury panel noteWorkingONS treasury panel carries a “Solana (Menese) gateway — visibility only” row: the DAO’s Solana revenue is the ICP gateway fee (already in the ICP row); no SOL is sweepable

The frontend uses an ordered list of Solana RPCs with sequential fallback (first success wins). Source of truth: src/frontend/src/utils/solConstants.js (SOLANA_RPCS).

ProviderURLNotes
Helius (primary, keyed)https://mainnet.helius-rpc.com/?api-key=…Domain-restricted API key (Helius Allowed Domains, Origin/Referer check). Skipped entirely when no key is configured
PublicNode (fallback 1)https://solana-rpc.publicnode.comFree, public, no API key
dRPC (fallback 2)https://solana.drpc.orgFree, public
Ankr (fallback 3)https://rpc.ankr.com/solanaFree, public

The three public RPCs match the Menese SDK order. The Helius key ships in the frontend bundle by design (the app is a fully on-chain static frontend — there is no server to hold it); abuse protection is the domain allow list configured on the key, so requests from other origins are rejected by Helius. On non-allowed origins (e.g. local development) the Helius call fails and the loop falls through to the public RPCs.

RPCs must be added to CSP connect-src in .ic-assets.json5.


Stuck-state observability & recovery boundaries (Phase 2)

Section titled “Stuck-state observability & recovery boundaries (Phase 2)”

Design invariant: observability is DERIVED from on-chain state the user can already read for their own principal (tx signature, ICP block/balance, pool SwapRecord, getMySwaps, Sovereign-Send depositId) plus client-side localStorage. There is NO per-user on-chain error/status log — that would be a Sybil/DDoS vector.

SOL → ICP (deposit consumed, ICP not credited): after the Solana tx confirms, the UI polls the user’s ICP balance (waitForIcpCredit) and, if no credit is observed in ~4 minutes, escalates with the exact references (tx signature + Solscan link + Sovereign-Send depositId). This state is Menese-side: every recovery method on the swap pool is admin-gated by Menese’s key, and OhShii/DAO/guardian has NO lever to move funds inside the pool. The user’s reference set is what Menese needs to investigate. (A partner-callable refund is an open ask with Menese.)

ICP → SOL (swap errored or hangs): the pool’s SwapRecord is authoritative. The live flow polls getSwap(swapId) and renders Refunded (ICP returned by the pool), Failed (escalate to Menese with the swap id) or Completed. Funds on the user’s OWN derived SOL address are always re-spendable by retry — only the rent-exempt residue stays.

Fault owners: every error carries exactly one of ohshii-config (DAO toggle — wait), menese (pool/Sovereign Send — retry/escalate with references), dfinity-minter (ckBTC side), icpswap (ckBTC/ICP pool — self-service reclaim), network-user (retry/wait/top-up).

  • Do NOT use registerSession(developerKey) from frontend — replaced by backend registerPartnerUsers
  • Do NOT call getMySolanaAddress() on MeneseSDK — use Sovereign Send’s getMyAddress()
  • Do NOT call swapSolToIcp() after signDeposit() — ICP release is automatic in the partner flow
  • Do NOT use autonomous depositSol() — use signDeposit() + frontend broadcast (autonomous has stale blockhash bug)
  • Do NOT send full SOL balance — always reserve 935,880 lamports (890,880 rent-exempt + 45,000 fee) via maxSendLamports(). ~0.00089 SOL rent-exempt residue stays on the derived address and cannot currently be swept (no close-account method exists on Sovereign Send)
  • Do NOT block cached users when registerPartnerUsers fails — they can still deposit
  • Do NOT block the SOL→ICP deposit button based on registration status — only block if the user has no address

FilePurpose
src/frontend/src/utils/solConstants.jsAll canister IDs, RPC URLs, fee constants, utility functions
src/frontend/src/utils/sovereignSend.jsSOL→ICP, SOL→mSOL, mSOL→SOL, mSOL→ICP, RPC helpers incl. broadcast + confirmation polling
src/frontend/src/utils/solSwap.jsICP→SOL (swap pool), price/quote queries, governance fee read (legacy autonomous-SDK paths removed)
src/frontend/src/hooks/useSolanaGatewayHealth.jsShared live gateway-health snapshot (partner count, shield/breaker, pool pause + reserves); fail-closed contract
src/frontend/src/components/common/SolanaGatewayStatus.jsxShared in-page status surface (SolanaTab + DAO participation box)
src/frontend/src/components/common/SolanaTxProgress.jsxShared tx progress/error callout with Solscan explorer link
src/frontend/src/components/common/MeneseGatewaySection.jsxSystemStatus “Menese / Solana Gateway” section (env-aware partner proof + treasury visibility)
src/frontend/src/components/SolanaDaoParticipationBox.jsxDAO-page SOL → ICP → LGE onramp (sibling of the Bitcoin box)
src/frontend/src/components/TokenManagement/SolanaTab.jsxFull Solana swap UI
src/frontend/src/components/TokenManagement/BridgeTab.jsxCross-Chain Gateway tab wrapper (Bitcoin + Solana); filename kept for API stability