multichain-protocol

star 7

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.

modbender By modbender schedule Updated 3/6/2026

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:

sh -ci "$(curl -fsSL https://internetcomputer.org/install.sh)"

Verify it works:

dfx --version

That's it. No accounts needed, no wallets to set up — dfx handles everything.

Step 2: Install the skill

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)

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.

// 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;
    };
  };
};
# 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.

// Example: get SOL address
let info = await menese.getMySolanaAddress();
let myAddress = info.address;  // "5xK2abc..."
# 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.

// 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.

// 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>
// 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)
// Example: swap 1 SOL → USDC on Raydium
let SOL = "So11111111111111111111111111111111111111112";
let USDC = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v";

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
// 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)
// 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
// 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
// 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.

// Read Chainlink ETH/USD price (FREE)
let result = await menese.callEvmContractRead(
  "0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419",  // 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).

// 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 }
// 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.

// 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(
    "So11111111111111111111111111111111111111112",  // SOL
    "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",  // 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.

// 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.

// 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.

// 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 = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v";  // 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.

// 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.

// 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.

// 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.

// 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.

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.

// 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(
    "0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419",  // 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 namesevmAddress 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
Install via CLI
npx skills add https://github.com/modbender/skill-library-mcp --skill multichain-protocol
Repository Details
star Stars 7
call_split Forks 2
navigation Branch main
article Path SKILL.md
More from Creator