Skill: kyounesmercatura/multichain-protocol (version 1.0.0)

--- name: multichain-protocol description: Turn any AI agent into a 19-chain crypto wallet via MeneseSDK on ICP. Send tokens, swap on DEXes (Raydium, Uniswap, ICPSwap, KongSwap, Cetus, Minswap), bridge cross-chain, manage DeFi positions (Aave, Lido, LP), automate trading (DCA, stop-loss, rebalancing), and process payments — all from a single ICP canister. version: 1.0.0 metadata: openclaw: emoji: "🌐" requires: bins: - name: dfx install: "sh -ci \"$(curl -fsSL https://internetcomputer.org/install.sh)\"" check: "dfx --version" - name: python3 install: "apt install -y python3 || brew install python3" check: "python3 --version" author: Menese Protocol Team license: MIT tags: - crypto - wallet - defi - multi-chain - icp - payments --- # Multichain Protocol — MeneseSDK Wallet Skill Operate across **19 blockchains** from a single ICP canister or CLI: Solana, Ethereum, Bitcoin, Arbitrum, Base, Polygon, BSC, Optimism, ICP, XRP, SUI, TON, Cardano, Aptos, NEAR, Tron, Litecoin, CloakCoin, THORChain. **Powered by Menese Protocol** | **Canister ID (mainnet):** `urs2a-ziaaa-aaaad-aembq-cai` --- ## Pricing **First 5 transactions are FREE** — no signup, no credit card, just install and go. After the free tier, transaction signing is charged per action via your DeveloperKey (`msk_*`). Read-only operations (balances, addresses, pool queries) are always free. --- ## Quickstart (2 minutes) ### Step 1: Install dfx (ICP SDK) If you don't have `dfx` installed, run this single command: ```bash sh -ci "$(curl -fsSL https://internetcomputer.org/install.sh)" ``` Verify it works: ```bash dfx --version ``` That's it. No accounts needed, no wallets to set up — dfx handles everything. ### Step 2: Install the skill ```bash clawhub install multichain-protocol ``` Or manually: copy `SKILL.md` + `wallet_commands.py` to your workspace. ### Step 3: Create an ICP identity (if you don't have one) ```bash dfx identity new my-wallet dfx identity use my-wallet ``` ### Step 4: Try it — your first 5 sends are free Ask your bot: ``` "show my wallet addresses" "what's my SOL balance?" "send 0.1 ETH to 0xABC..." "swap 100 USDC to SOL on Raydium" "set up a DCA: buy $50 of BTC every day" ``` ### Step 5 (optional): Deploy your own canister for production For multi-user, automation, and timers — deploy `WalletBot.mo` as your own ICP canister. --- ## Two Integration Approaches | | Canister (Recommended) | CLI (Quick) | |---|---|---| | **Flow** | User → ClawdBot → Your Canister → MeneseSDK | User → ClawdBot → `dfx canister call` → MeneseSDK | | **Setup** | Deploy `WalletBot.mo`, register with SDK | Copy `scripts/wallet_commands.py`, done | | **Best for** | Multi-user, automation, timers, production | Single-user, prototyping, testing | | **Automation** | ICP timers for DCA/rebalance/bots | None (manual only) | --- ## Best Practice: Cache Addresses Addresses are **deterministic** — the same principal always gets the same addresses on every chain. Fetch once, cache forever. ```motoko // Canister pattern — cache in stable var stable var cachedAddresses : ?AddressBook = null; public shared func getAddresses() : async AddressBook { switch (cachedAddresses) { case (?addrs) { addrs }; // Return cached — no inter-canister call case null { let sol = await menese.getMySolanaAddress(); let evm = await menese.getMyEvmAddress(); // ... fetch all chains once ... let addrs = { solana = sol.address; evm = evm.evmAddress; /* ... */ }; cachedAddresses := ?addrs; addrs; }; }; }; ``` ```python # CLI pattern — fetch once, store in file import json, os CACHE_FILE = "addresses_cache.json" def get_addresses(): if os.path.exists(CACHE_FILE): return json.load(open(CACHE_FILE)) addrs = fetch_all_addresses() # dfx calls json.dump(addrs, open(CACHE_FILE, "w")) return addrs ``` **Why**: Saves inter-canister call latency + cycles. Addresses never change for a given principal. --- ## EVM Chains — Bring Your Own RPC All EVM operations (ETH, Arbitrum, Base, Polygon, BSC, Optimism) require **your own RPC endpoint**. MeneseSDK does not manage EVM RPCs. | Chain | Chain ID | Free Public RPC | |-------|----------|----------------| | Ethereum | 1 | `https://eth.llamarpc.com` | | Arbitrum | 42161 | `https://arb1.arbitrum.io/rpc` | | Base | 8453 | `https://mainnet.base.org` | | Polygon | 137 | `https://polygon-rpc.com` | | BSC | 56 | `https://bsc-dataseed1.binance.org` | | Optimism | 10 | `https://mainnet.optimism.io` | Use Alchemy/Infura for production reliability. --- ## Complete Tool Reference Every operation available, organized by category. Each tool shows the function, parameters, return type, cost, and a usage example. ### Tool 1: Get Addresses (FREE) Deterministic per-principal. **Cache after first call.** | Chain | Function | Field to Extract | |-------|----------|-----------------| | Solana | `getMySolanaAddress` | `.address` | | EVM (all 6) | `getMyEvmAddress` | `.evmAddress` (NOT `.address`) | | Bitcoin | `getMyBitcoinAddress` | `.bech32Address` | | Litecoin | `getMyLitecoinAddress` | `.bech32Address` | | SUI | `getMySuiAddress` | `.suiAddress` (NOT `.address`) | | XRP | `getMyXrpAddress` | `.classicAddress` | | TON | `getMyTonAddress` | `.nonBounceable` (NOT `.address`) | | Cardano | `getMyCardanoAddress` | `.bech32Address` | | Aptos | `getMyAptosAddress` | `.address` | | NEAR | `getMyNearAddress` | `.implicitAccountId` (NOT `.accountId`) | | Tron | `getTronAddress` | `.base58Address` (NOT `.base58`) | | CloakCoin | `getMyCloakAddress` | `.base58Address` | | THORChain | `getMyThorAddress` | `.bech32Address` | **Batch**: `getAllAddresses()` — all chains in one call. **Solana ATA**: `getMySolanaAta(mintBase58)` — get associated token account for an SPL token. ```motoko // Example: get SOL address let info = await menese.getMySolanaAddress(); let myAddress = info.address; // "5xK2abc..." ``` ```bash # CLI dfx canister call urs2a-ziaaa-aaaad-aembq-cai getMySolanaAddress --network ic --query ``` ### Tool 2: Check Balances (FREE) | Chain | Function | Returns | Unit | |-------|----------|---------|------| | Solana | `getMySolanaBalance` | `Result<Nat64, Text>` | lamports (÷10^9) | | ICP | `getICPBalance` | `Result<Nat64, Text>` | e8s (÷10^8) | | ICP (for addr) | `getICPBalanceFor(principal)` | `Result<Nat64, Text>` | e8s (÷10^8) | | Bitcoin | `getBitcoinBalance` | `Nat64` | satoshis (÷10^8) | | Litecoin | `getLitecoinBalance` | `Nat64` | litoshis (÷10^8) | | EVM | `getMyEvmBalance(rpcUrl)` | `Result<Nat, Text>` | wei (÷10^18) | | XRP | `getMyXrpBalance` | `Result<Text, Text>` | drops as text | | SUI | `getMySuiBalance` | `Nat64` | mist (÷10^9) | | TON | `getMyTonBalance` | `Result<Nat64, Text>` | nanotons (÷10^9) | | Cardano | `getCardanoBalance` | `Result<Nat64, Text>` | lovelace (÷10^6) | | Aptos | `getAptosBalance` | `Result<Nat64, Text>` | octas (÷10^8) | | NEAR | `getMyNearBalance` | `Nat` | yoctoNEAR (÷10^24) | | THORChain | `getThorBalance` | `[{amount, denom}]` | units (÷10^8) | | CloakCoin | `getCloakBalance` | `Result<{address, balance, utxoCount}, Text>` | units (÷10^6) | | Tron | `getTrxBalance(address)` | `Result<Nat64, Text>` | sun (÷10^6) | | ICRC-1 tokens | `getICRC1Balance(ledgerId)` | `Result<Nat, Text>` | smallest unit | | ICRC-1 (for addr) | `getICRC1BalanceFor(principal, ledgerId)` | `Result<Nat, Text>` | smallest unit | | ICRC-1 info | `getICRC1TokenInfo(ledgerId)` | `Result<TokenInfo, Text>` | name, symbol, decimals | | ICP tokens list | `getSupportedICPTokens()` | `[{name,symbol,canisterId,type_,category}]` | query | | TRC-20 tokens | `getMyTrc20Balance(contract)` | `Result<Nat, Text>` | smallest unit | **Batch**: `getAllBalances()` — parallel fetch across all chains. **Performance tip**: For high-frequency reads, query chain RPCs directly using cached addresses. MeneseSDK is best for signing; your own RPC is faster for reads. ```motoko // Example: check SOL balance, convert to human-readable switch (await menese.getMySolanaBalance()) { case (#ok(lamports)) { /* lamports / 1_000_000_000 = SOL */ }; case (#err(e)) { /* handle error */ }; }; ``` ### Tool 3: Send Tokens ($0.05 client / $0.10 agent) **Return types differ by chain** — getting this wrong causes runtime errors. | Chain | Function | Params | Return | |-------|----------|--------|--------| | Solana | `sendSolTransaction` | `(to, lamports:Nat64)` | `Result<Text, Text>` | | ICP | `sendICP` | `(to:Principal, e8s:Nat64)` | `Result<SendICPResult, Text>` | | ICRC-1 | `sendICRC1` | `(to:Principal, amount:Nat, ledger:Text)` | `Result<SendICRC1Result, Text>` | | Bitcoin | `sendBitcoin` | `(to, sats:Nat64)` | `Result<SendResultBtcLtc, Text>` | | Litecoin | `sendLitecoin` | `(to, litoshis:Nat64)` | `Result<SendResult, Text>` (NOT BtcLtc!) | | EVM | `sendEvmNativeTokenAutonomous` | `(to, wei:Nat, rpc, chainId:Nat, ?quoteId)` | `Result<SendResultEvm, Text>` | | XRP | `sendXrpAutonomous` | `(to, amountXrp:Text, ?destTag:Nat32)` | **FLAT** `SendResultXrp` | | SUI | `sendSui` | `(to, mist:Nat64)` | `Result<SendResult, Text>` | | TON | `sendTonSimple` | `(to, nanotons:Nat64)` | **FLAT** `SendResultTon` | | Aptos | `sendAptos` | `(to, octas:Nat64)` | `Result<SendResult, Text>` | | NEAR | `sendNearTransfer` | `(to, yocto:Nat)` | `Result<Text, Text>` | | Tron | `sendTrx` | `(to, sun:Nat64)` | `Result<Text, Text>` | | Cardano | `sendCardanoTransaction` | `(to, lovelace:Nat64)` | `Result<Text, Text>` | | CloakCoin | `sendCloak` | `(to, amount:Nat64)` | `Result<SendResultCloak, Text>` | | THORChain | `sendThor` | `(to, amount:Nat64, memo:Text)` | `Result<Text, Text>` | | SPL Token | `transferSplToken` | `(amount:Nat64, srcAta, dstAta)` | `TransferAndSendResult` | | XRP IOU | `sendXrpIOU` | `(dest, currency, issuer, amount, ?tag)` | `SendResultXrp` | | TRC-20 | `sendTrc20` | `(contract, to, amount:Nat, feeLimit:Nat64)` | `Result<Text, Text>` | **Variant extras**: `sendBitcoinDynamicFee`, `sendBitcoinWithFee`, `sendLitecoinWithFee`, `sendSuiMax`, `sendTon` (with bounce/comment), `sendTonWithComment`. ```motoko // Example: send 0.5 SOL switch (await menese.sendSolTransaction("5xK2abc...", 500_000_000)) { case (#ok(txHash)) { /* success — txHash is the Solana TX signature */ }; case (#err(e)) { /* handle error */ }; }; // Example: send XRP (FLAT return — check .success, not #ok) let r = await menese.sendXrpAutonomous("rDestAddr...", "10.5", null); if (r.success) { /* r.txHash, r.explorerUrl */ } else { /* r.message has error */ }; ``` ### Tool 3b: ICRC-2 Approve & TransferFrom ($0.05 client / $0.10 agent) ICRC-2 adds ERC-20-style `approve` + `transferFrom` to ICP tokens. Use when building escrow, payment splitters, or any pattern where a canister needs to spend tokens on behalf of users. | Operation | Function | Params | Return | |-----------|----------|--------|--------| | Approve spender | `approveICRC2` | `(spender:Principal, amount:Nat, expiresAt:?Nat64, ledgerId:Text)` | `Result<ApproveResult, Text>` | | Check allowance | `getICRC2Allowance` | `(owner:Principal, spender:Principal, ledgerId:Text)` | `Result<Allowance, Text>` — FREE | | TransferFrom | `transferFromICRC2` | `(from:Principal, to:Principal, amount:Nat, ledgerId:Text)` | `Result<TransferFromResult, Text>` | ```motoko // Approve MeneseSDK canister to spend 100 ckUSDC on your behalf let ckUSDC = "xevnm-gaaaa-aaaar-qafnq-cai"; let sdk = Principal.fromText("urs2a-ziaaa-aaaad-aembq-cai"); let r = await menese.approveICRC2(sdk, 100_000_000, null, ckUSDC); // 100 ckUSDC (6 dec) // Check remaining allowance (FREE) let allowance = await menese.getICRC2Allowance(myPrincipal, sdk, ckUSDC); // Transfer from (requires prior approval) let t = await menese.transferFromICRC2(userPrincipal, treasuryPrincipal, 50_000_000, ckUSDC); ``` ### Tool 4: Swap on DEXes ($0.075 client / $0.15 agent) | DEX | Chain | Function | Key Details | |-----|-------|----------|-------------| | Raydium | Solana | `swapRaydiumApiUser` | 8 params, FLAT return | | Uniswap V3 | EVM | `swapTokens`, `swapTokensMultiHop` | Pass quoteId or rpc+chainId | | Uniswap shortcuts | EVM | `swapETHForUSDC`, `swapUSDCForETH` | Quick ETH↔USDC | | ICPSwap/KongSwap | ICP | `executeICPDexSwap(SwapRequest)` | Auto-routes best price | | Cetus | SUI | `executeSuiSwap(network, from, to, amountIn, minOut)` | Network: `#mainnet` | | Minswap | Cardano | `executeMinswapSwap(tokenIn, tokenOut, amount, slippage)` | Float slippage % | | XRP DEX | XRP | `xrpSwap(destAmount, sendMax, paths, slipBps)` | Use xrpFindPaths first | **Always get a quote first (FREE)**: - Raydium: `getRaydiumQuote(inputMint, outputMint, amount, slipBps)` - Uniswap: `getTokenQuote(from, to, amountIn, rpc)` - ICP: `getICPDexQuote(tokenIn, tokenOut, amountIn, slipPct)` → `AggregatedQuote` (compares ICPSwap vs KongSwap) - SUI: `getSuiSwapQuote(network, from, to, amountIn, slipBps)` - Cardano: `getMinswapQuote(tokenIn, tokenOut, amountIn, slipPct)` - XRP: `xrpFindPaths(destAmount, sourceCurrencies)` ```motoko // Example: swap 1 SOL → USDC on Raydium let SOL = [REDACTED_SECRET]; let USDC = [REDACTED_SECRET]; let result = await menese.swapRaydiumApiUser( SOL, USDC, 1_000_000_000, // 1 SOL in lamports 150, // 1.5% slippage true, // wrapSol: input is native SOL false, // unwrapSol: output is USDC not SOL null, null // auto-detect ATAs ); // FLAT record — access directly: // result.txSignature, result.outputAmount, result.priceImpactPct ``` ```motoko // Example: swap on ICP DEX (auto-routes to ICPSwap or KongSwap) let swapReq : Menese.SwapRequest = { tokenIn = "ryjl3-tyaaa-aaaaa-aaaba-cai"; // ICP ledger tokenOut = "mxzaz-hqaaa-aaaar-qaada-cai"; // ckUSDC amountIn = 100_000_000; // 1 ICP minAmountOut = 0; slippagePct = 1.0; preferredDex = null; // auto-pick best price }; let result = await menese.executeICPDexSwap(swapReq); ``` ### Tool 5: Bridge ETH↔SOL ($0.10 client / $0.20 agent) | Direction | Function | Params | |-----------|----------|--------| | ETH→SOL | `quickUltrafastEthToSol` | `(ethWei:Nat)` | | USDC→SOL | `quickUltrafastUsdcToSol` | `(usdc:Nat)` | | ETH→Token | `quickUltrafastEthToToken` | `(ethWei:Nat, outputMint, slipBps:Nat)` | | SOL→ETH | `quickSolToEth` | `(solLamports:Nat64, slipBps:Nat)` | | USDC SOL→ETH | `quickUsdcBridgeSolToEth` | `(usdc:Nat64)` | | CCTP (any) | `quickCctpBridge` | `(srcChainId, usdc, outputToken, fast, slipBps, ethRpc)` | ```motoko // Example: bridge 0.1 ETH to Solana let result = await menese.quickUltrafastEthToSol(100_000_000_000_000_000); // 0.1 ETH in wei // Result<Text, Text> — ok = status text ``` ### Tool 6: DeFi — Aave V3 ($0.10 agent) | Operation | Function | Params | |-----------|----------|--------| | Supply ETH | `aaveSupplyEth` | `(wei:Nat, rpc, ?quoteId)` → `Result<SupplyEthResult, Text>` | | Withdraw ETH | `aaveWithdrawEth` | `(wei:Nat, rpc, ?quoteId)` → `Result<WithdrawEthResult, Text>` | | Supply ERC-20 | `aaveSupplyToken` | `(tokenAddr, amount:Nat, rpc, ?quoteId)` | | Withdraw ERC-20 | `aaveWithdrawToken` | `(tokenAddr, amount:Nat, rpc, ?quoteId)` | | Read aWETH bal | `getAWethBalance` | `(user, rpc)` → FREE | | Read aToken bal | `getATokenBalance` | `(aTokenAddr, user, rpc)` → FREE | ```motoko // Supply 0.5 ETH to Aave → receive aWETH (~2-3% APY) let r = await menese.aaveSupplyEth(500_000_000_000_000_000, ethRpc, null); switch (r) { case (#ok(res)) { /* res.txHash, res.aTokenAddress */ }; case (#err(e)) { /* error */ }; }; ``` ### Tool 7: DeFi — Lido Staking ($0.10 agent) | Operation | Function | Return | |-----------|----------|--------| | Stake ETH→stETH | `stakeEthForStEth(wei, rpc, ?quoteId)` | `Result<StakeResult, Text>` | | Wrap stETH→wstETH | `wrapStEth(amount, rpc, ?quoteId)` | `Result<WrapResult, Text>` | | Unwrap wstETH→stETH | `unwrapWstEth(amount, rpc, ?quoteId)` | `Result<UnwrapResult, Text>` | | Read stETH bal | `getStEthBalance(user, rpc)` | FREE | | Read wstETH bal | `getWstEthBalance(user, rpc)` | FREE | ```motoko // Stake 1 ETH with Lido (~3-4% APY), then wrap for DeFi composability ignore await menese.stakeEthForStEth(1_000_000_000_000_000_000, ethRpc, null); ignore await menese.wrapStEth(1_000_000_000_000_000_000, ethRpc, null); ``` ### Tool 8: DeFi — Uniswap V3 Liquidity ($0.10 agent) | Operation | Function | |-----------|----------| | Add ETH+Token LP | `addLiquidityETH(tokenSymbol, tokenAmt, ethAmt, slipBps, rpc, ?quoteId)` | | Add Token+Token LP | `addLiquidity(tokenA, tokenB, amtA, amtB, slipBps, rpc, ?quoteId)` | | Remove ETH LP | `removeLiquidityETH(tokenSymbol, lpAmt, slipBps, feeOnTransfer, rpc, ?quoteId)` | | Remove Token LP | `removeLiquidity(tokenA, tokenB, lpAmt, slipBps, rpc, ?quoteId)` | | Read reserves | `getPoolReserves(tokenA, tokenB, rpc)` — FREE | | Get pair addr | `getPairAddress(tokenA, tokenB, rpc)` — FREE | ### Tool 9: Custom EVM Contract Calls | Operation | Function | Cost | |-----------|----------|------| | Read (view) | `callEvmContractRead(contract, selector4byte, argsHexes, rpc)` | FREE | | Write (tx) | `callEvmContractWrite(contract, selector, args, rpc, chainId, value, ?quoteId)` | $0.10 | Selector = first 4 bytes of `keccak256("functionName(type1,type2)")`, hex-encoded, no `0x` prefix. ```motoko // Read Chainlink ETH/USD price (FREE) let result = await menese.callEvmContractRead( [REDACTED_SECRET], // ETH/USD feed "feaf968c", // latestRoundData() [], ethRpc ); ``` ### Tool 10: Strategy Engine (rule creation FREE, execution per-action pricing) | Operation | Function | Cost | |-----------|----------|------| | Create rule | `addStrategyRule(Rule)` | FREE | | List rules | `getMyStrategyRules()` | FREE | | Update status | `updateStrategyRuleStatus(ruleId, status)` | FREE | | Delete rule | `deleteStrategyRule(ruleId)` | FREE | | View logs | `getStrategyLogs()` | FREE | | Init automation | `initAutomation()` | FREE | Rule types: `#DCA`, `#StopLoss`, `#TakeProfit`, `#Rebalance`, `#Scheduled`, `#APYMigration`, `#LiquidityProvision`, `#VolatilityTrigger`. Rule statuses: `#Active`, `#Paused`, `#Cancelled`, `#Executed`, `#Executing`, `#Failed`, `#Draft`, `#Confirmed`, `#Ready`. ### Tool 11: Solana ATA / XRP Trustlines (setup) | Operation | Function | Cost | |-----------|----------|------| | Create Solana ATA | `createMySolanaAtaForMint(mint, ata)` | Send pricing | | Create ATA (custom program) | `createMySolanaAtaForMintWithProgram(mint, ata, programId)` | Send pricing | | Set XRP trustline | `xrpSetTrustline(currency, issuer, limit)` | Send pricing | | Read XRP trustlines | `xrpGetAccountLines()` | FREE | | Get Solana ATA | `getMySolanaAta(mint)` | FREE | ### Tool 12: Developer/Billing | Operation | Function | Cost | |-----------|----------|------| | Register canister | `registerDeveloperCanister(Principal, appName)` | FREE | | Get dev key | `getMyDeveloperKey()` | FREE | | Regenerate key | `regenerateDeveloperKey()` | FREE | | Validate key | `validateDeveloperKey(key)` | FREE | | Check account | `getMyGatewayAccount()` → `UserAccount` | FREE | | Check dev account | `getMyDeveloperAccount()` → `?DeveloperAccountV3` | FREE | | Deposit credits | `depositGatewayCredits(currency, amount)` | ICP cost | ### Tool 13: Utility | Operation | Function | Cost | |-----------|----------|------| | BTC max send | `getBitcoinMaxSendAmount(?feeRate)` → `{maxAmount, fee, utxoCount}` | FREE | | LTC max send | `getLitecoinMaxSendAmount(?feeRate)` → `{maxAmount, fee, utxoCount}` | FREE | | Health check | `health()` | FREE | | Version | `version()` | FREE | ### Tool 14: ICP DEX LP Management ($0.10 agent) Manage liquidity positions on ICPSwap and KongSwap. The SDK aggregates both DEXes. | Operation | Function | Cost | |-----------|----------|------| | List pools | `getICPDexPools()` → `[PoolInfo]` | FREE | | List tokens | `getICPDexTokens()` → `[DexToken]` | FREE | | View positions | `getICPLPPositions()` → `[LPPosition]` | FREE | | Add liquidity | `addICPLiquidity(AddLiquidityRequest)` → `Result<AddLiquidityResult, Text>` | $0.10 | | Remove liquidity | `removeICPLiquidity(RemoveLiquidityRequest)` → `Result<RemoveLiquidityResult, Text>` | $0.10 | **Types:** ``` AddLiquidityRequest = { poolId:Text, dex:{#ICPSwap|#KongSwap}, token0:Text, token1:Text, token0Amount:Nat, token1Amount:Nat, slippagePct:Float } RemoveLiquidityRequest = { poolId:Text, dex:{#ICPSwap|#KongSwap}, lpTokens:Nat, slippagePct:Float } LPPosition = { poolId, dex, token0, token1, token0Symbol, token1Symbol, liquidity:Nat, token0Amount, token1Amount, unclaimedFees:?(Nat,Nat), valueUsd:?Nat } PoolInfo = { poolId, dex, token0, token1, token0Symbol, token1Symbol, reserve0, reserve1, fee, tvl:?Nat, apr:?Float, volume24h:?Nat } DexToken = { canisterId, symbol, name, decimals:Nat8, fee:Nat, standard:{#ICRC1|#ICRC2|#DIP20}, logo:?Text, category:?Text, availableOn:[DexId] } ``` **Well-known pools:** ICP/ckUSDC, ckBTC/ICP, ICP/ckETH, ckUSDT/ckUSDC, CHAT/ICP (on both ICPSwap and KongSwap). ```motoko // Discover pools, then add liquidity let pools = await menese.getICPDexPools(); // Find ICP/ckUSDC pool let pool = Array.find<DexTypes.PoolInfo>(pools, func(p) { p.token0Symbol == "ICP" and p.token1Symbol == "ckUSDC" }); switch (pool) { case (?p) { let req : DexTypes.AddLiquidityRequest = { poolId = p.poolId; dex = p.dex; token0 = "ryjl3-tyaaa-aaaaa-aaaba-cai"; // ICP ledger token1 = "xevnm-gaaaa-aaaar-qafnq-cai"; // ckUSDC token0Amount = 100_000_000; // 1 ICP token1Amount = 10_000_000; // 10 ckUSDC slippagePct = 1.0; }; let result = await menese.addICPLiquidity(req); }; case null { /* pool not found */ }; }; // View positions let positions = await menese.getICPLPPositions(); // Remove liquidity let removeReq : DexTypes.RemoveLiquidityRequest = { poolId = positions[0].poolId; dex = positions[0].dex; lpTokens = positions[0].liquidity; // Remove all slippagePct = 1.0; }; let removed = await menese.removeICPLiquidity(removeReq); ``` ### Tool 15: ICP AI Rebalancer (FREE) AI-powered portfolio rebalancing recommendations using Herfindahl-Hirschman Index diversification scoring, impermanent loss estimation, and risk-adjusted APY analysis. | Operation | Function | Cost | |-----------|----------|------| | Get recommendations | `getICPRebalanceRecommendations(preferences, tokenBalances, pools?)` → `[RebalanceRecommendation]` | FREE | **Types:** ``` RebalancePreferences = { targetCategories:[Text], riskTolerance:Text, minApy:?Float, maxImpermanentLoss:?Float, autoCompound:Bool } targetCategories: ["stablecoin", "defi", "lst", "yield", "wrapped", "meme", "ecosystem"] riskTolerance: "conservative" | "moderate" | "aggressive" RebalanceRecommendation = { id, action:{#Swap|#AddLiquidity|#RemoveLiquidity|#Compound}, fromToken, toToken, fromSymbol, toSymbol, amount:Nat, reason:Text, estimatedApy:?Float, currentApy:?Float, impermanentLossRisk:{#Low|#Medium|#High}, confidence:Float, estimatedGasUsd:?Float } ``` ```motoko // Get rebalancing recommendations for your ICP portfolio let prefs : DexTypes.RebalancePreferences = { targetCategories = ["stablecoin", "defi", "lst"]; riskTolerance = "moderate"; minApy = ?5.0; // Only suggest >5% APY maxImpermanentLoss = ?10.0; // Max 10% IL risk autoCompound = true; }; // Pass current balances: [(canisterId, amount)] let balances = [ ("ryjl3-tyaaa-aaaaa-aaaba-cai", 500_000_000), // 5 ICP ("xevnm-gaaaa-aaaar-qafnq-cai", 100_000_000), // 100 ckUSDC ("mxzaz-hqaaa-aaaar-qaada-cai", 50_000), // 0.0005 ckBTC ]; let recommendations = await menese.getICPRebalanceRecommendations(prefs, balances, null); for (rec in recommendations.vals()) { Debug.print(rec.reason # " | Confidence: " # Float.toText(rec.confidence)); // e.g., "Swap 2 ICP → ckUSDC and add to ICP/ckUSDC pool for 12.5% APY | Confidence: 0.85" }; ``` --- ## Combining Tools — Practical Automation Examples The real power is combining these tools. Below are complete patterns showing how tools work together. ### Example 1: DCA Bot (Timer + Balance + Swap) Buy USDC with SOL every hour if balance exceeds threshold. ```motoko // Tools used: getMySolanaBalance (FREE) + swapRaydiumApiUser ($0.075) func dcaCycle() : async () { let balance = switch (await menese.getMySolanaBalance()) { case (#ok(v)) v; case (#err(_)) return; }; if (balance < 500_000_000) return; // < 0.5 SOL, skip let swapAmt = balance - 50_000_000; // Keep 0.05 SOL for rent let _ = await menese.swapRaydiumApiUser( [REDACTED_SECRET], // SOL [REDACTED_SECRET], // USDC swapAmt, 150, true, false, null, null ); }; // Run every hour let timerId = Timer.recurringTimer<system>(#seconds(3600), dcaCycle); ``` ### Example 2: Stop-Loss via Strategy Engine Set a stop-loss rule that auto-sells when price drops. No timer needed — MeneseSDK evaluates. ```motoko // Tool used: addStrategyRule (FREE to create, execution costs per action) let rule : Menese.Rule = { id = 0; ruleType = #StopLoss; status = #Active; chainType = #Solana; triggerPrice = 120_000_000; // Trigger at this price level sizePct = 100; // Sell 100% of position positionId = 0; createdAt = Time.now(); dcaConfig = null; lpConfig = null; scheduledConfig = null; apyMigrationConfig = null; volatilityConfig = null; swapAmountLamports = ?1_000_000_000; // 1 SOL swapAmountWei = null; }; let ruleId = await menese.addStrategyRule(rule); ``` ### Example 3: Take-Profit + Stop-Loss Combo Set both on the same position — whichever triggers first wins. ```motoko // Tools: addStrategyRule × 2 // Take-profit at 200 let tp : Menese.Rule = { /* ... */ ruleType = #TakeProfit; triggerPrice = 200_000_000; sizePct = 50; /* sell half */ /* ... */ }; let tpId = await menese.addStrategyRule(tp); // Stop-loss at 100 let sl : Menese.Rule = { /* ... */ ruleType = #StopLoss; triggerPrice = 100_000_000; sizePct = 100; /* sell all */ /* ... */ }; let slId = await menese.addStrategyRule(sl); // When one triggers, cancel the other // Check via getMyStrategyRules() or getStrategyLogs() in your timer ``` ### Example 4: DCA via Strategy Engine (no custom timer) Let MeneseSDK handle the scheduling internally. ```motoko // Tool: addStrategyRule with DCA config let dca : Menese.Rule = { id = 0; ruleType = #DCA; status = #Active; chainType = #Solana; triggerPrice = 0; sizePct = 100; positionId = 0; createdAt = Time.now(); dcaConfig = ?{ amountPerInterval = 100_000_000; // 0.1 SOL per buy currentInterval = 0; intervalSeconds = 3600; // Every hour lastExecutedAt = 0; maxIntervals = 168; // Run for 1 week (168 hours) targetToken = [REDACTED_SECRET]; // Buy USDC totalSpent = 0; }; lpConfig = null; scheduledConfig = null; apyMigrationConfig = null; volatilityConfig = null; swapAmountLamports = ?100_000_000; swapAmountWei = null; }; ignore await menese.addStrategyRule(dca); ``` ### Example 5: Multi-Chain Sweep (Balance + Send across chains) Check all balances, sweep any above threshold to treasury. ```motoko // Tools: getAllBalances (FREE) + sendSolTransaction + sendICP + sendEvmNativeTokenAutonomous func sweepCycle() : async () { let bals = await menese.getAllBalances(); // Sweep SOL if > 1 SOL switch (bals.solana) { case (#ok(lamports)) { if (lamports > 1_000_000_000) { ignore await menese.sendSolTransaction(solTreasury, lamports - 50_000_000); }; }; case (#err(_)) {}; }; // Sweep ICP if > 1 ICP switch (bals.icp) { case (#ok(e8s)) { if (e8s > 100_000_000) { ignore await menese.sendICP(Principal.fromText(icpTreasury), e8s - 100_000); }; }; case (#err(_)) {}; }; // Sweep ETH if > 0.1 ETH switch (await menese.getMyEvmBalance(ethRpc)) { case (#ok(wei)) { if (wei > 100_000_000_000_000_000) { ignore await menese.sendEvmNativeTokenAutonomous( ethTreasury, wei - 50_000_000_000_000_000, ethRpc, 1, null ); }; }; case (#err(_)) {}; }; }; ``` ### Example 6: DeFi Yield Rebalancer (Aave + Lido + LP) Allocate idle ETH across DeFi protocols on a timer. ```motoko // Tools: getMyEvmBalance + getAWethBalance + getStEthBalance (all FREE) // + aaveSupplyEth + stakeEthForStEth + wrapStEth ($0.10 each) func rebalanceCycle() : async () { let evmAddr = (await menese.getMyEvmAddress()).evmAddress; // Cached ideally let ethBal = switch (await menese.getMyEvmBalance(ethRpc)) { case (#ok(v)) v; case _ 0 }; let aaveBal = switch (await menese.getAWethBalance(evmAddr, ethRpc)) { case (#ok(v)) v; case _ 0 }; let lidoBal = switch (await menese.getStEthBalance(evmAddr, ethRpc)) { case (#ok(v)) v; case _ 0 }; let reserve = 50_000_000_000_000_000; // 0.05 ETH for gas if (ethBal <= reserve) return; let deployable = ethBal - reserve; // 50% Aave, 50% Lido let aaveTarget = deployable / 2; let lidoTarget = deployable / 2; if (aaveTarget > aaveBal and aaveTarget - aaveBal > 10_000_000_000_000_000) { ignore await menese.aaveSupplyEth(aaveTarget - aaveBal, ethRpc, null); }; if (lidoTarget > lidoBal and lidoTarget - lidoBal > 10_000_000_000_000_000) { ignore await menese.stakeEthForStEth(lidoTarget - lidoBal, ethRpc, null); ignore await menese.wrapStEth(lidoTarget - lidoBal, ethRpc, null); }; }; // Run every 6 hours let timerId = Timer.recurringTimer<system>(#seconds(21600), rebalanceCycle); ``` ### Example 7: Cross-Chain Arbitrage (Bridge + Swap) Move funds between Ethereum and Solana to capture price differences. ```motoko // Tools: getTokenQuote (FREE) + getRaydiumQuote (FREE) // + quickUltrafastEthToSol ($0.10) + swapRaydiumApiUser ($0.075) // 1. Check ETH USDC price on Uniswap let ethQuote = await menese.getTokenQuote("USDC", "WETH", 1000_000_000, ethRpc); // 2. Check SOL USDC price on Raydium let solQuote = await menese.getRaydiumQuote(USDC_MINT, SOL_MINT, 1000_000_000, 100); // 3. If profitable, bridge and swap // Bridge ETH → SOL: quickUltrafastEthToSol // Swap on Raydium: swapRaydiumApiUser // Bridge back: quickSolToEth ``` ### Example 8: Merchant Payment Flow (Address + Balance + Sweep) Accept payments and auto-sweep to treasury. ```motoko // Tools: getMySolanaAddress (FREE, cached) + getMySolanaBalance (FREE) // + sendSolTransaction ($0.05) // 1. Show payment address to customer (from cache) let payAddr = cachedAddresses.solana; // 2. Periodically check if payment arrived (FREE) let bal = switch (await menese.getMySolanaBalance()) { case (#ok(v)) v; case _ 0 }; if (bal >= invoiceAmount) { // 3. Mark paid, sweep to treasury ignore await menese.sendSolTransaction(treasury, bal - 50_000_000); }; ``` ### Example 9: Scheduled Weekly Swap (Strategy Engine) Use `#Scheduled` rule type for time-based operations without custom timers. ```motoko let weekly : Menese.Rule = { id = 0; ruleType = #Scheduled; status = #Active; chainType = #Solana; triggerPrice = 0; sizePct = 100; positionId = 0; createdAt = Time.now(); dcaConfig = null; scheduledConfig = ?{}; // SDK handles scheduling details lpConfig = null; apyMigrationConfig = null; volatilityConfig = null; swapAmountLamports = ?500_000_000; // 0.5 SOL swapAmountWei = null; }; ignore await menese.addStrategyRule(weekly); ``` ### Example 10: Monitor + React to Volatility Use `#VolatilityTrigger` or custom timer with price feeds. ```motoko // Strategy engine approach: let volRule : Menese.Rule = { id = 0; ruleType = #VolatilityTrigger; status = #Active; chainType = #EVM; triggerPrice = 0; sizePct = 50; positionId = 0; createdAt = Time.now(); dcaConfig = null; lpConfig = null; scheduledConfig = null; apyMigrationConfig = null; volatilityConfig = ?{}; // SDK evaluates volatility conditions swapAmountLamports = null; swapAmountWei = ?500_000_000_000_000_000; // 0.5 ETH }; // Custom approach: read Chainlink price feed + react func checkVolatility() : async () { let price = await menese.callEvmContractRead( [REDACTED_SECRET], // ETH/USD Chainlink "feaf968c", [], ethRpc ); // Parse price, compare to threshold, execute swap if needed }; ``` --- ## Unit Conversion Quick Reference | Chain | Unit | Decimals | 1 Token = | |-------|------|----------|-----------| | Solana | lamports | 9 | 1,000,000,000 | | ICP | e8s | 8 | 100,000,000 | | Bitcoin | satoshis | 8 | 100,000,000 | | Litecoin | litoshis | 8 | 100,000,000 | | EVM | wei | 18 | 10^18 | | XRP | drops (Text) | 6 | "1.0" | | SUI | mist | 9 | 1,000,000,000 | | TON | nanotons | 9 | 1,000,000,000 | | Cardano | lovelace | 6 | 1,000,000 | | Aptos | octas | 8 | 100,000,000 | | NEAR | yoctoNEAR | 24 | 10^24 | | Tron | sun | 6 | 1,000,000 | | CloakCoin | units | 6 | 1,000,000 | | THORChain | units | 8 | 100,000,000 | --- ## Common Pitfalls 1. **Wrong field names** — `evmAddress` not `address`, `suiAddress` not `address`, `nonBounceable` not `address`, `implicitAccountId` not `accountId`, `base58Address` not `base58` 2. **Flat vs variant returns** — XRP and TON send return FLAT records (check `.success`). Raydium swap also returns FLAT. Everything else uses `Result<T, Text>` with `#ok/#err`. 3. **Litecoin ≠ Bitcoin return type** — Litecoin = `SendResult` (`.txHash`), Bitcoin = `SendResultBtcLtc` (`.txid` + `.fee`) 4. **CloakCoin = 6 decimals**, never 8 5. **EVM needs your RPC** — configure endpoints before any EVM operation 6. **XRP amount is Text** — pass `"1.5"` not `1500000` 7. **Cache addresses** — deterministic per principal, fetch once and store 8. **Always keep a reserve** — leave min balance for rent/fees (0.05 SOL, 0.001 ICP, 0.05 ETH) 9. **Get quotes before swaps** — all quote functions are FREE 10. **Strategy rules are FREE to create** — you only pay when execution happens --- ## Pricing Summary | Operation | Client Mode | Agent Mode | |-----------|------------|------------| | Addresses/Balances/Quotes | FREE | FREE | | Strategy rule CRUD | FREE | FREE | | Send/Transfer | $0.05 | $0.10 | | DEX Swap | $0.075 | $0.15 | | Bridge | $0.10 | $0.20 | | DeFi (Aave/Lido/LP/Custom) | — | $0.10 | | Tier | Price | Actions/Month | |------|-------|---------------| | Free | $0 | 5 (lifetime) | | Developer | $35/mo | 1,000 | | Pro | $99/mo | 5,000 | | Enterprise | $249/mo | Unlimited | --- ## Files in This Skill | File | Purpose | |------|---------| | `SKILL.md` | This guide — all tools, examples, best practices | | `WalletBot.mo` | ICP canister wrapping MeneseSDK (production use) | | `scripts/wallet_commands.py` | Python CLI for dfx calls (prototyping/testing) | | `references/api-surface.md` | Full API — every type definition and function signature | | `references/automation.md` | Deep dive — timer bots, DeFi yield, strategy patterns, custom contracts |


Security scan verdicts — ClawScan: suspicious | static analysis:  | VirusTotal: suspicious | SkillSpector: suspicious (CRITICAL)