# 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*