Menese Protocol Integration
Last updated: 2026-03-26
Repository
Section titled “Repository”- SDK: https://github.com/Menese-Protocol/MeneseSDK-V0
- mSOL/ICP-SOL integration: https://github.com/Menese-Protocol/MeneseSDK-V0/tree/main/msol-icp-sol-integration
- Sovereign Send integration: https://github.com/Menese-Protocol/MeneseSDK-V0/tree/main/sovereign-send-integration
- Billing guide: https://github.com/Menese-Protocol/MeneseSDK-V0/blob/main/msol-icp-sol-integration/developer-billing-integration-guide.md
OhShii is a Partner dApp
Section titled “OhShii is a Partner dApp”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/signSendoperations - Backend canister allowlisted — can call
registerPartnerUsers()to vouch for users
Partner vs non-partner
Section titled “Partner vs non-partner”| Partner (OhShii) | Non-partner | |
|---|---|---|
getMyAddress() cost | Free (vouched users) | 45B cycles (caller pays) |
| Subscription needed | No | Yes ($20-$323/mo) or cycles |
| Revenue share | 20% of 0.1% protocol fee | None |
| User registration | Backend calls registerPartnerUsers() | Must use registerSession(developerKey) |
depositSol() fee | 0% (pool’s own fee applies) | 0% (same) |
sendSol() fee | 0.1% atomic | 0.1% atomic |
Partner configuration (by Menese team)
Section titled “Partner configuration (by Menese team)”- Allowlisted backend canister:
vil43-piaaa-aaaal-qsibq-cai(prod) - Treasury for fee split:
BuqZagKKiRFUfxNFRKK7w3JymQgxuCy2rxFBdQTysMW4(derived from our principal)
Canister IDs
Section titled “Canister IDs”| Canister | ID | Role |
|---|---|---|
| Sovereign Send | fxjsq-raaaa-aaaab-agdaa-cai | Signing canister — 0.1% fee model, no subscription |
| ICP-SOL Oracle Swap Pool | w2vjc-2yaaa-aaaab-ae6zq-cai | Zero-slippage SOL↔ICP swaps |
| ckSOL minter | crmds-kqaaa-aaaaf-qf5aq-cai | mSOL minting/redemption |
| mSOL ledger (ICRC-2) | 2ykjj-eyaaa-aaaae-af4ma-cai | mSOL token |
| MeneseSDK (legacy) | urs2a-ziaaa-aaaad-aembq-cai | Old subscription model — used only for legacy recovery |
| ICP Ledger | ryjl3-tyaaa-aaaaa-aaaba-cai | ICP token |
Architecture Overview
Section titled “Architecture Overview”Conversion Flows
Section titled “Conversion Flows”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.
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, returningokwhile 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
okwhile the Solana transaction fails silently.
Key facts:
signDeposithas 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
registerPartnerUsersis temporarily failing
Flow 2: ICP → SOL (via Oracle)
Section titled “Flow 2: ICP → SOL (via Oracle)”Status: Implemented and working.
Frontend code path: solSwap.js → swapIcpToSol(e8s, solAddress, identityOrAgent)
Key facts:
- Oracle pulls ICP via ICRC-2
transfer_fromafter the user’sicrc2_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 RPC3. Frontend: sovereignSend.signDeposit(CKSOL_MINTER, lamports, blockhash)4. Frontend: broadcastSolTx(signedTxBase64) via Solana RPC5. Frontend: cksol.expectCkSolDeposit(txHash, lamports) ← verifies on-chain6. ckSOL minter mints mSOL to user's principalNote: 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.
Flow 4: mSOL → SOL (redemption)
Section titled “Flow 4: mSOL → SOL (redemption)”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 signedTxBase644. Frontend: broadcasts signedTxBase64 to Solana via RPC5. User receives SOL at their Solana addressFlow 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
Summary: Where does mSOL fit?
Section titled “Summary: Where does mSOL fit?”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
User Registration (Partner Flow)
Section titled “User Registration (Partner Flow)”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 addressThe 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.cachedUserssurvives 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.
Fee Structure
Section titled “Fee Structure”Fees we pay (to Menese/pool)
Section titled “Fees we pay (to Menese/pool)”| Operation | Fee | Paid to |
|---|---|---|
getMyAddress() | Free (partner) | — |
depositSol() | 0% Sovereign Send + pool’s 0.15% LP fee | Pool LPs |
sendSol() | 0.1% atomic | Menese protocol |
swapIcpToSol() | 0.15% LP fee + dynamic spread (max 0.3%) | Pool LPs + oracle |
Fees we charge (to users)
Section titled “Fees we charge (to users)”| Operation | Fee | Collected by |
|---|---|---|
| ICP → SOL | 0.3% (30 bps) of ICP amount | Backend canister (before approve) |
| SOL → ICP | Not yet implemented frontend-side | — |
Revenue share (from Menese)
Section titled “Revenue share (from Menese)”- 20% of the 0.1% protocol fee on
sendSol/signSendoperations - 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).
3-layer defense in Sovereign Send
Section titled “3-layer defense in Sovereign Send”- Circuit breaker — hard stop when canister cycles < 500B. Cached users keep working.
- Global throttle — max 20 new derivations per 60s sliding window.
- Caller allowlist — partner canister bypasses throttle (not circuit breaker).
Cost model
Section titled “Cost model”- 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
Sovereign Send API Reference
Section titled “Sovereign Send API Reference”Core operations
Section titled “Core operations”| Method | Type | Fee | Description |
|---|---|---|---|
getMyAddress() | update | free (partner) | Get user’s derived SOL address |
signSend(to, lamports, blockhash) | update | 0.1% atomic | Sign SOL transfer — caller broadcasts |
sendSol(to, lamports) | update | 0.1% atomic | Autonomous — avoid (stale blockhash bug) |
signDeposit(target, lamports, blockhash, borrowParams?) | update | 0% | Sign-only deposit — RECOMMENDED |
depositSol(target, lamports, borrowParams?) | update | 0% | Autonomous deposit — avoid (stale blockhash bug) |
Partner management (called by allowlisted backend)
Section titled “Partner management (called by allowlisted backend)”| Method | Description |
|---|---|
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”| Target | Canister | What you get |
|---|---|---|
| ICP-SOL Swap | w2vjc-2yaaa-aaaab-ae6zq-cai | ICP at oracle rate |
| mSOL (ckSOL) | crmds-kqaaa-aaaaf-qf5aq-cai | mSOL (yield-bearing wrapped SOL) |
| SOL Borrow V3 | p7teu-wyaaa-aaaab-afnvq-cai | USDC loan against SOL collateral |
Implementation Status
Section titled “Implementation Status”| Component | Status | Notes |
|---|---|---|
| ICP → SOL swap | Working | Oracle direct call, 0.3% bridge fee |
| Price/quote queries | Working | Oracle query calls |
| User registration (partner) | Working | Backend register_menese_user(), graceful degradation for cached users |
| SOL address derivation | Working | Sovereign Send getMyAddress() (cached users survive allowlist resets) |
| SOL balance check | Working | Via PublicNode/dRPC/Ankr RPCs |
| SOL → ICP deposit | Working | Sign-only (signDeposit + frontend broadcast) — more reliable than autonomous |
| SOL → mSOL deposit | Working | Sign-only (signDeposit + frontend broadcast), expectCkSolDeposit TODO |
| mSOL balance display | Working | ICRC-1 balance_of on mSOL ledger |
| mSOL → SOL redemption | Implemented | Approve + redeem + broadcast |
| mSOL → ICP conversion | Implemented | Two-step with progress indicator |
| Legacy SOL recovery | Disabled | Funds recovered, code commented out |
| Fee auto-sweep (backend) | Not started | Timer-based SOL→ICP conversion |
Solana RPC Configuration
Section titled “Solana RPC Configuration”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.
| Provider | URL | Notes |
|---|---|---|
| PublicNode (primary) | https://solana-rpc.publicnode.com | Free, public, no API key |
| dRPC (fallback 1) | https://solana.drpc.org | Free, public |
| Ankr (fallback 2) | https://rpc.ankr.com/solana | Free, public |
RPCs must be added to CSP connect-src in .ic-assets.json5.
What NOT to do
Section titled “What NOT to do”- Do NOT use
registerSession(developerKey)from frontend — replaced by backendregisterPartnerUsers - Do NOT call
getMySolanaAddress()on MeneseSDK — use Sovereign Send’sgetMyAddress() - Do NOT call
swapSolToIcp()aftersignDeposit()— ICP release is automatic in the partner flow - Do NOT use autonomous
depositSol()— usesignDeposit()+ 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
registerPartnerUsersfails — they can still deposit - Do NOT block the SOL→ICP deposit button based on registration status — only block if the user has no address
Frontend Code Map
Section titled “Frontend Code Map”| File | Purpose |
|---|---|
src/frontend/src/utils/solConstants.js | All canister IDs, RPC URLs, fee constants, utility functions |
src/frontend/src/utils/sovereignSend.js | SOL→ICP, SOL→mSOL, mSOL→SOL, mSOL→ICP, RPC helpers |
src/frontend/src/utils/solSwap.js | ICP→SOL (oracle), price queries, legacy recovery |
src/frontend/src/components/TokenManagement/SolanaTab.jsx | Full Solana swap UI |
src/frontend/src/components/TokenManagement/BridgeTab.jsx | Bridge tab wrapper (Bitcoin + Solana) |