From 10e73d484bdf76cb5e443be04cdc745f7d449b99 Mon Sep 17 00:00:00 2001 From: Kayos Date: Thu, 26 Mar 2026 19:56:20 -0700 Subject: [PATCH] Add comprehensive Cardano specs review for native wallet implementation Covers: - CIP-1852 key derivation with code examples - Transaction building (cardano-client-lib + CSL) - Android library comparison (cardano-client-lib recommended) - CIP-0013 payment URI parsing - CIP-0030/CIP-0045 dApp connectivity - Koios vs Blockfrost API analysis - MVP architecture recommendations --- CARDANO-SPECS-REVIEW.md | 879 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 879 insertions(+) create mode 100644 CARDANO-SPECS-REVIEW.md diff --git a/CARDANO-SPECS-REVIEW.md b/CARDANO-SPECS-REVIEW.md new file mode 100644 index 0000000000..cb1453950a --- /dev/null +++ b/CARDANO-SPECS-REVIEW.md @@ -0,0 +1,879 @@ +# Cardano Technical Specifications Review for Element X ADA Wallet + +**Date:** 2026-03-26 +**Purpose:** Deep technical review for building a native Cardano wallet inside an Android app (Element X fork) + +--- + +## Table of Contents +1. [Key Derivation (CIP-1852)](#1-key-derivation-cip-1852) +2. [Transaction Building & Signing](#2-transaction-building--signing) +3. [Android Library Options](#3-android-library-options) +4. [Minimum Viable TX Flow](#4-minimum-viable-tx-flow) +5. [CIP-0013: Payment URI Format](#5-cip-0013-payment-uri-format) +6. [CIP-0030: dApp-Wallet Bridge](#6-cip-0030-dapp-wallet-bridge) +7. [CIP-0045: WebRTC dApp-Wallet Communication](#7-cip-0045-webrtc-dapp-wallet-communication) +8. [WalletConnect v2 on Cardano](#8-walletconnect-v2-on-cardano) +9. [Backend APIs: Koios vs Blockfrost](#9-backend-apis-koios-vs-blockfrost) +10. [Recommendations](#10-recommendations) + +--- + +## 1. Key Derivation (CIP-1852) + +### Standard Derivation Path + +CIP-1852 defines the HD wallet derivation for Cardano Shelley-era wallets, based on BIP-44/BIP-32-Ed25519. + +**Standard path:** +``` +m / purpose' / coin_type' / account' / role / index +``` + +**Concrete values:** +- `purpose`: **1852'** (hardened) - Shelley wallets (or 44' for Byron) +- `coin_type`: **1815'** (hardened) - Cardano's registered coin type +- `account`: **0'** (hardened) - First account +- `role`: + - **0** = External chain (receiving addresses) + - **1** = Internal chain (change addresses) + - **2** = Staking key +- `index`: Sequential address index + +**Example derivation paths:** +``` +m/1852'/1815'/0'/0/0 → First external/receiving address +m/1852'/1815'/0'/1/0 → First internal/change address +m/1852'/1815'/0'/2/0 → Staking key +``` + +### Code Example (cardano-serialization-lib / JavaScript) + +```javascript +function harden(num) { + return 0x80000000 + num; +} + +// From mnemonic to root key +import { mnemonicToEntropy } from 'bip39'; + +const entropy = mnemonicToEntropy( + "test walk nut penalty hip pave soap entry language right filter choice" +); + +const rootKey = CardanoWasm.Bip32PrivateKey.from_bip39_entropy( + Buffer.from(entropy, 'hex'), + Buffer.from(''), // empty password +); + +// Derive account key +const accountKey = rootKey + .derive(harden(1852)) // purpose + .derive(harden(1815)) // coin type + .derive(harden(0)); // account #0 + +// Derive payment key (external, index 0) +const utxoPubKey = accountKey + .derive(0) // external chain + .derive(0) // index 0 + .to_public(); + +// Derive staking key +const stakeKey = accountKey + .derive(2) // staking role + .derive(0) + .to_public(); + +// Create base address (payment + staking) +const baseAddr = CardanoWasm.BaseAddress.new( + CardanoWasm.NetworkInfo.mainnet().network_id(), + CardanoWasm.StakeCredential.from_keyhash(utxoPubKey.to_raw_key().hash()), + CardanoWasm.StakeCredential.from_keyhash(stakeKey.to_raw_key().hash()), +); + +console.log(baseAddr.to_address().to_bech32()); +// Output: addr1q... +``` + +### Code Example (cardano-client-lib / Java/Kotlin) + +```kotlin +import com.bloxbean.cardano.client.account.Account +import com.bloxbean.cardano.client.common.model.Networks + +// Create new wallet from mnemonic +val mnemonic = "test walk nut penalty hip pave soap entry language right filter choice" +val account = Account(Networks.mainnet(), mnemonic) + +// Get addresses +val baseAddress = account.baseAddress() // addr1q... +val enterpriseAddress = account.enterpriseAddress() +val stakeAddress = account.stakeAddress() // stake1u... + +// For derivation at specific index +val account5 = Account(Networks.mainnet(), mnemonic, 5) // account index 5 +``` + +--- + +## 2. Transaction Building & Signing + +### Simple ADA Transfer with cardano-client-lib (Java/Kotlin) + +This is the **recommended approach for Android** as it's pure Java/Kotlin. + +```kotlin +import com.bloxbean.cardano.client.account.Account +import com.bloxbean.cardano.client.backend.blockfrost.service.BFBackendService +import com.bloxbean.cardano.client.common.model.Networks +import com.bloxbean.cardano.client.quicktx.QuickTxBuilder +import com.bloxbean.cardano.client.quicktx.Tx +import com.bloxbean.cardano.client.function.helper.SignerProviders + +// Setup +val backendService = BFBackendService( + "https://cardano-mainnet.blockfrost.io/api/v0", + "" +) + +val sender = Account(Networks.mainnet(), "") +val senderAddress = sender.baseAddress() +val receiverAddress = "addr1q..." + +// Build and submit transaction +val tx = Tx() + .payToAddress(receiverAddress, Amount.ada(5.0)) + .from(senderAddress) + +val quickTxBuilder = QuickTxBuilder(backendService) +val result = quickTxBuilder + .compose(tx) + .withSigner(SignerProviders.signerFrom(sender)) + .completeAndWait { txHash -> println("Submitted: $txHash") } + +if (result.isSuccessful) { + println("Transaction hash: ${result.value}") +} +``` + +### Low-level Transaction Building (cardano-client-lib) + +```kotlin +import com.bloxbean.cardano.client.function.TxBuilder +import com.bloxbean.cardano.client.function.TxBuilderContext +import com.bloxbean.cardano.client.function.Output +import com.bloxbean.cardano.client.function.helper.* +import com.bloxbean.cardano.client.backend.api.DefaultUtxoSupplier +import com.bloxbean.cardano.client.backend.api.DefaultProtocolParamsSupplier + +// Define outputs +val output = Output.builder() + .address(receiverAddress) + .assetName("lovelace") + .qty(5_000_000) // 5 ADA in lovelace + .build() + +// Build transaction +val txBuilder = output.outputBuilder() + .buildInputs(InputBuilders.createFromSender(senderAddress, senderAddress)) + .andThen(BalanceTxBuilders.balanceTx(senderAddress, 1)) + +val utxoSupplier = DefaultUtxoSupplier(backendService.utxoService) +val protocolParamsSupplier = DefaultProtocolParamsSupplier(backendService.epochService) + +// Build and sign +val signedTx = TxBuilderContext.init(utxoSupplier, protocolParamsSupplier) + .buildAndSign(txBuilder, SignerProviders.signerFrom(sender)) + +// Submit +val result = backendService.transactionService.submitTransaction(signedTx.serialize()) +``` + +### Transaction Building with cardano-serialization-lib (JavaScript/WASM) + +```javascript +// Protocol parameters (these change - fetch from backend) +const linearFee = CardanoWasm.LinearFee.new( + CardanoWasm.BigNum.from_str('44'), + CardanoWasm.BigNum.from_str('155381') +); + +const txBuilderCfg = CardanoWasm.TransactionBuilderConfigBuilder.new() + .fee_algo(linearFee) + .pool_deposit(CardanoWasm.BigNum.from_str('500000000')) + .key_deposit(CardanoWasm.BigNum.from_str('2000000')) + .max_value_size(4000) + .max_tx_size(8000) + .coins_per_utxo_word(CardanoWasm.BigNum.from_str('34482')) + .build(); + +const txBuilder = CardanoWasm.TransactionBuilder.new(txBuilderCfg); + +// Add input (must provide UTxO details from backend) +txBuilder.add_key_input( + prvKey.to_public().hash(), + CardanoWasm.TransactionInput.new( + CardanoWasm.TransactionHash.from_bytes( + Buffer.from("8561258e210352fba2ac0488afed67b3427a27ccf1d41ec030c98a8199bc22ec", "hex") + ), + 0 // index + ), + CardanoWasm.Value.new(CardanoWasm.BigNum.from_str('10000000')) +); + +// Add output +const outputAddr = CardanoWasm.Address.from_bech32("addr_test1qpu..."); +txBuilder.add_output( + CardanoWasm.TransactionOutput.new( + outputAddr, + CardanoWasm.Value.new(CardanoWasm.BigNum.from_str('5000000')) + ) +); + +// Set TTL (time-to-live) +txBuilder.set_ttl(410021); + +// Add change output (calculates fee automatically) +const changeAddr = CardanoWasm.Address.from_bech32("addr_test1..."); +txBuilder.add_change_if_needed(changeAddr); + +// Build transaction body +const txBody = txBuilder.build(); +const txHash = CardanoWasm.hash_transaction(txBody); + +// Sign +const witnesses = CardanoWasm.TransactionWitnessSet.new(); +const vkeyWitnesses = CardanoWasm.Vkeywitnesses.new(); +const vkeyWitness = CardanoWasm.make_vkey_witness(txHash, prvKey); +vkeyWitnesses.add(vkeyWitness); +witnesses.set_vkeys(vkeyWitnesses); + +// Assemble final transaction +const transaction = CardanoWasm.Transaction.new(txBody, witnesses, undefined); +const signedTxCbor = transaction.to_bytes(); +``` + +--- + +## 3. Android Library Options + +### Option A: cardano-client-lib (RECOMMENDED) + +**Repository:** https://github.com/bloxbean/cardano-client-lib + +**Why it's the best choice for Android:** +- **Pure Java** - No JNI/native dependencies +- **Works on Android out of the box** - Min SDK 21+ +- **Active development** - Regular releases, good documentation +- **Modular design** - Include only what you need +- **Backend-agnostic** - Supports Blockfrost, Koios, Ogmios/Kupo +- **CIP implementations** - CIP-8 (message signing), CIP-20, CIP-25, CIP-30, CIP-67, CIP-68 + +**Gradle dependencies:** +```groovy +dependencies { + // Core library + implementation 'com.bloxbean.cardano:cardano-client-lib:0.7.1' + + // Pick ONE backend: + implementation 'com.bloxbean.cardano:cardano-client-backend-blockfrost:0.7.1' + // OR + implementation 'com.bloxbean.cardano:cardano-client-backend-koios:0.7.1' +} +``` + +**Key modules:** +| Module | Purpose | +|--------|---------| +| `cardano-client-crypto` | BIP32, BIP39, CIP1852 key derivation | +| `cardano-client-address` | Address generation (Base, Enterprise, Stake) | +| `cardano-client-transaction-spec` | Transaction serialization (CBOR) | +| `cardano-client-quicktx` | High-level transaction builder | +| `cardano-client-cip30` | dApp-Wallet bridge implementation | + +### Option B: cardano-serialization-lib (CSL) + +**Repository:** https://github.com/Emurgo/cardano-serialization-lib + +**Android challenges:** +- Written in **Rust**, compiled to WebAssembly +- For Android, requires **JNI bindings** via Rust FFI +- React Native wrapper exists (`react-native-haskell-shelley`) but adds complexity +- **No official AAR** published +- Would need to compile Rust to Android NDK targets (arm64-v8a, armeabi-v7a, x86, x86_64) + +**When to consider:** +- If you need exact byte-level compatibility with other CSL implementations +- If building a complex dApp that needs Plutus script support +- If your team already has Rust/JNI expertise + +**Build requirements for Android:** +```bash +# Would need to set up: +rustup target add aarch64-linux-android +rustup target add armv7-linux-androideabi +rustup target add i686-linux-android +rustup target add x86_64-linux-android + +# Then build with cargo-ndk or similar +``` + +### Option C: Blockfrost Kotlin SDK + +**Repository:** https://github.com/blockfrost/blockfrost-kotlin + +**Purpose:** API client only - does NOT handle key management or transaction building + +**Use for:** Querying blockchain data, submitting pre-built transactions + +```kotlin +import io.blockfrost.sdk_kotlin.api.CardanoAddressesApi +import io.blockfrost.sdk_kotlin.infrastructure.ApiClient + +ApiClient.apiKey["project_id"] = "" + +val addressApi = CardanoAddressesApi("https://cardano-mainnet.blockfrost.io/api/v0") +val utxos = addressApi.getAddressUtxos("addr1q...") +``` + +### Recommendation Matrix + +| Use Case | Recommended Library | +|----------|---------------------| +| Simple wallet (send/receive ADA) | cardano-client-lib | +| NFT minting | cardano-client-lib | +| Staking delegation | cardano-client-lib | +| Complex smart contracts | cardano-serialization-lib (with significant integration effort) | +| Just querying data | Blockfrost Kotlin SDK | + +--- + +## 4. Minimum Viable TX Flow + +### High-Level Flow + +``` +1. USER INITIATES PAYMENT + └─> Parse recipient address, amount + +2. FETCH UTXOs + └─> Query backend for sender's unspent outputs + └─> Select UTxOs covering amount + fees + +3. BUILD TRANSACTION + ├─> Inputs: Selected UTxOs + ├─> Outputs: Payment output + change output + ├─> Calculate fees based on tx size + └─> Set TTL (current slot + ~7200 slots = ~2 hours) + +4. SIGN TRANSACTION + └─> Use payment private key to create witness + +5. SUBMIT TRANSACTION + └─> POST serialized CBOR to backend + +6. CONFIRM + └─> Poll for confirmation or use websocket +``` + +### Kotlin Implementation + +```kotlin +class CardanoWallet( + private val backendService: BackendService, + private val account: Account +) { + suspend fun sendAda( + recipientAddress: String, + amountAda: Double + ): Result = withContext(Dispatchers.IO) { + try { + val tx = Tx() + .payToAddress(recipientAddress, Amount.ada(amountAda)) + .from(account.baseAddress()) + + val result = QuickTxBuilder(backendService) + .compose(tx) + .withSigner(SignerProviders.signerFrom(account)) + .complete() + + if (result.isSuccessful) { + // Submit + val submitResult = backendService.transactionService + .submitTransaction(result.value.serialize()) + + if (submitResult.isSuccessful) { + Result.success(submitResult.value) + } else { + Result.failure(Exception(submitResult.response)) + } + } else { + Result.failure(Exception("Failed to build transaction")) + } + } catch (e: Exception) { + Result.failure(e) + } + } +} +``` + +### Fee Calculation + +Cardano fees are calculated as: +``` +fee = a * tx_size_bytes + b +``` + +Current mainnet parameters (as of March 2026): +- `a` = 44 lovelace/byte +- `b` = 155,381 lovelace (base fee) + +Typical simple transaction: ~250-350 bytes → ~165,000-175,000 lovelace (~0.17 ADA) + +--- + +## 5. CIP-0013: Payment URI Format + +### URI Format + +``` +web+cardano:
[?amount=][&message=] +``` + +### Parameters + +| Parameter | Required | Description | +|-----------|----------|-------------| +| `address` | Yes | Bech32 address (addr1...) or $handle | +| `amount` | No | Amount in lovelace (1 ADA = 1,000,000 lovelace) | +| `message` | No | URL-encoded message (for tx metadata) | + +### Examples + +``` +# Simple payment request +web+cardano:addr1qxck... + +# With amount (5 ADA = 5,000,000 lovelace) +web+cardano:addr1qxck...?amount=5000000 + +# With amount and message +web+cardano:addr1qxck...?amount=5000000&message=Coffee%20payment + +# Using ADA handle +web+cardano:$kayos +``` + +### Kotlin Parser + +```kotlin +data class CardanoPaymentRequest( + val address: String, + val amount: Long? = null, // in lovelace + val message: String? = null +) + +fun parseCardanoUri(uri: String): CardanoPaymentRequest? { + val prefix = "web+cardano:" + if (!uri.startsWith(prefix)) return null + + val withoutPrefix = uri.removePrefix(prefix) + val parts = withoutPrefix.split("?", limit = 2) + + val address = parts[0] + val params = if (parts.size > 1) { + parts[1].split("&").associate { param -> + val kv = param.split("=", limit = 2) + kv[0] to (if (kv.size > 1) URLDecoder.decode(kv[1], "UTF-8") else "") + } + } else emptyMap() + + return CardanoPaymentRequest( + address = address, + amount = params["amount"]?.toLongOrNull(), + message = params["message"] + ) +} + +// Usage +val request = parseCardanoUri("web+cardano:addr1q...?amount=5000000&message=Test") +// CardanoPaymentRequest(address="addr1q...", amount=5000000, message="Test") +``` + +### Android Intent Filter + +```xml + + + + + + +``` + +--- + +## 6. CIP-0030: dApp-Wallet Bridge + +### Overview + +CIP-0030 defines a JavaScript API for web dApps to communicate with browser extension wallets. For a mobile wallet, this is relevant when: +- Implementing WalletConnect-style connectivity +- Building an in-app dApp browser +- Providing SDK for dApps to integrate + +### API Methods + +**Initialization:** +```javascript +// dApp requests connection +const api = await window.cardano.nami.enable(); +``` + +**Key Methods:** +| Method | Returns | Description | +|--------|---------|-------------| +| `getNetworkId()` | `Promise` | 0 = testnet, 1 = mainnet | +| `getUtxos()` | `Promise` | Wallet's UTxOs in CBOR | +| `getBalance()` | `Promise` | Total balance (CBOR) | +| `getUsedAddresses()` | `Promise` | Used addresses | +| `getUnusedAddresses()` | `Promise` | Fresh addresses | +| `getChangeAddress()` | `Promise
` | Address for change | +| `getRewardAddresses()` | `Promise` | Staking addresses | +| `signTx(tx, partialSign)` | `Promise` | Sign transaction | +| `signData(addr, payload)` | `Promise` | CIP-8 message signing | +| `submitTx(tx)` | `Promise` | Submit to network | + +### cardano-client-lib CIP-30 Support + +```kotlin +import com.bloxbean.cardano.client.cip.cip30.CIP30DataSigner + +// Sign arbitrary data (CIP-8) +val signature = CIP30DataSigner.sign( + account.hdKeyPair().privateKey, + "Hello Cardano!".toByteArray() +) + +// Returns: { signature: "...", key: "..." } +``` + +--- + +## 7. CIP-0045: WebRTC dApp-Wallet Communication + +### Overview + +CIP-0045 enables **P2P communication** between dApps and mobile wallets using WebRTC/WebTorrent, eliminating the need for browser extensions. + +### How It Works + +``` +┌─────────────┐ WebRTC/WebTorrent ┌──────────────┐ +│ dApp │ <─────────────────────────────────>│ Mobile Wallet│ +│ (Browser) │ P2P via BitTorrent trackers │ (App) │ +└─────────────┘ └──────────────┘ + 1. dApp displays QR code + 2. Wallet scans, establishes P2P + 3. CIP-30 API over the channel +``` + +### Reference Implementation + +**Library:** `@fabianbormann/cardano-peer-connect` +**Repository:** https://github.com/fabianbormann/cardano-peer-connect + +### Wallet Implementation (TypeScript/React Native) + +```typescript +import { CardanoPeerConnect } from '@fabianbormann/cardano-peer-connect'; + +class MyWalletPeerConnect extends CardanoPeerConnect { + constructor() { + super({ + name: 'Element X ADA', + version: '1.0.0', + icon: '' + }); + } + + // Implement CIP-30 methods + async getRewardAddresses(): Promise { + return [this.wallet.stakeAddress]; + } + + async getUsedAddresses(): Promise { + return [this.wallet.baseAddress]; + } + + async signTx(tx: string, partialSign: boolean): Promise { + // Show UI for user approval + const approved = await this.showSigningDialog(tx); + if (!approved) throw new Error('User rejected'); + + return this.wallet.sign(tx); + } +} + +// Connect to dApp +const peerConnect = new MyWalletPeerConnect(); + +peerConnect.setOnConnect((message) => { + console.log('Connected to dApp:', message.dApp.name); +}); + +// Scan QR code to get dApp identifier +const dAppId = 'bYUh6Bn6A...388LR1JCrED'; + +peerConnect.connect(dAppId, [ + 'wss://tracker.openwebtorrent.com:443/announce', + 'wss://tracker.btorrent.xyz' +]); +``` + +### Android Wallets Supporting CIP-0045 + +| Wallet | CIP-0045 Support | Notes | +|--------|-----------------|-------| +| Eternl | ✅ | Full support | +| Typhon | ✅ | Full support | +| Vespr | ⚠️ | WalletConnect preferred | +| Nami | ❌ | Browser extension only | +| Lace | ❌ | Browser extension only | + +### Recommendation + +CIP-0045 is powerful but complex. For MVP: +1. **Start without CIP-0045** - focus on send/receive +2. **Phase 2:** Add CIP-0013 URI handling +3. **Phase 3:** Consider CIP-0045 for dApp integration + +--- + +## 8. WalletConnect v2 on Cardano + +### Current State + +WalletConnect v2 on Cardano is **fragmented**: + +- **No official Cardano namespace** in WC2 registry +- Different wallets use different approaches: + - **VESPR**: Uses custom implementation + - **Flint**: Has WC2 support + - **Most wallets**: Prefer CIP-0045 + +### VESPR Wallet + +VESPR (https://vespr.xyz/) is a popular mobile wallet: +- **Android:** Yes (Play Store) +- **iOS:** Yes (App Store) +- Supports both CIP-0045 and a custom WC-style protocol +- Closed source + +### For Element X ADA + +**Recommendation:** Implement CIP-0045 rather than WalletConnect v2: +- Better Cardano ecosystem adoption +- Open standard with reference implementation +- Works with more dApps + +--- + +## 9. Backend APIs: Koios vs Blockfrost + +### Feature Comparison + +| Feature | Blockfrost | Koios | +|---------|------------|-------| +| **Pricing** | Freemium (50k req/day free) | Free (community) | +| **Rate Limits** | 10 req/sec, 500 burst | 100 req/10s | +| **Decentralization** | Centralized | Decentralized nodes | +| **Kotlin SDK** | ✅ Official | ❌ (REST only) | +| **Java SDK** | ✅ Official | Via cardano-client-lib | +| **Self-hosting** | ❌ | ✅ | +| **IPFS** | ✅ | ❌ | +| **Reliability** | Very high | Good (varies by node) | + +### API Comparison + +**Get UTxOs:** + +```bash +# Blockfrost +curl -H "project_id: " \ + "https://cardano-mainnet.blockfrost.io/api/v0/addresses/addr1.../utxos" + +# Koios +curl "https://api.koios.rest/api/v1/address_utxos?_address=addr1..." +``` + +**Submit Transaction:** + +```bash +# Blockfrost +curl -X POST -H "project_id: " \ + -H "Content-Type: application/cbor" \ + --data-binary @tx.signed \ + "https://cardano-mainnet.blockfrost.io/api/v0/tx/submit" + +# Koios +curl -X POST \ + -H "Content-Type: application/cbor" \ + --data-binary @tx.signed \ + "https://api.koios.rest/api/v1/submittx" +``` + +### Kotlin/Android Usage + +**Blockfrost with cardano-client-lib:** +```kotlin +val backendService = BFBackendService( + Constants.BLOCKFROST_MAINNET_URL, + "" +) + +val utxos = backendService.utxoService.getUtxos(address, 100, 1) +``` + +**Koios with cardano-client-lib:** +```kotlin +val backendService = KoiosBackendService( + Constants.KOIOS_MAINNET_URL +) + +val utxos = backendService.utxoService.getUtxos(address, 100, 1) +``` + +### Recommendation for Element X ADA + +**Use Blockfrost** for MVP: +- Official Kotlin SDK +- Higher reliability guarantees +- Free tier sufficient for development/testing +- Easy to switch to Koios later (same cardano-client-lib interface) + +**Consider Koios** for production if: +- Need to minimize costs at scale +- Want decentralization +- Plan to self-host infrastructure + +--- + +## 10. Recommendations + +### Architecture Summary + +``` +┌─────────────────────────────────────────────────────────┐ +│ Element X ADA │ +├─────────────────────────────────────────────────────────┤ +│ UI Layer (Compose) │ +│ ├─ WalletScreen (balance, history) │ +│ ├─ SendScreen (CIP-0013 URI parsing) │ +│ └─ ReceiveScreen (QR generation) │ +├─────────────────────────────────────────────────────────┤ +│ Wallet Core │ +│ ├─ KeyManager (encrypted mnemonic storage) │ +│ ├─ TransactionBuilder (QuickTx API) │ +│ └─ AddressGenerator (CIP-1852) │ +├─────────────────────────────────────────────────────────┤ +│ cardano-client-lib (0.7.1) │ +│ ├─ cardano-client-lib │ +│ ├─ cardano-client-backend-blockfrost │ +│ └─ cardano-client-cip30 (for future dApp support) │ +├─────────────────────────────────────────────────────────┤ +│ Backend │ +│ └─ Blockfrost API (mainnet/testnet) │ +└─────────────────────────────────────────────────────────┘ +``` + +### MVP Feature Set + +1. **Wallet Management** + - Generate new wallet (24-word mnemonic) + - Import existing wallet + - Secure storage (Android Keystore) + +2. **Basic Operations** + - View ADA balance + - View transaction history + - Send ADA (manual entry) + - Receive ADA (QR code + address) + +3. **CIP Support (Phase 1)** + - CIP-1852: Key derivation ✅ + - CIP-0013: Payment URIs ✅ + +4. **Future Enhancements (Phase 2+)** + - Native tokens display + - Staking delegation + - CIP-0045: dApp connectivity + - NFT gallery + +### Dependencies (build.gradle) + +```groovy +dependencies { + // Cardano core + implementation 'com.bloxbean.cardano:cardano-client-lib:0.7.1' + implementation 'com.bloxbean.cardano:cardano-client-backend-blockfrost:0.7.1' + + // For CIP-30 support (Phase 2) + implementation 'com.bloxbean.cardano:cardano-client-cip30:0.7.1' + + // Security + implementation 'androidx.security:security-crypto:1.1.0-alpha06' + + // QR codes + implementation 'com.google.zxing:core:3.5.2' + implementation 'com.journeyapps:zxing-android-embedded:4.3.0' +} +``` + +### Security Considerations + +1. **Mnemonic Storage:** + - Use Android Keystore + EncryptedSharedPreferences + - Never store plaintext + - Consider hardware-backed keys on supported devices + +2. **Transaction Signing:** + - Derive keys in memory only + - Clear sensitive data after use + - Require biometric/PIN for signing + +3. **Network:** + - Use certificate pinning for API calls + - Validate all addresses before sending + +--- + +## Appendix: Quick Reference + +### Address Prefixes + +| Type | Mainnet Prefix | Testnet Prefix | +|------|---------------|----------------| +| Base | addr1 | addr_test1 | +| Enterprise | addr1 | addr_test1 | +| Stake | stake1 | stake_test1 | +| Script | addr1 | addr_test1 | + +### Lovelace Conversions + +``` +1 ADA = 1,000,000 lovelace +1 lovelace = 0.000001 ADA + +// Kotlin helper +fun adaToLovelace(ada: Double): Long = (ada * 1_000_000).toLong() +fun lovelaceToAda(lovelace: Long): Double = lovelace / 1_000_000.0 +``` + +### Testnet Faucet + +https://docs.cardano.org/cardano-testnets/tools/faucet/ + +--- + +*Document prepared for Element X ADA project. Last updated: 2026-03-26*