|
| 1 | +import 'package:flutter/wallet_core.dart'; |
| 2 | +import 'package:flutter/wallet_core_bindings.dart'; |
| 3 | + |
| 4 | +import 'dart:ffi'; |
| 5 | +import 'package:ffi/ffi.dart'; |
| 6 | + |
| 7 | +void main(List<String> arguments) { |
| 8 | + final walletCore = load(); |
| 9 | + |
| 10 | + final passwordTwString = walletCore.TWStringCreateWithUTF8Bytes( |
| 11 | + "".toNativeUtf8().cast<Char>(), |
| 12 | + ); |
| 13 | + |
| 14 | + print("Creating a new HD wallet ... "); |
| 15 | + final walletNew = walletCore.TWHDWalletCreate(128, passwordTwString); |
| 16 | + print("done."); |
| 17 | + print( |
| 18 | + "Secret mnemonic for new wallet: '${walletCore.TWStringUTF8Bytes(walletCore.TWHDWalletMnemonic(walletNew)).cast<Utf8>().toDartString()}'.", |
| 19 | + ); |
| 20 | + walletCore.TWHDWalletDelete(walletNew); |
| 21 | + |
| 22 | + // Alternative: Import wallet with existing recovery phrase (mnemonic) |
| 23 | + print("Importing an HD wallet from earlier ... "); |
| 24 | + final secretMnemonic = walletCore.TWStringCreateWithUTF8Bytes( |
| 25 | + "ripple scissors kick mammal hire column oak again sun offer wealth tomorrow wagon turn fatal" |
| 26 | + .toNativeUtf8() |
| 27 | + .cast<Char>(), |
| 28 | + ); |
| 29 | + final walletImp = walletCore.TWHDWalletCreateWithMnemonic( |
| 30 | + secretMnemonic, |
| 31 | + walletCore.TWStringCreateWithUTF8Bytes("".toNativeUtf8().cast<Char>()), |
| 32 | + ); |
| 33 | + walletCore.TWStringDelete(secretMnemonic); |
| 34 | + print("done."); |
| 35 | + print( |
| 36 | + "Secret mnemonic for imported wallet: '${walletCore.TWStringUTF8Bytes(walletCore.TWHDWalletMnemonic(walletImp)).cast<Utf8>().toDartString()}'.", |
| 37 | + ); |
| 38 | + |
| 39 | + // coin type: we use Ethereum |
| 40 | + const coinType = TWCoinType.TWCoinTypeEthereum; |
| 41 | + print( |
| 42 | + "Working with coin: ${walletCore.TWStringUTF8Bytes(walletCore.TWCoinTypeConfigurationGetName(coinType)).cast<Utf8>().toDartString()} ${walletCore.TWStringUTF8Bytes(walletCore.TWCoinTypeConfigurationGetSymbol(coinType)).cast<Utf8>().toDartString()}", |
| 43 | + ); |
| 44 | + |
| 45 | + // Derive default address |
| 46 | + print("Obtaining default address ... "); |
| 47 | + final address = walletCore.TWStringUTF8Bytes( |
| 48 | + walletCore.TWHDWalletGetAddressForCoin(walletImp, coinType), |
| 49 | + ).cast<Utf8>().toDartString(); |
| 50 | + print("done."); |
| 51 | + print("Default address: '$address'"); |
| 52 | + |
| 53 | + // Alternative: Derive address using default derivation path |
| 54 | + // Done in 2 steps: derive private key, then address from private key |
| 55 | + // Note that private key is passed around between the two calls by the wallet -- be always cautious when handling secrets, avoid the risk of leaking secrets |
| 56 | + print( |
| 57 | + "Default derivation path: ${walletCore.TWStringUTF8Bytes(walletCore.TWCoinTypeDerivationPath(coinType)).cast<Utf8>().toDartString()}", |
| 58 | + ); |
| 59 | + final secretPrivateKeyDefault = walletCore.TWHDWalletGetKeyForCoin( |
| 60 | + walletImp, |
| 61 | + coinType, |
| 62 | + ); |
| 63 | + final addressDefault = walletCore.TWStringUTF8Bytes( |
| 64 | + walletCore.TWCoinTypeDeriveAddress(coinType, secretPrivateKeyDefault), |
| 65 | + ).cast<Utf8>().toDartString(); |
| 66 | + print("Address from default key: '$addressDefault'"); |
| 67 | + |
| 68 | + // Alternative: Derive address using custom derivation path |
| 69 | + final customDerivationPath = walletCore.TWStringCreateWithUTF8Bytes( |
| 70 | + "m/44'/60'/1'/0/0".toNativeUtf8().cast<Char>(), |
| 71 | + ); |
| 72 | + final secretPrivateKeyCustom = walletCore.TWHDWalletGetKey( |
| 73 | + walletImp, |
| 74 | + coinType, |
| 75 | + customDerivationPath, |
| 76 | + ); |
| 77 | + walletCore.TWStringDelete(customDerivationPath); |
| 78 | + final addressCustom = walletCore.TWStringUTF8Bytes( |
| 79 | + walletCore.TWCoinTypeDeriveAddress(coinType, secretPrivateKeyCustom), |
| 80 | + ).cast<Utf8>().toDartString(); |
| 81 | + print("Custom-derived address: '$addressCustom'"); |
| 82 | + print(""); |
| 83 | + |
| 84 | + print( |
| 85 | + "RECEIVE funds: Perform send from somewhere else to this address: $address", |
| 86 | + ); |
| 87 | + print(""); |
| 88 | + |
| 89 | + // Steps for sending: |
| 90 | + // 1. put together a send message (contains sender and receiver address, amount, gas price, etc.) |
| 91 | + // 2. sign this message |
| 92 | + // 3. broadcast this message to the P2P network -- not done in this sample |
| 93 | + print("SEND funds:"); |
| 94 | + const dummyReceiverAddress = "0xC37054b3b48C3317082E7ba872d7753D13da4986"; |
| 95 | + final secretPrivKey = walletCore.TWPrivateKeyData(secretPrivateKeyDefault); |
| 96 | + |
| 97 | + print("preparing transaction (using AnySigner) ... "); |
| 98 | + const chainIdB64 = "AQ=="; // base64(parse_hex("01")) |
| 99 | + const gasPriceB64 = |
| 100 | + "1pOkAA=="; // base64(parse_hex("d693a4")) decimal 3600000000 |
| 101 | + const gasLimitB64 = "Ugg="; // base64(parse_hex("5208")) decimal 21000 |
| 102 | + const amountB64 = |
| 103 | + "A0i8paFgAA=="; // base64(parse_hex("0348bca5a160")) 924400000000000 |
| 104 | + final transaction = |
| 105 | + "{" |
| 106 | + "\"chainId\":\"$chainIdB64" |
| 107 | + "\",\"gasPrice\":\"$gasPriceB64" |
| 108 | + "\",\"gasLimit\":\"$gasLimitB64" |
| 109 | + "\",\"toAddress\":\"$dummyReceiverAddress" |
| 110 | + "\",\"transaction\":{\"transfer\":{\"amount\":\"$amountB64" |
| 111 | + "\"}}}"; |
| 112 | + print("transaction: $transaction"); |
| 113 | + |
| 114 | + print("signing transaction ... "); |
| 115 | + final json = walletCore.TWStringCreateWithUTF8Bytes( |
| 116 | + transaction.toNativeUtf8().cast<Char>(), |
| 117 | + ); |
| 118 | + final result = walletCore.TWAnySignerSignJSON( |
| 119 | + json, |
| 120 | + secretPrivKey, |
| 121 | + TWCoinType.TWCoinTypeEthereum, |
| 122 | + ); |
| 123 | + final signedTransaction = walletCore.TWStringUTF8Bytes( |
| 124 | + result, |
| 125 | + ).cast<Utf8>().toDartString(); |
| 126 | + print("done"); |
| 127 | + print( |
| 128 | + "Signed transaction data (to be broadcast to network): (len ${signedTransaction.length}) '$signedTransaction'", |
| 129 | + ); |
| 130 | + // see e.g. https://github.com/flightwallet/decode-eth-tx for checking binary output content |
| 131 | + print(""); |
| 132 | + walletCore.TWStringDelete(json); |
| 133 | + walletCore.TWStringDelete(result); |
| 134 | + |
| 135 | + print("Bye!"); |
| 136 | + walletCore.TWHDWalletDelete(walletImp); |
| 137 | + |
| 138 | + walletCore.TWStringDelete(passwordTwString); |
| 139 | +} |
0 commit comments