Skip to content

Menese Protocol Integration

Last updated: 2026-03-26


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)
  • Treasury for fee split: BuqZagKKiRFUfxNFRKK7w3JymQgxuCy2rxFBdQTysMW4 (derived from our principal)

CanisterIDRole
Sovereign Sendfxjsq-raaaa-aaaab-agdaa-caiSigning canister — 0.1% fee model, no subscription
ICP-SOL Oracle Swap Poolw2vjc-2yaaa-aaaab-ae6zq-caiZero-slippage SOL↔ICP swaps
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

depositSol

swapIcpToSol

registerPartnerUsers

Browser (Frontend)

Sovereign Send (fxjsq)

ICP-SOL Oracle (w2vjc)

OhShii Backend (hxurj)

Solana Network (threshold Schnorr)


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.

ICP-SOL Pool (w2vjc)Solana RPCSovereign Send (fxjsq)OhShii FrontendUser Wallet (Phantom)ICP-SOL Pool (w2vjc)Solana RPCSovereign Send (fxjsq)OhShii FrontendUser Wallet (Phantom)1. Send SOL to derived address2. fetchBlockhash3. signDeposit(pool, lamports, blockhash)signedTxBase644. broadcastSolTx(signedTxBase64)5. Pool receives SOL, credits ICP6. 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.

Key facts:

  • signDeposit has 0% Sovereign Send fee — the pool applies its own 0.15% LP fee
  • Min deposit: 0.05 SOL
  • Always reserve 50,000 lamports for Solana network fees (maxSendLamports())
  • No second call needed after broadcast — ICP release is automatic
  • Cached users can deposit even if registerPartnerUsers is temporarily failing

Status: Implemented and working.

ICP-SOL Oracle (w2vjc)OhShii FrontendUserICP-SOL Oracle (w2vjc)OhShii FrontendUser1. Bridge fee (0.3% to backend)2. icrc2_approve (ICP_LEDGER, spender=oracle)4. Oracle pulls ICP, signs SOL tx, broadcasts to Solana3. swapIcpToSol(e8s, solAddress)5. SOL arrives at Solana addr

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

Key facts:

  • Oracle pulls ICP via ICRC-2 transfer_from after the user’s icrc2_approve
  • OhShii charges 0.3% bridge fee (30 bps) in ICP before the swap
  • The oracle has its own fee (0.15% for LPs + dynamic spread up to 0.3%)

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: Sign-only deposit implemented, expectCkSolDeposit call TODO.

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: Implemented in sovereignSend.js → redeemMsolToSol().

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: Implemented in sovereignSend.js → convertMsolToIcp().

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

When a user opens the Solana tab:

1. Frontend: 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 and stores registered principals to avoid re-registering.

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: “SOL → ICP bridge temporarily unavailable for new users”.
  • ICP → SOL (oracle 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 + oracle
OperationFeeCollected by
ICP → SOL0.3% (30 bps) of ICP amountBackend canister (before approve)
SOL → ICPNot yet implemented frontend-side
  • 20% of the 0.1% protocol fee on sendSol/signSend operations
  • Distributed in SOL on Solana, 7-day epochs, minimum 0.05 SOL
  • Treasury address: BuqZagKKiRFUfxNFRKK7w3JymQgxuCy2rxFBdQTysMW4

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 oracle 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 swapWorkingOracle direct call, 0.3% bridge fee
Price/quote queriesWorkingOracle query calls
User registration (partner)WorkingBackend register_menese_user(), graceful degradation for cached users
SOL address derivationWorkingSovereign Send getMyAddress() (cached users survive allowlist resets)
SOL balance checkWorkingVia PublicNode/dRPC/Ankr RPCs
SOL → ICP depositWorkingSign-only (signDeposit + frontend broadcast) — more reliable than autonomous
SOL → mSOL depositWorkingSign-only (signDeposit + frontend broadcast), expectCkSolDeposit TODO
mSOL balance displayWorkingICRC-1 balance_of on mSOL ledger
mSOL → SOL redemptionImplementedApprove + redeem + broadcast
mSOL → ICP conversionImplementedTwo-step with progress indicator
Legacy SOL recoveryDisabledFunds recovered, code commented out
Fee auto-sweep (backend)Not startedTimer-based SOL→ICP conversion

The frontend uses three public Solana RPCs (no API key), tried in this order — matching the Menese SDK order. Source of truth: src/frontend/src/utils/solConstants.js.

ProviderURLNotes
PublicNode (primary)https://solana-rpc.publicnode.comFree, public, no API key
dRPC (fallback 1)https://solana.drpc.orgFree, public
Ankr (fallback 2)https://rpc.ankr.com/solanaFree, public

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


  • 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 50,000 lamports for network fees (maxSendLamports())
  • 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
src/frontend/src/utils/solSwap.jsICP→SOL (oracle), price queries, legacy recovery
src/frontend/src/components/TokenManagement/SolanaTab.jsxFull Solana swap UI
src/frontend/src/components/TokenManagement/BridgeTab.jsxBridge tab wrapper (Bitcoin + Solana)