@authon/react-native-wallet 0.0.0-stage → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +20 -0
- package/README.md +251 -2
- package/android/build.gradle +40 -0
- package/android/consumer-rules.pro +10 -0
- package/android/src/main/AndroidManifest.xml +8 -0
- package/android/src/main/java/dev/authon/reactnative/wallet/AuthonWalletModule.kt +235 -0
- package/android/src/vendor/GENERATED.md +4 -0
- package/android/src/vendor/kotlin/dev/authon/sdk/wallet/AndroidCapabilityProbe.kt +86 -0
- package/android/src/vendor/kotlin/dev/authon/sdk/wallet/AuthonWallet.kt +218 -0
- package/android/src/vendor/kotlin/dev/authon/sdk/wallet/Bitcoin.kt +296 -0
- package/android/src/vendor/kotlin/dev/authon/sdk/wallet/Bytes.kt +74 -0
- package/android/src/vendor/kotlin/dev/authon/sdk/wallet/Chains.kt +157 -0
- package/android/src/vendor/kotlin/dev/authon/sdk/wallet/CredentialManagerPasskeys.kt +192 -0
- package/android/src/vendor/kotlin/dev/authon/sdk/wallet/Der.kt +17 -0
- package/android/src/vendor/kotlin/dev/authon/sdk/wallet/DeviceStore.kt +170 -0
- package/android/src/vendor/kotlin/dev/authon/sdk/wallet/DialogWalletApprover.kt +141 -0
- package/android/src/vendor/kotlin/dev/authon/sdk/wallet/Eip712.kt +176 -0
- package/android/src/vendor/kotlin/dev/authon/sdk/wallet/Encodings.kt +133 -0
- package/android/src/vendor/kotlin/dev/authon/sdk/wallet/EvmAccount.kt +45 -0
- package/android/src/vendor/kotlin/dev/authon/sdk/wallet/EvmTransaction.kt +180 -0
- package/android/src/vendor/kotlin/dev/authon/sdk/wallet/GfTables.kt +44 -0
- package/android/src/vendor/kotlin/dev/authon/sdk/wallet/HdWallet.kt +130 -0
- package/android/src/vendor/kotlin/dev/authon/sdk/wallet/Http.kt +86 -0
- package/android/src/vendor/kotlin/dev/authon/sdk/wallet/JsValues.kt +83 -0
- package/android/src/vendor/kotlin/dev/authon/sdk/wallet/KeystoreDeviceStore.kt +236 -0
- package/android/src/vendor/kotlin/dev/authon/sdk/wallet/Prf.kt +111 -0
- package/android/src/vendor/kotlin/dev/authon/sdk/wallet/Proof.kt +134 -0
- package/android/src/vendor/kotlin/dev/authon/sdk/wallet/Shamir.kt +106 -0
- package/android/src/vendor/kotlin/dev/authon/sdk/wallet/SignSummary.kt +335 -0
- package/android/src/vendor/kotlin/dev/authon/sdk/wallet/Solana.kt +195 -0
- package/android/src/vendor/kotlin/dev/authon/sdk/wallet/Tron.kt +184 -0
- package/android/src/vendor/kotlin/dev/authon/sdk/wallet/WalletApi.kt +361 -0
- package/android/src/vendor/kotlin/dev/authon/sdk/wallet/WalletCapabilities.kt +91 -0
- package/android/src/vendor/kotlin/dev/authon/sdk/wallet/WalletError.kt +74 -0
- package/android/src/vendor/kotlin/dev/authon/sdk/wallet/WalletOps.kt +551 -0
- package/android/src/vendor/kotlin/dev/authon/sdk/wallet/WalletTypes.kt +78 -0
- package/android/src/vendor/kotlin/dev/authon/sdk/wallet/Xrp.kt +193 -0
- package/build/authonContext.d.ts +19 -0
- package/build/authonContext.d.ts.map +1 -0
- package/build/authonContext.js +20 -0
- package/build/authonContext.js.map +1 -0
- package/build/client.d.ts +92 -0
- package/build/client.d.ts.map +1 -0
- package/build/client.js +431 -0
- package/build/client.js.map +1 -0
- package/build/errors.d.ts +25 -0
- package/build/errors.d.ts.map +1 -0
- package/build/errors.js +15 -0
- package/build/errors.js.map +1 -0
- package/build/index.d.ts +7 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +4 -0
- package/build/index.js.map +1 -0
- package/build/native.d.ts +43 -0
- package/build/native.d.ts.map +1 -0
- package/build/native.js +12 -0
- package/build/native.js.map +1 -0
- package/build/types.d.ts +167 -0
- package/build/types.d.ts.map +1 -0
- package/build/types.js +3 -0
- package/build/types.js.map +1 -0
- package/build/useAuthonWallet.d.ts +25 -0
- package/build/useAuthonWallet.d.ts.map +1 -0
- package/build/useAuthonWallet.js +77 -0
- package/build/useAuthonWallet.js.map +1 -0
- package/expo-module.config.json +9 -0
- package/ios/AuthonReactNativeWallet.podspec +41 -0
- package/ios/AuthonWalletModule.swift +233 -0
- package/ios/vendor/AuthonWallet/AuthonWallet.swift +232 -0
- package/ios/vendor/AuthonWallet/Capabilities.swift +149 -0
- package/ios/vendor/AuthonWallet/Chains/Bitcoin.swift +296 -0
- package/ios/vendor/AuthonWallet/Chains/Solana.swift +207 -0
- package/ios/vendor/AuthonWallet/Chains/Tron.swift +181 -0
- package/ios/vendor/AuthonWallet/Chains/XRP.swift +191 -0
- package/ios/vendor/AuthonWallet/Crypto/BIP39Wordlist.swift +262 -0
- package/ios/vendor/AuthonWallet/Crypto/Chains.swift +278 -0
- package/ios/vendor/AuthonWallet/Crypto/HDKey.swift +108 -0
- package/ios/vendor/AuthonWallet/Crypto/Secp256k1.swift +128 -0
- package/ios/vendor/AuthonWallet/Crypto/Shamir.swift +123 -0
- package/ios/vendor/AuthonWallet/EVM/Address.swift +57 -0
- package/ios/vendor/AuthonWallet/EVM/EvmAccount.swift +48 -0
- package/ios/vendor/AuthonWallet/EVM/Transaction.swift +165 -0
- package/ios/vendor/AuthonWallet/EVM/TypedData.swift +193 -0
- package/ios/vendor/AuthonWallet/Support/BigInteger.swift +220 -0
- package/ios/vendor/AuthonWallet/Support/Bytes.swift +89 -0
- package/ios/vendor/AuthonWallet/Support/JSONValue.swift +341 -0
- package/ios/vendor/AuthonWallet/UI/WalletApprover.swift +250 -0
- package/ios/vendor/AuthonWallet/Wallet/Backup.swift +69 -0
- package/ios/vendor/AuthonWallet/Wallet/DeviceStore.swift +351 -0
- package/ios/vendor/AuthonWallet/Wallet/Passkeys.swift +190 -0
- package/ios/vendor/AuthonWallet/Wallet/Proof.swift +143 -0
- package/ios/vendor/AuthonWallet/Wallet/Shares.swift +116 -0
- package/ios/vendor/AuthonWallet/Wallet/Summary.swift +342 -0
- package/ios/vendor/AuthonWallet/Wallet/WalletAPI.swift +361 -0
- package/ios/vendor/AuthonWallet/Wallet/WalletOps.swift +654 -0
- package/ios/vendor/AuthonWallet/WalletError.swift +84 -0
- package/ios/vendor/CKeccak/LICENSE +22 -0
- package/ios/vendor/CKeccak/README.md +30 -0
- package/ios/vendor/CKeccak/byte_order.h +59 -0
- package/ios/vendor/CKeccak/include/ckeccak.h +13 -0
- package/ios/vendor/CKeccak/memzero.c +75 -0
- package/ios/vendor/CKeccak/memzero.h +8 -0
- package/ios/vendor/CKeccak/options.h +99 -0
- package/ios/vendor/CKeccak/ripemd160.c +343 -0
- package/ios/vendor/CKeccak/ripemd160.h +22 -0
- package/ios/vendor/CKeccak/sha3.c +413 -0
- package/ios/vendor/CKeccak/sha3.h +89 -0
- package/ios/vendor/CMonocypher/LICENCE.md +173 -0
- package/ios/vendor/CMonocypher/README.md +27 -0
- package/ios/vendor/CMonocypher/include/monocypher-ed25519.h +140 -0
- package/ios/vendor/CMonocypher/include/monocypher.h +321 -0
- package/ios/vendor/CMonocypher/monocypher-ed25519.c +500 -0
- package/ios/vendor/CMonocypher/monocypher.c +2956 -0
- package/ios/vendor/CSecp256k1/COPYING +19 -0
- package/ios/vendor/CSecp256k1/README.md +35 -0
- package/ios/vendor/CSecp256k1/build/precomputed_ecmult.c +3 -0
- package/ios/vendor/CSecp256k1/build/precomputed_ecmult_gen.c +3 -0
- package/ios/vendor/CSecp256k1/build/secp256k1.c +3 -0
- package/ios/vendor/CSecp256k1/include/secp256k1.h +899 -0
- package/ios/vendor/CSecp256k1/include/secp256k1_preallocated.h +134 -0
- package/ios/vendor/CSecp256k1/include/secp256k1_recovery.h +113 -0
- package/ios/vendor/CSecp256k1/src/assumptions.h +87 -0
- package/ios/vendor/CSecp256k1/src/checkmem.h +102 -0
- package/ios/vendor/CSecp256k1/src/ecdsa.h +21 -0
- package/ios/vendor/CSecp256k1/src/ecdsa_impl.h +304 -0
- package/ios/vendor/CSecp256k1/src/eckey.h +25 -0
- package/ios/vendor/CSecp256k1/src/eckey_impl.h +92 -0
- package/ios/vendor/CSecp256k1/src/ecmult.h +61 -0
- package/ios/vendor/CSecp256k1/src/ecmult_const.h +38 -0
- package/ios/vendor/CSecp256k1/src/ecmult_const_impl.h +399 -0
- package/ios/vendor/CSecp256k1/src/ecmult_gen.h +143 -0
- package/ios/vendor/CSecp256k1/src/ecmult_gen_impl.h +341 -0
- package/ios/vendor/CSecp256k1/src/ecmult_impl.h +853 -0
- package/ios/vendor/CSecp256k1/src/field.h +350 -0
- package/ios/vendor/CSecp256k1/src/field_10x26.h +57 -0
- package/ios/vendor/CSecp256k1/src/field_10x26_impl.h +1232 -0
- package/ios/vendor/CSecp256k1/src/field_5x52.h +62 -0
- package/ios/vendor/CSecp256k1/src/field_5x52_impl.h +522 -0
- package/ios/vendor/CSecp256k1/src/field_5x52_int128_impl.h +274 -0
- package/ios/vendor/CSecp256k1/src/field_impl.h +457 -0
- package/ios/vendor/CSecp256k1/src/group.h +212 -0
- package/ios/vendor/CSecp256k1/src/group_impl.h +974 -0
- package/ios/vendor/CSecp256k1/src/hash.h +44 -0
- package/ios/vendor/CSecp256k1/src/hash_impl.h +299 -0
- package/ios/vendor/CSecp256k1/src/hsort.h +33 -0
- package/ios/vendor/CSecp256k1/src/hsort_impl.h +125 -0
- package/ios/vendor/CSecp256k1/src/int128.h +90 -0
- package/ios/vendor/CSecp256k1/src/int128_impl.h +18 -0
- package/ios/vendor/CSecp256k1/src/int128_native.h +19 -0
- package/ios/vendor/CSecp256k1/src/int128_native_impl.h +94 -0
- package/ios/vendor/CSecp256k1/src/int128_struct.h +14 -0
- package/ios/vendor/CSecp256k1/src/int128_struct_impl.h +205 -0
- package/ios/vendor/CSecp256k1/src/modinv32.h +43 -0
- package/ios/vendor/CSecp256k1/src/modinv32_impl.h +725 -0
- package/ios/vendor/CSecp256k1/src/modinv64.h +47 -0
- package/ios/vendor/CSecp256k1/src/modinv64_impl.h +780 -0
- package/ios/vendor/CSecp256k1/src/modules/recovery/main_impl.h +159 -0
- package/ios/vendor/CSecp256k1/src/precomputed_ecmult.c +16456 -0
- package/ios/vendor/CSecp256k1/src/precomputed_ecmult.h +38 -0
- package/ios/vendor/CSecp256k1/src/precomputed_ecmult_gen.c +1779 -0
- package/ios/vendor/CSecp256k1/src/precomputed_ecmult_gen.h +26 -0
- package/ios/vendor/CSecp256k1/src/scalar.h +105 -0
- package/ios/vendor/CSecp256k1/src/scalar_4x64.h +19 -0
- package/ios/vendor/CSecp256k1/src/scalar_4x64_impl.h +1000 -0
- package/ios/vendor/CSecp256k1/src/scalar_8x32.h +19 -0
- package/ios/vendor/CSecp256k1/src/scalar_8x32_impl.h +816 -0
- package/ios/vendor/CSecp256k1/src/scalar_impl.h +321 -0
- package/ios/vendor/CSecp256k1/src/scalar_low.h +24 -0
- package/ios/vendor/CSecp256k1/src/scalar_low_impl.h +206 -0
- package/ios/vendor/CSecp256k1/src/scratch.h +44 -0
- package/ios/vendor/CSecp256k1/src/scratch_impl.h +99 -0
- package/ios/vendor/CSecp256k1/src/secp256k1.c +831 -0
- package/ios/vendor/CSecp256k1/src/selftest.h +32 -0
- package/ios/vendor/CSecp256k1/src/util.h +451 -0
- package/ios/vendor/GENERATED.md +4 -0
- package/package.json +66 -3
- package/src/authonContext.ts +30 -0
- package/src/client.ts +459 -0
- package/src/errors.ts +58 -0
- package/src/index.ts +26 -0
- package/src/native.ts +52 -0
- package/src/types.ts +171 -0
- package/src/useAuthonWallet.ts +103 -0
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
// GENERATED by scripts/sync-native-wallet.mjs from kotlin/authon/src/main/kotlin/dev/authon/sdk/wallet/Solana.kt. Do not edit: change the source and run the script.
|
|
2
|
+
package dev.authon.sdk.wallet
|
|
3
|
+
|
|
4
|
+
import org.bouncycastle.crypto.params.Ed25519PrivateKeyParameters
|
|
5
|
+
import org.bouncycastle.crypto.signers.Ed25519Signer
|
|
6
|
+
import java.math.BigInteger
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Solana transaction signing (NATIVE.md section 12, `apps/wallet/src/solana.ts`). The app builds
|
|
10
|
+
* the serialized Message (legacy or v0); the SDK parses it only to describe it and signs the exact
|
|
11
|
+
* bytes with Ed25519 (Bouncy Castle, deterministic).
|
|
12
|
+
*/
|
|
13
|
+
internal object Solana {
|
|
14
|
+
const val MAX_MESSAGE = 1232
|
|
15
|
+
private const val SYSTEM = "11111111111111111111111111111111"
|
|
16
|
+
private const val TOKEN = "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"
|
|
17
|
+
private const val TOKEN_2022 = "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"
|
|
18
|
+
private const val ASSOCIATED_TOKEN = "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"
|
|
19
|
+
private const val COMPUTE_BUDGET = "ComputeBudget111111111111111111111111111111"
|
|
20
|
+
private const val MEMO = "MemoSq4gqABAXKb96qnH8TyNoJ6CDrADxwaj4uuQ"
|
|
21
|
+
|
|
22
|
+
class Instruction(val programId: String, val accounts: List<String?>, val data: ByteArray)
|
|
23
|
+
|
|
24
|
+
class Message(
|
|
25
|
+
val version: Int?,
|
|
26
|
+
val numRequiredSignatures: Int,
|
|
27
|
+
val accountKeys: List<String>,
|
|
28
|
+
val recentBlockhash: String,
|
|
29
|
+
val instructions: List<Instruction>,
|
|
30
|
+
val lookupAccounts: Int,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
private fun invalid(message: String = "not a valid Solana message") = walletError(WalletErrorCode.INVALID_REQUEST, message)
|
|
34
|
+
|
|
35
|
+
private class Reader(private val bytes: ByteArray) {
|
|
36
|
+
private var pos = 0
|
|
37
|
+
val done get() = pos == bytes.size
|
|
38
|
+
|
|
39
|
+
fun u8(): Int {
|
|
40
|
+
if (pos >= bytes.size) throw invalid()
|
|
41
|
+
return bytes[pos++].toInt() and 0xff
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
fun take(n: Int): ByteArray {
|
|
45
|
+
if (n < 0 || pos + n > bytes.size) throw invalid()
|
|
46
|
+
return bytes.copyOfRange(pos, pos + n).also { pos += n }
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** compact-u16 (shortvec). */
|
|
50
|
+
fun len(): Int {
|
|
51
|
+
var value = 0
|
|
52
|
+
for (i in 0 until 3) {
|
|
53
|
+
val b = u8()
|
|
54
|
+
value = value or ((b and 0x7f) shl (7 * i))
|
|
55
|
+
if (b and 0x80 == 0) return value
|
|
56
|
+
}
|
|
57
|
+
throw invalid()
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
fun parse(bytes: ByteArray): Message {
|
|
62
|
+
if (bytes.isEmpty() || bytes.size > MAX_MESSAGE) throw invalid()
|
|
63
|
+
val r = Reader(bytes)
|
|
64
|
+
var version: Int? = null
|
|
65
|
+
var first = r.u8()
|
|
66
|
+
if (first and 0x80 != 0) {
|
|
67
|
+
if (first and 0x7f != 0) throw invalid("unsupported Solana message version")
|
|
68
|
+
version = 0
|
|
69
|
+
first = r.u8()
|
|
70
|
+
}
|
|
71
|
+
val numRequired = first
|
|
72
|
+
r.u8()
|
|
73
|
+
r.u8()
|
|
74
|
+
val keyCount = r.len()
|
|
75
|
+
val keys = List(keyCount) { Base58.encode(r.take(32)) }
|
|
76
|
+
val blockhash = Base58.encode(r.take(32))
|
|
77
|
+
class Raw(val program: Int, val accounts: List<Int>, val data: ByteArray)
|
|
78
|
+
val raw = List(r.len()) {
|
|
79
|
+
val program = r.u8()
|
|
80
|
+
val accounts = List(r.len()) { r.u8() }
|
|
81
|
+
Raw(program, accounts, r.take(r.len()))
|
|
82
|
+
}
|
|
83
|
+
var lookup = 0
|
|
84
|
+
if (version == 0) {
|
|
85
|
+
repeat(r.len()) {
|
|
86
|
+
r.take(32)
|
|
87
|
+
lookup += r.take(r.len()).size
|
|
88
|
+
lookup += r.take(r.len()).size
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
if (!r.done) throw invalid()
|
|
92
|
+
if (numRequired < 1 || numRequired > keyCount) throw invalid()
|
|
93
|
+
val total = keyCount + lookup
|
|
94
|
+
fun key(i: Int): String? {
|
|
95
|
+
if (i >= total) throw invalid()
|
|
96
|
+
return if (i < keyCount) keys[i] else null
|
|
97
|
+
}
|
|
98
|
+
val instructions = raw.map { ix ->
|
|
99
|
+
val program = key(ix.program) ?: throw invalid()
|
|
100
|
+
Instruction(program, ix.accounts.map { key(it) }, ix.data)
|
|
101
|
+
}
|
|
102
|
+
return Message(version, numRequired, keys, blockhash, instructions, lookup)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
private fun u64(d: ByteArray, at: Int): BigInteger {
|
|
106
|
+
if (d.size < at + 8) throw invalid()
|
|
107
|
+
var v = BigInteger.ZERO
|
|
108
|
+
for (i in 7 downTo 0) v = v.shiftLeft(8).or(BigInteger.valueOf((d[at + i].toInt() and 0xff).toLong()))
|
|
109
|
+
return v
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
private fun u32(d: ByteArray, at: Int): Long {
|
|
113
|
+
if (d.size < at + 4) throw invalid()
|
|
114
|
+
var v = 0L
|
|
115
|
+
for (i in 3 downTo 0) v = (v shl 8) or (d[at + i].toLong() and 0xff)
|
|
116
|
+
return v
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
private fun decimal(amount: BigInteger, decimals: Int): String {
|
|
120
|
+
if (decimals == 0) return amount.toString()
|
|
121
|
+
val base = BigInteger.TEN.pow(decimals)
|
|
122
|
+
val (whole, rem) = amount.divideAndRemainder(base)
|
|
123
|
+
val frac = rem.toString().padStart(decimals, '0').trimEnd('0')
|
|
124
|
+
return if (frac.isEmpty()) whole.toString() else "$whole.$frac"
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
private fun addr(a: String?) = a ?: "(address from a lookup table)"
|
|
128
|
+
|
|
129
|
+
/** The approval summary. The signer must be the fee payer (account 0). */
|
|
130
|
+
fun summarize(msg: Message, signer: String, network: String): SignSummary {
|
|
131
|
+
if (msg.accountKeys[0] != signer) throw walletError(WalletErrorCode.ADDRESS_MISMATCH, "the fee payer is not the signing account")
|
|
132
|
+
val rows = mutableListOf(SignRow("Network", network), SignRow("Account", signer, mono = true))
|
|
133
|
+
val warnings = mutableListOf<String>()
|
|
134
|
+
if (msg.numRequiredSignatures > 1) {
|
|
135
|
+
warnings += "This transaction needs ${msg.numRequiredSignatures} signatures; you sign as the fee payer."
|
|
136
|
+
}
|
|
137
|
+
if (msg.lookupAccounts > 0) warnings += "Some accounts come from address lookup tables and cannot be shown."
|
|
138
|
+
for (ix in msg.instructions) {
|
|
139
|
+
val d = ix.data
|
|
140
|
+
val d0 = d.getOrNull(0)?.toInt()?.and(0xff)
|
|
141
|
+
val isToken = ix.programId == TOKEN || ix.programId == TOKEN_2022
|
|
142
|
+
when {
|
|
143
|
+
ix.programId == SYSTEM && d.size >= 12 && u32(d, 0) == 2L -> {
|
|
144
|
+
rows += SignRow("Send", decimal(u64(d, 4), 9) + " SOL")
|
|
145
|
+
rows += SignRow("To", addr(ix.accounts.getOrNull(1)), mono = true)
|
|
146
|
+
if (ix.accounts.getOrNull(0) != signer) warnings += "SOL is sent from an account other than yours."
|
|
147
|
+
}
|
|
148
|
+
isToken && d0 == 12 && d.size >= 10 -> {
|
|
149
|
+
rows += SignRow("Send token", decimal(u64(d, 1), d[9].toInt() and 0xff))
|
|
150
|
+
rows += SignRow("Token", addr(ix.accounts.getOrNull(1)), mono = true)
|
|
151
|
+
rows += SignRow("To token account", addr(ix.accounts.getOrNull(2)), mono = true)
|
|
152
|
+
}
|
|
153
|
+
isToken && d0 == 3 && d.size >= 9 -> {
|
|
154
|
+
rows += SignRow("Send token (raw units)", u64(d, 1).toString())
|
|
155
|
+
rows += SignRow("To token account", addr(ix.accounts.getOrNull(1)), mono = true)
|
|
156
|
+
warnings += "Token amount is shown in raw units (no decimals in this instruction)."
|
|
157
|
+
}
|
|
158
|
+
ix.programId == ASSOCIATED_TOKEN && (d.isEmpty() || d0 == 0 || d0 == 1) ->
|
|
159
|
+
rows += SignRow("Create token account", addr(ix.accounts.getOrNull(1)), mono = true)
|
|
160
|
+
ix.programId == COMPUTE_BUDGET && d0 == 2 && d.size >= 5 -> rows += SignRow("Compute limit", u32(d, 1).toString())
|
|
161
|
+
ix.programId == COMPUTE_BUDGET && d0 == 3 && d.size >= 9 ->
|
|
162
|
+
rows += SignRow("Priority fee", "${u64(d, 1)} micro-lamports per unit")
|
|
163
|
+
ix.programId == MEMO -> rows += SignRow("Memo", String(d, Charsets.UTF_8))
|
|
164
|
+
else -> {
|
|
165
|
+
rows += SignRow("Program call", ix.programId, mono = true)
|
|
166
|
+
warnings += "Calls a program this wallet cannot describe (${ix.programId}). Approve only if you trust the app."
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
rows += SignRow("Recent blockhash", msg.recentBlockhash, mono = true)
|
|
171
|
+
return SignSummary(SignSummary.Kind.TRANSACTION, "Solana transaction", rows, null, warnings, SignRisk.HIGH)
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/** SLIP-0010 key of account [index]. Callers wipe it. */
|
|
175
|
+
fun privateKey(secret: ByteArray, index: Long): ByteArray {
|
|
176
|
+
if (!HdWallet.isAccountIndex(index)) throw walletError(WalletErrorCode.INVALID_REQUEST, "invalid account index")
|
|
177
|
+
if (!HdWallet.isHdSecret(secret)) throw walletError(WalletErrorCode.ACCOUNT_UNAVAILABLE, "this wallet has only EVM account 0")
|
|
178
|
+
val seed = HdWallet.seedOf(secret)
|
|
179
|
+
try {
|
|
180
|
+
return Chains.ed25519Key(seed, intArrayOf(44, 501, index.toInt(), 0))
|
|
181
|
+
} finally {
|
|
182
|
+
Bytes.wipe(seed)
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
fun addressOf(privateKey: ByteArray): String = Base58.encode(Ed25519PrivateKeyParameters(privateKey, 0).generatePublicKey().encoded)
|
|
187
|
+
|
|
188
|
+
/** Ed25519 over the exact message bytes, base58. */
|
|
189
|
+
fun sign(message: ByteArray, privateKey: ByteArray): String {
|
|
190
|
+
val signer = Ed25519Signer()
|
|
191
|
+
signer.init(true, Ed25519PrivateKeyParameters(privateKey, 0))
|
|
192
|
+
signer.update(message, 0, message.size)
|
|
193
|
+
return Base58.encode(signer.generateSignature())
|
|
194
|
+
}
|
|
195
|
+
}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
// GENERATED by scripts/sync-native-wallet.mjs from kotlin/authon/src/main/kotlin/dev/authon/sdk/wallet/Tron.kt. Do not edit: change the source and run the script.
|
|
2
|
+
package dev.authon.sdk.wallet
|
|
3
|
+
|
|
4
|
+
import org.web3j.crypto.ECKeyPair
|
|
5
|
+
import org.web3j.crypto.Sign
|
|
6
|
+
import java.math.BigInteger
|
|
7
|
+
import java.time.Instant
|
|
8
|
+
import java.time.ZoneOffset
|
|
9
|
+
import java.time.format.DateTimeFormatter
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* TRON transaction signing (NATIVE.md section 12, `apps/wallet/src/tron.ts`). The app builds
|
|
13
|
+
* raw_data; the SDK decodes it only to describe it and signs SHA-256(raw_data) like TronWeb
|
|
14
|
+
* (RFC 6979 via web3j, r || s || recovery + 27).
|
|
15
|
+
*/
|
|
16
|
+
internal object Tron {
|
|
17
|
+
const val MAX_RAW_DATA = 4096
|
|
18
|
+
|
|
19
|
+
class Contract(
|
|
20
|
+
val type: Long,
|
|
21
|
+
var owner: String? = null,
|
|
22
|
+
var to: String? = null,
|
|
23
|
+
var amount: BigInteger? = null,
|
|
24
|
+
var contractAddress: String? = null,
|
|
25
|
+
var callValue: BigInteger? = null,
|
|
26
|
+
var data: ByteArray? = null,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
class RawData(
|
|
30
|
+
val expiration: BigInteger?,
|
|
31
|
+
val timestamp: BigInteger?,
|
|
32
|
+
val feeLimit: BigInteger?,
|
|
33
|
+
val memo: ByteArray?,
|
|
34
|
+
val contracts: List<Contract>,
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
private fun invalid() = walletError(WalletErrorCode.INVALID_REQUEST, "not a valid TRON raw_data")
|
|
38
|
+
|
|
39
|
+
private class Field(val no: Int, val wire: Int, val varint: BigInteger? = null, val bytes: ByteArray? = null)
|
|
40
|
+
|
|
41
|
+
/** Minimal protobuf reader: varint (0), 64-bit (1), length-delimited (2), 32-bit (5). */
|
|
42
|
+
private fun fields(buf: ByteArray): List<Field> {
|
|
43
|
+
val out = ArrayList<Field>()
|
|
44
|
+
var pos = 0
|
|
45
|
+
fun varint(): BigInteger {
|
|
46
|
+
var v = BigInteger.ZERO
|
|
47
|
+
var shift = 0
|
|
48
|
+
while (shift < 70) {
|
|
49
|
+
if (pos >= buf.size) throw invalid()
|
|
50
|
+
val b = buf[pos++].toInt() and 0xff
|
|
51
|
+
v = v.or(BigInteger.valueOf((b and 0x7f).toLong()).shiftLeft(shift))
|
|
52
|
+
if (b and 0x80 == 0) return v
|
|
53
|
+
shift += 7
|
|
54
|
+
}
|
|
55
|
+
throw invalid()
|
|
56
|
+
}
|
|
57
|
+
while (pos < buf.size) {
|
|
58
|
+
val key = varint()
|
|
59
|
+
val no = key.shiftRight(3)
|
|
60
|
+
val wire = key.and(BigInteger.valueOf(7)).toInt()
|
|
61
|
+
if (no.signum() == 0 || no.bitLength() > 31) throw invalid()
|
|
62
|
+
when (wire) {
|
|
63
|
+
0 -> out += Field(no.toInt(), wire, varint = varint())
|
|
64
|
+
2 -> {
|
|
65
|
+
val len = varint()
|
|
66
|
+
if (len.bitLength() > 31 || pos + len.toInt() > buf.size) throw invalid()
|
|
67
|
+
out += Field(no.toInt(), wire, bytes = buf.copyOfRange(pos, pos + len.toInt()))
|
|
68
|
+
pos += len.toInt()
|
|
69
|
+
}
|
|
70
|
+
1, 5 -> {
|
|
71
|
+
val len = if (wire == 1) 8 else 4
|
|
72
|
+
if (pos + len > buf.size) throw invalid()
|
|
73
|
+
pos += len
|
|
74
|
+
out += Field(no.toInt(), wire)
|
|
75
|
+
}
|
|
76
|
+
else -> throw invalid()
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return out
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
fun address(raw: ByteArray): String {
|
|
83
|
+
if (raw.size != 21 || raw[0] != 0x41.toByte()) throw invalid()
|
|
84
|
+
return Base58.encodeCheck(raw)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
private fun List<Field>.one(no: Int) = firstOrNull { it.no == no }
|
|
88
|
+
|
|
89
|
+
fun parse(raw: ByteArray): RawData {
|
|
90
|
+
if (raw.isEmpty() || raw.size > MAX_RAW_DATA) throw invalid()
|
|
91
|
+
val top = fields(raw)
|
|
92
|
+
val contracts = top.filter { it.no == 11 && it.bytes != null }.map { f ->
|
|
93
|
+
val c = fields(f.bytes!!)
|
|
94
|
+
val type = c.one(1)?.varint?.toLong() ?: 0L
|
|
95
|
+
val any = c.one(2)?.bytes
|
|
96
|
+
val value = any?.let { fields(it).one(2)?.bytes }
|
|
97
|
+
val contract = Contract(type)
|
|
98
|
+
if (value != null && (type == 1L || type == 31L)) {
|
|
99
|
+
val v = fields(value)
|
|
100
|
+
fun addr(no: Int) = v.one(no)?.bytes?.let { address(it) }
|
|
101
|
+
contract.owner = addr(1)
|
|
102
|
+
if (type == 1L) {
|
|
103
|
+
contract.to = addr(2)
|
|
104
|
+
contract.amount = v.one(3)?.varint ?: BigInteger.ZERO
|
|
105
|
+
} else {
|
|
106
|
+
contract.contractAddress = addr(2)
|
|
107
|
+
contract.callValue = v.one(3)?.varint ?: BigInteger.ZERO
|
|
108
|
+
contract.data = v.one(4)?.bytes ?: ByteArray(0)
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
contract
|
|
112
|
+
}
|
|
113
|
+
if (contracts.isEmpty()) throw invalid()
|
|
114
|
+
return RawData(top.one(8)?.varint, top.one(14)?.varint, top.one(18)?.varint, top.one(10)?.bytes, contracts)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
private val MILLION = BigInteger.valueOf(1_000_000)
|
|
118
|
+
private val MAX_UINT256: BigInteger = BigInteger.ONE.shiftLeft(256).subtract(BigInteger.ONE)
|
|
119
|
+
private val ISO = DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss.SSS'Z'").withZone(ZoneOffset.UTC)
|
|
120
|
+
|
|
121
|
+
private fun trx(sun: BigInteger): String {
|
|
122
|
+
val (whole, rem) = sun.divideAndRemainder(MILLION)
|
|
123
|
+
val frac = rem.toString().padStart(6, '0').trimEnd('0')
|
|
124
|
+
return "$whole${if (frac.isEmpty()) "" else ".$frac"} TRX"
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
private fun word(data: ByteArray, i: Int) = data.copyOfRange(4 + 32 * i, 4 + 32 * (i + 1))
|
|
128
|
+
|
|
129
|
+
private fun wordAddress(data: ByteArray, i: Int) = address(byteArrayOf(0x41) + word(data, i).copyOfRange(12, 32))
|
|
130
|
+
|
|
131
|
+
fun summarize(tx: RawData, signer: String): SignSummary {
|
|
132
|
+
val rows = mutableListOf(SignRow("Network", "TRON"), SignRow("Account", signer, mono = true))
|
|
133
|
+
val warnings = mutableListOf<String>()
|
|
134
|
+
if (tx.contracts.size != 1) warnings += "This transaction has ${tx.contracts.size} contracts."
|
|
135
|
+
for (c in tx.contracts) {
|
|
136
|
+
if (c.owner != null && c.owner != signer) {
|
|
137
|
+
throw walletError(WalletErrorCode.ADDRESS_MISMATCH, "the transaction owner is not the signing account")
|
|
138
|
+
}
|
|
139
|
+
val data = c.data
|
|
140
|
+
if (c.type == 1L && !c.to.isNullOrEmpty()) {
|
|
141
|
+
rows += SignRow("Send", trx(c.amount ?: BigInteger.ZERO))
|
|
142
|
+
rows += SignRow("To", c.to!!, mono = true)
|
|
143
|
+
} else if (c.type == 31L && data != null) {
|
|
144
|
+
val selector = Bytes.toHex(data.copyOf(minOf(4, data.size)))
|
|
145
|
+
rows += SignRow("Contract", c.contractAddress ?: "?", mono = true)
|
|
146
|
+
val callValue = c.callValue
|
|
147
|
+
if (callValue != null && callValue.signum() > 0) rows += SignRow("Send", trx(callValue))
|
|
148
|
+
if (selector == "a9059cbb" && data.size == 68) {
|
|
149
|
+
rows += SignRow("Token transfer to", wordAddress(data, 0), mono = true)
|
|
150
|
+
rows += SignRow("Token amount (raw units)", BigInteger(1, word(data, 1)).toString())
|
|
151
|
+
} else if (selector == "095ea7b3" && data.size == 68) {
|
|
152
|
+
val amount = BigInteger(1, word(data, 1))
|
|
153
|
+
rows += SignRow("Allow spender", wordAddress(data, 0), mono = true)
|
|
154
|
+
rows += SignRow("Allowance (raw units)", if (amount == MAX_UINT256) "Unlimited" else amount.toString())
|
|
155
|
+
warnings += "This lets another address spend your tokens."
|
|
156
|
+
} else {
|
|
157
|
+
rows += SignRow("Call data", "0x" + Bytes.toHex(data), mono = true)
|
|
158
|
+
warnings += "Calls a contract function this wallet cannot describe. Approve only if you trust the app."
|
|
159
|
+
}
|
|
160
|
+
} else {
|
|
161
|
+
rows += SignRow("Contract type", c.type.toString())
|
|
162
|
+
warnings += "TRON contract type ${c.type} cannot be described. Approve only if you trust the app."
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
tx.feeLimit?.let { rows += SignRow("Fee limit", trx(it)) }
|
|
166
|
+
tx.memo?.takeIf { it.isNotEmpty() }?.let { rows += SignRow("Memo", String(it, Charsets.UTF_8)) }
|
|
167
|
+
tx.expiration?.let {
|
|
168
|
+
val text = try {
|
|
169
|
+
ISO.format(Instant.ofEpochMilli(it.longValueExact()))
|
|
170
|
+
} catch (_: Exception) {
|
|
171
|
+
throw walletError(WalletErrorCode.INVALID_REQUEST, "invalid expiration")
|
|
172
|
+
}
|
|
173
|
+
rows += SignRow("Expires", text)
|
|
174
|
+
}
|
|
175
|
+
return SignSummary(SignSummary.Kind.TRANSACTION, "TRON transaction", rows, null, warnings, SignRisk.HIGH)
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/** txID = SHA-256(raw_data); signature r || s || (recovery + 27), lowercase hex like TronWeb. */
|
|
179
|
+
fun sign(raw: ByteArray, privateKey: ByteArray): Map<String, String> {
|
|
180
|
+
val txId = Bytes.sha256(raw)
|
|
181
|
+
val sig = Sign.signMessage(txId, ECKeyPair.create(privateKey), false)
|
|
182
|
+
return mapOf("txID" to Bytes.toHex(txId), "signature" to Bytes.toHex(sig.r + sig.s + sig.v))
|
|
183
|
+
}
|
|
184
|
+
}
|