@ledgerhq/coin-xrp 0.2.0-next.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (198) hide show
  1. package/.eslintrc.js +20 -0
  2. package/.turbo/turbo-build.log +4 -0
  3. package/.unimportedrc.json +27 -0
  4. package/CHANGELOG.md +19 -0
  5. package/LICENSE.txt +21 -0
  6. package/jest.config.js +6 -0
  7. package/lib/api/index.d.ts +12 -0
  8. package/lib/api/index.d.ts.map +1 -0
  9. package/lib/api/index.js +117 -0
  10. package/lib/api/index.js.map +1 -0
  11. package/lib/api/types.d.ts +188 -0
  12. package/lib/api/types.d.ts.map +1 -0
  13. package/lib/api/types.js +3 -0
  14. package/lib/api/types.js.map +1 -0
  15. package/lib/bridge/js.d.ts +10 -0
  16. package/lib/bridge/js.d.ts.map +1 -0
  17. package/lib/bridge/js.js +47 -0
  18. package/lib/bridge/js.js.map +1 -0
  19. package/lib/broadcast.d.ts +4 -0
  20. package/lib/broadcast.d.ts.map +1 -0
  21. package/lib/broadcast.js +25 -0
  22. package/lib/broadcast.js.map +1 -0
  23. package/lib/cli-transaction.d.ts +26 -0
  24. package/lib/cli-transaction.d.ts.map +1 -0
  25. package/lib/cli-transaction.js +32 -0
  26. package/lib/cli-transaction.js.map +1 -0
  27. package/lib/config.d.ts +9 -0
  28. package/lib/config.d.ts.map +1 -0
  29. package/lib/config.js +16 -0
  30. package/lib/config.js.map +1 -0
  31. package/lib/createTransaction.d.ts +4 -0
  32. package/lib/createTransaction.d.ts.map +1 -0
  33. package/lib/createTransaction.js +18 -0
  34. package/lib/createTransaction.js.map +1 -0
  35. package/lib/datasets/dataset-1.d.ts +5 -0
  36. package/lib/datasets/dataset-1.d.ts.map +1 -0
  37. package/lib/datasets/dataset-1.js +154 -0
  38. package/lib/datasets/dataset-1.js.map +1 -0
  39. package/lib/deviceTransactionConfig.d.ts +11 -0
  40. package/lib/deviceTransactionConfig.d.ts.map +1 -0
  41. package/lib/deviceTransactionConfig.js +27 -0
  42. package/lib/deviceTransactionConfig.js.map +1 -0
  43. package/lib/estimateMaxSpendable.d.ts +4 -0
  44. package/lib/estimateMaxSpendable.d.ts.map +1 -0
  45. package/lib/estimateMaxSpendable.js +31 -0
  46. package/lib/estimateMaxSpendable.js.map +1 -0
  47. package/lib/getTransactionStatus.d.ts +4 -0
  48. package/lib/getTransactionStatus.d.ts.map +1 -0
  49. package/lib/getTransactionStatus.js +82 -0
  50. package/lib/getTransactionStatus.js.map +1 -0
  51. package/lib/hw-getAddress.d.ts +6 -0
  52. package/lib/hw-getAddress.d.ts.map +1 -0
  53. package/lib/hw-getAddress.js +23 -0
  54. package/lib/hw-getAddress.js.map +1 -0
  55. package/lib/logic.d.ts +11 -0
  56. package/lib/logic.d.ts.map +1 -0
  57. package/lib/logic.js +93 -0
  58. package/lib/logic.js.map +1 -0
  59. package/lib/prepareTransaction.d.ts +4 -0
  60. package/lib/prepareTransaction.d.ts.map +1 -0
  61. package/lib/prepareTransaction.js +53 -0
  62. package/lib/prepareTransaction.js.map +1 -0
  63. package/lib/signOperation.d.ts +6 -0
  64. package/lib/signOperation.d.ts.map +1 -0
  65. package/lib/signOperation.js +99 -0
  66. package/lib/signOperation.js.map +1 -0
  67. package/lib/signer.d.ts +11 -0
  68. package/lib/signer.d.ts.map +1 -0
  69. package/lib/signer.js +3 -0
  70. package/lib/signer.js.map +1 -0
  71. package/lib/specs.d.ts +7 -0
  72. package/lib/specs.d.ts.map +1 -0
  73. package/lib/specs.js +66 -0
  74. package/lib/specs.js.map +1 -0
  75. package/lib/speculos-deviceActions.d.ts +4 -0
  76. package/lib/speculos-deviceActions.d.ts.map +1 -0
  77. package/lib/speculos-deviceActions.js +49 -0
  78. package/lib/speculos-deviceActions.js.map +1 -0
  79. package/lib/synchronization.d.ts +3 -0
  80. package/lib/synchronization.d.ts.map +1 -0
  81. package/lib/synchronization.js +74 -0
  82. package/lib/synchronization.js.map +1 -0
  83. package/lib/transaction.d.ts +15 -0
  84. package/lib/transaction.d.ts.map +1 -0
  85. package/lib/transaction.js +56 -0
  86. package/lib/transaction.js.map +1 -0
  87. package/lib/types.d.ts +30 -0
  88. package/lib/types.d.ts.map +1 -0
  89. package/lib/types.js +3 -0
  90. package/lib/types.js.map +1 -0
  91. package/lib-es/api/index.d.ts +12 -0
  92. package/lib-es/api/index.d.ts.map +1 -0
  93. package/lib-es/api/index.js +105 -0
  94. package/lib-es/api/index.js.map +1 -0
  95. package/lib-es/api/types.d.ts +188 -0
  96. package/lib-es/api/types.d.ts.map +1 -0
  97. package/lib-es/api/types.js +2 -0
  98. package/lib-es/api/types.js.map +1 -0
  99. package/lib-es/bridge/js.d.ts +10 -0
  100. package/lib-es/bridge/js.d.ts.map +1 -0
  101. package/lib-es/bridge/js.js +40 -0
  102. package/lib-es/bridge/js.js.map +1 -0
  103. package/lib-es/broadcast.d.ts +4 -0
  104. package/lib-es/broadcast.d.ts.map +1 -0
  105. package/lib-es/broadcast.js +21 -0
  106. package/lib-es/broadcast.js.map +1 -0
  107. package/lib-es/cli-transaction.d.ts +26 -0
  108. package/lib-es/cli-transaction.d.ts.map +1 -0
  109. package/lib-es/cli-transaction.js +26 -0
  110. package/lib-es/cli-transaction.js.map +1 -0
  111. package/lib-es/config.d.ts +9 -0
  112. package/lib-es/config.d.ts.map +1 -0
  113. package/lib-es/config.js +11 -0
  114. package/lib-es/config.js.map +1 -0
  115. package/lib-es/createTransaction.d.ts +4 -0
  116. package/lib-es/createTransaction.d.ts.map +1 -0
  117. package/lib-es/createTransaction.js +11 -0
  118. package/lib-es/createTransaction.js.map +1 -0
  119. package/lib-es/datasets/dataset-1.d.ts +5 -0
  120. package/lib-es/datasets/dataset-1.d.ts.map +1 -0
  121. package/lib-es/datasets/dataset-1.js +148 -0
  122. package/lib-es/datasets/dataset-1.js.map +1 -0
  123. package/lib-es/deviceTransactionConfig.d.ts +11 -0
  124. package/lib-es/deviceTransactionConfig.d.ts.map +1 -0
  125. package/lib-es/deviceTransactionConfig.js +25 -0
  126. package/lib-es/deviceTransactionConfig.js.map +1 -0
  127. package/lib-es/estimateMaxSpendable.d.ts +4 -0
  128. package/lib-es/estimateMaxSpendable.d.ts.map +1 -0
  129. package/lib-es/estimateMaxSpendable.js +24 -0
  130. package/lib-es/estimateMaxSpendable.js.map +1 -0
  131. package/lib-es/getTransactionStatus.d.ts +4 -0
  132. package/lib-es/getTransactionStatus.d.ts.map +1 -0
  133. package/lib-es/getTransactionStatus.js +75 -0
  134. package/lib-es/getTransactionStatus.js.map +1 -0
  135. package/lib-es/hw-getAddress.d.ts +6 -0
  136. package/lib-es/hw-getAddress.d.ts.map +1 -0
  137. package/lib-es/hw-getAddress.js +21 -0
  138. package/lib-es/hw-getAddress.js.map +1 -0
  139. package/lib-es/logic.d.ts +11 -0
  140. package/lib-es/logic.d.ts.map +1 -0
  141. package/lib-es/logic.js +82 -0
  142. package/lib-es/logic.js.map +1 -0
  143. package/lib-es/prepareTransaction.d.ts +4 -0
  144. package/lib-es/prepareTransaction.d.ts.map +1 -0
  145. package/lib-es/prepareTransaction.js +46 -0
  146. package/lib-es/prepareTransaction.js.map +1 -0
  147. package/lib-es/signOperation.d.ts +6 -0
  148. package/lib-es/signOperation.d.ts.map +1 -0
  149. package/lib-es/signOperation.js +92 -0
  150. package/lib-es/signOperation.js.map +1 -0
  151. package/lib-es/signer.d.ts +11 -0
  152. package/lib-es/signer.d.ts.map +1 -0
  153. package/lib-es/signer.js +2 -0
  154. package/lib-es/signer.js.map +1 -0
  155. package/lib-es/specs.d.ts +7 -0
  156. package/lib-es/specs.d.ts.map +1 -0
  157. package/lib-es/specs.js +61 -0
  158. package/lib-es/specs.js.map +1 -0
  159. package/lib-es/speculos-deviceActions.d.ts +4 -0
  160. package/lib-es/speculos-deviceActions.d.ts.map +1 -0
  161. package/lib-es/speculos-deviceActions.js +46 -0
  162. package/lib-es/speculos-deviceActions.js.map +1 -0
  163. package/lib-es/synchronization.d.ts +3 -0
  164. package/lib-es/synchronization.d.ts.map +1 -0
  165. package/lib-es/synchronization.js +67 -0
  166. package/lib-es/synchronization.js.map +1 -0
  167. package/lib-es/transaction.d.ts +15 -0
  168. package/lib-es/transaction.d.ts.map +1 -0
  169. package/lib-es/transaction.js +50 -0
  170. package/lib-es/transaction.js.map +1 -0
  171. package/lib-es/types.d.ts +30 -0
  172. package/lib-es/types.d.ts.map +1 -0
  173. package/lib-es/types.js +2 -0
  174. package/lib-es/types.js.map +1 -0
  175. package/package.json +81 -0
  176. package/please-add-coverage.test.ts +3 -0
  177. package/src/api/index.ts +138 -0
  178. package/src/api/types.ts +191 -0
  179. package/src/bridge/js.ts +56 -0
  180. package/src/broadcast.ts +20 -0
  181. package/src/cli-transaction.ts +44 -0
  182. package/src/config.ts +19 -0
  183. package/src/createTransaction.ts +13 -0
  184. package/src/datasets/dataset-1.ts +153 -0
  185. package/src/deviceTransactionConfig.ts +41 -0
  186. package/src/estimateMaxSpendable.ts +25 -0
  187. package/src/getTransactionStatus.ts +86 -0
  188. package/src/hw-getAddress.ts +20 -0
  189. package/src/logic.ts +101 -0
  190. package/src/prepareTransaction.ts +48 -0
  191. package/src/signOperation.ts +129 -0
  192. package/src/signer.ts +17 -0
  193. package/src/specs.ts +73 -0
  194. package/src/speculos-deviceActions.ts +53 -0
  195. package/src/synchronization.ts +73 -0
  196. package/src/transaction.ts +79 -0
  197. package/src/types.ts +39 -0
  198. package/tsconfig.json +13 -0
@@ -0,0 +1,138 @@
1
+ import { BigNumber } from "bignumber.js";
2
+ import network from "@ledgerhq/live-network/network";
3
+ import { parseCurrencyUnit } from "@ledgerhq/coin-framework/currencies";
4
+ import { getCryptoCurrencyById } from "@ledgerhq/cryptoassets/currencies";
5
+ import { NEW_ACCOUNT_ERROR_MESSAGE } from "../logic";
6
+ import { getCoinConfig } from "../config";
7
+ import {
8
+ AccountInfoResponse,
9
+ AccountTxResponse,
10
+ LedgerResponse,
11
+ ServerInfoResponse,
12
+ SubmitReponse,
13
+ } from "./types";
14
+
15
+ const rippleUnit = getCryptoCurrencyById("ripple").units[0];
16
+
17
+ export const parseAPIValue = (value: string): BigNumber => parseCurrencyUnit(rippleUnit, value);
18
+
19
+ export const submit = async (signature: string): Promise<SubmitReponse> => {
20
+ const {
21
+ data: { result },
22
+ } = await network<{ result: SubmitReponse }>({
23
+ method: "POST",
24
+ url: getCoinConfig().node,
25
+ data: {
26
+ method: "submit",
27
+ params: [
28
+ {
29
+ tx_blob: signature,
30
+ },
31
+ ],
32
+ },
33
+ });
34
+ return result;
35
+ };
36
+
37
+ export const getAccountInfo = async (
38
+ recipient: string,
39
+ current?: boolean,
40
+ ): Promise<AccountInfoResponse> => {
41
+ const {
42
+ data: { result },
43
+ } = await network<{ result: AccountInfoResponse }>({
44
+ method: "POST",
45
+ url: getCoinConfig().node,
46
+ data: {
47
+ method: "account_info",
48
+ params: [
49
+ {
50
+ account: recipient,
51
+ ledger_index: current ? "current" : "validated",
52
+ },
53
+ ],
54
+ },
55
+ });
56
+
57
+ if (result.status !== "success" && result.error !== NEW_ACCOUNT_ERROR_MESSAGE) {
58
+ throw new Error(`couldn't fetch account info ${recipient}`);
59
+ }
60
+
61
+ return result;
62
+ };
63
+
64
+ export const getServerInfos = async (
65
+ endpointConfig?: string | null | undefined,
66
+ ): Promise<ServerInfoResponse> => {
67
+ const {
68
+ data: { result },
69
+ } = await network<{ result: ServerInfoResponse }>({
70
+ method: "POST",
71
+ url: endpointConfig ?? getCoinConfig().node,
72
+ data: {
73
+ method: "server_info",
74
+ params: [
75
+ {
76
+ ledger_index: "validated",
77
+ },
78
+ ],
79
+ },
80
+ });
81
+
82
+ if (result.status !== "success") {
83
+ throw new Error(`couldn't fetch server info`);
84
+ }
85
+
86
+ return result;
87
+ };
88
+
89
+ export const getTransactions = async (
90
+ address: string,
91
+ options: { ledger_index_min?: number; ledger_index_max?: number } | undefined,
92
+ ): Promise<AccountTxResponse["transactions"]> => {
93
+ const {
94
+ data: { result },
95
+ } = await network<{ result: AccountTxResponse }>({
96
+ method: "POST",
97
+ url: getCoinConfig().node,
98
+ data: {
99
+ method: "account_tx",
100
+ params: [
101
+ {
102
+ account: address,
103
+ ledger_index: "validated",
104
+ ...options,
105
+ },
106
+ ],
107
+ },
108
+ });
109
+
110
+ if (result.status !== "success") {
111
+ throw new Error(`couldn't getTransactions for ${address}`);
112
+ }
113
+
114
+ return result.transactions;
115
+ };
116
+
117
+ export async function getLedgerIndex(): Promise<number> {
118
+ const {
119
+ data: { result },
120
+ } = await network<{ result: LedgerResponse }>({
121
+ method: "POST",
122
+ url: getCoinConfig().node,
123
+ data: {
124
+ method: "ledger",
125
+ params: [
126
+ {
127
+ ledger_index: "validated",
128
+ },
129
+ ],
130
+ },
131
+ });
132
+
133
+ if (result.status !== "success") {
134
+ throw new Error(`couldn't fetch getLedgerIndex`);
135
+ }
136
+
137
+ return result.ledger_index;
138
+ }
@@ -0,0 +1,191 @@
1
+ export type XrplOperation = {
2
+ meta: {
3
+ AffectedNodes: {
4
+ ModifiedNode: {
5
+ FinalFields: {
6
+ Account: string;
7
+ Balance: string;
8
+ Flags: number;
9
+ OwnerCount: number;
10
+ Sequence: number;
11
+ };
12
+ LedgerEntryType: string;
13
+ LedgerIndex: string;
14
+ PreviousFields: {
15
+ Balance: string;
16
+ };
17
+ PreviousTxnID: string;
18
+ PreviousTxnLgrSeq: number;
19
+ };
20
+ }[];
21
+ TransactionIndex: number;
22
+ TransactionResult: string;
23
+ delivered_amount: string;
24
+ };
25
+ tx: {
26
+ Account: string;
27
+ Amount: string;
28
+ DeliverMax: string;
29
+ Destination: string;
30
+ DestinationTag: number;
31
+ Fee: string;
32
+ Flags: number;
33
+ LastLedgerSequence: number;
34
+ Memos: {
35
+ Memo: {
36
+ MemoData: string;
37
+ };
38
+ }[];
39
+ Sequence: number;
40
+ SigningPubKey: string;
41
+ TransactionType: string;
42
+ TxnSignature: string;
43
+ date: number;
44
+ hash: string;
45
+ inLedger: number;
46
+ ledger_index: number;
47
+ };
48
+ validated: boolean;
49
+ };
50
+
51
+ type ResponseStatus =
52
+ | { status: string; error?: never }
53
+ | {
54
+ status?: never;
55
+ error: string;
56
+ };
57
+
58
+ export type AccountInfoResponse = {
59
+ account_data: {
60
+ Account: string;
61
+ Balance: string;
62
+ Flags: number;
63
+ LedgerEntryType: string;
64
+ OwnerCount: number;
65
+ PreviousTxnID: string;
66
+ PreviousTxnLgrSeq: number;
67
+ Sequence: number;
68
+ index: string;
69
+ };
70
+ account_flags: {
71
+ allowTrustLineClawback: boolean;
72
+ defaultRipple: boolean;
73
+ depositAuth: boolean;
74
+ disableMasterKey: boolean;
75
+ disallowIncomingCheck: boolean;
76
+ disallowIncomingNFTokenOffer: boolean;
77
+ disallowIncomingPayChan: boolean;
78
+ disallowIncomingTrustline: boolean;
79
+ disallowIncomingXRP: boolean;
80
+ globalFreeze: boolean;
81
+ noFreeze: boolean;
82
+ passwordSpent: boolean;
83
+ requireAuthorization: boolean;
84
+ requireDestinationTag: boolean;
85
+ };
86
+ ledger_hash: string;
87
+ ledger_index: number;
88
+ validated: boolean;
89
+ } & ResponseStatus;
90
+
91
+ export type SubmitReponse = {
92
+ accepted: boolean;
93
+ account_sequence_available: number;
94
+ account_sequence_next: number;
95
+ applied: boolean;
96
+ broadcast: boolean;
97
+ engine_result: string;
98
+ engine_result_code: 0;
99
+ engine_result_message: string;
100
+ kept: boolean;
101
+ open_ledger_cost: string;
102
+ queued: false;
103
+ status: string;
104
+ tx_blob: string;
105
+ tx_json: {
106
+ Account: string;
107
+ Amount: string;
108
+ Destination: string;
109
+ Fee: string;
110
+ Flags: number;
111
+ LastLedgerSequence: number;
112
+ Sequence: number;
113
+ SigningPubKey: string;
114
+ TransactionType: string;
115
+ TxnSignature: string;
116
+ hash: string;
117
+ };
118
+ validated_ledger_index: number;
119
+ };
120
+
121
+ export type ServerInfoResponse = {
122
+ info: {
123
+ build_version: string;
124
+ complete_ledgers: string;
125
+ hostid: string;
126
+ initial_sync_duration_us: string;
127
+ io_latency_ms: number;
128
+ jq_trans_overflow: string;
129
+ last_close: {
130
+ converge_time_s: number;
131
+ proposers: number;
132
+ };
133
+ load_factor: number;
134
+ network_id: number;
135
+ peer_disconnects: string;
136
+ peer_disconnects_resources: string;
137
+ peers: number;
138
+ ports: { port: string; protocol: ["ws" | "http" | "peer"] }[];
139
+ pubkey_node: string;
140
+ server_state: string;
141
+ server_state_duration_us: string;
142
+ state_accounting: Record<
143
+ "connected" | "disconnected" | "full" | "syncing" | "tracking",
144
+ {
145
+ duration_us: string;
146
+ transitions: string;
147
+ }
148
+ >;
149
+ time: string;
150
+ uptime: number;
151
+ validated_ledger: {
152
+ age: number;
153
+ base_fee_xrp: number;
154
+ hash: string;
155
+ reserve_base_xrp: number;
156
+ reserve_inc_xrp: number;
157
+ seq: number;
158
+ };
159
+ validation_quorum: number;
160
+ };
161
+ } & ResponseStatus;
162
+
163
+ export type AccountTxResponse = {
164
+ account: string;
165
+ ledger_index_max: number;
166
+ ledger_index_min: number;
167
+ limit: number;
168
+ transactions: XrplOperation[];
169
+ validated: boolean;
170
+ } & ResponseStatus;
171
+
172
+ export type LedgerResponse = {
173
+ ledger: {
174
+ account_hash: string;
175
+ close_flags: number;
176
+ close_time: number;
177
+ close_time_human: string;
178
+ close_time_iso: string;
179
+ close_time_resolution: number;
180
+ closed: boolean;
181
+ ledger_hash: string;
182
+ ledger_index: string;
183
+ parent_close_time: number;
184
+ parent_hash: string;
185
+ total_coins: string;
186
+ transaction_hash: string;
187
+ };
188
+ ledger_hash: string;
189
+ ledger_index: number;
190
+ validated: boolean;
191
+ } & ResponseStatus;
@@ -0,0 +1,56 @@
1
+ import {
2
+ makeAccountBridgeReceive,
3
+ defaultUpdateTransaction,
4
+ makeScanAccounts,
5
+ makeSync,
6
+ } from "@ledgerhq/coin-framework/bridge/jsHelpers";
7
+ import { SignerContext } from "@ledgerhq/coin-framework/signer";
8
+ import getAddressWrapper from "@ledgerhq/coin-framework/bridge/getAddressWrapper";
9
+ import type { AccountBridge, CurrencyBridge } from "@ledgerhq/types-live";
10
+ import { getTransactionStatus } from "../getTransactionStatus";
11
+ import { estimateMaxSpendable } from "../estimateMaxSpendable";
12
+ import { prepareTransaction } from "../prepareTransaction";
13
+ import { createTransaction } from "../createTransaction";
14
+ import { getAccountShape } from "../synchronization";
15
+ import { buildSignOperation } from "../signOperation";
16
+ import { XrpConfig, setCoinConfig } from "../config";
17
+ import type { Transaction } from "../types";
18
+ import { broadcast } from "../broadcast";
19
+ import resolver from "../hw-getAddress";
20
+ import { XrpSigner } from "../signer";
21
+
22
+ export function createBridges(
23
+ signerContext: SignerContext<XrpSigner>,
24
+ coinConfig: () => XrpConfig,
25
+ ) {
26
+ setCoinConfig(coinConfig);
27
+
28
+ const getAddress = resolver(signerContext);
29
+
30
+ const scanAccounts = makeScanAccounts({ getAccountShape, getAddressFn: getAddress });
31
+ const currencyBridge: CurrencyBridge = {
32
+ preload: () => Promise.resolve({}),
33
+ hydrate: () => {},
34
+ scanAccounts,
35
+ };
36
+
37
+ const receive = makeAccountBridgeReceive(getAddressWrapper(getAddress));
38
+ const signOperation = buildSignOperation(signerContext);
39
+ const sync = makeSync({ getAccountShape });
40
+ const accountBridge: AccountBridge<Transaction> = {
41
+ createTransaction,
42
+ updateTransaction: defaultUpdateTransaction<Transaction>,
43
+ prepareTransaction,
44
+ getTransactionStatus,
45
+ estimateMaxSpendable,
46
+ sync,
47
+ receive,
48
+ signOperation,
49
+ broadcast,
50
+ };
51
+
52
+ return {
53
+ currencyBridge,
54
+ accountBridge,
55
+ };
56
+ }
@@ -0,0 +1,20 @@
1
+ import { AccountBridge } from "@ledgerhq/types-live";
2
+ import { patchOperationWithHash } from "@ledgerhq/coin-framework/operation";
3
+ import { Transaction } from "./types";
4
+ import { submit } from "./api";
5
+
6
+ export const broadcast: AccountBridge<Transaction>["broadcast"] = async ({
7
+ signedOperation: { signature, operation },
8
+ }) => {
9
+ const submittedPayment = await submit(signature);
10
+
11
+ if (
12
+ submittedPayment.engine_result !== "tesSUCCESS" &&
13
+ submittedPayment.engine_result !== "terQUEUED"
14
+ ) {
15
+ throw new Error(submittedPayment.engine_result_message);
16
+ }
17
+
18
+ const { hash } = submittedPayment.tx_json;
19
+ return patchOperationWithHash(operation, hash);
20
+ };
@@ -0,0 +1,44 @@
1
+ import invariant from "invariant";
2
+ import type { AccountLike } from "@ledgerhq/types-live";
3
+ import type { Transaction } from "./types";
4
+ import BigNumber from "bignumber.js";
5
+
6
+ const options = [
7
+ {
8
+ name: "fee",
9
+ type: String,
10
+ desc: "how much fee",
11
+ },
12
+ {
13
+ name: "tag",
14
+ type: Number,
15
+ desc: "ripple tag",
16
+ },
17
+ ];
18
+
19
+ function inferTransactions(
20
+ transactions: Array<{
21
+ account: AccountLike;
22
+ transaction: Transaction;
23
+ }>,
24
+ opts: { tag?: number | null | undefined; fee?: string },
25
+ {
26
+ inferAmount,
27
+ }: { inferAmount: (account: AccountLike, fee?: string) => BigNumber | null | undefined },
28
+ ): Transaction[] {
29
+ return transactions.flatMap(({ transaction, account }) => {
30
+ invariant(transaction.family === "xrp", "XRP family");
31
+ return {
32
+ ...transaction,
33
+ fee: inferAmount(account, opts.fee || "0.001xrp"),
34
+ tag: opts.tag,
35
+ };
36
+ });
37
+ }
38
+
39
+ export default function makeCliTools() {
40
+ return {
41
+ options,
42
+ inferTransactions,
43
+ };
44
+ }
package/src/config.ts ADDED
@@ -0,0 +1,19 @@
1
+ import { CurrencyConfig } from "@ledgerhq/coin-framework/config";
2
+
3
+ export type XrpConfig = CurrencyConfig & {
4
+ node: string;
5
+ };
6
+
7
+ let coinConfig: () => XrpConfig | undefined;
8
+
9
+ export const setCoinConfig = (config: typeof coinConfig): void => {
10
+ coinConfig = config;
11
+ };
12
+
13
+ export const getCoinConfig = (): XrpConfig => {
14
+ if (!coinConfig?.()) {
15
+ throw new Error("Xrp module config not set");
16
+ }
17
+
18
+ return coinConfig()!;
19
+ };
@@ -0,0 +1,13 @@
1
+ import BigNumber from "bignumber.js";
2
+ import { Transaction } from "./types";
3
+ import { AccountBridge } from "@ledgerhq/types-live";
4
+
5
+ export const createTransaction: AccountBridge<Transaction>["createTransaction"] = () => ({
6
+ family: "xrp",
7
+ amount: new BigNumber(0),
8
+ recipient: "",
9
+ fee: null,
10
+ tag: undefined,
11
+ networkInfo: null,
12
+ feeCustomUnit: null,
13
+ });
@@ -0,0 +1,153 @@
1
+ import BigNumber from "bignumber.js";
2
+ import { DatasetTest } from "@ledgerhq/types-live";
3
+ import { InvalidAddressBecauseDestinationIsAlsoSource } from "@ledgerhq/errors";
4
+ import { fromTransactionRaw } from "../transaction";
5
+ import { Transaction } from "../types";
6
+
7
+ export const newAddress1 = "rZvBc5e2YR1A9otS3r9DyGh3NDP8XLLp4";
8
+
9
+ export const dataset: DatasetTest<Transaction> = {
10
+ implementations: ["mock", "ripplejs"],
11
+ currencies: {
12
+ ripple: {
13
+ scanAccounts: [
14
+ {
15
+ name: "ripple seed 1",
16
+ unstableAccounts: true,
17
+ // our account is getting spammed...
18
+ apdus: `
19
+ => e00200400d038000002c8000009080000000
20
+ <= 2103c73f64083463fa923e1530af6f558204853873c6a45cbfb1f2f1e2ac2a5d989c2272734a4675764165634c333153513750594864504b6b3335625a456f78446d5231789000
21
+ => e002004015058000002c80000090800000000000000000000000
22
+ <= 2103d1adcff3e0cf1232b1416a75cd6f23b49dd6a25c69bc291a1f6783ec6825ec062272616765584842365134566276765764547a4b414e776a65435434485846434b58379000
23
+ => e002004015058000002c80000090800000010000000000000000
24
+ <= 21036da109ee84825eab0f55fb57bcf9ef0b05621e71fb0400266fb42d6f68f9487c2272425065393169766d67384347573450414e6f657555555173756d337470786a55469000
25
+ => e002004015058000002c80000090800000020000000000000000
26
+ <= 2102df9a55b79fb3668dac70fee7372806195841cd713ab8da9fba82240f9db8a23921725a76426335653259523141396f745333723944794768334e445038584c4c70349000
27
+ `,
28
+ },
29
+ ],
30
+ accounts: [
31
+ {
32
+ transactions: [
33
+ // FIXME
34
+
35
+ /*
36
+ {
37
+ name: "not enough spendable balance with base reserve",
38
+ transaction: fromTransactionRaw({
39
+ family: "xrp",
40
+ recipient: "rB6pwovsyrFWhPYUsjj9V3CHck985QjiXi",
41
+ amount: "15000000",
42
+ tag: null,
43
+ fee: "1",
44
+ feeCustomUnit: null,
45
+ networkInfo: null,
46
+ }),
47
+ expectedStatus: {
48
+ amount: BigNumber("15000000"),
49
+ estimatedFees: BigNumber("1"),
50
+ errors: {
51
+ amount: new NotEnoughSpendableBalance(null, {
52
+ minimumAmount: formatCurrencyUnit(
53
+ rippleUnit,
54
+ BigNumber("20"),
55
+ {
56
+ disableRounding: true,
57
+ useGrouping: false,
58
+ showCode: true,
59
+ }
60
+ ),
61
+ }),
62
+ },
63
+ warnings: {},
64
+ totalSpent: BigNumber("15000001"),
65
+ },
66
+ },
67
+ */
68
+ // FIXME
69
+
70
+ /*
71
+ {
72
+ name: "operation amount to low to create the recipient account",
73
+ transaction: fromTransactionRaw({
74
+ family: "xrp",
75
+ recipient: newAddress1,
76
+ amount: "10000000",
77
+ tag: null,
78
+ fee: "1",
79
+ feeCustomUnit: null,
80
+ networkInfo: null
81
+ }),
82
+ expectedStatus: {
83
+ amount: BigNumber("10000000"),
84
+ estimatedFees: BigNumber("1"),
85
+ errors: {
86
+ amount: new NotEnoughBalanceBecauseDestinationNotCreated()
87
+ },
88
+ warnings: {},
89
+ totalSpent: BigNumber("10000001")
90
+ }
91
+ },
92
+ */
93
+ {
94
+ name: "recipient and sender must not be the same",
95
+ transaction: fromTransactionRaw({
96
+ family: "xrp",
97
+ recipient: "rageXHB6Q4VbvvWdTzKANwjeCT4HXFCKX7",
98
+ amount: "10000000",
99
+ tag: null,
100
+ fee: "1",
101
+ feeCustomUnit: null,
102
+ networkInfo: null,
103
+ }),
104
+ expectedStatus: {
105
+ amount: new BigNumber("10000000"),
106
+ estimatedFees: new BigNumber("1"),
107
+ errors: {
108
+ recipient: new InvalidAddressBecauseDestinationIsAlsoSource(),
109
+ },
110
+ warnings: {},
111
+ totalSpent: new BigNumber("10000001"),
112
+ },
113
+ },
114
+ {
115
+ name: "Operation with tag succeed",
116
+ transaction: fromTransactionRaw({
117
+ family: "xrp",
118
+ recipient: "rB6pwovsyrFWhPYUsjj9V3CHck985QjiXi",
119
+ amount: "10000000",
120
+ tag: 12345,
121
+ fee: "1",
122
+ feeCustomUnit: null,
123
+ networkInfo: null,
124
+ }),
125
+ expectedStatus: {
126
+ amount: new BigNumber("10000000"),
127
+ estimatedFees: new BigNumber("1"),
128
+ errors: {},
129
+ warnings: {},
130
+ totalSpent: new BigNumber("10000001"),
131
+ },
132
+ },
133
+ ],
134
+ raw: {
135
+ id: "ripplejs:2:ripple:rageXHB6Q4VbvvWdTzKANwjeCT4HXFCKX7:",
136
+ seedIdentifier: "rageXHB6Q4VbvvWdTzKANwjeCT4HXFCKX7",
137
+ name: "XRP 1",
138
+ derivationMode: "",
139
+ index: 0,
140
+ freshAddress: "rageXHB6Q4VbvvWdTzKANwjeCT4HXFCKX7",
141
+ freshAddressPath: "44'/144'/0'/0/0",
142
+ blockHeight: 0,
143
+ operations: [],
144
+ pendingOperations: [],
145
+ currencyId: "ripple",
146
+ lastSyncDate: "",
147
+ balance: "21000310",
148
+ },
149
+ },
150
+ ],
151
+ },
152
+ },
153
+ };
@@ -0,0 +1,41 @@
1
+ import type { AccountLike, Account } from "@ledgerhq/types-live";
2
+ import type { Transaction, TransactionStatus } from "./types";
3
+ import type { CommonDeviceTransactionField } from "@ledgerhq/coin-framework/transaction/common";
4
+
5
+ function getDeviceTransactionConfig({
6
+ transaction: { tag },
7
+ status: { amount, estimatedFees },
8
+ }: {
9
+ account: AccountLike;
10
+ parentAccount: Account | null | undefined;
11
+ transaction: Transaction;
12
+ status: TransactionStatus;
13
+ }): Array<CommonDeviceTransactionField> {
14
+ const fields: Array<CommonDeviceTransactionField> = [];
15
+
16
+ if (!amount.isZero()) {
17
+ fields.push({
18
+ type: "amount",
19
+ label: "Amount",
20
+ });
21
+ }
22
+
23
+ if (!estimatedFees.isZero()) {
24
+ fields.push({
25
+ type: "fees",
26
+ label: "Fees",
27
+ });
28
+ }
29
+
30
+ if (tag) {
31
+ fields.push({
32
+ type: "text",
33
+ label: "Tag",
34
+ value: tag ? String(tag) : "",
35
+ });
36
+ }
37
+
38
+ return fields;
39
+ }
40
+
41
+ export default getDeviceTransactionConfig;
@@ -0,0 +1,25 @@
1
+ import BigNumber from "bignumber.js";
2
+ import { AccountBridge } from "@ledgerhq/types-live";
3
+ import { getMainAccount } from "@ledgerhq/coin-framework/account/index";
4
+ import { getAbandonSeedAddress } from "@ledgerhq/cryptoassets/abandonseed";
5
+ import { getTransactionStatus } from "./getTransactionStatus";
6
+ import { prepareTransaction } from "./prepareTransaction";
7
+ import { createTransaction } from "./createTransaction";
8
+ import { Transaction } from "./types";
9
+
10
+ export const estimateMaxSpendable: AccountBridge<Transaction>["estimateMaxSpendable"] = async ({
11
+ account,
12
+ parentAccount,
13
+ transaction,
14
+ }) => {
15
+ const mainAccount = getMainAccount(account, parentAccount);
16
+ const newTransaction = await prepareTransaction(mainAccount, {
17
+ ...createTransaction(account),
18
+ ...transaction,
19
+ recipient: transaction?.recipient || getAbandonSeedAddress("ripple"),
20
+ // public testing seed abandonx11,about
21
+ amount: new BigNumber(0),
22
+ });
23
+ const status = await getTransactionStatus(mainAccount, newTransaction);
24
+ return BigNumber.max(0, account.spendableBalance.minus(status.estimatedFees));
25
+ };