element-x-ada/CARDANO-SPECS-REVIEW.md
Kayos 10e73d484b 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
2026-03-26 19:56:20 -07:00

26 KiB

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)
  2. Transaction Building & Signing
  3. Android Library Options
  4. Minimum Viable TX Flow
  5. CIP-0013: Payment URI Format
  6. CIP-0030: dApp-Wallet Bridge
  7. CIP-0045: WebRTC dApp-Wallet Communication
  8. WalletConnect v2 on Cardano
  9. Backend APIs: Koios vs Blockfrost
  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)

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)

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.

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",
    "<PROJECT_ID>"
)

val sender = Account(Networks.mainnet(), "<mnemonic>")
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)

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)

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

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:

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:

# 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

import io.blockfrost.sdk_kotlin.api.CardanoAddressesApi
import io.blockfrost.sdk_kotlin.infrastructure.ApiClient

ApiClient.apiKey["project_id"] = "<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

class CardanoWallet(
    private val backendService: BackendService,
    private val account: Account
) {
    suspend fun sendAda(
        recipientAddress: String,
        amountAda: Double
    ): Result<String> = 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:<address>[?amount=<amount>][&message=<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

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

<intent-filter>
    <action android:name="android.intent.action.VIEW" />
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />
    <data android:scheme="web+cardano" />
</intent-filter>

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:

// dApp requests connection
const api = await window.cardano.nami.enable();

Key Methods:

Method Returns Description
getNetworkId() Promise<number> 0 = testnet, 1 = mainnet
getUtxos() Promise<TransactionUnspentOutput[]> Wallet's UTxOs in CBOR
getBalance() Promise<Value> Total balance (CBOR)
getUsedAddresses() Promise<Address[]> Used addresses
getUnusedAddresses() Promise<Address[]> Fresh addresses
getChangeAddress() Promise<Address> Address for change
getRewardAddresses() Promise<Address[]> Staking addresses
signTx(tx, partialSign) Promise<TransactionWitnessSet> Sign transaction
signData(addr, payload) Promise<DataSignature> CIP-8 message signing
submitTx(tx) Promise<TransactionHash> Submit to network

cardano-client-lib CIP-30 Support

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)

import { CardanoPeerConnect } from '@fabianbormann/cardano-peer-connect';

class MyWalletPeerConnect extends CardanoPeerConnect {
    constructor() {
        super({
            name: 'Element X ADA',
            version: '1.0.0',
            icon: '<base64-icon>'
        });
    }
    
    // Implement CIP-30 methods
    async getRewardAddresses(): Promise<string[]> {
        return [this.wallet.stakeAddress];
    }
    
    async getUsedAddresses(): Promise<string[]> {
        return [this.wallet.baseAddress];
    }
    
    async signTx(tx: string, partialSign: boolean): Promise<string> {
        // 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:

# Blockfrost
curl -H "project_id: <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:

# Blockfrost
curl -X POST -H "project_id: <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:

val backendService = BFBackendService(
    Constants.BLOCKFROST_MAINNET_URL,
    "<PROJECT_ID>"
)

val utxos = backendService.utxoService.getUtxos(address, 100, 1)

Koios with cardano-client-lib:

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)

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