Magic Money Wallet Magic Money Wallet Developer Docs
Developer preview

Magic Money Wallet Docs

Integration notes for dApp developers building against Magic Money Wallet across EVM, Solana, Cardano, Bitcoin, and WalletConnect. Use this page to detect the wallet, request accounts, submit signing requests, and understand the security boundaries.

Overview

Magic Money Wallet exposes standard wallet interfaces instead of a custom-only API. EVM dApps use EIP-1193 and EIP-6963, Solana dApps use the Wallet Standard and legacy window.solana, Cardano dApps use window.cardano.magicmoney, Bitcoin and Ordinals dApps use window.unisat or window.btc_providers, and QR or URI based flows use WalletConnect v2.

EVM EIP-1193 + EIP-6963

Detect Magic Money alongside MetaMask, Rabby, Coinbase Wallet, and other injected wallets.

Solana Wallet Standard

Supports connect, message signing, transaction signing, and sign-and-send flows.

Cardano CIP-30

Connect at window.cardano.magicmoney with UTxO, balance, signing, and submit methods.

Bitcoin UniSat + WBIP

Connect payment and ordinals addresses, sign messages, sign PSBTs, broadcast, and send BTC.

Quick Start

For the widest compatibility, detect Magic Money through standards first and only fall back to direct globals when the dApp framework requires it.

EVM connect
async function connectMagicMoneyEvm() {
  const provider = window.ethereum;
  if (!provider) throw new Error("No EVM wallet found");

  const accounts = await provider.request({
    method: "eth_requestAccounts"
  });

  const chainId = await provider.request({ method: "eth_chainId" });
  return { address: accounts[0], chainId };
}
Cardano connect
async function connectMagicMoneyCardano() {
  const wallet = window.cardano?.magicmoney;
  if (!wallet) throw new Error("Magic Money Cardano provider not found");

  const api = await wallet.enable();
  const networkId = await api.getNetworkId();
  const changeAddressHex = await api.getChangeAddress();

  return { api, networkId, changeAddressHex };
}
Bitcoin connect
async function connectMagicMoneyBitcoin() {
  const provider = window.unisat;
  if (!provider?.isMagicMoney) throw new Error("Magic Money Bitcoin provider not found");

  const [paymentAddress] = await provider.requestAccounts();
  const publicKey = await provider.getPublicKey();
  const balance = await provider.getBalance();

  return { paymentAddress, publicKey, balance };
}

Detect Wallet

On EVM pages, prefer EIP-6963. Magic Money announces with the reverse DNS identifier info.chainlens.magicmoney. If multiple wallets are installed, show the user a picker.

EIP-6963 discovery
const discoveredWallets = [];

window.addEventListener("eip6963:announceProvider", (event) => {
  const { info, provider } = event.detail;

  discoveredWallets.push({ info, provider });

  if (info.rdns === "info.chainlens.magicmoney") {
    console.log("Magic Money Wallet found", info.name);
  }
});

window.dispatchEvent(new Event("eip6963:requestProvider"));
Tip: Do not assume window.ethereum belongs to Magic Money. Browser users may have several wallets installed, and another extension can own that global.

EVM Provider

Magic Money implements the common EIP-1193 request shape. User consent is required for account access and signing requests.

MethodUseNotes
eth_requestAccountsConnect walletPrompts the user for origin approval.
eth_accountsRead connected accountsReturns an empty array until approved.
eth_chainIdRead active chainReturns a hex chain ID such as 0x1.
wallet_switchEthereumChainSwitch chainSupports configured EVM chains in Magic Money.
wallet_addEthereumChainAdd or select chainUsed for dApps that provide chain metadata.
personal_signMessage signingShown to the user before signing.
eth_signTypedData_v4EIP-712 typed dataUse this for structured login and permit flows.
eth_sendTransactionSend transactionTransaction is reviewed, signed locally, then broadcast.
Switch chain
await provider.request({
  method: "wallet_switchEthereumChain",
  params: [{ chainId: "0x2105" }] // Base mainnet
});

Solana Provider

Magic Money supports Solana dApps through Wallet Standard discovery and a legacy provider shape compatible with common Solana wallet adapter flows.

Legacy Solana connect
async function connectMagicMoneySolana() {
  const provider = window.solana;
  if (!provider) throw new Error("No Solana wallet found");

  const result = await provider.connect();
  const publicKey = result.publicKey.toString();

  const message = new TextEncoder().encode("Sign in to my dApp");
  const signed = await provider.signMessage(message, "utf8");

  return { publicKey, signature: signed.signature };
}

Transaction flows should use signTransaction, signAllTransactions, or signAndSendTransaction depending on whether your app or the wallet should broadcast.

Cardano CIP-30

Cardano is exposed at window.cardano.magicmoney. The provider follows the CIP-30 pattern where enable() returns the full API object after user approval.

MethodPurpose
isEnabled()Check if the current origin has permission.
enable()Request access and return the full wallet API.
getNetworkId()Returns 1 for mainnet.
getBalance()Returns balance CBOR hex.
getUtxos()Returns UTxO CBOR hex values.
getChangeAddress()Returns the change address as hex.
getRewardAddresses()Returns staking reward addresses as hex.
signTx(txHex, partialSign)Returns witness set CBOR hex.
signData(addressHex, payloadHex)Returns a signed data payload.
submitTx(txHex)Submits a signed transaction.
CIP-30 signing
const wallet = window.cardano?.magicmoney;
const api = await wallet.enable();

const txHex = "84a4..."; // unsigned transaction CBOR hex
const witnessSetHex = await api.signTx(txHex, true);

console.log({ witnessSetHex });

Bitcoin Provider

Magic Money exposes Bitcoin dApp connectivity for payment, Ordinals, Runes, and BRC-20 style apps. It supports a UniSat-compatible window.unisat surface and a sats-connect/WBIP style provider through window.btc_providers.

Bitcoin access is approval-gated by origin. The wallet returns a native SegWit payment address for BTC transfers and a Taproot address for ordinals workflows.

AddressPurposeType
PaymentBTC send/receive, PSBT payment inputsp2wpkh native SegWit, bc1q...
OrdinalsOrdinals, inscriptions, Runes, BRC-20p2tr Taproot, bc1p...

UniSat-compatible API

Use window.unisat when your dApp already supports UniSat-style Bitcoin wallets. Magic Money only claims this global when another Bitcoin wallet has not already locked it, so dApps should still allow wallet selection when multiple wallets are available.

MethodPurpose
requestAccounts()Prompt the user and return the payment address array.
getAccounts()Return connected payment accounts after approval.
getPublicKey()Return the payment public key as hex.
getNetwork()Returns livenet.
getBalance()Returns confirmed, unconfirmed, and total satoshi balances.
signMessage(message, type)Sign a message after user approval. Default type is ecdsa.
signPsbt(psbtHex, options)Sign one PSBT and return signed PSBT hex.
signPsbts(psbtHexArray, options)Sign multiple PSBTs.
pushPsbt(psbtHex)Broadcast an extracted raw transaction hex.
pushTx(rawtx)Broadcast a raw transaction hex.
sendBitcoin(to, satoshis)Build, sign, and broadcast a BTC transfer from the payment address.
Bitcoin message signing
const provider = window.unisat;
const [address] = await provider.requestAccounts();

const signature = await provider.signMessage(
  "Sign in to my Bitcoin dApp",
  "ecdsa"
);

console.log({ address, signature });
Bitcoin PSBT signing
const signedPsbtHex = await window.unisat.signPsbt(psbtHex, {
  autoFinalized: true,
  broadcast: false
});

// If your app extracts a raw transaction from the signed PSBT:
const txid = await window.unisat.pushTx({ rawtx: rawTxHex });
Send BTC
const sats = 25_000;
const txid = await window.unisat.sendBitcoin(
  "bc1qexamplepaymentaddress...",
  sats
);

sats-connect / WBIP provider

Apps that use WBIP-style providers can discover Magic Money through window.btc_providers and then call the matching provider from window.MagicMoneyProviders.BitcoinProvider or window.BitcoinProvider.

WBIP discovery
const entry = window.btc_providers?.find(
  (provider) => provider.id === "MagicMoneyProviders.BitcoinProvider"
);

if (!entry) throw new Error("Magic Money Bitcoin provider not found");

const provider = window.MagicMoneyProviders.BitcoinProvider;

const addressesResult = await provider.request("getAddresses", {
  purposes: ["payment", "ordinals"]
});

const psbtResult = await provider.request("signPsbt", {
  psbt: psbtBase64,
  signInputs: {},
  broadcast: false,
  finalize: true
});
Developer note: Magic Money avoids spending Taproot ordinals outputs as ordinary payment change. Build PSBTs with explicit inputs and outputs, and show the user clear intent when requesting ordinal or rune transfers.

WalletConnect

Magic Money uses WalletConnect v2 for dApps that prefer URI based pairing or for embedded ChainLens contexts where direct provider injection is not available.

NamespaceChainsTypical methods
eip155All supported EVM chainspersonal_sign, eth_sendTransaction, eth_signTypedData_v4
solanaSolana mainnet-betaSign message, sign transaction, sign and send
cardanoPlanned for finalized WC Cardano namespace supportCIP-30 remains the primary Cardano browser path.

Networks

Magic Money derives addresses from one seed phrase and groups them into one portfolio. EVM networks share the same EOA; non-EVM ecosystems have their own derivation paths and address formats.

Ethereum Arbitrum Optimism Base Polygon Avalanche Blast Gnosis Abstract ApeChain Ronin Soneium WorldChain Zora Monad HyperEVM Solana Cardano Bitcoin Polkadot Tron Dogecoin

Security Model

  • The renderer, popup, and dApp pages only receive public addresses, balances, metadata, and user-approved signatures.
  • Private keys are derived transiently inside the privileged runtime for signing and are not exposed to dApp JavaScript.
  • Desktop runs with contextIsolation, sandboxing, and no renderer nodeIntegration.
  • The extension inject script provides wallet APIs in the page, but storage and signing live behind the extension message bridge.
  • Each origin must be approved before account access or signing requests can proceed.

API Proxy

Keyed providers are routed through a hosted Cloudflare Worker so API secrets are not shipped in the desktop app or browser extension bundle. The Worker injects provider keys server side, adds cache behavior for expensive reads, and keeps normal users from needing any local configuration.

Advanced operators can self-host the Worker and point Magic Money at their own endpoint. Public, keyless services like CoinGecko, DexScreener, DefiLlama, mempool.space, and Magic Eden can remain direct to avoid collapsing all users onto one shared proxy IP.

Build Notes

The wallet codebase ships as Electron desktop and Chrome Manifest V3 extension builds. Node.js 20+ and npm 10+ are expected for local development.

Development commands
npm install

# Desktop app with hot reload
npm run dev

# Browser extension build
npm run build:extension

# Typecheck and tests
npm run typecheck
npm test

The dApp injection preloads are built through a separate inject bundle step. Keep that step attached to development, production, and package commands so EVM, Solana, and Cardano provider injection remains available.

Troubleshooting

ProblemCheck
Magic Money is not in your wallet list.Listen for EIP-6963 announcements and dispatch eip6963:requestProvider after your listener is registered.
eth_accounts returns empty.Call eth_requestAccounts and wait for user approval.
Cardano methods are missing.Use window.cardano.magicmoney.enable(); the full CIP-30 API is returned after approval.
Bitcoin provider is not visible.Check window.unisat, window.btc_providers, and window.MagicMoneyProviders.BitcoinProvider. Another wallet may own window.unisat.
Bitcoin signing fails with permission errors.Call requestAccounts() or getAddresses first so the origin is approved before message or PSBT signing.
Ordinals app gets the payment address only.Use the WBIP getAddresses path with purposes: ["payment", "ordinals"] to receive the Taproot ordinals address.
Signing request is rejected.Treat user rejection as normal control flow and let the user retry from your dApp UI.
Wrong EVM chain.Call wallet_switchEthereumChain before building chain-specific transactions.