@witnet/sdk 1.0.4 → 1.0.6

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 (213) hide show
  1. package/.env_witnet +6 -6
  2. package/LICENSE +21 -21
  3. package/README.md +103 -103
  4. package/dist/package.json +3 -4
  5. package/dist/src/bin/helpers.js +2 -2
  6. package/dist/src/index.js +1 -1
  7. package/dist/src/lib/crypto/account.d.ts +3 -1
  8. package/dist/src/lib/crypto/account.d.ts.map +1 -1
  9. package/dist/src/lib/crypto/account.js +7 -1
  10. package/dist/src/lib/crypto/coinbase.js +1 -1
  11. package/dist/src/lib/crypto/index.js +1 -1
  12. package/dist/src/lib/crypto/interfaces.d.ts +2 -0
  13. package/dist/src/lib/crypto/interfaces.d.ts.map +1 -1
  14. package/dist/src/lib/crypto/interfaces.js +1 -1
  15. package/dist/src/lib/crypto/payloads/DataRequestPayload.js +1 -1
  16. package/dist/src/lib/crypto/payloads/StakePayload.js +1 -1
  17. package/dist/src/lib/crypto/payloads/UnstakePayload.js +1 -1
  18. package/dist/src/lib/crypto/payloads/ValueTransferPayload.js +1 -1
  19. package/dist/src/lib/crypto/payloads.d.ts.map +1 -1
  20. package/dist/src/lib/crypto/payloads.js +12 -1
  21. package/dist/src/lib/crypto/signer.d.ts +1 -0
  22. package/dist/src/lib/crypto/signer.d.ts.map +1 -1
  23. package/dist/src/lib/crypto/signer.js +18 -1
  24. package/dist/src/lib/crypto/transmitters/DataRequests.js +1 -1
  25. package/dist/src/lib/crypto/transmitters/StakeDeposits.js +1 -1
  26. package/dist/src/lib/crypto/transmitters/StakeWithdrawals.js +1 -1
  27. package/dist/src/lib/crypto/transmitters/ValueTransfers.js +1 -1
  28. package/dist/src/lib/crypto/transmitters.d.ts +1 -1
  29. package/dist/src/lib/crypto/transmitters.d.ts.map +1 -1
  30. package/dist/src/lib/crypto/transmitters.js +4 -4
  31. package/dist/src/lib/crypto/types.d.ts.map +1 -1
  32. package/dist/src/lib/crypto/types.js +2 -1
  33. package/dist/src/lib/crypto/utils.js +1 -1
  34. package/dist/src/lib/crypto/wallet.d.ts +3 -1
  35. package/dist/src/lib/crypto/wallet.d.ts.map +1 -1
  36. package/dist/src/lib/crypto/wallet.js +10 -1
  37. package/dist/src/lib/index.js +1 -1
  38. package/dist/src/lib/radon/ccdr/eth.js +1 -1
  39. package/dist/src/lib/radon/ccdr/index.js +1 -1
  40. package/dist/src/lib/radon/ccdr/wit.js +1 -1
  41. package/dist/src/lib/radon/filters.js +1 -1
  42. package/dist/src/lib/radon/index.js +1 -1
  43. package/dist/src/lib/radon/reducers.js +1 -1
  44. package/dist/src/lib/radon/types.js +1 -1
  45. package/dist/src/lib/radon/utils.js +1 -1
  46. package/dist/src/lib/rpc/index.js +1 -1
  47. package/dist/src/lib/rpc/nodes.js +1 -1
  48. package/dist/src/lib/rpc/provider.js +1 -1
  49. package/dist/src/lib/rpc/types.d.ts +6 -4
  50. package/dist/src/lib/rpc/types.d.ts.map +1 -1
  51. package/dist/src/lib/rpc/types.js +1 -1
  52. package/dist/src/lib/types.d.ts +0 -1
  53. package/dist/src/lib/types.d.ts.map +1 -1
  54. package/dist/src/lib/types.js +1 -1
  55. package/dist/src/lib/utils.js +1 -1
  56. package/dist/witnet/assets/index.js +1 -1
  57. package/dist/witnet/assets/modals/index.js +1 -1
  58. package/dist/witnet/assets/modals/web3/eth.js +1 -1
  59. package/dist/witnet/assets/modals/web3/ipfs.js +1 -1
  60. package/dist/witnet/assets/modals/web3/wit.js +1 -1
  61. package/dist/witnet/assets/requests.js +1 -1
  62. package/package.json +3 -4
  63. package/src/bin/cli/history.js +31 -31
  64. package/src/bin/cli/inspect.js +405 -405
  65. package/src/bin/cli/network.js +594 -594
  66. package/src/bin/cli/nodes.js +364 -364
  67. package/src/bin/cli/radon.js +817 -815
  68. package/src/bin/cli/wallet.js +1121 -1117
  69. package/src/bin/helpers.js +840 -840
  70. package/src/bin/postinstall.js +9 -9
  71. package/src/bin/toolkit.js +295 -295
  72. package/witnet/assets/_index.js +8 -8
  73. package/witnet/assets/_requests.js +25 -25
  74. package/witnet/assets/_sources.js +36 -36
  75. package/witnet/assets/_templates.js +36 -36
  76. package/witnet/assets/index.js +4 -4
  77. package/witnet/assets/modals/index.js +9 -9
  78. package/witnet/assets/modals/web3/eth.js +29 -29
  79. package/witnet/assets/modals/web3/ipfs.js +19 -19
  80. package/witnet/assets/modals/web3/wit.js +21 -21
  81. package/witnet/assets/requests.js +95 -95
  82. package/dist/bin/helpers.d.ts +0 -88
  83. package/dist/bin/helpers.d.ts.map +0 -1
  84. package/dist/bin/helpers.js +0 -866
  85. package/dist/index.d.ts +0 -4
  86. package/dist/index.d.ts.map +0 -1
  87. package/dist/index.js +0 -42
  88. package/dist/lib/crypto/account.d.ts +0 -18
  89. package/dist/lib/crypto/account.d.ts.map +0 -1
  90. package/dist/lib/crypto/account.js +0 -152
  91. package/dist/lib/crypto/coinbase.d.ts +0 -9
  92. package/dist/lib/crypto/coinbase.d.ts.map +0 -1
  93. package/dist/lib/crypto/coinbase.js +0 -39
  94. package/dist/lib/crypto/index.d.ts +0 -7
  95. package/dist/lib/crypto/index.d.ts.map +0 -1
  96. package/dist/lib/crypto/index.js +0 -28
  97. package/dist/lib/crypto/interfaces.d.ts +0 -80
  98. package/dist/lib/crypto/interfaces.d.ts.map +0 -1
  99. package/dist/lib/crypto/interfaces.js +0 -3
  100. package/dist/lib/crypto/payloads/DataRequestPayload.d.ts +0 -47
  101. package/dist/lib/crypto/payloads/DataRequestPayload.d.ts.map +0 -1
  102. package/dist/lib/crypto/payloads/DataRequestPayload.js +0 -384
  103. package/dist/lib/crypto/payloads/StakePayload.d.ts +0 -27
  104. package/dist/lib/crypto/payloads/StakePayload.d.ts.map +0 -1
  105. package/dist/lib/crypto/payloads/StakePayload.js +0 -184
  106. package/dist/lib/crypto/payloads/UnstakePayload.d.ts +0 -35
  107. package/dist/lib/crypto/payloads/UnstakePayload.d.ts.map +0 -1
  108. package/dist/lib/crypto/payloads/UnstakePayload.js +0 -244
  109. package/dist/lib/crypto/payloads/ValueTransferPayload.d.ts +0 -24
  110. package/dist/lib/crypto/payloads/ValueTransferPayload.d.ts.map +0 -1
  111. package/dist/lib/crypto/payloads/ValueTransferPayload.js +0 -182
  112. package/dist/lib/crypto/payloads.d.ts +0 -54
  113. package/dist/lib/crypto/payloads.d.ts.map +0 -1
  114. package/dist/lib/crypto/payloads.js +0 -224
  115. package/dist/lib/crypto/signer.d.ts +0 -26
  116. package/dist/lib/crypto/signer.d.ts.map +0 -1
  117. package/dist/lib/crypto/signer.js +0 -299
  118. package/dist/lib/crypto/transmitters/DataRequests.d.ts +0 -14
  119. package/dist/lib/crypto/transmitters/DataRequests.d.ts.map +0 -1
  120. package/dist/lib/crypto/transmitters/DataRequests.js +0 -62
  121. package/dist/lib/crypto/transmitters/StakeDeposits.d.ts +0 -11
  122. package/dist/lib/crypto/transmitters/StakeDeposits.d.ts.map +0 -1
  123. package/dist/lib/crypto/transmitters/StakeDeposits.js +0 -48
  124. package/dist/lib/crypto/transmitters/StakeWithdrawals.d.ts +0 -17
  125. package/dist/lib/crypto/transmitters/StakeWithdrawals.d.ts.map +0 -1
  126. package/dist/lib/crypto/transmitters/StakeWithdrawals.js +0 -115
  127. package/dist/lib/crypto/transmitters/ValueTransfers.d.ts +0 -10
  128. package/dist/lib/crypto/transmitters/ValueTransfers.d.ts.map +0 -1
  129. package/dist/lib/crypto/transmitters/ValueTransfers.js +0 -47
  130. package/dist/lib/crypto/transmitters.d.ts +0 -46
  131. package/dist/lib/crypto/transmitters.d.ts.map +0 -1
  132. package/dist/lib/crypto/transmitters.js +0 -506
  133. package/dist/lib/crypto/types.d.ts +0 -127
  134. package/dist/lib/crypto/types.d.ts.map +0 -1
  135. package/dist/lib/crypto/types.js +0 -261
  136. package/dist/lib/crypto/utils.d.ts +0 -10
  137. package/dist/lib/crypto/utils.d.ts.map +0 -1
  138. package/dist/lib/crypto/utils.js +0 -97
  139. package/dist/lib/crypto/wallet.d.ts +0 -26
  140. package/dist/lib/crypto/wallet.d.ts.map +0 -1
  141. package/dist/lib/crypto/wallet.js +0 -327
  142. package/dist/lib/helpers.d.ts +0 -90
  143. package/dist/lib/helpers.d.ts.map +0 -1
  144. package/dist/lib/helpers.js +0 -1031
  145. package/dist/lib/index.d.ts +0 -5
  146. package/dist/lib/index.d.ts.map +0 -1
  147. package/dist/lib/index.js +0 -21
  148. package/dist/lib/radon/artifacts.d.ts +0 -55
  149. package/dist/lib/radon/artifacts.d.ts.map +0 -1
  150. package/dist/lib/radon/artifacts.js +0 -347
  151. package/dist/lib/radon/ccdr/eth.d.ts +0 -100
  152. package/dist/lib/radon/ccdr/eth.d.ts.map +0 -1
  153. package/dist/lib/radon/ccdr/eth.js +0 -237
  154. package/dist/lib/radon/ccdr/index.d.ts +0 -34
  155. package/dist/lib/radon/ccdr/index.d.ts.map +0 -1
  156. package/dist/lib/radon/ccdr/index.js +0 -63
  157. package/dist/lib/radon/ccdr/wit.d.ts +0 -29
  158. package/dist/lib/radon/ccdr/wit.d.ts.map +0 -1
  159. package/dist/lib/radon/ccdr/wit.js +0 -60
  160. package/dist/lib/radon/filters.d.ts +0 -14
  161. package/dist/lib/radon/filters.d.ts.map +0 -1
  162. package/dist/lib/radon/filters.js +0 -47
  163. package/dist/lib/radon/index.d.ts +0 -36
  164. package/dist/lib/radon/index.d.ts.map +0 -1
  165. package/dist/lib/radon/index.js +0 -154
  166. package/dist/lib/radon/reducers.d.ts +0 -29
  167. package/dist/lib/radon/reducers.d.ts.map +0 -1
  168. package/dist/lib/radon/reducers.js +0 -101
  169. package/dist/lib/radon/retrievals.d.ts +0 -120
  170. package/dist/lib/radon/retrievals.d.ts.map +0 -1
  171. package/dist/lib/radon/retrievals.js +0 -358
  172. package/dist/lib/radon/sources.d.ts +0 -102
  173. package/dist/lib/radon/sources.d.ts.map +0 -1
  174. package/dist/lib/radon/sources.js +0 -294
  175. package/dist/lib/radon/types.d.ts +0 -521
  176. package/dist/lib/radon/types.d.ts.map +0 -1
  177. package/dist/lib/radon/types.js +0 -1066
  178. package/dist/lib/radon/utils.d.ts +0 -55
  179. package/dist/lib/radon/utils.d.ts.map +0 -1
  180. package/dist/lib/radon/utils.js +0 -181
  181. package/dist/lib/rpc/farm.d.ts +0 -66
  182. package/dist/lib/rpc/farm.d.ts.map +0 -1
  183. package/dist/lib/rpc/farm.js +0 -808
  184. package/dist/lib/rpc/index.d.ts +0 -3
  185. package/dist/lib/rpc/index.d.ts.map +0 -1
  186. package/dist/lib/rpc/index.js +0 -19
  187. package/dist/lib/rpc/node.d.ts +0 -38
  188. package/dist/lib/rpc/node.d.ts.map +0 -1
  189. package/dist/lib/rpc/node.js +0 -335
  190. package/dist/lib/rpc/nodes.d.ts +0 -40
  191. package/dist/lib/rpc/nodes.d.ts.map +0 -1
  192. package/dist/lib/rpc/nodes.js +0 -531
  193. package/dist/lib/rpc/provider.d.ts +0 -72
  194. package/dist/lib/rpc/provider.d.ts.map +0 -1
  195. package/dist/lib/rpc/provider.js +0 -402
  196. package/dist/lib/rpc/reporter.d.ts +0 -18
  197. package/dist/lib/rpc/reporter.d.ts.map +0 -1
  198. package/dist/lib/rpc/reporter.js +0 -99
  199. package/dist/lib/rpc/types.d.ts +0 -396
  200. package/dist/lib/rpc/types.d.ts.map +0 -1
  201. package/dist/lib/rpc/types.js +0 -81
  202. package/dist/lib/rpc/wallet.d.ts +0 -72
  203. package/dist/lib/rpc/wallet.d.ts.map +0 -1
  204. package/dist/lib/rpc/wallet.js +0 -41
  205. package/dist/lib/types.d.ts +0 -19
  206. package/dist/lib/types.d.ts.map +0 -1
  207. package/dist/lib/types.js +0 -7
  208. package/dist/lib/utils.d.ts +0 -5
  209. package/dist/lib/utils.d.ts.map +0 -1
  210. package/dist/lib/utils.js +0 -51
  211. package/dist/src/lib/rpc/reporter.d.ts +0 -17
  212. package/dist/src/lib/rpc/reporter.d.ts.map +0 -1
  213. package/dist/src/lib/rpc/reporter.js +0 -27
@@ -1,1117 +1,1121 @@
1
- const qrcodes = require("qrcode-terminal")
2
- const prompt = require("inquirer").createPromptModule()
3
-
4
- const { utils, Witnet } = require("../../../dist/src")
5
-
6
- const helpers = require("../helpers")
7
- const { loadAssets } = require("./radon")
8
-
9
- const { colors, whole_wits } = helpers
10
-
11
- const options = {
12
- await: {
13
- hint: "Await any involved transaction to get eventually mined (default: false).",
14
- },
15
- confirmations: {
16
- hint: "Number of epochs to await after any involved transaction gets mined (implies --await).",
17
- param: "NUMBER",
18
- },
19
- fees: {
20
- hint: "Specific transaction fees (supersedes --priority).",
21
- param: "WITS",
22
- },
23
- force: {
24
- hint: "Broadcast transaction/s without user's final confirmation.",
25
- },
26
- from: {
27
- hint: "Specific wallet's address that will pay for the transaction, other than default.",
28
- param: "WALLET_ADDRESS",
29
- },
30
- priority: {
31
- hint: "Transaction priority: `stinky`, `low`, `medium`, `high`, `opulent`.",
32
- param: "PRIORITY",
33
- },
34
- strategy: {
35
- hint: "UTXOs selection strategy: `big-first`, `random`, `slim-fit`, `small-first` (default: `slim-fit`).",
36
- param: "STRATEGY",
37
- },
38
- }
39
-
40
- /// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
41
- /// CLI SUBMODULE CONSTANTS ===========================================================================================
42
-
43
- module.exports = {
44
- envars: {
45
- WITNET_SDK_PROVIDER_URL: "=> Wit/Oracle RPC provider(s) to connect to, if no otherwise specified.",
46
- WITNET_SDK_WALLET_MASTER_KEY: "=> Wallet's master key in XPRV format, as exported from either a node, Sheikah or myWitWallet.",
47
- },
48
- flags: {
49
- gap: {
50
- hint: "Max indexing gap when searching for wallet accounts (default: 10).",
51
- param: "NUMBER",
52
- },
53
- provider: {
54
- hint: "Public Wit/Oracle JSON-RPC provider, other than default.",
55
- param: "URL",
56
- },
57
- verbose: {
58
- hint: "Outputs detailed information.",
59
- },
60
- },
61
- router: {
62
- accounts: {
63
- hint: "List wallet's HD-accounts treasuring staked, locked or unlocked Wits.",
64
- params: ["[WIT_ADDRESSES ...]"],
65
- options: {
66
- ethereum: {
67
- hint: "Generate account ownership proofs verifiable in Ethereum networks.",
68
- param: "EVM_ADDRESS"
69
- },
70
- limit: {
71
- hint: "Max number of HD-accounts to derive.",
72
- param: "LIMIT",
73
- },
74
- "no-funds": {
75
- hint: "Derive accounts even if they hold no funds.",
76
- },
77
- qrcode: {
78
- hint: "Prints QR codes for all selected accounts.",
79
- },
80
- },
81
- },
82
- // "create*": {
83
- // hint: "Create some random master key.",
84
- // options: {
85
- // vanity: {
86
- // hint: "Vanity prefix of the resulting public key hash address (e.g. `herrer0`)",
87
- // param: "BECH32_PREFIX",
88
- // },
89
- // }
90
- // },
91
- coinbase: {
92
- hint: "List withdrawers delegating stake into the coinbase address.",
93
- options: {
94
- authorize: {
95
- hint: "Generate stake authorization code for the specified withdrawer address.",
96
- param: "WIT_ADDRESS",
97
- },
98
- "node-master-key": {
99
- hint: "Node's master key other than the one set up in environment.",
100
- param: "XPRV",
101
- },
102
- },
103
- },
104
- decipherMasterKey: {
105
- hint: "Decipher some master key as exported from myWitWallet.",
106
- },
107
- delegatees: {
108
- hint: "List validators treasuring delegated stake from any of the wallet's accounts.",
109
- },
110
- notarize: {
111
- hint: "Ask the Wit/Oracle to notarize and forever store the resolution to some Radon asset.",
112
- params: ["RAD_BYTECODE | RAD_HASH | RADON_ASSET", "[RADON_ARGS]"],
113
- options: {
114
- ...options,
115
- fees: {
116
- hint: "Specific unitary reward for every involved validator (supersedes --priority).",
117
- param: "WITS",
118
- },
119
- module: {
120
- hint: "NPM package where to search for Radon assets.",
121
- param: "NPM_PACKAGE",
122
- },
123
- witnesses: {
124
- hint: "Number of witnesses in the Witnet network required to attend the oracle query (default: 3).",
125
- param: "NUMBER",
126
- },
127
- },
128
- },
129
- provider: {
130
- hint: "Show the underlying Wit/Oracle RPC provider being used.",
131
- },
132
- signMessage: {
133
- hint: "Prove ownership of a wallet address by signing some given message.",
134
- param: "TEXT",
135
- options: {
136
- signer: {
137
- hint: "Wallet's signer address other than wallet's default.",
138
- param: "WALLET_ADDRESS",
139
- },
140
- },
141
- },
142
- verifyMessage: {
143
- hint: "Verify authenticity of some given message and signature.",
144
- param: "TEXT",
145
- options: {
146
- publicKey: {
147
- hint: "Hexified public key of the alleged signer of the message.",
148
- param: "<HEX_STRING>",
149
- },
150
- signature: {
151
- hint: "Hexified signature produced with the signer's private key.",
152
- param: "<HEX_STRING>",
153
- },
154
- },
155
- },
156
- stake: {
157
- hint: "Stake specified amount of Wits by using some given authorization code.",
158
- params: "AUTH_CODE",
159
- options: {
160
- ...options,
161
- value: {
162
- hint: "Amount in Wits to stake into the validator that signed the authorization (min: 10 KWits).",
163
- param: "WITS | `all`",
164
- },
165
- withdrawer: {
166
- hint: "Wallet's address with rights to eventually withdraw the staked deposit, plus benefits.",
167
- param: "WALLET_ADDRESS",
168
- },
169
- },
170
- },
171
- transfer: {
172
- hint: "Transfer specified amount of Wits to given address.",
173
- options: {
174
- ...options,
175
- into: {
176
- hint: "Recipient address.",
177
- param: "WIT_ADDRESS",
178
- },
179
- metadata: {
180
- hint: "Optional 20-byte metadata info.",
181
- param: "HEX_STRING"
182
- },
183
- value: {
184
- hint: "Amount in Wits to be transfered (e.g. `0.5` Wits).",
185
- param: "WITS | `all`",
186
- },
187
- },
188
- },
189
- utxos: {
190
- hint: "List currently available UTXOs on wallet's specified address, or on all funded accounts otherwise.",
191
- options: {
192
- ...options,
193
- into: {
194
- hint: "Alternative wallet address where to JOIN or SPLIT the selected UTXOs, other than default.",
195
- param: "WALLET_ADDRESS",
196
- },
197
- join: { hint: "Join selected UTXOs together into a single UTXO (requires --value)." },
198
- limit: {
199
- hint: "Max number of HD-accounts to derive.",
200
- param: "LIMIT",
201
- },
202
- splits: {
203
- hint: "Number of UTXOs to split the target balance into (max: 50; requires --value).",
204
- param: "NUMBER",
205
- },
206
- value: {
207
- hint: "Amount in Wits to be either joined or split apart.",
208
- param: "WITS | `all`",
209
- },
210
- },
211
- },
212
- withdraw: {
213
- hint: "Withdraw specified amount of staked Wits from some given delegatee.",
214
- options: {
215
- await: options.await,
216
- confirmations: options.confirmations,
217
- force: options.force,
218
- from: {
219
- hint: "Validator address from whom to withdraw the specified amount.",
220
- param: "DELEGATEE_PKH",
221
- },
222
- into: {
223
- hint: "Wallet address with rights to withdraw from the delegatee (default: wallet's first account).",
224
- param: "WALLET_ADDRESS",
225
- },
226
- value: {
227
- hint: "Amount in Wits to withdraw (default: `all`).",
228
- param: "WITS | `all`",
229
- },
230
- },
231
- },
232
- },
233
- subcommands: {
234
- accounts, coinbase, delegatees: validators,
235
- notarize: resolve, stake, transfer, withdraw: unstake, utxos,
236
- decipherMasterKey: decipher, provider,
237
- signMessage, verifyMessage,
238
- },
239
- }
240
-
241
- /// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
242
- /// CLI SUBMODULE COMMANDS ============================================================================================
243
-
244
- async function signMessage (options = {}, [...words]) {
245
- const wallet = await _loadWallet({
246
- ...options,
247
- limit: 1,
248
- "no-funds": true,
249
- })
250
- if (!words.length) {
251
- throw Error(`some message must be entered.`)
252
- }
253
- const text = words.join(" ")
254
- let ledger
255
- if (options?.signer) {
256
- ledger = options.signer === wallet.coinbase.pkh ? wallet.coinbase : wallet.getAccount(options.signer)
257
- } else {
258
- ledger = wallet
259
- }
260
- if (!ledger) {
261
- throw Error(`no private key available for signer address ${options?.signer}`)
262
- }
263
- console.info(ledger.getSigner().signMessage(text))
264
- }
265
-
266
- async function verifyMessage (options = {}, [...words]) {
267
- if (!words.length) {
268
- throw Error(`some text must be entered.`)
269
- }
270
- const text = words.join(" ")
271
- const digest = utils.digestMessage(text)
272
- let { publicKey, signature } = options
273
- if (!publicKey) {
274
- throw Error(`--publicKey must be specified.`)
275
- } else if (!signature) {
276
- throw Error(`--signature must be specified`)
277
- }
278
- if (publicKey.startsWith("0x")) publicKey = publicKey.slice(2);
279
- if (signature.startsWith("0x")) signature = signature.slice(2);
280
- console.info(`Message: "${text}"`)
281
- console.info(`Digest: ${utils.toHexString(digest)}`)
282
- console.info(`Public key: ${publicKey}`)
283
- console.info(`Signature: ${signature}`)
284
- console.info("-".repeat(120))
285
- if (utils.ecdsaVerify(digest, publicKey, signature.slice(2))) {
286
- console.info(
287
- `^ Signed by ${
288
- Witnet.PublicKey.fromUint8Array(utils.fromHexString(publicKey)).hash().toBech32("mainnet")
289
- }.\n^ Message is authentic.`
290
- );
291
- } else {
292
- console.error(`^ Invalid signature.`)
293
- }
294
- }
295
-
296
- async function accounts (options = {}, args = []) {
297
- const { ethereum, verbose } = options
298
-
299
- let wallet
300
- if (args.length === 0) {
301
- wallet = await _loadWallet({
302
- ...options,
303
- limit: options?.limit || (options["no-funds"] && 10),
304
- "no-funds": options["no-funds"],
305
- })
306
- } else {
307
- wallet = await _loadWallet({ provider: options?.provider, limit: 1 })
308
- args.forEach(pkh => {
309
- if (!wallet.getSigner(pkh)) {
310
- throw Error(`Input address ${pkh} doesn't belong to the wallet`)
311
- }
312
- })
313
- }
314
-
315
- if (options?.qrcode) {
316
- let addrs = []
317
- if (args.length > 0) {
318
- addrs = wallet.accounts.filter(account => args.includes(account.pkh))
319
- } else {
320
- addrs = wallet.accounts
321
- }
322
- addrs.forEach(account => {
323
- qrcodes.generate(account.pkh)
324
- console.info(`Wallet account #${account.index + 1}: ${colors.lmagenta(account.pkh)}\n`)
325
- })
326
- return
327
- }
328
-
329
- const coinbaseWithdrawers = await wallet.coinbase.getWithdrawers()
330
- const coinbaseBalance = await wallet.coinbase.getBalance()
331
- const coinbaseColor = utils.totalCoins(coinbaseBalance).pedros > 0n ? colors.mred : (coinbaseWithdrawers.length > 0 ? colors.mcyan : colors.cyan)
332
- const coinbase = coinbaseWithdrawers.length > 0 || utils.totalCoins(coinbaseBalance).pedros > 0n || utils.totalCoins(await wallet.getBalance()).pedros === 0n
333
-
334
- const records = []
335
-
336
- if (coinbase) {
337
- const coinbaseUtxos = await wallet.coinbase.getUtxos()
338
- records.push([0, coinbaseColor(wallet.coinbase.pkh), coinbaseUtxos.length, coinbaseBalance])
339
- }
340
- records.push(
341
- ...await Promise.all(
342
- wallet.accounts.map(async account => {
343
- const balance = await account.getBalance()
344
- const utxos = await account.getUtxos()
345
- return [
346
- account.index + 1,
347
- balance.unlocked > 0 ? colors.mmagenta(account.pkh) : colors.magenta(account.pkh),
348
- utxos.length,
349
- balance,
350
- ...(ethereum ? [
351
- account.getSigner().authorizeEvmAddress(ethereum),
352
- ] : [])
353
- ]
354
- }),
355
- )
356
- )
357
-
358
- let unlocked = 0n
359
- helpers.traceTable(
360
- records.map(([index, pkh, count, balance, signature]) => {
361
- unlocked += balance.unlocked
362
- return [
363
- index, pkh,
364
- ...(ethereum ? [ signature ] : (
365
- verbose
366
- ? [
367
- count,
368
- Witnet.Coins.fromNanowits(balance.locked).wits.toFixed(2),
369
- Witnet.Coins.fromNanowits(balance.staked).wits.toFixed(2),
370
- Witnet.Coins.fromNanowits(balance.unlocked).wits.toFixed(2),
371
- utils.totalCoins(balance).wits.toFixed(2),
372
- ]
373
- : [
374
- count,
375
- Witnet.Coins.fromNanowits(balance.unlocked).wits.toFixed(2),
376
- ]
377
- )
378
- ),
379
- ]
380
- }), {
381
- headlines: [
382
- "INDEX", `:WITNET WALLET ${wallet.network.toUpperCase()} ACCOUNTS`,
383
- ...(ethereum ? [`ETHEREUM ${wallet.network.toUpperCase()} WRAPPING AUTHORIZATION CODE [${ethereum}]`] : (
384
- verbose
385
- ? ["# UTXOS", "Locked ($WIT)", "Staked ($WIT)", "Available ($WIT)", "BALANCE ($WIT)"]
386
- : ["# UTXOS", "Available ($WIT)"]
387
- )
388
- )
389
- ],
390
- humanizers: [
391
- helpers.commas,,
392
- ...(ethereum
393
- ? [,]
394
- : [helpers.commas, helpers.commas, helpers.commas, helpers.commas, helpers.commas]
395
- ),
396
- ],
397
- colors: [
398
- ,,
399
- ...(ethereum ? [colors.mblue, colors.gray] : (
400
- verbose
401
- ? [colors.white, colors.gray, colors.yellow, colors.myellow, colors.lyellow]
402
- : [colors.white, colors.myellow]
403
- )
404
- ),
405
- ],
406
- maxColumnWidth: 132,
407
- }
408
- )
409
- console.info(`^ Available balance: ${colors.lyellow(whole_wits(unlocked, 2))}`)
410
- }
411
-
412
- async function coinbase (options = {}) {
413
- const masterWallet = await _loadWallet({ ...options })
414
- let wallet
415
- if (options["node-master-key"]) {
416
- utils.parseXprv(options["node-master-key"])
417
- wallet = await _loadWallet({ provider: options?.provider, limit: 1, xprv: options["node-master-key"] })
418
- } else {
419
- wallet = masterWallet
420
- }
421
-
422
- const coinbaseColor = utils.totalCoins(await wallet.coinbase.getBalance()) > 0 ? colors.mred : colors.lcyan
423
- console.info(`> ${options["node-master-key"] ? "Coinbase " : "Wallet's coinbase"} address: ${coinbaseColor(wallet.coinbase.pkh)}`)
424
-
425
- if (options?.authorize) {
426
- const withdrawer = options.authorize
427
- const authcode = wallet.coinbase.authorizeStake(withdrawer)
428
- qrcodes.generate(authcode)
429
- console.info(`${colors.white(authcode)}`)
430
- console.info("^ Withdrawer address:", colors.mmagenta(withdrawer))
431
- } else {
432
- const records = await wallet.coinbase.getWithdrawers({ by: Witnet.StakesOrderBy.Coins, reverse: true })
433
- if (records.length > 0) {
434
- const { verbose } = options
435
- let staked = 0
436
- helpers.traceTable(
437
- records.map((record, index) => {
438
- staked += record.value.coins
439
- const withdrawer = (record.key.withdrawer === wallet.coinbase.pkh || record.key.withdrawer === masterWallet.coinbase.PublicKeyHash
440
- ? (record.value.epochs.witnessing > record.value.nonce || record.value.epochs.mining > record.value.nonce
441
- ? colors.mred(record.key.withdrawer)
442
- : colors.red(record.key.withdrawer)
443
- )
444
- : (record.value.epochs.witnessing > record.value.nonce || record.value.epochs.mining > record.value.nonce
445
- ? (masterWallet.getSigner(record.key.withdrawer) ? colors.mgreen(record.key.withdrawer) : colors.mmagenta(record.key.withdrawer))
446
- : (masterWallet.getSigner(record.key.withdrawer) ? colors.green(record.key.withdrawer) : colors.magenta(record.key.withdrawer))
447
- )
448
- )
449
- const nonce = (record.value.epochs.witnessing > record.value.nonce || record.value.epochs.mining > record.value.nonce
450
- ? record.value.nonce
451
- : colors.gray(record.value.nonce || "")
452
- )
453
- return [
454
- index + 1,
455
- withdrawer,
456
- nonce,
457
- ...(verbose
458
- ? [record.value.epochs.witnessing || "", record.value.epochs.mining || ""]
459
- : []
460
- ),
461
- colors.yellow(Witnet.Coins.fromNanowits(record.value.coins).wits),
462
- ]
463
- }), {
464
- headlines: [
465
- "RANK",
466
- "STAKE WITHDRAWERS",
467
- ...(verbose
468
- ? ["Nonce", "LW_Epoch", "LM_Epoch"]
469
- : ["Nonce"]
470
- ),
471
- "STAKED ($WIT)",
472
- ],
473
- humanizers: [
474
- ,, ...(verbose
475
- ? [helpers.commas, helpers.commas, helpers.commas]
476
- : [helpers.commas]
477
- ),
478
- helpers.commas,
479
- ],
480
- colors: [
481
- ,,, ...(verbose
482
- ? [colors.magenta, colors.cyan, colors.myellow]
483
- : [colors.myellow]
484
- ),
485
- ],
486
- }
487
- )
488
- console.info(`^ Total stake: ${colors.lyellow(whole_wits(staked, 2))}`)
489
- } else {
490
- console.info("> Holds no delegated stake.")
491
- }
492
- }
493
- }
494
-
495
- async function decipher () {
496
- const user = await prompt([
497
- {
498
- message: "Enter XPRV:",
499
- name: "xprv",
500
- }, {
501
- type: "password",
502
- mask: "*",
503
- message: "Enter password:",
504
- name: "passwd",
505
- },
506
- ])
507
- console.info(utils.decipherXprv(user.xprv, user.passwd))
508
- }
509
-
510
- async function provider (options = {}) {
511
- const wallet = await _loadWallet({ unlocked: true, limit: 1, ...options })
512
- wallet.provider.endpoints.forEach(url => {
513
- console.info(helpers.colors.colors.magenta(url))
514
- })
515
- }
516
-
517
- async function resolve (options = {}, [pattern, ...args]) {
518
- const wallet = await _loadWallet({ ...options })
519
- const ledger = (
520
- options?.from
521
- ? (options.from === wallet.coinbase.pkh ? wallet.coinbase : wallet.getAccount(options.from))
522
- : wallet
523
- )
524
- if (!ledger) {
525
- throw Error("--from address not found in wallet")
526
- }
527
- const request = await _loadRadonRequest({ ...options, pattern, args })
528
- await helpers.traceTransaction(
529
- Witnet.DataRequests.from(ledger, request), {
530
- headline: "DATA REQUEST TRANSACTION",
531
- color: colors.bgreen,
532
- ...await _loadTransactionParams({ ...options }),
533
- }
534
- )
535
- }
536
-
537
- async function stake (options = {}, [authorization]) {
538
- if (!authorization) {
539
- throw Error("No authorization code was provided")
540
- } else if (!options?.value) {
541
- throw Error("No --value was specified")
542
- } else if (!options?.withdrawer) {
543
- throw Error("No --withdrawer was specified")
544
- }
545
-
546
- const wallet = await _loadWallet({ ...options })
547
-
548
- // determine ledger and available funds
549
- let available = 0; let ledger
550
- if (options?.from) {
551
- ledger = options.from === wallet.coinbase.pkh ? wallet.coinbase : wallet.getAccount(options.from)
552
- if (ledger) available = (await ledger.getBalance()).unlocked
553
- } else {
554
- ledger = wallet
555
- available = (await wallet.getBalance()).unlocked + (await wallet.coinbase.getBalance()).unlocked
556
- }
557
- if (!ledger) {
558
- throw Error(`--from address ${options?.from} doesn't belong to the wallet`)
559
- }
560
-
561
- // validate withdrawer address
562
- const withdrawer = Witnet.PublicKeyHash.fromBech32(options?.withdrawer).toBech32(wallet.network)
563
- if (!wallet.getAccount(withdrawer) && withdrawer !== wallet.coinbase.pkh && !options?.force) {
564
- const user = await prompt([{
565
- message: `Withdrawer ${withdrawer} doesn't belong to the wallet. Proceed anyway?`,
566
- name: "continue",
567
- type: "confirm",
568
- default: false,
569
- }])
570
- if (!user.continue) {
571
- throw Error(`--withdrawer address ${withdrawer} not found in wallet`)
572
- }
573
- }
574
-
575
- // determine stake value
576
- const params = await _loadTransactionParams({ ...options })
577
- const coins = params?.value === "all" ? Witnet.Coins.fromPedros(available - params.fees.pedros) : Witnet.Coins.fromWits(params?.value)
578
- if (available < coins.pedros) {
579
- throw Error(`Insufficient funds ${options?.from ? `on address ${options.from}.` : "on wallet."}`)
580
- } else if (params?.fees && coins.pedros <= params.fees.pedros) {
581
- throw Error(`Fees equal or greater than value: ${params.fees.pedros} >= ${coins.pedros}`)
582
- }
583
-
584
- // todo: validate withdrawer matches delegatee's withdrawer
585
-
586
- // deposit stake
587
- await helpers.traceTransaction(
588
- Witnet.StakeDeposits.from(ledger), {
589
- headline: "STAKE DEPOSIT TRANSACTION",
590
- color: colors.bcyan,
591
- ...options,
592
- authorization,
593
- value: coins,
594
- withdrawer,
595
- }
596
- )
597
- }
598
-
599
- async function transfer (options = {}) {
600
- if (!options?.value) {
601
- throw Error("No --value was specified")
602
- }
603
-
604
- const wallet = await _loadWallet({ ...options })
605
-
606
- // determine ledger and available funds
607
- let available = 0n;
608
- let ledger
609
- if (options?.from) {
610
- ledger = options.from === wallet.coinbase.pkh ? wallet.coinbase : wallet.getAccount(options.from)
611
- } else {
612
- ledger = wallet
613
- }
614
- if (!ledger) {
615
- throw Error(`--from address ${options?.from} doesn't belong to the wallet`)
616
- } else {
617
- const balance = await ledger.getBalance()
618
- available = balance.unlocked
619
- }
620
-
621
- // validate metadata info, if specified
622
- let metadata
623
- if (options?.metadata) {
624
- if (!utils.isHexStringOfLength(options.metadata, 20)) {
625
- throw Error(`--metadata must be a 20-byte hex string`)
626
- }
627
- metadata = Witnet.PublicKeyHash.fromHexString(options.metadata).toBech32(wallet.network)
628
- }
629
-
630
- // validate recipient address
631
- if (!options?.into) {
632
- throw Error("--into address must be specified")
633
- }
634
- const into = Witnet.PublicKeyHash.fromBech32(options?.into).toBech32(wallet.network)
635
-
636
- // determine transfer params
637
- const params = await _loadTransactionParams({ ...options })
638
- const coins = params?.value === "all"
639
- ? Witnet.Coins.fromPedros(BigInt(available - params.fees.pedros - BigInt(options?.metadata ? 1: 0)))
640
- : Witnet.Coins.fromWits(params?.value)
641
- if (available < coins.pedros) {
642
- throw Error(`Insufficient funds ${options?.from ? `on address ${options.from}.` : "on wallet."}`)
643
- } else if (params?.fees && coins.pedros <= params.fees.pedros) {
644
- throw Error(`Fees equal or greater than value: ${params.fees.pedros} >= ${coins.pedros}`)
645
- }
646
-
647
- // transfer value
648
- const recipients = options?.metadata
649
- ? [[into, coins], [metadata, Witnet.Coins.fromPedros(1n)]]
650
- : [[into, coins]]
651
- await helpers.traceTransaction(
652
- Witnet.ValueTransfers.from(ledger), {
653
- headline: "VALUE TRANSFER TRANSACTION",
654
- color: colors.bblue,
655
- ...params,
656
- recipients,
657
- }
658
- )
659
- }
660
-
661
- async function unstake (options = {}) {
662
-
663
- // load wallet:
664
- const wallet = await _loadWallet({ ...options })
665
-
666
- // determine validator address:
667
- let validator
668
- if (options?.from) {
669
- validator = Witnet.PublicKeyHash.fromBech32(options?.from).toBech32(wallet.network)
670
-
671
- } else {
672
- const delegatees =
673
- (await wallet.getDelegatees({ by: Witnet.StakesOrderBy.Coins, reverse: true }, false))
674
- .filter(entry => !options?.into || entry.key.withdrawer === options?.into)
675
- if (delegatees.length === 0) throw new Error(`No delegatees to withdraw from.`);
676
- else if (delegatees.length === 1) validator = delegatees[0].key.validator;
677
- else {
678
- const choices = delegatees
679
- .map(entry => `${colors.mcyan(entry.key.validator)}`)
680
- .filter((pkh, index, array) => index === array.indexOf(pkh))
681
- const user = await prompt([{
682
- choices,
683
- message: "Validator address ?",
684
- name: "key",
685
- type: "list",
686
- pageSize: 24,
687
- }]);
688
- validator = helpers.colorstrip(user.key.split(' ')[0])
689
- }
690
- }
691
-
692
- // determine withdrawer address:
693
- let withdrawer
694
- if (options?.into) {
695
- withdrawer = Witnet.PublicKeyHash.fromBech32(options?.into).toBech32(wallet.network)
696
-
697
- } else {
698
- const stakes = (await wallet.provider.stakes({ filter: { validator }})).filter(entry => wallet.getSigner(entry.key.withdrawer) !== undefined);
699
- if (stakes.length === 0) throw new Error(`Nothing to withdraw from validator ${validator}.`);
700
- else if (stakes.length === 1) withdrawer = stakes[0].key.withdrawer;
701
- else {
702
- const choices = stakes.map(entry => `${colors.mmagenta(entry.key.withdrawer)} [${colors.yellow(Witnet.Coins.fromPedros(entry.value.coins).toString(2))}]`);
703
- const user = await prompt([{
704
- choices,
705
- message: "Withdrawer address ?",
706
- name: "key",
707
- type: "list",
708
- pageSize: 24,
709
- }]);
710
- withdrawer = helpers.colorstrip(user.key.split(' ')[0])
711
- }
712
- }
713
-
714
- // validate withdrawer address
715
- const ledger = await wallet.getSigner(withdrawer)
716
- if (!ledger) {
717
- throw Error(`--into address ${withdrawer} doesn't belong to the wallet`)
718
- }
719
-
720
- // valite delegatee address:
721
- const delegatee = (await ledger.getDelegatees()).find(stake =>
722
- stake.key.validator === validator && stake.key.withdrawer === withdrawer
723
- )
724
- if (!delegatee) {
725
- throw Error(`Nothing to withdraw from ${validator} into ${withdrawer}`)
726
- }
727
- const available = BigInt(delegatee.value.coins)
728
-
729
- // determine withdrawal value:
730
- const params = await _loadTransactionParams({ ...options, fees: 0n })
731
-
732
- const value = (params?.value || `all`) === "all"
733
- ? Witnet.Coins.fromPedros(available - params.fees.pedros)
734
- : Witnet.Coins.fromWits(params?.value)
735
-
736
- // validate withdrawal amount:
737
- if (available < value.pedros + params?.fees.pedros) {
738
- throw Error(`Cannot withdraw that much: ${value.pedros} > ${available}`)
739
- } else if (params?.fees && value.pedros <= params.fees.pedros) {
740
- throw Error(`Fees equal or greater than value: ${params.fees.pedros} >= ${value.pedros}`)
741
- }
742
-
743
- // withdraw deposit from validator into withdrawer:
744
- await helpers.traceTransaction(
745
- Witnet.StakeWithdrawals.from(ledger), {
746
- headline: "STAKE WITHDRAWAL TRANSACTION",
747
- ...params,
748
- validator,
749
- value,
750
- }
751
- )
752
- }
753
-
754
- async function utxos (options = {}) {
755
- const wallet = await _loadWallet({ ...options })
756
-
757
- // determine ledger and available funds
758
- let ledger
759
- let available = 0n;
760
- if (options?.from) {
761
- ledger = options.from === wallet.coinbase.pkh ? wallet.coinbase : wallet.getAccount(options.from)
762
- if (ledger) available = (await ledger.getBalance()).unlocked;
763
- } else {
764
- ledger = wallet
765
- available = (await wallet.getBalance()).unlocked + (await wallet.coinbase.getBalance()).unlocked
766
- }
767
- if (!ledger) {
768
- throw Error(`Address ${options?.from} doesn't belong to the wallet`)
769
- }
770
-
771
- // determine into address
772
- let into = options?.into
773
- if (into) {
774
- if (into !== options?.from && !wallet.getAccount(into) && into !== wallet.coinbase.pkh && !options?.force) {
775
- const user = await prompt([{
776
- message: `Recipient address ${into} doesn't belong to the wallet. Proceed anyway?`,
777
- name: "continue",
778
- type: "input",
779
- }])
780
- if (!user.continue.toLowerCase().startsWith("y")) {
781
- throw Error(`--into address ${into} not found in wallet`)
782
- }
783
- }
784
- } else {
785
- into = wallet.pkh
786
- }
787
-
788
- // determine if --value is required
789
- if ((options?.join || options?.split) && !options?.value) {
790
- throw Error("--value must be specified on JOIN and SPLITS operations.")
791
- }
792
-
793
- // extract transaction params
794
- const params = await _loadTransactionParams({
795
- ...options,
796
- force: options?.force || (!options?.join && !options?.splits),
797
- })
798
-
799
- // select utxos of either the from account (if specified) or from all funded accounts in the wallet (including the coinbase)
800
- let value = params?.value === "all"
801
- ? Witnet.Coins.fromPedros(available - params.fees.pedros)
802
- : (params?.value ? Witnet.Coins.fromWits(params?.value) : Witnet.Coins.fromPedros(available))
803
- if (available < value.pedros) {
804
- throw Error(`Insufficient funds ${options?.from ? `on address ${options.from}.` : "on wallet."}`)
805
- } else if (params?.fees && value.pedros <= params.fees.pedros) {
806
- throw Error(`Fees equal or greater than value: ${params.fees.pedros} >= ${value.pedros}`)
807
- }
808
- const utxos = await ledger.selectUtxos({ value })
809
- const covered = utxos.map(utxo => BigInt(utxo.value))?.reduce((prev, curr) => prev + curr, 0n) || 0n
810
- // if (value && covered < value.pedros) {
811
- // console.log(value, covered)
812
- // throw Error(`Insufficient unlocked UTXOs in ${options?.from ? `wallet account ${ledger.pkh}` : "wallet"}`)
813
- // }
814
-
815
- // only if at least one utxo is selected, proceed with report and other operations, if any
816
- if (utxos.length > 0) {
817
- if (options?.verbose || (!options?.join && !options?.splits)) {
818
- helpers.traceTable(
819
- utxos.map((utxo, index) => [
820
- index + 1,
821
- utxo.signer === wallet.coinbase.pkh ? colors.mcyan(utxo.signer) : colors.mmagenta(utxo.signer),
822
- utxo?.internal ? colors.green(utxo.output_pointer) : colors.mgreen(utxo.output_pointer),
823
- utxo.value,
824
- ]), {
825
- headlines: ["INDEX", `WITNET WALLET ${wallet.network.toUpperCase()} ADDRESSES`, ":Unlocked UTXO pointers", "UTXO value ($pedros)"],
826
- humanizers: [helpers.commas,,, helpers.commas],
827
- colors: [,,, colors.myellow],
828
- }
829
- )
830
- console.info(`^ Available balance: ${colors.lyellow(whole_wits(covered, 2))}`)
831
- }
832
-
833
- const valueTransfer = Witnet.ValueTransfers.from(ledger)
834
-
835
- if (options?.join) {
836
- const recipients = [[
837
- // if a split is expected, join utxos into selected account or wallet
838
- options?.splits ? ledger.pkh : into,
839
- value,
840
- ]]
841
- await helpers.traceTransaction(valueTransfer, {
842
- headline: `JOINING UTXOs: ${utxos.length} -> ${value.pedros < covered ? 2 : 1}`,
843
- ...params,
844
- await: params?.await || options?.splits !== undefined,
845
- recipients,
846
- })
847
- }
848
- if (options?.splits) {
849
- const recipients = []
850
- const splits = parseInt(options.splits)
851
- if (splits > 50) {
852
- throw Error("Not possible to split into more than 50 UTXOs")
853
- }
854
- value = Witnet.Coins.fromPedros(BigInt(Math.floor(Number(value.pedros) / splits)))
855
- recipients.push(...Array(splits).fill([into, value]))
856
- await helpers.traceTransaction(
857
- valueTransfer, {
858
- headline: `SPLITTING UTXOs: ${utxos.length} -> ${Number(value.pedros) * splits < covered ? splits + 1 : splits}`,
859
- ...params,
860
- recipients,
861
- reload: options?.join,
862
- })
863
- }
864
- } else {
865
- console.info("> No available UTXOs at the moment.")
866
- }
867
- }
868
-
869
- async function validators (options = {}) {
870
- const { verbose } = options
871
- const wallet = await _loadWallet({ ...options })
872
- const order = { by: Witnet.StakesOrderBy.Coins, reverse: true }
873
-
874
- const coinbaseBalance = await wallet.coinbase.getBalance()
875
- const coinbase = coinbaseBalance.staked > 0n
876
-
877
- const records = await wallet.getDelegatees(order, true)
878
- if (records.length > 0) {
879
- let staked = 0n
880
- helpers.traceTable(
881
- records.map(record => {
882
- staked += BigInt(record.value.coins)
883
- return [
884
- record.key.withdrawer === wallet.coinbase.pkh
885
- ? colors.mred(record.key.withdrawer)
886
- : (record.key.validator !== "" ? colors.mmagenta(record.key.withdrawer) : colors.magenta(record.key.withdrawer)),
887
- ...(record.value.epochs.witnessing > record.value.nonce || record.value.epochs.mining > record.value.nonce
888
- ? [colors.mcyan(record.key.validator), record.value.nonce]
889
- : [colors.cyan(record.key.validator), colors.gray(record.value.nonce || "")]
890
- ),
891
- ...(verbose
892
- ? [record.value.epochs.witnessing || "", record.value.epochs.mining || ""]
893
- : []
894
- ),
895
- colors.yellow(record.value.coins),
896
- ]
897
- }), {
898
- headlines: [
899
- // "INDEX",
900
- coinbase ? "WITNET WALLET COINBASE" : `WITNET WALLET ${wallet.network.toUpperCase()} ACCOUNTS`,
901
- "STAKE DELEGATEES",
902
- ...(verbose
903
- ? ["Nonce", "LW_Epoch", "LM_Epoch"]
904
- : ["Nonce"]
905
- ),
906
- "STAKED ($pedros)",
907
- ],
908
- humanizers: [
909
- ,, ...(verbose
910
- ? [helpers.commas, helpers.commas, helpers.commas]
911
- : [helpers.commas]
912
- ),
913
- helpers.commas,
914
- ],
915
- colors: [
916
- ,,, ...(verbose
917
- ? [colors.magenta, colors.cyan, colors.myellow]
918
- : [colors.myellow]
919
- ),
920
- ],
921
- }
922
- )
923
- console.info(`^ Total deposits: ${colors.lyellow(whole_wits(staked, 2))}`)
924
- } else {
925
- console.info("> No delegatees found.")
926
- }
927
- }
928
-
929
- /// ===================================================================================================================
930
- /// --- Internal functions --------------------------------------------------------------------------------------------
931
-
932
- async function _loadRadonRequest (options = {}) {
933
- const args = options?.args || []
934
- if (options?.pattern && typeof options.pattern === 'string' && utils.isHexString(options.pattern)) {
935
- // if (options?.pattern && typeof options.pattern === 'string' && utils.isHexString(options.pattern)) {
936
- // if (utils.isHexStringOfLength(options.pattern, 32)) {
937
- // throw `Searching RADON_BYTECODE by RAD_HASH not yet supported.`
938
- try {
939
- return Witnet.Radon.RadonRequest.fromBytecode(options.pattern)
940
- } catch (e) {
941
- throw Error(`Invalid RADON_BYTECODE: ${e}`)
942
- }
943
- }
944
-
945
- // load Radon assets from environment
946
- let assets = utils.searchRadonAssets(
947
- {
948
- assets: loadAssets(options),
949
- pattern: options?.pattern,
950
- },
951
- (key, pattern) => key.toLowerCase().indexOf(pattern.toLowerCase()) >= 0
952
- )
953
-
954
- if (args.length > 0) {
955
- // ignore RadonRequests if args were passed from the CLI
956
- assets = assets.filter(([, artifact]) => !(artifact instanceof Witnet.Radon.RadonRequest))
957
- }
958
-
959
- // sort Radon assets alphabetically
960
- assets = assets.sort((a, b) => {
961
- if (a[0] < b[0]) return -1
962
- else if (a[0] > b[0]) return 1
963
- else return 0
964
- })
965
-
966
- let artifact, key
967
- if (Object.keys(assets).length === 0) {
968
- if (options?.pattern) {
969
- throw Error(`No Radon assets named after "${options.pattern}"`)
970
- } else {
971
- throw Error("No Radon assets declared yet")
972
- }
973
- } else if (Object.keys(assets).length > 1) {
974
- const user = await prompt([{
975
- choices: assets.map(([key]) => key),
976
- message: "Please, select a Radon asset:",
977
- name: "key",
978
- type: "list",
979
- pageSize: 24,
980
- }]);
981
- [key, artifact] = assets.find(([key]) => key === user.key)
982
- } else {
983
- [key, artifact] = Object.values(assets)[0]
984
- }
985
-
986
- if (!(artifact instanceof Witnet.Radon.RadonRequest)) {
987
- let templateArgs = []
988
- if (args.length === 0 && artifact?.samples) {
989
- const sample = await prompt([{
990
- choices: Object.keys(artifact.samples),
991
- message: "Select pre-settled Radon args: ",
992
- name: "key",
993
- type: "list",
994
- }])
995
- templateArgs = artifact.samples[sample.key]
996
- } else if (args.length === 1 && artifact?.samples) {
997
- const sample = Object.keys(artifact.samples).find(sample => sample.toLowerCase() === args[0].toLowerCase())
998
- if (sample) templateArgs = artifact.samples[sample]
999
- }
1000
-
1001
- if (artifact instanceof Witnet.Radon.RadonRetrieval) {
1002
- if (templateArgs.length === 0) templateArgs = [...args]
1003
- if (templateArgs.length < artifact.argsCount) {
1004
- throw Error(`${key}: missing ${artifact.argsCount - templateArgs.length} out of ${artifact.argsCount} parameters`)
1005
- }
1006
- artifact = new Witnet.Radon.RadonRequest({ sources: artifact.foldArgs(templateArgs) })
1007
- } else {
1008
- if (artifact instanceof Witnet.Radon.RadonModal) {
1009
- if (templateArgs.length === 0) templateArgs = [...args]
1010
- if (templateArgs.length === 0 && templateArgs.length < artifact.argsCount + 1) {
1011
- throw Error(`${key}: missing ${artifact.argsCount + 1 - templateArgs.length} out of ${artifact.argsCount + 1} parameters.`)
1012
- }
1013
- artifact.providers = templateArgs.splice(0, 1)[0].split(";")
1014
- artifact = artifact.buildRadonRequest(templateArgs)
1015
- } else if (artifact instanceof Witnet.Radon.RadonTemplate) {
1016
- if (templateArgs.length === 0) {
1017
- templateArgs = new Array(artifact.sources.length)
1018
- artifact.sources.forEach((retrieval, index) => {
1019
- templateArgs[index] = args.splice(0, retrieval.argsCount)
1020
- if (templateArgs[index].length < retrieval.argsCount) {
1021
- throw Error(`${key}: missing ${
1022
- retrieval.argsCount - templateArgs[index].length
1023
- } out of ${
1024
- retrieval.argsCount
1025
- } expected args for template source #${index + 1}`)
1026
- }
1027
- })
1028
- }
1029
- artifact = artifact.buildRadonRequest(templateArgs)
1030
- } else {
1031
- throw Error(`${key}: unsupported Radon asset type ${artifact?.constructor.name}`)
1032
- }
1033
- }
1034
- }
1035
- return artifact
1036
- }
1037
-
1038
- async function _loadTransactionParams (options = {}) {
1039
- const confirmations = options?.confirmations ? parseInt(options?.confirmations) : (options?.await ? 0 : undefined)
1040
- let fees = options?.fees ? Witnet.Coins.fromWits(options.fees) : (options?.fees === 0n ? Witnet.Coins.zero() : undefined)
1041
- const value = options?.value ? (options?.value.toLowerCase() === "all" ? "all" : options.value) : undefined
1042
- if (fees === undefined) {
1043
- if (value === "all") {
1044
- throw Error("--fees must be specified if --value is set to `all`")
1045
- }
1046
- let priority = (!options?.priority && options?.force) ? Witnet.TransactionPriority.Medium : options?.priority
1047
- if (!priority) {
1048
- const priorities = {
1049
- "< 60 seconds": Witnet.TransactionPriority.Opulent,
1050
- "< 5 minutes": Witnet.TransactionPriority.High,
1051
- "< 15 minutes": Witnet.TransactionPriority.Medium,
1052
- "< 1 hour": Witnet.TransactionPriority.Low,
1053
- "< 6 hours": Witnet.TransactionPriority.Stinky,
1054
- }
1055
- const user = await prompt([{
1056
- choices: Object.keys(priorities),
1057
- message: "Please, select time to block expectancy:",
1058
- name: "priority",
1059
- type: "list",
1060
- }])
1061
- priority = priorities[user.priority]
1062
- } else if (!Object.values(Witnet.TransactionPriority).includes(priority)) {
1063
- throw Error(`Invalid priority "${priority}"`)
1064
- }
1065
- fees = Witnet.TransactionPriority[priority.charAt(0).toUpperCase() + priority.slice(1)]
1066
- }
1067
- return {
1068
- await: options?.await,
1069
- confirmations,
1070
- fees,
1071
- from: options?.from,
1072
- force: options?.force,
1073
- value,
1074
- verbose: options?.verbose,
1075
- witnesses: options?.witnesses,
1076
- }
1077
- }
1078
-
1079
- async function _loadWallet (options = {}) {
1080
- if (!process.env.WITNET_SDK_WALLET_MASTER_KEY) {
1081
- throw Error("No WITNET_SDK_WALLET_MASTER_KEY is settled in environment")
1082
- } else {
1083
- const provider = new Witnet.JsonRpcProvider(options?.provider)
1084
- const strategies = {
1085
- "small-first": Witnet.UtxoSelectionStrategy.SmallFirst,
1086
- "slim-fit": Witnet.UtxoSelectionStrategy.SlimFit,
1087
- "big-first": Witnet.UtxoSelectionStrategy.BigFirst,
1088
- random: Witnet.UtxoSelectionStrategy.Random,
1089
- }
1090
- if (options?.strategy && !strategies[options.strategy]) {
1091
- throw Error(`Unrecognised UTXO selection strategy "${options.strategy}"`)
1092
- }
1093
- const strategy = strategies[options?.strategy || "slim-fit"] || Witnet.UtxoSelectionStrategy.SlimFit
1094
- const gap = options?.gap || 10
1095
- let wallet; const xprv = options?.xprv || process.env.WITNET_SDK_WALLET_MASTER_KEY
1096
- if (xprv.length === 293) {
1097
- const user = await prompt([{ type: "password", mask: "*", message: "Enter password:", name: "passwd" }])
1098
- wallet = Witnet.Wallet.fromEncryptedXprv(xprv, user.passwd, {
1099
- gap,
1100
- provider,
1101
- strategy,
1102
- limit: options?.limit,
1103
- onlyWithFunds: !options["no-funds"],
1104
- })
1105
- } else {
1106
- wallet = Witnet.Wallet.fromXprv(xprv, {
1107
- gap,
1108
- provider,
1109
- strategy,
1110
- limit: options?.limit,
1111
- onlyWithFunds: !options["no-funds"],
1112
- })
1113
- }
1114
-
1115
- return options["no-funds"] ? await wallet : await helpers.prompter(wallet)
1116
- }
1117
- }
1
+ const qrcodes = require("qrcode-terminal")
2
+ const prompt = require("inquirer").createPromptModule()
3
+
4
+ const { utils, Witnet } = require("../../../dist/src")
5
+
6
+ const helpers = require("../helpers")
7
+ const { loadAssets } = require("./radon")
8
+
9
+ const { colors, whole_wits } = helpers
10
+
11
+ const options = {
12
+ await: {
13
+ hint: "Await any involved transaction to get eventually mined (default: false).",
14
+ },
15
+ confirmations: {
16
+ hint: "Number of epochs to await after any involved transaction gets mined (implies --await).",
17
+ param: "NUMBER",
18
+ },
19
+ fees: {
20
+ hint: "Specific transaction fees (supersedes --priority).",
21
+ param: "WITS",
22
+ },
23
+ force: {
24
+ hint: "Broadcast transaction/s without user's final confirmation.",
25
+ },
26
+ from: {
27
+ hint: "Specific wallet's address that will pay for the transaction, other than default.",
28
+ param: "WALLET_ADDRESS",
29
+ },
30
+ priority: {
31
+ hint: "Transaction priority: `stinky`, `low`, `medium`, `high`, `opulent`.",
32
+ param: "PRIORITY",
33
+ },
34
+ strategy: {
35
+ hint: "UTXOs selection strategy: `big-first`, `random`, `slim-fit`, `small-first` (default: `slim-fit`).",
36
+ param: "STRATEGY",
37
+ },
38
+ }
39
+
40
+ /// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
41
+ /// CLI SUBMODULE CONSTANTS ===========================================================================================
42
+
43
+ module.exports = {
44
+ envars: {
45
+ WITNET_SDK_PROVIDER_URL: "=> Wit/Oracle RPC provider(s) to connect to, if no otherwise specified.",
46
+ WITNET_SDK_WALLET_MASTER_KEY: "=> Wallet's master key in XPRV format, as exported from either a node, Sheikah or myWitWallet.",
47
+ },
48
+ flags: {
49
+ gap: {
50
+ hint: "Max indexing gap when searching for wallet accounts (default: 10).",
51
+ param: "NUMBER",
52
+ },
53
+ provider: {
54
+ hint: "Public Wit/Oracle JSON-RPC provider, other than default.",
55
+ param: "URL",
56
+ },
57
+ verbose: {
58
+ hint: "Outputs detailed information.",
59
+ },
60
+ },
61
+ router: {
62
+ accounts: {
63
+ hint: "List wallet's HD-accounts treasuring staked, locked or unlocked Wits.",
64
+ params: ["[WIT_ADDRESSES ...]"],
65
+ options: {
66
+ ethereum: {
67
+ hint: "Generate account ownership proofs verifiable in Ethereum networks.",
68
+ param: "EVM_ADDRESS"
69
+ },
70
+ limit: {
71
+ hint: "Max number of HD-accounts to derive.",
72
+ param: "LIMIT",
73
+ },
74
+ "no-funds": {
75
+ hint: "Derive accounts even if they hold no funds.",
76
+ },
77
+ qrcode: {
78
+ hint: "Prints QR codes for all selected accounts.",
79
+ },
80
+ },
81
+ },
82
+ // "create*": {
83
+ // hint: "Create some random master key.",
84
+ // options: {
85
+ // vanity: {
86
+ // hint: "Vanity prefix of the resulting public key hash address (e.g. `herrer0`)",
87
+ // param: "BECH32_PREFIX",
88
+ // },
89
+ // }
90
+ // },
91
+ coinbase: {
92
+ hint: "List withdrawers delegating stake into the coinbase address.",
93
+ options: {
94
+ authorize: {
95
+ hint: "Generate stake authorization code for the specified withdrawer address.",
96
+ param: "WIT_ADDRESS",
97
+ },
98
+ "node-master-key": {
99
+ hint: "Node's master key other than the one set up in environment.",
100
+ param: "XPRV",
101
+ },
102
+ },
103
+ },
104
+ decipherMasterKey: {
105
+ hint: "Decipher some master key as exported from myWitWallet.",
106
+ },
107
+ delegatees: {
108
+ hint: "List validators treasuring delegated stake from any of the wallet's accounts.",
109
+ },
110
+ notarize: {
111
+ hint: "Ask the Wit/Oracle to notarize and forever store the resolution to some Radon asset.",
112
+ params: ["RAD_BYTECODE | RAD_HASH | RADON_ASSET", "[RADON_ARGS]"],
113
+ options: {
114
+ ...options,
115
+ fees: {
116
+ hint: "Specific unitary reward for every involved validator (supersedes --priority).",
117
+ param: "WITS",
118
+ },
119
+ module: {
120
+ hint: "NPM package where to search for Radon assets.",
121
+ param: "NPM_PACKAGE",
122
+ },
123
+ witnesses: {
124
+ hint: "Number of witnesses in the Witnet network required to attend the oracle query (default: 3).",
125
+ param: "NUMBER",
126
+ },
127
+ },
128
+ },
129
+ provider: {
130
+ hint: "Show the underlying Wit/Oracle RPC provider being used.",
131
+ },
132
+ signMessage: {
133
+ hint: "Prove ownership of a wallet address by signing some given message.",
134
+ param: "TEXT",
135
+ options: {
136
+ signer: {
137
+ hint: "Wallet's signer address other than wallet's default.",
138
+ param: "WALLET_ADDRESS",
139
+ },
140
+ },
141
+ },
142
+ verifyMessage: {
143
+ hint: "Verify authenticity of some given message and signature.",
144
+ param: "TEXT",
145
+ options: {
146
+ publicKey: {
147
+ hint: "Hexified public key of the alleged signer of the message.",
148
+ param: "<HEX_STRING>",
149
+ },
150
+ signature: {
151
+ hint: "Hexified signature produced with the signer's private key.",
152
+ param: "<HEX_STRING>",
153
+ },
154
+ },
155
+ },
156
+ stake: {
157
+ hint: "Stake specified amount of Wits by using some given authorization code.",
158
+ params: "AUTH_CODE",
159
+ options: {
160
+ ...options,
161
+ value: {
162
+ hint: "Amount in Wits to stake into the validator that signed the authorization (min: 10 KWits).",
163
+ param: "WITS | `all`",
164
+ },
165
+ withdrawer: {
166
+ hint: "Wallet's address with rights to eventually withdraw the staked deposit, plus benefits.",
167
+ param: "WALLET_ADDRESS",
168
+ },
169
+ },
170
+ },
171
+ transfer: {
172
+ hint: "Transfer specified amount of Wits to given address.",
173
+ options: {
174
+ ...options,
175
+ into: {
176
+ hint: "Recipient address.",
177
+ param: "WIT_ADDRESS",
178
+ },
179
+ metadata: {
180
+ hint: "Optional 20-byte metadata info.",
181
+ param: "HEX_STRING"
182
+ },
183
+ value: {
184
+ hint: "Amount in Wits to be transfered (e.g. `0.5` Wits).",
185
+ param: "WITS | `all`",
186
+ },
187
+ },
188
+ },
189
+ utxos: {
190
+ hint: "List currently available UTXOs on wallet's specified address, or on all funded accounts otherwise.",
191
+ params: "[WALLET_ADDRESS]",
192
+ options: {
193
+ ...options,
194
+ from: {},
195
+ into: {
196
+ hint: "Alternative wallet address where to JOIN or SPLIT the selected UTXOs, other than default.",
197
+ param: "WALLET_ADDRESS",
198
+ },
199
+ join: { hint: "Join selected UTXOs together into a single UTXO (requires --value)." },
200
+ limit: {
201
+ hint: "Max number of HD-accounts to derive.",
202
+ param: "LIMIT",
203
+ },
204
+ splits: {
205
+ hint: "Number of UTXOs to split the target balance into (max: 50; requires --value).",
206
+ param: "NUMBER",
207
+ },
208
+ value: {
209
+ hint: "Amount in Wits to be either joined or split apart.",
210
+ param: "WITS | `all`",
211
+ },
212
+ },
213
+ },
214
+ withdraw: {
215
+ hint: "Withdraw specified amount of staked Wits from some given delegatee.",
216
+ options: {
217
+ await: options.await,
218
+ confirmations: options.confirmations,
219
+ force: options.force,
220
+ from: {
221
+ hint: "Validator address from whom to withdraw the specified amount.",
222
+ param: "DELEGATEE_PKH",
223
+ },
224
+ into: {
225
+ hint: "Wallet address with rights to withdraw from the delegatee (default: wallet's first account).",
226
+ param: "WALLET_ADDRESS",
227
+ },
228
+ value: {
229
+ hint: "Amount in Wits to withdraw (default: `all`).",
230
+ param: "WITS | `all`",
231
+ },
232
+ },
233
+ },
234
+ },
235
+ subcommands: {
236
+ accounts, coinbase, delegatees: validators,
237
+ notarize: resolve, stake, transfer, withdraw: unstake, utxos,
238
+ decipherMasterKey: decipher, provider,
239
+ signMessage, verifyMessage,
240
+ },
241
+ }
242
+
243
+ /// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
244
+ /// CLI SUBMODULE COMMANDS ============================================================================================
245
+
246
+ async function signMessage (options = {}, [...words]) {
247
+ const wallet = await _loadWallet({
248
+ ...options,
249
+ limit: 1,
250
+ "no-funds": true,
251
+ })
252
+ if (!words.length) {
253
+ throw Error(`some message must be entered.`)
254
+ }
255
+ const text = words.join(" ")
256
+ let ledger
257
+ if (options?.signer) {
258
+ ledger = options.signer === wallet.coinbase.pkh ? wallet.coinbase : wallet.getAccount(options.signer)
259
+ } else {
260
+ ledger = wallet
261
+ }
262
+ if (!ledger) {
263
+ throw Error(`no private key available for signer address ${options?.signer}`)
264
+ }
265
+ console.info(ledger.getSigner().signMessage(text))
266
+ }
267
+
268
+ async function verifyMessage (options = {}, [...words]) {
269
+ if (!words.length) {
270
+ throw Error(`some text must be entered.`)
271
+ }
272
+ const text = words.join(" ")
273
+ const digest = utils.digestMessage(text)
274
+ let { publicKey, signature } = options
275
+ if (!publicKey) {
276
+ throw Error(`--publicKey must be specified.`)
277
+ } else if (!signature) {
278
+ throw Error(`--signature must be specified`)
279
+ }
280
+ if (publicKey.startsWith("0x")) publicKey = publicKey.slice(2);
281
+ if (signature.startsWith("0x")) signature = signature.slice(2);
282
+ console.info(`Message: "${text}"`)
283
+ console.info(`Digest: ${utils.toHexString(digest)}`)
284
+ console.info(`Public key: ${publicKey}`)
285
+ console.info(`Signature: ${signature}`)
286
+ console.info("-".repeat(120))
287
+ if (utils.ecdsaVerify(digest, publicKey, signature.slice(2))) {
288
+ console.info(
289
+ `^ Signed by ${
290
+ Witnet.PublicKey.fromUint8Array(utils.fromHexString(publicKey)).hash().toBech32("mainnet")
291
+ }.\n^ Message is authentic.`
292
+ );
293
+ } else {
294
+ console.error(`^ Invalid signature.`)
295
+ }
296
+ }
297
+
298
+ async function accounts (options = {}, args = []) {
299
+ const { ethereum, verbose } = options
300
+
301
+ let wallet
302
+ if (args.length === 0) {
303
+ wallet = await _loadWallet({
304
+ ...options,
305
+ limit: options?.limit || (options["no-funds"] && 10),
306
+ "no-funds": options["no-funds"],
307
+ })
308
+ } else {
309
+ wallet = await _loadWallet({ provider: options?.provider, limit: 1 })
310
+ args.forEach(pkh => {
311
+ if (!wallet.getSigner(pkh)) {
312
+ throw Error(`Input address ${pkh} doesn't belong to the wallet`)
313
+ }
314
+ })
315
+ }
316
+
317
+ if (options?.qrcode) {
318
+ let addrs = []
319
+ if (args.length > 0) {
320
+ addrs = wallet.accounts.filter(account => args.includes(account.pkh))
321
+ } else {
322
+ addrs = wallet.accounts
323
+ }
324
+ addrs.forEach(account => {
325
+ qrcodes.generate(account.pkh)
326
+ console.info(`Wallet account #${account.index + 1}: ${colors.lmagenta(account.pkh)}\n`)
327
+ })
328
+ return
329
+ }
330
+
331
+ const coinbaseWithdrawers = await wallet.coinbase.getWithdrawers()
332
+ const coinbaseBalance = await wallet.coinbase.getBalance()
333
+ const coinbaseColor = utils.totalCoins(coinbaseBalance).pedros > 0n ? colors.mred : (coinbaseWithdrawers.length > 0 ? colors.mcyan : colors.cyan)
334
+ const coinbase = coinbaseWithdrawers.length > 0 || utils.totalCoins(coinbaseBalance).pedros > 0n || utils.totalCoins(await wallet.getBalance()).pedros === 0n
335
+
336
+ const records = []
337
+
338
+ if (coinbase) {
339
+ const coinbaseUtxos = await wallet.coinbase.getUtxos()
340
+ records.push([0, coinbaseColor(wallet.coinbase.pkh), coinbaseUtxos.length, coinbaseBalance])
341
+ }
342
+ records.push(
343
+ ...await Promise.all(
344
+ wallet.accounts.map(async account => {
345
+ const balance = await account.getBalance()
346
+ const utxos = await account.getUtxos()
347
+ return [
348
+ account.index + 1,
349
+ balance.unlocked > 0 ? colors.mmagenta(account.pkh) : colors.magenta(account.pkh),
350
+ utxos.length,
351
+ balance,
352
+ ...(ethereum ? [
353
+ account.getSigner().authorizeEvmAddress(ethereum),
354
+ // utils.toHexString(account.publicKey.hash().toBytes20(), true)
355
+ ] : [])
356
+ ]
357
+ }),
358
+ )
359
+ )
360
+
361
+ let unlocked = 0n
362
+ helpers.traceTable(
363
+ records.map(([index, pkh, count, balance, signature]) => {
364
+ unlocked += balance.unlocked
365
+ return [
366
+ index, pkh,
367
+ ...(ethereum ? [ signature ] : (
368
+ verbose
369
+ ? [
370
+ count,
371
+ Witnet.Coins.fromNanowits(balance.locked).wits.toFixed(2),
372
+ Witnet.Coins.fromNanowits(balance.staked).wits.toFixed(2),
373
+ Witnet.Coins.fromNanowits(balance.unlocked).wits.toFixed(2),
374
+ utils.totalCoins(balance).wits.toFixed(2),
375
+ ]
376
+ : [
377
+ count,
378
+ Witnet.Coins.fromNanowits(balance.unlocked).wits.toFixed(2),
379
+ ]
380
+ )
381
+ ),
382
+ ]
383
+ }), {
384
+ headlines: [
385
+ "INDEX", `:WITNET WALLET ${wallet.network.toUpperCase()} ACCOUNTS`,
386
+ ...(ethereum ? [`ETHEREUM ${wallet.network.toUpperCase()} WRAPPING AUTHORIZATION CODE [${ethereum}]`] : (
387
+ verbose
388
+ ? ["# UTXOS", "Locked ($WIT)", "Staked ($WIT)", "Available ($WIT)", "BALANCE ($WIT)"]
389
+ : ["# UTXOS", "Available ($WIT)"]
390
+ )
391
+ )
392
+ ],
393
+ humanizers: [
394
+ helpers.commas,,
395
+ ...(ethereum
396
+ ? [,]
397
+ : [helpers.commas, helpers.commas, helpers.commas, helpers.commas, helpers.commas]
398
+ ),
399
+ ],
400
+ colors: [
401
+ ,,
402
+ ...(ethereum ? [colors.mblue, colors.gray] : (
403
+ verbose
404
+ ? [colors.white, colors.gray, colors.yellow, colors.myellow, colors.lyellow]
405
+ : [colors.white, colors.myellow]
406
+ )
407
+ ),
408
+ ],
409
+ maxColumnWidth: 132,
410
+ }
411
+ )
412
+ console.info(`^ Available balance: ${colors.lyellow(whole_wits(unlocked, 2))}`)
413
+ }
414
+
415
+ async function coinbase (options = {}) {
416
+ const masterWallet = await _loadWallet({ ...options })
417
+ let wallet
418
+ if (options["node-master-key"]) {
419
+ utils.parseXprv(options["node-master-key"])
420
+ wallet = await _loadWallet({ provider: options?.provider, limit: 1, xprv: options["node-master-key"] })
421
+ } else {
422
+ wallet = masterWallet
423
+ }
424
+
425
+ const coinbaseColor = utils.totalCoins(await wallet.coinbase.getBalance()) > 0 ? colors.mred : colors.lcyan
426
+ console.info(`> ${options["node-master-key"] ? "Coinbase " : "Wallet's coinbase"} address: ${coinbaseColor(wallet.coinbase.pkh)}`)
427
+
428
+ if (options?.authorize) {
429
+ const withdrawer = options.authorize
430
+ const authcode = wallet.coinbase.authorizeStake(withdrawer)
431
+ qrcodes.generate(authcode)
432
+ console.info(`${colors.white(authcode)}`)
433
+ console.info("^ Withdrawer address:", colors.mmagenta(withdrawer))
434
+ } else {
435
+ const records = await wallet.coinbase.getWithdrawers({ by: Witnet.StakesOrderBy.Coins, reverse: true })
436
+ if (records.length > 0) {
437
+ const { verbose } = options
438
+ let staked = 0
439
+ helpers.traceTable(
440
+ records.map((record, index) => {
441
+ staked += record.value.coins
442
+ const withdrawer = (record.key.withdrawer === wallet.coinbase.pkh || record.key.withdrawer === masterWallet.coinbase.PublicKeyHash
443
+ ? (record.value.epochs.witnessing > record.value.nonce || record.value.epochs.mining > record.value.nonce
444
+ ? colors.mred(record.key.withdrawer)
445
+ : colors.red(record.key.withdrawer)
446
+ )
447
+ : (record.value.epochs.witnessing > record.value.nonce || record.value.epochs.mining > record.value.nonce
448
+ ? (masterWallet.getSigner(record.key.withdrawer) ? colors.mgreen(record.key.withdrawer) : colors.mmagenta(record.key.withdrawer))
449
+ : (masterWallet.getSigner(record.key.withdrawer) ? colors.green(record.key.withdrawer) : colors.magenta(record.key.withdrawer))
450
+ )
451
+ )
452
+ const nonce = (record.value.epochs.witnessing > record.value.nonce || record.value.epochs.mining > record.value.nonce
453
+ ? record.value.nonce
454
+ : colors.gray(record.value.nonce || "")
455
+ )
456
+ return [
457
+ index + 1,
458
+ withdrawer,
459
+ nonce,
460
+ ...(verbose
461
+ ? [record.value.epochs.witnessing || "", record.value.epochs.mining || ""]
462
+ : []
463
+ ),
464
+ colors.yellow(Witnet.Coins.fromNanowits(record.value.coins).wits),
465
+ ]
466
+ }), {
467
+ headlines: [
468
+ "RANK",
469
+ "STAKE WITHDRAWERS",
470
+ ...(verbose
471
+ ? ["Nonce", "LW_Epoch", "LM_Epoch"]
472
+ : ["Nonce"]
473
+ ),
474
+ "STAKED ($WIT)",
475
+ ],
476
+ humanizers: [
477
+ ,, ...(verbose
478
+ ? [helpers.commas, helpers.commas, helpers.commas]
479
+ : [helpers.commas]
480
+ ),
481
+ helpers.commas,
482
+ ],
483
+ colors: [
484
+ ,,, ...(verbose
485
+ ? [colors.magenta, colors.cyan, colors.myellow]
486
+ : [colors.myellow]
487
+ ),
488
+ ],
489
+ }
490
+ )
491
+ console.info(`^ Total stake: ${colors.lyellow(whole_wits(staked, 2))}`)
492
+ } else {
493
+ console.info("> Holds no delegated stake.")
494
+ }
495
+ }
496
+ }
497
+
498
+ async function decipher () {
499
+ const user = await prompt([
500
+ {
501
+ message: "Enter XPRV:",
502
+ name: "xprv",
503
+ }, {
504
+ type: "password",
505
+ mask: "*",
506
+ message: "Enter password:",
507
+ name: "passwd",
508
+ },
509
+ ])
510
+ console.info(utils.decipherXprv(user.xprv, user.passwd))
511
+ }
512
+
513
+ async function provider (options = {}) {
514
+ const wallet = await _loadWallet({ unlocked: true, limit: 1, ...options })
515
+ wallet.provider.endpoints.forEach(url => {
516
+ console.info(helpers.colors.colors.magenta(url))
517
+ })
518
+ }
519
+
520
+ async function resolve (options = {}, [pattern, ...args]) {
521
+ const wallet = await _loadWallet({ ...options })
522
+ const ledger = (
523
+ options?.from
524
+ ? (options.from === wallet.coinbase.pkh ? wallet.coinbase : wallet.getAccount(options.from))
525
+ : wallet
526
+ )
527
+ if (!ledger) {
528
+ throw Error("--from address not found in wallet")
529
+ }
530
+ const request = await _loadRadonRequest({ ...options, pattern, args })
531
+ // console.log(request)
532
+ await helpers.traceTransaction(
533
+ Witnet.DataRequests.from(ledger, request), {
534
+ headline: "DATA REQUEST TRANSACTION",
535
+ color: colors.bgreen,
536
+ ...await _loadTransactionParams({ ...options }),
537
+ }
538
+ )
539
+ }
540
+
541
+ async function stake (options = {}, [authorization]) {
542
+ if (!authorization) {
543
+ throw Error("No authorization code was provided")
544
+ } else if (!options?.value) {
545
+ throw Error("No --value was specified")
546
+ } else if (!options?.withdrawer) {
547
+ throw Error("No --withdrawer was specified")
548
+ }
549
+
550
+ const wallet = await _loadWallet({ ...options })
551
+
552
+ // determine ledger and available funds
553
+ let available = 0; let ledger
554
+ if (options?.from) {
555
+ ledger = options.from === wallet.coinbase.pkh ? wallet.coinbase : wallet.getAccount(options.from)
556
+ if (ledger) available = (await ledger.getBalance()).unlocked
557
+ } else {
558
+ ledger = wallet
559
+ available = (await wallet.getBalance()).unlocked + (await wallet.coinbase.getBalance()).unlocked
560
+ }
561
+ if (!ledger) {
562
+ throw Error(`--from address ${options?.from} doesn't belong to the wallet`)
563
+ }
564
+
565
+ // validate withdrawer address
566
+ const withdrawer = Witnet.PublicKeyHash.fromBech32(options?.withdrawer).toBech32(wallet.network)
567
+ if (!wallet.getAccount(withdrawer) && withdrawer !== wallet.coinbase.pkh && !options?.force) {
568
+ const user = await prompt([{
569
+ message: `Withdrawer ${withdrawer} doesn't belong to the wallet. Proceed anyway?`,
570
+ name: "continue",
571
+ type: "confirm",
572
+ default: false,
573
+ }])
574
+ if (!user.continue) {
575
+ throw Error(`--withdrawer address ${withdrawer} not found in wallet`)
576
+ }
577
+ }
578
+
579
+ // determine stake value
580
+ const params = await _loadTransactionParams({ ...options })
581
+ const coins = params?.value === "all" ? Witnet.Coins.fromPedros(available - params.fees.pedros) : Witnet.Coins.fromWits(params?.value)
582
+ if (available < coins.pedros) {
583
+ throw Error(`Insufficient funds ${options?.from ? `on address ${options.from}.` : "on wallet."}`)
584
+ } else if (params?.fees && coins.pedros <= params.fees.pedros) {
585
+ throw Error(`Fees equal or greater than value: ${params.fees.pedros} >= ${coins.pedros}`)
586
+ }
587
+
588
+ // todo: validate withdrawer matches delegatee's withdrawer
589
+
590
+ // deposit stake
591
+ await helpers.traceTransaction(
592
+ Witnet.StakeDeposits.from(ledger), {
593
+ headline: "STAKE DEPOSIT TRANSACTION",
594
+ color: colors.bcyan,
595
+ ...options,
596
+ authorization,
597
+ value: coins,
598
+ withdrawer,
599
+ }
600
+ )
601
+ }
602
+
603
+ async function transfer (options = {}) {
604
+ if (!options?.value) {
605
+ throw Error("No --value was specified")
606
+ }
607
+
608
+ const wallet = await _loadWallet({ ...options })
609
+
610
+ // determine ledger and available funds
611
+ let available = 0n;
612
+ let ledger
613
+ if (options?.from) {
614
+ ledger = options.from === wallet.coinbase.pkh ? wallet.coinbase : wallet.getAccount(options.from)
615
+ } else {
616
+ ledger = wallet
617
+ }
618
+ if (!ledger) {
619
+ throw Error(`--from address ${options?.from} doesn't belong to the wallet`)
620
+ } else {
621
+ const balance = await ledger.getBalance()
622
+ available = balance.unlocked
623
+ }
624
+
625
+ // validate metadata info, if specified
626
+ let metadata
627
+ if (options?.metadata) {
628
+ if (!utils.isHexStringOfLength(options.metadata, 20)) {
629
+ throw Error(`--metadata must be a 20-byte hex string`)
630
+ }
631
+ metadata = Witnet.PublicKeyHash.fromHexString(options.metadata).toBech32(wallet.network)
632
+ }
633
+
634
+ // validate recipient address
635
+ if (!options?.into) {
636
+ throw Error("--into address must be specified")
637
+ }
638
+ const into = Witnet.PublicKeyHash.fromBech32(options?.into).toBech32(wallet.network)
639
+
640
+ // determine transfer params
641
+ const params = await _loadTransactionParams({ ...options })
642
+ const coins = params?.value === "all"
643
+ ? Witnet.Coins.fromPedros(BigInt(available - params.fees.pedros - BigInt(options?.metadata ? 1: 0)))
644
+ : Witnet.Coins.fromWits(params?.value)
645
+ if (available < coins.pedros) {
646
+ throw Error(`Insufficient funds ${options?.from ? `on address ${options.from}.` : "on wallet."}`)
647
+ } else if (params?.fees && coins.pedros <= params.fees.pedros) {
648
+ throw Error(`Fees equal or greater than value: ${params.fees.pedros} >= ${coins.pedros}`)
649
+ }
650
+
651
+ // transfer value
652
+ const recipients = options?.metadata
653
+ ? [[into, coins], [metadata, Witnet.Coins.fromPedros(1n)]]
654
+ : [[into, coins]]
655
+ await helpers.traceTransaction(
656
+ Witnet.ValueTransfers.from(ledger), {
657
+ headline: "VALUE TRANSFER TRANSACTION",
658
+ color: colors.bblue,
659
+ ...params,
660
+ recipients,
661
+ }
662
+ )
663
+ }
664
+
665
+ async function unstake (options = {}) {
666
+
667
+ // load wallet:
668
+ const wallet = await _loadWallet({ ...options })
669
+
670
+ // determine validator address:
671
+ let validator
672
+ if (options?.from) {
673
+ validator = Witnet.PublicKeyHash.fromBech32(options?.from).toBech32(wallet.network)
674
+
675
+ } else {
676
+ const delegatees =
677
+ (await wallet.getDelegatees({ by: Witnet.StakesOrderBy.Coins, reverse: true }, false))
678
+ .filter(entry => !options?.into || entry.key.withdrawer === options?.into)
679
+ if (delegatees.length === 0) throw new Error(`No delegatees to withdraw from.`);
680
+ else if (delegatees.length === 1) validator = delegatees[0].key.validator;
681
+ else {
682
+ const choices = delegatees
683
+ .map(entry => `${colors.mcyan(entry.key.validator)}`)
684
+ .filter((pkh, index, array) => index === array.indexOf(pkh))
685
+ const user = await prompt([{
686
+ choices,
687
+ message: "Validator address ?",
688
+ name: "key",
689
+ type: "list",
690
+ pageSize: 24,
691
+ }]);
692
+ validator = helpers.colorstrip(user.key.split(' ')[0])
693
+ }
694
+ }
695
+
696
+ // determine withdrawer address:
697
+ let withdrawer
698
+ if (options?.into) {
699
+ withdrawer = Witnet.PublicKeyHash.fromBech32(options?.into).toBech32(wallet.network)
700
+
701
+ } else {
702
+ const stakes = (await wallet.provider.stakes({ filter: { validator }})).filter(entry => wallet.getSigner(entry.key.withdrawer) !== undefined);
703
+ if (stakes.length === 0) throw new Error(`Nothing to withdraw from validator ${validator}.`);
704
+ else if (stakes.length === 1) withdrawer = stakes[0].key.withdrawer;
705
+ else {
706
+ const choices = stakes.map(entry => `${colors.mmagenta(entry.key.withdrawer)} [${colors.yellow(Witnet.Coins.fromPedros(entry.value.coins).toString(2))}]`);
707
+ const user = await prompt([{
708
+ choices,
709
+ message: "Withdrawer address ?",
710
+ name: "key",
711
+ type: "list",
712
+ pageSize: 24,
713
+ }]);
714
+ withdrawer = helpers.colorstrip(user.key.split(' ')[0])
715
+ }
716
+ }
717
+
718
+ // validate withdrawer address
719
+ const ledger = await wallet.getSigner(withdrawer)
720
+ if (!ledger) {
721
+ throw Error(`--into address ${withdrawer} doesn't belong to the wallet`)
722
+ }
723
+
724
+ // valite delegatee address:
725
+ const delegatee = (await ledger.getDelegatees()).find(stake =>
726
+ stake.key.validator === validator && stake.key.withdrawer === withdrawer
727
+ )
728
+ if (!delegatee) {
729
+ throw Error(`Nothing to withdraw from ${validator} into ${withdrawer}`)
730
+ }
731
+ const available = BigInt(delegatee.value.coins)
732
+
733
+ // determine withdrawal value:
734
+ const params = await _loadTransactionParams({ ...options, fees: 0n })
735
+
736
+ const value = (params?.value || `all`) === "all"
737
+ ? Witnet.Coins.fromPedros(available - params.fees.pedros)
738
+ : Witnet.Coins.fromWits(params?.value)
739
+
740
+ // validate withdrawal amount:
741
+ if (available < value.pedros + params?.fees.pedros) {
742
+ throw Error(`Cannot withdraw that much: ${value.pedros} > ${available}`)
743
+ } else if (params?.fees && value.pedros <= params.fees.pedros) {
744
+ throw Error(`Fees equal or greater than value: ${params.fees.pedros} >= ${value.pedros}`)
745
+ }
746
+
747
+ // withdraw deposit from validator into withdrawer:
748
+ await helpers.traceTransaction(
749
+ Witnet.StakeWithdrawals.from(ledger), {
750
+ headline: "STAKE WITHDRAWAL TRANSACTION",
751
+ ...params,
752
+ validator,
753
+ value,
754
+ }
755
+ )
756
+ }
757
+
758
+ async function utxos (options = {}, [from]) {
759
+ const wallet = await _loadWallet({ ...options })
760
+
761
+ // determine ledger and available funds
762
+ let ledger
763
+ let available = 0n;
764
+ if (from) {
765
+ ledger = from === wallet.coinbase.pkh ? wallet.coinbase : wallet.getAccount(from)
766
+ if (ledger) available = (await ledger.getBalance()).unlocked;
767
+ } else {
768
+ ledger = wallet
769
+ available = (await wallet.getBalance()).unlocked + (await wallet.coinbase.getBalance()).unlocked
770
+ }
771
+ if (!ledger) {
772
+ throw Error(`Address ${from} doesn't belong to the wallet`)
773
+ }
774
+
775
+ // determine into address
776
+ let into = options?.into
777
+ if (into) {
778
+ if (into !== from && !wallet.getAccount(into) && into !== wallet.coinbase.pkh && !options?.force) {
779
+ const user = await prompt([{
780
+ message: `Recipient address ${into} doesn't belong to the wallet. Proceed anyway?`,
781
+ name: "continue",
782
+ type: "input",
783
+ }])
784
+ if (!user.continue.toLowerCase().startsWith("y")) {
785
+ throw Error(`--into address ${into} not found in wallet`)
786
+ }
787
+ }
788
+ } else {
789
+ into = wallet.pkh
790
+ }
791
+
792
+ // determine if --value is required
793
+ if ((options?.join || options?.split) && !options?.value) {
794
+ throw Error("--value must be specified on JOIN and SPLITS operations.")
795
+ }
796
+
797
+ // extract transaction params
798
+ const params = await _loadTransactionParams({
799
+ ...options,
800
+ force: options?.force || (!options?.join && !options?.splits),
801
+ })
802
+
803
+ // select utxos of either the from account (if specified) or from all funded accounts in the wallet (including the coinbase)
804
+ let value = params?.value === "all"
805
+ ? Witnet.Coins.fromPedros(available - params.fees.pedros)
806
+ : (params?.value ? Witnet.Coins.fromWits(params?.value) : Witnet.Coins.fromPedros(available))
807
+ if (available < value.pedros) {
808
+ throw Error(`Insufficient funds ${from ? `on address ${from}.` : "on wallet."}`)
809
+ } else if (params?.fees && value.pedros <= params.fees.pedros) {
810
+ throw Error(`Fees equal or greater than value: ${params.fees.pedros} >= ${value.pedros}`)
811
+ }
812
+ const utxos = await ledger.selectUtxos({ value })
813
+ const covered = utxos.map(utxo => BigInt(utxo.value))?.reduce((prev, curr) => prev + curr, 0n) || 0n
814
+ // if (value && covered < value.pedros) {
815
+ // console.log(value, covered)
816
+ // throw Error(`Insufficient unlocked UTXOs in ${from ? `wallet account ${ledger.pkh}` : "wallet"}`)
817
+ // }
818
+
819
+ // only if at least one utxo is selected, proceed with report and other operations, if any
820
+ if (utxos.length > 0) {
821
+ if (options?.verbose || (!options?.join && !options?.splits)) {
822
+ helpers.traceTable(
823
+ utxos.map((utxo, index) => [
824
+ index + 1,
825
+ utxo.signer === wallet.coinbase.pkh ? colors.mcyan(utxo.signer) : colors.mmagenta(utxo.signer),
826
+ utxo?.internal ? colors.green(utxo.output_pointer) : colors.mgreen(utxo.output_pointer),
827
+ utxo.value,
828
+ ]), {
829
+ headlines: ["INDEX", `WITNET WALLET ${wallet.network.toUpperCase()} ADDRESSES`, ":Unlocked UTXO pointers", "UTXO value ($pedros)"],
830
+ humanizers: [helpers.commas,,, helpers.commas],
831
+ colors: [,,, colors.myellow],
832
+ }
833
+ )
834
+ console.info(`^ Available balance: ${colors.lyellow(whole_wits(covered, 2))}`)
835
+ }
836
+
837
+ const valueTransfer = Witnet.ValueTransfers.from(ledger)
838
+
839
+ if (options?.join) {
840
+ const recipients = [[
841
+ // if a split is expected, join utxos into selected account or wallet
842
+ options?.splits ? ledger.pkh : into,
843
+ value,
844
+ ]]
845
+ await helpers.traceTransaction(valueTransfer, {
846
+ headline: `JOINING UTXOs: ${utxos.length} -> ${value.pedros < covered ? 2 : 1}`,
847
+ ...params,
848
+ await: params?.await || options?.splits !== undefined,
849
+ recipients,
850
+ })
851
+ }
852
+ if (options?.splits) {
853
+ const recipients = []
854
+ const splits = parseInt(options.splits)
855
+ if (splits > 50) {
856
+ throw Error("Not possible to split into more than 50 UTXOs")
857
+ }
858
+ value = Witnet.Coins.fromPedros(BigInt(Math.floor(Number(value.pedros) / splits)))
859
+ recipients.push(...Array(splits).fill([into, value]))
860
+ await helpers.traceTransaction(
861
+ valueTransfer, {
862
+ headline: `SPLITTING UTXOs: ${utxos.length} -> ${Number(value.pedros) * splits < covered ? splits + 1 : splits}`,
863
+ ...params,
864
+ recipients,
865
+ reload: options?.join,
866
+ })
867
+ }
868
+ } else {
869
+ console.info("> No available UTXOs at the moment.")
870
+ }
871
+ }
872
+
873
+ async function validators (options = {}) {
874
+ const { verbose } = options
875
+ const wallet = await _loadWallet({ ...options })
876
+ const order = { by: Witnet.StakesOrderBy.Coins, reverse: true }
877
+
878
+ const coinbaseBalance = await wallet.coinbase.getBalance()
879
+ const coinbase = coinbaseBalance.staked > 0n
880
+
881
+ const records = await wallet.getDelegatees(order, true)
882
+ if (records.length > 0) {
883
+ let staked = 0n
884
+ helpers.traceTable(
885
+ records.map(record => {
886
+ staked += BigInt(record.value.coins)
887
+ return [
888
+ record.key.withdrawer === wallet.coinbase.pkh
889
+ ? colors.mred(record.key.withdrawer)
890
+ : (record.key.validator !== "" ? colors.mmagenta(record.key.withdrawer) : colors.magenta(record.key.withdrawer)),
891
+ ...(record.value.epochs.witnessing > record.value.nonce || record.value.epochs.mining > record.value.nonce
892
+ ? [colors.mcyan(record.key.validator), record.value.nonce]
893
+ : [colors.cyan(record.key.validator), colors.gray(record.value.nonce || "")]
894
+ ),
895
+ ...(verbose
896
+ ? [record.value.epochs.witnessing || "", record.value.epochs.mining || ""]
897
+ : []
898
+ ),
899
+ colors.yellow(record.value.coins),
900
+ ]
901
+ }), {
902
+ headlines: [
903
+ // "INDEX",
904
+ coinbase ? "WITNET WALLET COINBASE" : `WITNET WALLET ${wallet.network.toUpperCase()} ACCOUNTS`,
905
+ "STAKE DELEGATEES",
906
+ ...(verbose
907
+ ? ["Nonce", "LW_Epoch", "LM_Epoch"]
908
+ : ["Nonce"]
909
+ ),
910
+ "STAKED ($pedros)",
911
+ ],
912
+ humanizers: [
913
+ ,, ...(verbose
914
+ ? [helpers.commas, helpers.commas, helpers.commas]
915
+ : [helpers.commas]
916
+ ),
917
+ helpers.commas,
918
+ ],
919
+ colors: [
920
+ ,,, ...(verbose
921
+ ? [colors.magenta, colors.cyan, colors.myellow]
922
+ : [colors.myellow]
923
+ ),
924
+ ],
925
+ }
926
+ )
927
+ console.info(`^ Total deposits: ${colors.lyellow(whole_wits(staked, 2))}`)
928
+ } else {
929
+ console.info("> No delegatees found.")
930
+ }
931
+ }
932
+
933
+ /// ===================================================================================================================
934
+ /// --- Internal functions --------------------------------------------------------------------------------------------
935
+
936
+ async function _loadRadonRequest (options = {}) {
937
+ const args = options?.args || []
938
+ if (options?.pattern && typeof options.pattern === 'string' && utils.isHexString(options.pattern)) {
939
+ // if (utils.isHexStringOfLength(options.pattern, 32)) {
940
+ // throw `Searching RADON_BYTECODE by RAD_HASH not yet supported.`
941
+ // } else
942
+ try {
943
+ return Witnet.Radon.RadonRequest.fromBytecode(options.pattern)
944
+ } catch (e) {
945
+ throw Error(`Invalid RADON_BYTECODE: ${e}`)
946
+ }
947
+ }
948
+
949
+ // load Radon assets from environment
950
+ let assets = utils.searchRadonAssets(
951
+ {
952
+ assets: loadAssets(options),
953
+ pattern: options?.pattern,
954
+ },
955
+ (key, pattern) => key.toLowerCase().indexOf(pattern.toLowerCase()) >= 0
956
+ )
957
+
958
+ if (args.length > 0) {
959
+ // ignore RadonRequests if args were passed from the CLI
960
+ assets = assets.filter(([, artifact]) => !(artifact instanceof Witnet.Radon.RadonRequest))
961
+ }
962
+
963
+ // sort Radon assets alphabetically
964
+ assets = assets.sort((a, b) => {
965
+ if (a[0] < b[0]) return -1
966
+ else if (a[0] > b[0]) return 1
967
+ else return 0
968
+ })
969
+
970
+ let artifact, key
971
+ if (Object.keys(assets).length === 0) {
972
+ if (options?.pattern) {
973
+ throw Error(`No Radon assets named after "${options.pattern}"`)
974
+ } else {
975
+ throw Error("No Radon assets declared yet")
976
+ }
977
+ } else if (Object.keys(assets).length > 1) {
978
+ const user = await prompt([{
979
+ choices: assets.map(([key]) => key),
980
+ message: "Please, select a Radon asset:",
981
+ name: "key",
982
+ type: "list",
983
+ pageSize: 24,
984
+ }]);
985
+ [key, artifact] = assets.find(([key]) => key === user.key)
986
+ } else {
987
+ [key, artifact] = Object.values(assets)[0]
988
+ }
989
+
990
+ if (!(artifact instanceof Witnet.Radon.RadonRequest)) {
991
+ let templateArgs = []
992
+ if (args.length === 0 && artifact?.samples) {
993
+ const sample = await prompt([{
994
+ choices: Object.keys(artifact.samples),
995
+ message: "Select pre-settled Radon args: ",
996
+ name: "key",
997
+ type: "list",
998
+ }])
999
+ templateArgs = artifact.samples[sample.key]
1000
+ } else if (args.length === 1 && artifact?.samples) {
1001
+ const sample = Object.keys(artifact.samples).find(sample => sample.toLowerCase() === args[0].toLowerCase())
1002
+ if (sample) templateArgs = artifact.samples[sample]
1003
+ }
1004
+
1005
+ if (artifact instanceof Witnet.Radon.RadonRetrieval) {
1006
+ if (templateArgs.length === 0) templateArgs = [...args]
1007
+ if (templateArgs.length < artifact.argsCount) {
1008
+ throw Error(`${key}: missing ${artifact.argsCount - templateArgs.length} out of ${artifact.argsCount} parameters`)
1009
+ }
1010
+ artifact = new Witnet.Radon.RadonRequest({ sources: artifact.foldArgs(templateArgs) })
1011
+ } else {
1012
+ if (artifact instanceof Witnet.Radon.RadonModal) {
1013
+ if (templateArgs.length === 0) templateArgs = [...args]
1014
+ if (templateArgs.length === 0 && templateArgs.length < artifact.argsCount + 1) {
1015
+ throw Error(`${key}: missing ${artifact.argsCount + 1 - templateArgs.length} out of ${artifact.argsCount + 1} parameters.`)
1016
+ }
1017
+ artifact.providers = templateArgs.splice(0, 1)[0].split(";")
1018
+ artifact = artifact.buildRadonRequest(templateArgs)
1019
+ } else if (artifact instanceof Witnet.Radon.RadonTemplate) {
1020
+ if (templateArgs.length === 0) {
1021
+ templateArgs = new Array(artifact.sources.length)
1022
+ artifact.sources.forEach((retrieval, index) => {
1023
+ templateArgs[index] = args.splice(0, retrieval.argsCount)
1024
+ if (templateArgs[index].length < retrieval.argsCount) {
1025
+ throw Error(`${key}: missing ${
1026
+ retrieval.argsCount - templateArgs[index].length
1027
+ } out of ${
1028
+ retrieval.argsCount
1029
+ } expected args for template source #${index + 1}`)
1030
+ }
1031
+ })
1032
+ }
1033
+ artifact = artifact.buildRadonRequest(templateArgs)
1034
+ } else {
1035
+ throw Error(`${key}: unsupported Radon asset type ${artifact?.constructor.name}`)
1036
+ }
1037
+ }
1038
+ }
1039
+ return artifact
1040
+ }
1041
+
1042
+ async function _loadTransactionParams (options = {}) {
1043
+ const confirmations = options?.confirmations ? parseInt(options?.confirmations) : (options?.await ? 0 : undefined)
1044
+ let fees = options?.fees ? Witnet.Coins.fromWits(options.fees) : (options?.fees === 0n ? Witnet.Coins.zero() : undefined)
1045
+ const value = options?.value ? (options?.value.toLowerCase() === "all" ? "all" : options.value) : undefined
1046
+ if (fees === undefined) {
1047
+ if (value === "all") {
1048
+ throw Error("--fees must be specified if --value is set to `all`")
1049
+ }
1050
+ let priority = (!options?.priority && options?.force) ? Witnet.TransactionPriority.Medium : options?.priority
1051
+ if (!priority) {
1052
+ const priorities = {
1053
+ "< 60 seconds": Witnet.TransactionPriority.Opulent,
1054
+ "< 5 minutes": Witnet.TransactionPriority.High,
1055
+ "< 15 minutes": Witnet.TransactionPriority.Medium,
1056
+ "< 1 hour": Witnet.TransactionPriority.Low,
1057
+ "< 6 hours": Witnet.TransactionPriority.Stinky,
1058
+ }
1059
+ const user = await prompt([{
1060
+ choices: Object.keys(priorities),
1061
+ message: "Please, select time to block expectancy:",
1062
+ name: "priority",
1063
+ type: "list",
1064
+ }])
1065
+ priority = priorities[user.priority]
1066
+ } else if (!Object.values(Witnet.TransactionPriority).includes(priority)) {
1067
+ throw Error(`Invalid priority "${priority}"`)
1068
+ }
1069
+ fees = Witnet.TransactionPriority[priority.charAt(0).toUpperCase() + priority.slice(1)]
1070
+ }
1071
+ return {
1072
+ await: options?.await,
1073
+ confirmations,
1074
+ fees,
1075
+ from: options?.from,
1076
+ force: options?.force,
1077
+ value,
1078
+ verbose: options?.verbose,
1079
+ witnesses: options?.witnesses,
1080
+ }
1081
+ }
1082
+
1083
+ async function _loadWallet (options = {}) {
1084
+ if (!process.env.WITNET_SDK_WALLET_MASTER_KEY) {
1085
+ throw Error("No WITNET_SDK_WALLET_MASTER_KEY is settled in environment")
1086
+ } else {
1087
+ const provider = new Witnet.JsonRpcProvider(options?.provider)
1088
+ const strategies = {
1089
+ "small-first": Witnet.UtxoSelectionStrategy.SmallFirst,
1090
+ "slim-fit": Witnet.UtxoSelectionStrategy.SlimFit,
1091
+ "big-first": Witnet.UtxoSelectionStrategy.BigFirst,
1092
+ random: Witnet.UtxoSelectionStrategy.Random,
1093
+ }
1094
+ if (options?.strategy && !strategies[options.strategy]) {
1095
+ throw Error(`Unrecognised UTXO selection strategy "${options.strategy}"`)
1096
+ }
1097
+ const strategy = strategies[options?.strategy || "slim-fit"] || Witnet.UtxoSelectionStrategy.SlimFit
1098
+ const gap = options?.gap || 10
1099
+ let wallet; const xprv = options?.xprv || process.env.WITNET_SDK_WALLET_MASTER_KEY
1100
+ if (xprv.length === 293) {
1101
+ const user = await prompt([{ type: "password", mask: "*", message: "Enter password:", name: "passwd" }])
1102
+ wallet = Witnet.Wallet.fromEncryptedXprv(xprv, user.passwd, {
1103
+ gap,
1104
+ provider,
1105
+ strategy,
1106
+ limit: options?.limit,
1107
+ onlyWithFunds: !options["no-funds"],
1108
+ })
1109
+ } else {
1110
+ wallet = Witnet.Wallet.fromXprv(xprv, {
1111
+ gap,
1112
+ provider,
1113
+ strategy,
1114
+ limit: options?.limit,
1115
+ onlyWithFunds: !options["no-funds"],
1116
+ })
1117
+ }
1118
+
1119
+ return options["no-funds"] ? await wallet : await helpers.prompter(wallet)
1120
+ }
1121
+ }