@authon/react-native-wallet 0.0.0-stage → 0.1.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.
Files changed (159) hide show
  1. package/README.md +221 -2
  2. package/android/build.gradle +40 -0
  3. package/android/consumer-rules.pro +10 -0
  4. package/android/src/main/AndroidManifest.xml +4 -0
  5. package/android/src/main/java/dev/authon/reactnative/wallet/AuthonWalletModule.kt +181 -0
  6. package/android/src/vendor/GENERATED.md +4 -0
  7. package/android/src/vendor/kotlin/dev/authon/sdk/wallet/AuthonWallet.kt +191 -0
  8. package/android/src/vendor/kotlin/dev/authon/sdk/wallet/Bytes.kt +74 -0
  9. package/android/src/vendor/kotlin/dev/authon/sdk/wallet/CredentialManagerPasskeys.kt +192 -0
  10. package/android/src/vendor/kotlin/dev/authon/sdk/wallet/DeviceStore.kt +170 -0
  11. package/android/src/vendor/kotlin/dev/authon/sdk/wallet/DialogWalletApprover.kt +141 -0
  12. package/android/src/vendor/kotlin/dev/authon/sdk/wallet/Eip712.kt +176 -0
  13. package/android/src/vendor/kotlin/dev/authon/sdk/wallet/EvmAccount.kt +45 -0
  14. package/android/src/vendor/kotlin/dev/authon/sdk/wallet/EvmTransaction.kt +180 -0
  15. package/android/src/vendor/kotlin/dev/authon/sdk/wallet/GfTables.kt +44 -0
  16. package/android/src/vendor/kotlin/dev/authon/sdk/wallet/HdWallet.kt +130 -0
  17. package/android/src/vendor/kotlin/dev/authon/sdk/wallet/Http.kt +82 -0
  18. package/android/src/vendor/kotlin/dev/authon/sdk/wallet/JsValues.kt +83 -0
  19. package/android/src/vendor/kotlin/dev/authon/sdk/wallet/KeystoreDeviceStore.kt +232 -0
  20. package/android/src/vendor/kotlin/dev/authon/sdk/wallet/Prf.kt +111 -0
  21. package/android/src/vendor/kotlin/dev/authon/sdk/wallet/Proof.kt +134 -0
  22. package/android/src/vendor/kotlin/dev/authon/sdk/wallet/Shamir.kt +106 -0
  23. package/android/src/vendor/kotlin/dev/authon/sdk/wallet/SignSummary.kt +335 -0
  24. package/android/src/vendor/kotlin/dev/authon/sdk/wallet/WalletApi.kt +353 -0
  25. package/android/src/vendor/kotlin/dev/authon/sdk/wallet/WalletError.kt +58 -0
  26. package/android/src/vendor/kotlin/dev/authon/sdk/wallet/WalletOps.kt +449 -0
  27. package/android/src/vendor/kotlin/dev/authon/sdk/wallet/WalletTypes.kt +71 -0
  28. package/build/authonContext.d.ts +19 -0
  29. package/build/authonContext.d.ts.map +1 -0
  30. package/build/authonContext.js +20 -0
  31. package/build/authonContext.js.map +1 -0
  32. package/build/client.d.ts +76 -0
  33. package/build/client.d.ts.map +1 -0
  34. package/build/client.js +349 -0
  35. package/build/client.js.map +1 -0
  36. package/build/errors.d.ts +18 -0
  37. package/build/errors.d.ts.map +1 -0
  38. package/build/errors.js +9 -0
  39. package/build/errors.js.map +1 -0
  40. package/build/index.d.ts +7 -0
  41. package/build/index.d.ts.map +1 -0
  42. package/build/index.js +4 -0
  43. package/build/index.js.map +1 -0
  44. package/build/native.d.ts +39 -0
  45. package/build/native.d.ts.map +1 -0
  46. package/build/native.js +12 -0
  47. package/build/native.js.map +1 -0
  48. package/build/types.d.ts +121 -0
  49. package/build/types.d.ts.map +1 -0
  50. package/build/types.js +3 -0
  51. package/build/types.js.map +1 -0
  52. package/build/useAuthonWallet.d.ts +25 -0
  53. package/build/useAuthonWallet.d.ts.map +1 -0
  54. package/build/useAuthonWallet.js +77 -0
  55. package/build/useAuthonWallet.js.map +1 -0
  56. package/expo-module.config.json +9 -0
  57. package/ios/AuthonReactNativeWallet.podspec +37 -0
  58. package/ios/AuthonWalletModule.swift +190 -0
  59. package/ios/vendor/AuthonWallet/AuthonWallet.swift +212 -0
  60. package/ios/vendor/AuthonWallet/Crypto/BIP39Wordlist.swift +262 -0
  61. package/ios/vendor/AuthonWallet/Crypto/HDKey.swift +108 -0
  62. package/ios/vendor/AuthonWallet/Crypto/Secp256k1.swift +104 -0
  63. package/ios/vendor/AuthonWallet/Crypto/Shamir.swift +123 -0
  64. package/ios/vendor/AuthonWallet/EVM/Address.swift +57 -0
  65. package/ios/vendor/AuthonWallet/EVM/EvmAccount.swift +48 -0
  66. package/ios/vendor/AuthonWallet/EVM/Transaction.swift +165 -0
  67. package/ios/vendor/AuthonWallet/EVM/TypedData.swift +193 -0
  68. package/ios/vendor/AuthonWallet/Support/BigInteger.swift +220 -0
  69. package/ios/vendor/AuthonWallet/Support/Bytes.swift +89 -0
  70. package/ios/vendor/AuthonWallet/Support/JSONValue.swift +341 -0
  71. package/ios/vendor/AuthonWallet/UI/WalletApprover.swift +250 -0
  72. package/ios/vendor/AuthonWallet/Wallet/Backup.swift +69 -0
  73. package/ios/vendor/AuthonWallet/Wallet/DeviceStore.swift +351 -0
  74. package/ios/vendor/AuthonWallet/Wallet/Passkeys.swift +190 -0
  75. package/ios/vendor/AuthonWallet/Wallet/Proof.swift +143 -0
  76. package/ios/vendor/AuthonWallet/Wallet/Shares.swift +116 -0
  77. package/ios/vendor/AuthonWallet/Wallet/Summary.swift +342 -0
  78. package/ios/vendor/AuthonWallet/Wallet/WalletAPI.swift +350 -0
  79. package/ios/vendor/AuthonWallet/Wallet/WalletOps.swift +555 -0
  80. package/ios/vendor/AuthonWallet/WalletError.swift +54 -0
  81. package/ios/vendor/CKeccak/LICENSE +22 -0
  82. package/ios/vendor/CKeccak/README.md +27 -0
  83. package/ios/vendor/CKeccak/byte_order.h +59 -0
  84. package/ios/vendor/CKeccak/include/ckeccak.h +9 -0
  85. package/ios/vendor/CKeccak/memzero.c +75 -0
  86. package/ios/vendor/CKeccak/memzero.h +8 -0
  87. package/ios/vendor/CKeccak/options.h +99 -0
  88. package/ios/vendor/CKeccak/sha3.c +413 -0
  89. package/ios/vendor/CKeccak/sha3.h +89 -0
  90. package/ios/vendor/CSecp256k1/COPYING +19 -0
  91. package/ios/vendor/CSecp256k1/README.md +35 -0
  92. package/ios/vendor/CSecp256k1/build/precomputed_ecmult.c +3 -0
  93. package/ios/vendor/CSecp256k1/build/precomputed_ecmult_gen.c +3 -0
  94. package/ios/vendor/CSecp256k1/build/secp256k1.c +3 -0
  95. package/ios/vendor/CSecp256k1/include/secp256k1.h +899 -0
  96. package/ios/vendor/CSecp256k1/include/secp256k1_preallocated.h +134 -0
  97. package/ios/vendor/CSecp256k1/include/secp256k1_recovery.h +113 -0
  98. package/ios/vendor/CSecp256k1/src/assumptions.h +87 -0
  99. package/ios/vendor/CSecp256k1/src/checkmem.h +102 -0
  100. package/ios/vendor/CSecp256k1/src/ecdsa.h +21 -0
  101. package/ios/vendor/CSecp256k1/src/ecdsa_impl.h +304 -0
  102. package/ios/vendor/CSecp256k1/src/eckey.h +25 -0
  103. package/ios/vendor/CSecp256k1/src/eckey_impl.h +92 -0
  104. package/ios/vendor/CSecp256k1/src/ecmult.h +61 -0
  105. package/ios/vendor/CSecp256k1/src/ecmult_const.h +38 -0
  106. package/ios/vendor/CSecp256k1/src/ecmult_const_impl.h +399 -0
  107. package/ios/vendor/CSecp256k1/src/ecmult_gen.h +143 -0
  108. package/ios/vendor/CSecp256k1/src/ecmult_gen_impl.h +341 -0
  109. package/ios/vendor/CSecp256k1/src/ecmult_impl.h +853 -0
  110. package/ios/vendor/CSecp256k1/src/field.h +350 -0
  111. package/ios/vendor/CSecp256k1/src/field_10x26.h +57 -0
  112. package/ios/vendor/CSecp256k1/src/field_10x26_impl.h +1232 -0
  113. package/ios/vendor/CSecp256k1/src/field_5x52.h +62 -0
  114. package/ios/vendor/CSecp256k1/src/field_5x52_impl.h +522 -0
  115. package/ios/vendor/CSecp256k1/src/field_5x52_int128_impl.h +274 -0
  116. package/ios/vendor/CSecp256k1/src/field_impl.h +457 -0
  117. package/ios/vendor/CSecp256k1/src/group.h +212 -0
  118. package/ios/vendor/CSecp256k1/src/group_impl.h +974 -0
  119. package/ios/vendor/CSecp256k1/src/hash.h +44 -0
  120. package/ios/vendor/CSecp256k1/src/hash_impl.h +299 -0
  121. package/ios/vendor/CSecp256k1/src/hsort.h +33 -0
  122. package/ios/vendor/CSecp256k1/src/hsort_impl.h +125 -0
  123. package/ios/vendor/CSecp256k1/src/int128.h +90 -0
  124. package/ios/vendor/CSecp256k1/src/int128_impl.h +18 -0
  125. package/ios/vendor/CSecp256k1/src/int128_native.h +19 -0
  126. package/ios/vendor/CSecp256k1/src/int128_native_impl.h +94 -0
  127. package/ios/vendor/CSecp256k1/src/int128_struct.h +14 -0
  128. package/ios/vendor/CSecp256k1/src/int128_struct_impl.h +205 -0
  129. package/ios/vendor/CSecp256k1/src/modinv32.h +43 -0
  130. package/ios/vendor/CSecp256k1/src/modinv32_impl.h +725 -0
  131. package/ios/vendor/CSecp256k1/src/modinv64.h +47 -0
  132. package/ios/vendor/CSecp256k1/src/modinv64_impl.h +780 -0
  133. package/ios/vendor/CSecp256k1/src/modules/recovery/main_impl.h +159 -0
  134. package/ios/vendor/CSecp256k1/src/precomputed_ecmult.c +16456 -0
  135. package/ios/vendor/CSecp256k1/src/precomputed_ecmult.h +38 -0
  136. package/ios/vendor/CSecp256k1/src/precomputed_ecmult_gen.c +1779 -0
  137. package/ios/vendor/CSecp256k1/src/precomputed_ecmult_gen.h +26 -0
  138. package/ios/vendor/CSecp256k1/src/scalar.h +105 -0
  139. package/ios/vendor/CSecp256k1/src/scalar_4x64.h +19 -0
  140. package/ios/vendor/CSecp256k1/src/scalar_4x64_impl.h +1000 -0
  141. package/ios/vendor/CSecp256k1/src/scalar_8x32.h +19 -0
  142. package/ios/vendor/CSecp256k1/src/scalar_8x32_impl.h +816 -0
  143. package/ios/vendor/CSecp256k1/src/scalar_impl.h +321 -0
  144. package/ios/vendor/CSecp256k1/src/scalar_low.h +24 -0
  145. package/ios/vendor/CSecp256k1/src/scalar_low_impl.h +206 -0
  146. package/ios/vendor/CSecp256k1/src/scratch.h +44 -0
  147. package/ios/vendor/CSecp256k1/src/scratch_impl.h +99 -0
  148. package/ios/vendor/CSecp256k1/src/secp256k1.c +831 -0
  149. package/ios/vendor/CSecp256k1/src/selftest.h +32 -0
  150. package/ios/vendor/CSecp256k1/src/util.h +451 -0
  151. package/ios/vendor/GENERATED.md +4 -0
  152. package/package.json +65 -3
  153. package/src/authonContext.ts +30 -0
  154. package/src/client.ts +375 -0
  155. package/src/errors.ts +52 -0
  156. package/src/index.ts +21 -0
  157. package/src/native.ts +50 -0
  158. package/src/types.ts +120 -0
  159. package/src/useAuthonWallet.ts +103 -0
@@ -0,0 +1,57 @@
1
+ // GENERATED by scripts/sync-native-wallet.mjs from swift/Sources/AuthonWallet/EVM/Address.swift. Do not edit: change the source and run the script.
2
+ import Foundation
3
+
4
+ enum EvmAddress {
5
+ /// Address of an uncompressed secp256k1 public key (0x04 || x || y), EIP-55 checksummed.
6
+ static func of(publicKey: [UInt8]) -> String {
7
+ let hash = Keccak.hash256(Array(publicKey.dropFirst()))
8
+ return checksum(Bytes.hex(Array(hash[12..<32])))
9
+ }
10
+
11
+ static func of(privateKey: [UInt8]) throws -> String {
12
+ of(publicKey: try Secp256k1.publicKey(of: privateKey, compressed: false))
13
+ }
14
+
15
+ /// EIP-55 for 40 hex digits (no prefix, any case). Same result as viem getAddress.
16
+ static func checksum(_ hex40: String) -> String {
17
+ let lower = hex40.lowercased()
18
+ let hash = Keccak.hash256(Array(lower.utf8))
19
+ var out = "0x"
20
+ for (i, c) in lower.enumerated() {
21
+ let nibble = (hash[i / 2] >> (i % 2 == 0 ? 4 : 0)) & 0x0f
22
+ out.append(c.isLetter && nibble >= 8 ? Character(c.uppercased()) : c)
23
+ }
24
+ return out
25
+ }
26
+
27
+ /// viem isAddress: 0x + 40 hex digits; with `strict`, mixed case must be a valid checksum.
28
+ static func isAddress(_ value: String, strict: Bool = true) -> Bool {
29
+ guard value.count == 42, value.hasPrefix("0x"), Bytes.isHex(value) else { return false }
30
+ let body = String(value.dropFirst(2))
31
+ if !strict || body == body.lowercased() || body == body.uppercased() { return true }
32
+ return checksum(body) == value
33
+ }
34
+
35
+ static func bytes(_ value: String) -> [UInt8]? {
36
+ guard value.count == 42, value.hasPrefix("0x") else { return nil }
37
+ return Bytes.fromHex(value)
38
+ }
39
+
40
+ /// viem getAddress for display; anything else is shown as given.
41
+ static func display(_ value: JSONValue?) -> String {
42
+ if case .string(let s) = value, isAddress(s, strict: false) { return checksum(String(s.dropFirst(2))) }
43
+ return value.map(jsString) ?? "undefined"
44
+ }
45
+
46
+ /// JavaScript String(value) for the cases the summaries print.
47
+ static func jsString(_ value: JSONValue) -> String {
48
+ switch value {
49
+ case .null: return "null"
50
+ case .bool(let b): return b ? "true" : "false"
51
+ case .number(let n): return n
52
+ case .string(let s): return s
53
+ case .array(let a): return a.map(jsString).joined(separator: ",")
54
+ case .object: return "[object Object]"
55
+ }
56
+ }
57
+ }
@@ -0,0 +1,48 @@
1
+ // GENERATED by scripts/sync-native-wallet.mjs from swift/Sources/AuthonWallet/EVM/EvmAccount.swift. Do not edit: change the source and run the script.
2
+ import Foundation
3
+
4
+ /// A secp256k1 key in memory for the duration of one operation. Call `wipe()` when done.
5
+ final class EvmAccount {
6
+ private var key: [UInt8]
7
+ let address: String
8
+
9
+ init(privateKey: [UInt8]) throws {
10
+ key = privateKey
11
+ address = try EvmAddress.of(privateKey: privateKey)
12
+ }
13
+
14
+ deinit { key.wipe() }
15
+
16
+ func wipe() { key.wipe() }
17
+
18
+ /// r || s || v with v = 27/28 (viem serializeSignature).
19
+ private func signHash(_ hash: [UInt8]) throws -> String {
20
+ let sig = try Secp256k1.sign(hash: hash, key: key)
21
+ return Bytes.hex(sig.r + sig.s + [UInt8(27 + sig.recoveryId)], prefix: true)
22
+ }
23
+
24
+ enum Message {
25
+ case text(String)
26
+ case raw([UInt8])
27
+ }
28
+
29
+ static func hashMessage(_ message: Message) -> [UInt8] {
30
+ let body: [UInt8]
31
+ switch message {
32
+ case .text(let s): body = Array(s.utf8)
33
+ case .raw(let b): body = b
34
+ }
35
+ return Keccak.hash256(Array("\u{19}Ethereum Signed Message:\n\(body.count)".utf8) + body)
36
+ }
37
+
38
+ /// EIP-191 personal_sign.
39
+ func signMessage(_ message: Message) throws -> String { try signHash(EvmAccount.hashMessage(message)) }
40
+
41
+ func signTypedData(_ typedData: TypedData) throws -> String { try signHash(try typedData.digest()) }
42
+
43
+ /// Serialized signed transaction (0x-prefixed), as viem's account.signTransaction.
44
+ func signTransaction(_ tx: EvmTransaction) throws -> String {
45
+ let sig = try Secp256k1.sign(hash: Keccak.hash256(tx.serialized()), key: key)
46
+ return Bytes.hex(tx.serialized(signature: sig), prefix: true)
47
+ }
48
+ }
@@ -0,0 +1,165 @@
1
+ // GENERATED by scripts/sync-native-wallet.mjs from swift/Sources/AuthonWallet/EVM/Transaction.swift. Do not edit: change the source and run the script.
2
+ import Foundation
3
+
4
+ indirect enum RLPItem {
5
+ case bytes([UInt8])
6
+ case list([RLPItem])
7
+
8
+ func encoded() -> [UInt8] {
9
+ switch self {
10
+ case .bytes(let b):
11
+ if b.count == 1, b[0] < 0x80 { return b }
12
+ return RLPItem.header(b.count, offset: 0x80) + b
13
+ case .list(let items):
14
+ let body = items.flatMap { $0.encoded() }
15
+ return RLPItem.header(body.count, offset: 0xc0) + body
16
+ }
17
+ }
18
+
19
+ private static func header(_ length: Int, offset: UInt8) -> [UInt8] {
20
+ if length < 56 { return [offset + UInt8(length)] }
21
+ let len = BigUInt(UInt64(length)).bytes
22
+ return [offset + 55 + UInt8(len.count)] + len
23
+ }
24
+ }
25
+
26
+ /// An EVM transaction as viem serializes it (legacy with EIP-155, EIP-2930, EIP-1559).
27
+ struct EvmTransaction {
28
+ enum Kind: String { case legacy, eip2930, eip1559 }
29
+
30
+ struct AccessEntry {
31
+ var address: [UInt8]
32
+ var storageKeys: [[UInt8]]
33
+ }
34
+
35
+ var kind: Kind
36
+ var chainId: BigUInt
37
+ var nonce: BigUInt
38
+ var gas: BigUInt?
39
+ var gasPrice: BigUInt?
40
+ var maxFeePerGas: BigUInt?
41
+ var maxPriorityFeePerGas: BigUInt?
42
+ var to: [UInt8]?
43
+ var value: BigUInt?
44
+ var data: [UInt8]
45
+ var accessList: [AccessEntry]
46
+
47
+ private static let maxUint256 = BigUInt.power2(256) - BigUInt(1)
48
+
49
+ /// From the parsed request (see WalletOps.parseTransaction): numeric fields are already integers.
50
+ init(_ tx: JSONObject) throws {
51
+ func bad(_ m: String) -> WalletError { WalletError(.invalidRequest, m) }
52
+ func uint(_ name: String) throws -> BigUInt? {
53
+ guard let v = tx[name], v != .null else { return nil }
54
+ guard let n = v.jsBigInt, !n.negative else { throw bad("\(name) must be a non-negative integer") }
55
+ return n.magnitude
56
+ }
57
+ let maxFee = try uint("maxFeePerGas")
58
+ let maxPriority = try uint("maxPriorityFeePerGas")
59
+ let price = try uint("gasPrice")
60
+ let typeName: String
61
+ if let t = tx["type"], t.isTruthy {
62
+ guard case .string(let s) = t else { throw bad("unsupported transaction type") }
63
+ typeName = s
64
+ } else if tx["authorizationList"] != nil || tx["blobs"] != nil || tx["blobVersionedHashes"] != nil || tx["maxFeePerBlobGas"] != nil || tx["sidecars"] != nil {
65
+ throw bad("blob and EIP-7702 transactions are not supported")
66
+ } else if maxFee != nil || maxPriority != nil {
67
+ typeName = "eip1559"
68
+ } else if price != nil {
69
+ typeName = tx["accessList"] != nil ? "eip2930" : "legacy"
70
+ } else {
71
+ throw bad("cannot infer the transaction type")
72
+ }
73
+ if typeName == "eip4844" || typeName == "eip7702" { throw bad("blob and EIP-7702 transactions are not supported") }
74
+ // viem serializes any other type name as a legacy transaction.
75
+ kind = Kind(rawValue: typeName) ?? .legacy
76
+
77
+ guard let chain = try uint("chainId") else { throw bad("chainId required") }
78
+ chainId = chain
79
+ nonce = try uint("nonce") ?? BigUInt()
80
+ gas = try uint("gas")
81
+ gasPrice = price
82
+ maxFeePerGas = maxFee
83
+ maxPriorityFeePerGas = maxPriority
84
+ value = try uint("value")
85
+
86
+ if let t = tx["to"], t.isTruthy {
87
+ guard case .string(let s) = t, EvmAddress.isAddress(s), let b = EvmAddress.bytes(s) else { throw bad("invalid to address") }
88
+ to = b
89
+ } else {
90
+ to = nil
91
+ }
92
+ if let d = tx["data"], d != .null {
93
+ guard case .string(let s) = d, Bytes.isHex(s), let b = Bytes.fromHex(s) else { throw bad("data must be hex") }
94
+ data = b
95
+ } else {
96
+ data = []
97
+ }
98
+ var list = [AccessEntry]()
99
+ if kind != .legacy, let raw = tx["accessList"], raw != .null {
100
+ guard let entries = raw.arrayValue else { throw bad("accessList must be an array") }
101
+ for e in entries {
102
+ guard case .string(let a) = e["address"] ?? .null, EvmAddress.isAddress(a, strict: false), let ab = EvmAddress.bytes(a),
103
+ let keys = e["storageKeys"]?.arrayValue else { throw bad("invalid accessList entry") }
104
+ var sk = [[UInt8]]()
105
+ for k in keys {
106
+ guard case .string(let ks) = k, ks.count == 66, Bytes.isHex(ks), let kb = Bytes.fromHex(ks) else {
107
+ throw bad("storage keys must be 32 bytes")
108
+ }
109
+ sk.append(kb)
110
+ }
111
+ list.append(AccessEntry(address: ab, storageKeys: sk))
112
+ }
113
+ }
114
+ accessList = list
115
+
116
+ switch kind {
117
+ case .eip1559:
118
+ guard !chainId.isZero else { throw bad("invalid chainId") }
119
+ if let f = maxFeePerGas, f > EvmTransaction.maxUint256 { throw bad("maxFeePerGas too high") }
120
+ if let p = maxPriorityFeePerGas, let f = maxFeePerGas, !p.isZero, !f.isZero, p > f { throw bad("maxPriorityFeePerGas above maxFeePerGas") }
121
+ case .eip2930:
122
+ guard !chainId.isZero else { throw bad("invalid chainId") }
123
+ if maxFeePerGas?.isZero == false || maxPriorityFeePerGas?.isZero == false { throw bad("maxFeePerGas is not an EIP-2930 field") }
124
+ if let p = gasPrice, p > EvmTransaction.maxUint256 { throw bad("gasPrice too high") }
125
+ case .legacy:
126
+ guard !chainId.isZero else { throw bad("invalid chainId") }
127
+ if maxFeePerGas?.isZero == false || maxPriorityFeePerGas?.isZero == false { throw bad("maxFeePerGas is not a legacy field") }
128
+ if let p = gasPrice, p > EvmTransaction.maxUint256 { throw bad("gasPrice too high") }
129
+ }
130
+ }
131
+
132
+ private func num(_ v: BigUInt?) -> RLPItem { .bytes(v?.bytes ?? []) }
133
+
134
+ private var accessListItem: RLPItem {
135
+ .list(accessList.map { .list([.bytes($0.address), .list($0.storageKeys.map { .bytes($0) })]) })
136
+ }
137
+
138
+ /// viem serializeTransaction(tx, signature?).
139
+ func serialized(signature: Secp256k1.RecoverableSignature? = nil) -> [UInt8] {
140
+ let trim: ([UInt8]) -> [UInt8] = { BigUInt(bytes: $0).bytes }
141
+ switch kind {
142
+ case .legacy:
143
+ var fields: [RLPItem] = [num(nonce), num(gasPrice), num(gas), .bytes(to ?? []), num(value), .bytes(data)]
144
+ if let sig = signature {
145
+ let v = chainId * BigUInt(2) + BigUInt(UInt64(35 + sig.recoveryId))
146
+ fields += [num(v), .bytes(trim(sig.r)), .bytes(trim(sig.s))]
147
+ } else {
148
+ fields += [num(chainId), .bytes([]), .bytes([])]
149
+ }
150
+ return RLPItem.list(fields).encoded()
151
+ case .eip2930, .eip1559:
152
+ var fields: [RLPItem] = [num(chainId), num(nonce)]
153
+ if kind == .eip1559 {
154
+ fields += [num(maxPriorityFeePerGas), num(maxFeePerGas)]
155
+ } else {
156
+ fields += [num(gasPrice)]
157
+ }
158
+ fields += [num(gas), .bytes(to ?? []), num(value), .bytes(data), accessListItem]
159
+ if let sig = signature {
160
+ fields += [.bytes(sig.recoveryId == 1 ? [1] : []), .bytes(trim(sig.r)), .bytes(trim(sig.s))]
161
+ }
162
+ return [kind == .eip1559 ? 0x02 : 0x01] + RLPItem.list(fields).encoded()
163
+ }
164
+ }
165
+ }
@@ -0,0 +1,193 @@
1
+ // GENERATED by scripts/sync-native-wallet.mjs from swift/Sources/AuthonWallet/EVM/TypedData.swift. Do not edit: change the source and run the script.
2
+ import Foundation
3
+
4
+ /// EIP-712 v4 hashing with viem's rules (hashTypedData + validateTypedData).
5
+ struct TypedData {
6
+ struct Field {
7
+ var name: String
8
+ var type: String
9
+ }
10
+
11
+ var domain: JSONObject
12
+ var types: [String: [Field]]
13
+ var primaryType: String
14
+ var message: JSONValue
15
+
16
+ private static func bad(_ m: String) -> WalletError { WalletError(.invalidRequest, m) }
17
+
18
+ init(_ value: JSONValue) throws {
19
+ guard let obj = value.objectValue else { throw TypedData.bad("typedData must be an object") }
20
+ let domainValue = obj["domain"] ?? .null
21
+ if domainValue == .null {
22
+ domain = JSONObject()
23
+ } else {
24
+ guard let d = domainValue.objectValue else { throw TypedData.bad("domain must be an object") }
25
+ domain = d
26
+ }
27
+ guard case .string(let primary) = obj["primaryType"] ?? .null else { throw TypedData.bad("primaryType must be a string") }
28
+ primaryType = primary
29
+ message = obj["message"] ?? .null
30
+ guard let rawTypes = obj["types"]?.objectValue else { throw TypedData.bad("types must be an object") }
31
+ var parsed = [String: [Field]]()
32
+ parsed["EIP712Domain"] = TypedData.domainFields(domain)
33
+ for (name, list) in rawTypes.entries {
34
+ guard let arr = list.arrayValue else { throw TypedData.bad("types.\(name) must be an array") }
35
+ parsed[name] = try arr.map { f in
36
+ guard case .string(let n) = f["name"] ?? .null, case .string(let t) = f["type"] ?? .null else {
37
+ throw TypedData.bad("invalid field in \(name)")
38
+ }
39
+ return Field(name: n, type: t)
40
+ }
41
+ }
42
+ types = parsed
43
+ }
44
+
45
+ /// getTypesForEIP712Domain: chainId only when it is a number, the others when truthy.
46
+ private static func domainFields(_ d: JSONObject) -> [Field] {
47
+ var out = [Field]()
48
+ if case .string = d["name"] ?? .null { out.append(Field(name: "name", type: "string")) }
49
+ if d["version"]?.isTruthy == true { out.append(Field(name: "version", type: "string")) }
50
+ if case .number = d["chainId"] ?? .null { out.append(Field(name: "chainId", type: "uint256")) }
51
+ if d["verifyingContract"]?.isTruthy == true { out.append(Field(name: "verifyingContract", type: "address")) }
52
+ if d["salt"]?.isTruthy == true { out.append(Field(name: "salt", type: "bytes32")) }
53
+ return out
54
+ }
55
+
56
+ func digest() throws -> [UInt8] {
57
+ try validate()
58
+ var parts: [UInt8] = [0x19, 0x01]
59
+ parts += try hashStruct("EIP712Domain", .object(domain))
60
+ if primaryType != "EIP712Domain" { parts += try hashStruct(primaryType, message) }
61
+ return Keccak.hash256(parts)
62
+ }
63
+
64
+ // MARK: - Validation (validateTypedData)
65
+
66
+ private func validate() throws {
67
+ func validateData(_ fields: [Field], _ data: JSONValue) throws {
68
+ for f in fields {
69
+ let value = data[f.name]
70
+ let base = f.type.replacingOccurrences(of: #"(\[[0-9]*\])+$"#, with: "", options: .regularExpression)
71
+ if base == "int" || base == "uint" { throw TypedData.bad("invalid type \(f.type)") }
72
+ if let (signed, bits) = TypedData.integerType(f.type), let v = value, case .number = v {
73
+ guard let n = v.jsBigInt, n.fits(bits: bits, signed: signed) else { throw TypedData.bad("\(f.name) out of range") }
74
+ }
75
+ if f.type == "address", case .string(let s) = value ?? .null, !EvmAddress.isAddress(s) {
76
+ throw TypedData.bad("invalid address \(s)")
77
+ }
78
+ if let size = TypedData.fixedBytesSize(f.type), size > 0 {
79
+ guard case .string(let s) = value ?? .null, let b = Bytes.fromHex(s), Bytes.isHex(s), b.count == size else {
80
+ throw TypedData.bad("\(f.name) must be \(size) bytes")
81
+ }
82
+ }
83
+ if let nested = types[f.type] {
84
+ guard let v = value else { throw TypedData.bad("missing \(f.name)") }
85
+ try validateData(nested, v)
86
+ }
87
+ }
88
+ }
89
+ if let d = types["EIP712Domain"] { try validateData(d, .object(domain)) }
90
+ if primaryType != "EIP712Domain" {
91
+ guard let fields = types[primaryType] else { throw TypedData.bad("unknown primaryType \(primaryType)") }
92
+ try validateData(fields, message)
93
+ }
94
+ }
95
+
96
+ /// uint8..uint256 / int8..int256 in steps of 8.
97
+ static func integerType(_ type: String) -> (Bool, Int)? {
98
+ let signed = type.hasPrefix("int")
99
+ guard signed || type.hasPrefix("uint") else { return nil }
100
+ let digits = type.dropFirst(signed ? 3 : 4)
101
+ guard let bits = Int(digits), String(bits) == digits, bits >= 8, bits <= 256, bits % 8 == 0 else { return nil }
102
+ return (signed, bits)
103
+ }
104
+
105
+ /// bytes1..bytes32 (0 for dynamic `bytes`, nil otherwise).
106
+ static func fixedBytesSize(_ type: String) -> Int? {
107
+ guard type.hasPrefix("bytes") else { return nil }
108
+ let digits = type.dropFirst(5)
109
+ if digits.isEmpty { return 0 }
110
+ guard let n = Int(digits), String(n) == digits, n >= 1, n <= 32 else { return nil }
111
+ return n
112
+ }
113
+
114
+ // MARK: - Encoding
115
+
116
+ func encodeType(_ primary: String) -> String {
117
+ var deps = Set<String>()
118
+ findDependencies(primary, &deps)
119
+ deps.remove(primary)
120
+ let sorted = deps.sorted { $0.utf16.lexicographicallyPrecedes($1.utf16) }
121
+ return ([primary] + sorted).map { t in
122
+ "\(t)(" + (types[t] ?? []).map { "\($0.type) \($0.name)" }.joined(separator: ",") + ")"
123
+ }.joined()
124
+ }
125
+
126
+ private func findDependencies(_ type: String, _ results: inout Set<String>) {
127
+ let base = String(type.prefix { $0.isLetter || $0.isNumber || $0 == "_" })
128
+ guard !results.contains(base), let fields = types[base] else { return }
129
+ results.insert(base)
130
+ for f in fields { findDependencies(f.type, &results) }
131
+ }
132
+
133
+ func hashStruct(_ type: String, _ data: JSONValue) throws -> [UInt8] {
134
+ Keccak.hash256(try encodeData(type, data))
135
+ }
136
+
137
+ private func encodeData(_ type: String, _ data: JSONValue) throws -> [UInt8] {
138
+ guard let fields = types[type] else { throw TypedData.bad("unknown type \(type)") }
139
+ guard case .object = data else { throw TypedData.bad("\(type) value must be an object") }
140
+ var out = Keccak.hash256(Array(encodeType(type).utf8))
141
+ for f in fields { out += try encodeField(f.type, data[f.name]) }
142
+ return out
143
+ }
144
+
145
+ /// One 32-byte word per field.
146
+ private func encodeField(_ type: String, _ value: JSONValue?) throws -> [UInt8] {
147
+ if types[type] != nil {
148
+ guard let v = value else { throw TypedData.bad("missing \(type) value") }
149
+ return Keccak.hash256(try encodeData(type, v))
150
+ }
151
+ if type == "bytes" {
152
+ guard case .string(let s) = value ?? .null, Bytes.isHex(s), let b = Bytes.fromHex(s) else { throw TypedData.bad("bytes must be hex") }
153
+ return Keccak.hash256(b)
154
+ }
155
+ if type == "string" {
156
+ guard case .string(let s) = value ?? .null else { throw TypedData.bad("string expected") }
157
+ return Keccak.hash256(Array(s.utf8))
158
+ }
159
+ if type.hasSuffix("]"), let open = type.range(of: "[", options: .backwards) {
160
+ let inner = String(type[..<open.lowerBound])
161
+ guard let items = value?.arrayValue else { throw TypedData.bad("array expected for \(type)") }
162
+ var concat = [UInt8]()
163
+ for item in items { concat += try encodeField(inner, item) }
164
+ return Keccak.hash256(concat)
165
+ }
166
+ return try encodeAtomic(type, value)
167
+ }
168
+
169
+ private func encodeAtomic(_ type: String, _ value: JSONValue?) throws -> [UInt8] {
170
+ let v = value ?? .null
171
+ if type == "address" {
172
+ guard case .string(let s) = v, EvmAddress.isAddress(s), let b = EvmAddress.bytes(s) else { throw TypedData.bad("invalid address") }
173
+ return [UInt8](repeating: 0, count: 12) + b
174
+ }
175
+ if type == "bool" {
176
+ guard case .bool(let b) = v else { throw TypedData.bad("bool expected") }
177
+ return [UInt8](repeating: 0, count: 31) + [b ? 1 : 0]
178
+ }
179
+ if let (signed, bits) = TypedData.integerType(type) {
180
+ guard let n = v.jsBigInt, n.fits(bits: bits, signed: signed), let word = n.word() else {
181
+ throw TypedData.bad("\(type) value out of range")
182
+ }
183
+ return word
184
+ }
185
+ if let size = TypedData.fixedBytesSize(type), size > 0 {
186
+ guard case .string(let s) = v, Bytes.isHex(s), let b = Bytes.fromHex(s), b.count == size else {
187
+ throw TypedData.bad("\(type) size mismatch")
188
+ }
189
+ return b + [UInt8](repeating: 0, count: 32 - size)
190
+ }
191
+ throw TypedData.bad("unsupported type \(type)")
192
+ }
193
+ }
@@ -0,0 +1,220 @@
1
+ // GENERATED by scripts/sync-native-wallet.mjs from swift/Sources/AuthonWallet/Support/BigInteger.swift. Do not edit: change the source and run the script.
2
+ import Foundation
3
+
4
+ /// Unsigned arbitrary-precision integer for amounts, gas and EIP-712 integers. Only the
5
+ /// operations the wallet needs (parsing, printing, encoding, add/sub/mul, compare).
6
+ struct BigUInt: Comparable, Hashable, CustomStringConvertible {
7
+ /// Little-endian 32-bit limbs without trailing zero limbs.
8
+ private(set) var limbs: [UInt32]
9
+
10
+ init(_ value: UInt64 = 0) {
11
+ limbs = [UInt32(truncatingIfNeeded: value), UInt32(truncatingIfNeeded: value >> 32)]
12
+ normalize()
13
+ }
14
+
15
+ init(limbs: [UInt32]) {
16
+ self.limbs = limbs
17
+ normalize()
18
+ }
19
+
20
+ /// Big-endian bytes.
21
+ init(bytes: [UInt8]) {
22
+ var out = [UInt32]()
23
+ var i = bytes.count
24
+ while i > 0 {
25
+ var limb: UInt32 = 0
26
+ let start = Swift.max(0, i - 4)
27
+ for b in bytes[start..<i] { limb = limb << 8 | UInt32(b) }
28
+ out.append(limb)
29
+ i = start
30
+ }
31
+ self.init(limbs: out)
32
+ }
33
+
34
+ init?(decimal text: String) {
35
+ guard !text.isEmpty, text.utf8.allSatisfy({ $0 >= 0x30 && $0 <= 0x39 }) else { return nil }
36
+ var v = BigUInt()
37
+ for c in text.utf8 { v = v.multiplied(by: 10).adding(UInt32(c - 0x30)) }
38
+ self = v
39
+ }
40
+
41
+ init?(hexDigits text: String) {
42
+ guard !text.isEmpty, !text.contains("x"), !text.contains("X"), let bytes = Bytes.fromHex(text) else { return nil }
43
+ self.init(bytes: bytes)
44
+ }
45
+
46
+ private mutating func normalize() {
47
+ while let last = limbs.last, last == 0 { limbs.removeLast() }
48
+ }
49
+
50
+ var isZero: Bool { limbs.isEmpty }
51
+
52
+ var bitWidth: Int {
53
+ guard let last = limbs.last else { return 0 }
54
+ return (limbs.count - 1) * 32 + (32 - last.leadingZeroBitCount)
55
+ }
56
+
57
+ /// Minimal big-endian bytes; zero is empty.
58
+ var bytes: [UInt8] {
59
+ var out = [UInt8]()
60
+ for limb in limbs.reversed() {
61
+ out += [UInt8(limb >> 24), UInt8(truncatingIfNeeded: limb >> 16), UInt8(truncatingIfNeeded: limb >> 8), UInt8(truncatingIfNeeded: limb)]
62
+ }
63
+ if let first = out.firstIndex(where: { $0 != 0 }) { return Array(out[first...]) }
64
+ return []
65
+ }
66
+
67
+ /// Big-endian, left-padded to `size` bytes; nil when it does not fit.
68
+ func padded(to size: Int) -> [UInt8]? {
69
+ let b = bytes
70
+ guard b.count <= size else { return nil }
71
+ return [UInt8](repeating: 0, count: size - b.count) + b
72
+ }
73
+
74
+ var uint64: UInt64? {
75
+ guard limbs.count <= 2 else { return nil }
76
+ return limbs.enumerated().reduce(UInt64(0)) { $0 | UInt64($1.element) << (32 * UInt64($1.offset)) }
77
+ }
78
+
79
+ static func power2(_ n: Int) -> BigUInt {
80
+ var l = [UInt32](repeating: 0, count: n / 32 + 1)
81
+ l[n / 32] = 1 << UInt32(n % 32)
82
+ return BigUInt(limbs: l)
83
+ }
84
+
85
+ func adding(_ small: UInt32) -> BigUInt { self + BigUInt(UInt64(small)) }
86
+
87
+ func multiplied(by small: UInt32) -> BigUInt { self * BigUInt(UInt64(small)) }
88
+
89
+ static func + (a: BigUInt, b: BigUInt) -> BigUInt {
90
+ var out = [UInt32]()
91
+ var carry: UInt64 = 0
92
+ for i in 0..<Swift.max(a.limbs.count, b.limbs.count) {
93
+ let s = UInt64(i < a.limbs.count ? a.limbs[i] : 0) + UInt64(i < b.limbs.count ? b.limbs[i] : 0) + carry
94
+ out.append(UInt32(truncatingIfNeeded: s))
95
+ carry = s >> 32
96
+ }
97
+ if carry > 0 { out.append(UInt32(carry)) }
98
+ return BigUInt(limbs: out)
99
+ }
100
+
101
+ /// a - b for a >= b.
102
+ static func - (a: BigUInt, b: BigUInt) -> BigUInt {
103
+ precondition(a >= b)
104
+ var out = [UInt32]()
105
+ var borrow: Int64 = 0
106
+ for i in 0..<a.limbs.count {
107
+ var d = Int64(a.limbs[i]) - Int64(i < b.limbs.count ? b.limbs[i] : 0) - borrow
108
+ borrow = 0
109
+ if d < 0 { d += 1 << 32; borrow = 1 }
110
+ out.append(UInt32(d))
111
+ }
112
+ return BigUInt(limbs: out)
113
+ }
114
+
115
+ static func * (a: BigUInt, b: BigUInt) -> BigUInt {
116
+ if a.isZero || b.isZero { return BigUInt() }
117
+ var out = [UInt32](repeating: 0, count: a.limbs.count + b.limbs.count)
118
+ for i in 0..<a.limbs.count {
119
+ var carry: UInt64 = 0
120
+ for j in 0..<b.limbs.count {
121
+ let t = UInt64(a.limbs[i]) * UInt64(b.limbs[j]) + UInt64(out[i + j]) + carry
122
+ out[i + j] = UInt32(truncatingIfNeeded: t)
123
+ carry = t >> 32
124
+ }
125
+ out[i + b.limbs.count] = UInt32(carry)
126
+ }
127
+ return BigUInt(limbs: out)
128
+ }
129
+
130
+ func quotientAndRemainder(dividingBy d: UInt32) -> (BigUInt, UInt32) {
131
+ var q = [UInt32](repeating: 0, count: limbs.count)
132
+ var rem: UInt64 = 0
133
+ for i in stride(from: limbs.count - 1, through: 0, by: -1) {
134
+ let cur = rem << 32 | UInt64(limbs[i])
135
+ q[i] = UInt32(cur / UInt64(d))
136
+ rem = cur % UInt64(d)
137
+ }
138
+ return (BigUInt(limbs: q), UInt32(rem))
139
+ }
140
+
141
+ var description: String {
142
+ if isZero { return "0" }
143
+ var parts = [String]()
144
+ var v = self
145
+ while !v.isZero {
146
+ let (q, r) = v.quotientAndRemainder(dividingBy: 1_000_000_000)
147
+ parts.append(String(r))
148
+ v = q
149
+ }
150
+ var out = parts.removeLast()
151
+ for p in parts.reversed() { out += String(repeating: "0", count: 9 - p.count) + p }
152
+ return out
153
+ }
154
+
155
+ static func < (a: BigUInt, b: BigUInt) -> Bool {
156
+ if a.limbs.count != b.limbs.count { return a.limbs.count < b.limbs.count }
157
+ for i in stride(from: a.limbs.count - 1, through: 0, by: -1) where a.limbs[i] != b.limbs[i] {
158
+ return a.limbs[i] < b.limbs[i]
159
+ }
160
+ return false
161
+ }
162
+ }
163
+
164
+ /// Signed integer with JavaScript `BigInt()` parsing semantics.
165
+ struct BigInteger: Equatable, CustomStringConvertible {
166
+ var negative: Bool
167
+ var magnitude: BigUInt
168
+
169
+ init(_ magnitude: BigUInt, negative: Bool = false) {
170
+ self.magnitude = magnitude
171
+ self.negative = negative && !magnitude.isZero
172
+ }
173
+
174
+ /// `BigInt(string)`: surrounding whitespace ignored, empty is 0, 0x/0o/0b prefixes, optional sign for decimals.
175
+ init?(jsString raw: String) {
176
+ let s = raw.trimmingCharacters(in: .whitespacesAndNewlines)
177
+ if s.isEmpty { self.init(BigUInt()); return }
178
+ let lower = s.lowercased()
179
+ if lower.hasPrefix("0x") {
180
+ guard let v = BigUInt(hexDigits: String(s.dropFirst(2))) else { return nil }
181
+ self.init(v)
182
+ return
183
+ }
184
+ if lower.hasPrefix("0o") || lower.hasPrefix("0b") {
185
+ let radix: UInt32 = lower.hasPrefix("0o") ? 8 : 2
186
+ let digits = s.dropFirst(2)
187
+ guard !digits.isEmpty else { return nil }
188
+ var v = BigUInt()
189
+ for c in digits.utf8 {
190
+ let d = UInt32(c) &- 0x30
191
+ guard d < radix else { return nil }
192
+ v = v.multiplied(by: radix).adding(d)
193
+ }
194
+ self.init(v)
195
+ return
196
+ }
197
+ var body = Substring(s)
198
+ var neg = false
199
+ if body.hasPrefix("-") { neg = true; body = body.dropFirst() } else if body.hasPrefix("+") { body = body.dropFirst() }
200
+ guard let v = BigUInt(decimal: String(body)) else { return nil }
201
+ self.init(v, negative: neg)
202
+ }
203
+
204
+ var description: String { (negative ? "-" : "") + magnitude.description }
205
+
206
+ /// 32-byte two's complement.
207
+ func word() -> [UInt8]? {
208
+ if !negative { return magnitude.padded(to: 32) }
209
+ let mod = BigUInt.power2(256)
210
+ guard magnitude <= BigUInt.power2(255) else { return nil }
211
+ return (mod - magnitude).padded(to: 32)
212
+ }
213
+
214
+ /// Range check for uintN / intN.
215
+ func fits(bits: Int, signed: Bool) -> Bool {
216
+ if !signed { return !negative && magnitude.bitWidth <= bits }
217
+ if negative { return magnitude <= BigUInt.power2(bits - 1) }
218
+ return magnitude < BigUInt.power2(bits - 1)
219
+ }
220
+ }