@ledgerhq/coin-canton 0.7.0 → 0.8.0-nightly.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -0
- package/lib/api/index.d.ts.map +1 -1
- package/lib/api/index.js +3 -0
- package/lib/api/index.js.map +1 -1
- package/lib/api/lastBlock.integ.test.js +0 -15
- package/lib/api/lastBlock.integ.test.js.map +1 -1
- package/lib/bridge/getTransactionStatus.test.d.ts +2 -0
- package/lib/bridge/getTransactionStatus.test.d.ts.map +1 -0
- package/lib/bridge/getTransactionStatus.test.js +365 -0
- package/lib/bridge/getTransactionStatus.test.js.map +1 -0
- package/lib/bridge/index.d.ts.map +1 -1
- package/lib/bridge/index.js +5 -5
- package/lib/bridge/index.js.map +1 -1
- package/lib/bridge/onboard.d.ts +11 -6
- package/lib/bridge/onboard.d.ts.map +1 -1
- package/lib/bridge/onboard.integ.test.js +49 -27
- package/lib/bridge/onboard.integ.test.js.map +1 -1
- package/lib/bridge/onboard.js +45 -152
- package/lib/bridge/onboard.js.map +1 -1
- package/lib/bridge/signOperation.d.ts.map +1 -1
- package/lib/bridge/signOperation.js +5 -5
- package/lib/bridge/signOperation.js.map +1 -1
- package/lib/bridge/sync.d.ts +3 -2
- package/lib/bridge/sync.d.ts.map +1 -1
- package/lib/bridge/sync.integ.test.js +39 -17
- package/lib/bridge/sync.integ.test.js.map +1 -1
- package/lib/bridge/sync.js +71 -57
- package/lib/bridge/sync.js.map +1 -1
- package/lib/common-logic/utils.d.ts.map +1 -1
- package/lib/common-logic/utils.js +3 -1
- package/lib/common-logic/utils.js.map +1 -1
- package/lib/common-logic/utils.test.d.ts +2 -0
- package/lib/common-logic/utils.test.d.ts.map +1 -0
- package/lib/common-logic/utils.test.js +104 -0
- package/lib/common-logic/utils.test.js.map +1 -0
- package/lib/config.d.ts +1 -1
- package/lib/config.d.ts.map +1 -1
- package/lib/network/gateway.d.ts +14 -10
- package/lib/network/gateway.d.ts.map +1 -1
- package/lib/network/gateway.integ.test.js +31 -17
- package/lib/network/gateway.integ.test.js.map +1 -1
- package/lib/network/gateway.js +34 -16
- package/lib/network/gateway.js.map +1 -1
- package/lib/network/gateway.test.d.ts +2 -0
- package/lib/network/gateway.test.d.ts.map +1 -0
- package/lib/network/gateway.test.js +59 -0
- package/lib/network/gateway.test.js.map +1 -0
- package/lib/types/bridge.d.ts +6 -16
- package/lib/types/bridge.d.ts.map +1 -1
- package/lib/types/onboard.d.ts +5 -5
- package/lib/types/onboard.d.ts.map +1 -1
- package/lib/types/onboard.js +10 -10
- package/lib/types/onboard.js.map +1 -1
- package/lib-es/api/index.d.ts.map +1 -1
- package/lib-es/api/index.js +3 -0
- package/lib-es/api/index.js.map +1 -1
- package/lib-es/api/lastBlock.integ.test.js +0 -15
- package/lib-es/api/lastBlock.integ.test.js.map +1 -1
- package/lib-es/bridge/getTransactionStatus.test.d.ts +2 -0
- package/lib-es/bridge/getTransactionStatus.test.d.ts.map +1 -0
- package/lib-es/bridge/getTransactionStatus.test.js +360 -0
- package/lib-es/bridge/getTransactionStatus.test.js.map +1 -0
- package/lib-es/bridge/index.d.ts.map +1 -1
- package/lib-es/bridge/index.js +6 -6
- package/lib-es/bridge/index.js.map +1 -1
- package/lib-es/bridge/onboard.d.ts +11 -6
- package/lib-es/bridge/onboard.d.ts.map +1 -1
- package/lib-es/bridge/onboard.integ.test.js +37 -15
- package/lib-es/bridge/onboard.integ.test.js.map +1 -1
- package/lib-es/bridge/onboard.js +44 -152
- package/lib-es/bridge/onboard.js.map +1 -1
- package/lib-es/bridge/signOperation.d.ts.map +1 -1
- package/lib-es/bridge/signOperation.js +5 -5
- package/lib-es/bridge/signOperation.js.map +1 -1
- package/lib-es/bridge/sync.d.ts +3 -2
- package/lib-es/bridge/sync.d.ts.map +1 -1
- package/lib-es/bridge/sync.integ.test.js +34 -12
- package/lib-es/bridge/sync.integ.test.js.map +1 -1
- package/lib-es/bridge/sync.js +71 -56
- package/lib-es/bridge/sync.js.map +1 -1
- package/lib-es/common-logic/utils.d.ts.map +1 -1
- package/lib-es/common-logic/utils.js +3 -1
- package/lib-es/common-logic/utils.js.map +1 -1
- package/lib-es/common-logic/utils.test.d.ts +2 -0
- package/lib-es/common-logic/utils.test.d.ts.map +1 -0
- package/lib-es/common-logic/utils.test.js +99 -0
- package/lib-es/common-logic/utils.test.js.map +1 -0
- package/lib-es/config.d.ts +1 -1
- package/lib-es/config.d.ts.map +1 -1
- package/lib-es/network/gateway.d.ts +14 -10
- package/lib-es/network/gateway.d.ts.map +1 -1
- package/lib-es/network/gateway.integ.test.js +31 -17
- package/lib-es/network/gateway.integ.test.js.map +1 -1
- package/lib-es/network/gateway.js +34 -16
- package/lib-es/network/gateway.js.map +1 -1
- package/lib-es/network/gateway.test.d.ts +2 -0
- package/lib-es/network/gateway.test.d.ts.map +1 -0
- package/lib-es/network/gateway.test.js +54 -0
- package/lib-es/network/gateway.test.js.map +1 -0
- package/lib-es/types/bridge.d.ts +6 -16
- package/lib-es/types/bridge.d.ts.map +1 -1
- package/lib-es/types/onboard.d.ts +5 -5
- package/lib-es/types/onboard.d.ts.map +1 -1
- package/lib-es/types/onboard.js +9 -9
- package/lib-es/types/onboard.js.map +1 -1
- package/package.json +6 -5
- package/src/api/index.ts +9 -0
- package/src/api/lastBlock.integ.test.ts +0 -18
- package/src/bridge/getTransactionStatus.test.ts +446 -0
- package/src/bridge/index.ts +6 -6
- package/src/bridge/onboard.integ.test.ts +44 -31
- package/src/bridge/onboard.ts +61 -209
- package/src/bridge/signOperation.ts +5 -6
- package/src/bridge/sync.integ.test.ts +38 -13
- package/src/bridge/sync.ts +90 -72
- package/src/common-logic/utils.test.ts +108 -0
- package/src/common-logic/utils.ts +4 -1
- package/src/config.ts +1 -1
- package/src/network/gateway.integ.test.ts +48 -21
- package/src/network/gateway.test.ts +66 -0
- package/src/network/gateway.ts +60 -37
- package/src/types/bridge.ts +8 -19
- package/src/types/onboard.ts +5 -5
- package/lib/bridge/serialization.d.ts +0 -4
- package/lib/bridge/serialization.d.ts.map +0 -1
- package/lib/bridge/serialization.js +0 -31
- package/lib/bridge/serialization.js.map +0 -1
- package/lib-es/bridge/serialization.d.ts +0 -4
- package/lib-es/bridge/serialization.d.ts.map +0 -1
- package/lib-es/bridge/serialization.js +0 -27
- package/lib-es/bridge/serialization.js.map +0 -1
- package/src/bridge/serialization.ts +0 -36
package/src/bridge/onboard.ts
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import { Observable } from "rxjs";
|
|
2
2
|
import { SignerContext } from "@ledgerhq/coin-framework/signer";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import { CantonAccount, CantonSigner } from "../types";
|
|
7
|
-
import type { Account, DerivationMode } from "@ledgerhq/types-live";
|
|
3
|
+
import type { Account, Operation } from "@ledgerhq/types-live";
|
|
4
|
+
import type { CryptoCurrency } from "@ledgerhq/types-cryptoassets";
|
|
5
|
+
import { log } from "@ledgerhq/logs";
|
|
8
6
|
import {
|
|
9
7
|
prepareOnboarding,
|
|
10
8
|
submitOnboarding,
|
|
@@ -16,148 +14,84 @@ import {
|
|
|
16
14
|
} from "../network/gateway";
|
|
17
15
|
import {
|
|
18
16
|
OnboardStatus,
|
|
19
|
-
|
|
17
|
+
AuthorizeStatus,
|
|
20
18
|
CantonOnboardProgress,
|
|
21
19
|
CantonOnboardResult,
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
PrepareTransactionResponse,
|
|
20
|
+
CantonAuthorizeProgress,
|
|
21
|
+
CantonAuthorizeResult,
|
|
25
22
|
} from "../types/onboard";
|
|
26
23
|
import resolver from "../signer";
|
|
27
|
-
import {
|
|
24
|
+
import type { CantonSigner } from "../types";
|
|
28
25
|
|
|
29
|
-
async
|
|
30
|
-
signerContext: SignerContext<CantonSigner>,
|
|
31
|
-
deviceId: string,
|
|
32
|
-
derivationPath: string,
|
|
33
|
-
) {
|
|
34
|
-
return signerContext(deviceId, async signer => {
|
|
35
|
-
const { publicKey, address } = await signer.getAddress(derivationPath);
|
|
36
|
-
return { signer, publicKey: publicKey.replace("0x", ""), address };
|
|
37
|
-
});
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export const isAccountOnboarded = async (
|
|
41
|
-
currency: CryptoCurrency,
|
|
42
|
-
publicKey: string,
|
|
43
|
-
): Promise<{ isOnboarded: boolean; party_id?: string }> => {
|
|
26
|
+
export const isAccountOnboarded = async (currency: CryptoCurrency, publicKey: string) => {
|
|
44
27
|
try {
|
|
45
28
|
const { party_id } = await getPartyByPubKey(currency, publicKey);
|
|
46
29
|
|
|
47
30
|
if (party_id) {
|
|
48
|
-
return { isOnboarded: true, party_id };
|
|
31
|
+
return { isOnboarded: true, partyId: party_id };
|
|
49
32
|
} else {
|
|
50
33
|
return { isOnboarded: false };
|
|
51
34
|
}
|
|
52
35
|
} catch (err) {
|
|
53
|
-
log("[isAccountOnboarded] Error checking party status (likely not onboarded):", err);
|
|
54
36
|
return { isOnboarded: false };
|
|
55
37
|
}
|
|
56
38
|
};
|
|
57
39
|
|
|
40
|
+
export const isAccountAuthorized = async (operations: Operation[], partyId: string) => {
|
|
41
|
+
// temporary solution to check if the account is authorized
|
|
42
|
+
return operations.some(operation => operation.senders.includes(partyId));
|
|
43
|
+
};
|
|
44
|
+
|
|
58
45
|
export const buildOnboardAccount =
|
|
59
46
|
(signerContext: SignerContext<CantonSigner>) =>
|
|
60
47
|
(
|
|
61
48
|
currency: CryptoCurrency,
|
|
62
49
|
deviceId: string,
|
|
63
|
-
|
|
50
|
+
account: Account,
|
|
64
51
|
): Observable<CantonOnboardProgress | CantonOnboardResult> =>
|
|
65
|
-
new Observable(
|
|
52
|
+
new Observable(o => {
|
|
66
53
|
async function main() {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
});
|
|
70
|
-
const derivationMode = getDerivationModesForCurrency(currency)[0];
|
|
54
|
+
o.next({ status: OnboardStatus.INIT });
|
|
55
|
+
|
|
71
56
|
const getAddress = resolver(signerContext);
|
|
72
|
-
const {
|
|
73
|
-
path:
|
|
57
|
+
const { publicKey } = await getAddress(deviceId, {
|
|
58
|
+
path: account.freshAddressPath,
|
|
74
59
|
currency,
|
|
75
|
-
derivationMode: derivationMode
|
|
60
|
+
derivationMode: account.derivationMode,
|
|
76
61
|
});
|
|
77
62
|
|
|
78
|
-
|
|
79
|
-
status: OnboardStatus.PREPARE,
|
|
80
|
-
});
|
|
63
|
+
o.next({ status: OnboardStatus.PREPARE });
|
|
81
64
|
|
|
82
|
-
|
|
65
|
+
let { partyId } = await isAccountOnboarded(currency, publicKey);
|
|
83
66
|
if (partyId) {
|
|
84
|
-
|
|
85
|
-
address,
|
|
86
|
-
derivationPath,
|
|
87
|
-
partyId,
|
|
88
|
-
currency,
|
|
89
|
-
derivationMode,
|
|
90
|
-
});
|
|
91
|
-
observer.next({
|
|
92
|
-
partyId,
|
|
93
|
-
account,
|
|
94
|
-
});
|
|
95
|
-
observer.complete();
|
|
67
|
+
o.next({ partyId, account }); // success
|
|
96
68
|
return;
|
|
97
69
|
}
|
|
98
70
|
|
|
99
|
-
const preparedTransaction = await prepareOnboarding(currency, publicKey
|
|
71
|
+
const preparedTransaction = await prepareOnboarding(currency, publicKey);
|
|
72
|
+
partyId = preparedTransaction.party_id;
|
|
100
73
|
|
|
101
|
-
|
|
102
|
-
status: OnboardStatus.SIGN,
|
|
103
|
-
});
|
|
74
|
+
o.next({ status: OnboardStatus.SIGN });
|
|
104
75
|
|
|
105
76
|
const signature = await signerContext(deviceId, signer =>
|
|
106
|
-
signer.signTransaction(
|
|
77
|
+
signer.signTransaction(
|
|
78
|
+
account.freshAddressPath,
|
|
79
|
+
preparedTransaction.transactions.combined_hash,
|
|
80
|
+
),
|
|
107
81
|
);
|
|
108
82
|
|
|
109
|
-
|
|
110
|
-
status: OnboardStatus.SUBMIT,
|
|
111
|
-
});
|
|
83
|
+
o.next({ status: OnboardStatus.SUBMIT });
|
|
112
84
|
|
|
113
|
-
|
|
114
|
-
currency,
|
|
115
|
-
{ public_key: publicKey, public_key_type: "ed25519" },
|
|
116
|
-
preparedTransaction,
|
|
117
|
-
signature,
|
|
118
|
-
).catch(async err => {
|
|
119
|
-
if (err.type === "PARTY_ALREADY_EXISTS") {
|
|
120
|
-
const account = await createAccount({
|
|
121
|
-
address,
|
|
122
|
-
derivationPath,
|
|
123
|
-
partyId: preparedTransaction.party_id,
|
|
124
|
-
currency,
|
|
125
|
-
derivationMode,
|
|
126
|
-
});
|
|
127
|
-
observer.next({
|
|
128
|
-
partyId: preparedTransaction.party_id,
|
|
129
|
-
account,
|
|
130
|
-
});
|
|
131
|
-
return observer.complete();
|
|
132
|
-
}
|
|
133
|
-
throw err;
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
if (result) {
|
|
137
|
-
observer.next({
|
|
138
|
-
status: OnboardStatus.SUCCESS,
|
|
139
|
-
});
|
|
140
|
-
const account = await createAccount({
|
|
141
|
-
address,
|
|
142
|
-
derivationPath,
|
|
143
|
-
partyId: result.party.party_id,
|
|
144
|
-
currency,
|
|
145
|
-
derivationMode,
|
|
146
|
-
});
|
|
147
|
-
observer.next({
|
|
148
|
-
partyId: result.party.party_id,
|
|
149
|
-
account,
|
|
150
|
-
});
|
|
151
|
-
}
|
|
85
|
+
await submitOnboarding(currency, publicKey, preparedTransaction, signature);
|
|
152
86
|
|
|
153
|
-
|
|
87
|
+
o.next({ partyId, account }); // success
|
|
154
88
|
}
|
|
155
89
|
|
|
156
90
|
main().then(
|
|
157
|
-
() =>
|
|
91
|
+
() => o.complete(),
|
|
158
92
|
error => {
|
|
159
|
-
log("[
|
|
160
|
-
|
|
93
|
+
log("[canton:onboard] onboardAccount failed:", error);
|
|
94
|
+
o.error(error);
|
|
161
95
|
},
|
|
162
96
|
);
|
|
163
97
|
});
|
|
@@ -167,146 +101,64 @@ export const buildAuthorizePreapproval =
|
|
|
167
101
|
(
|
|
168
102
|
currency: CryptoCurrency,
|
|
169
103
|
deviceId: string,
|
|
170
|
-
|
|
104
|
+
account: Account,
|
|
171
105
|
partyId: string,
|
|
172
|
-
): Observable<
|
|
173
|
-
new Observable(
|
|
106
|
+
): Observable<CantonAuthorizeProgress | CantonAuthorizeResult> =>
|
|
107
|
+
new Observable(o => {
|
|
174
108
|
async function main() {
|
|
175
|
-
|
|
176
|
-
status: PreApprovalStatus.PREPARE,
|
|
177
|
-
});
|
|
109
|
+
o.next({ status: AuthorizeStatus.INIT });
|
|
178
110
|
|
|
179
|
-
const
|
|
180
|
-
currency,
|
|
181
|
-
partyId,
|
|
182
|
-
);
|
|
111
|
+
const isAuthorized = await isAccountAuthorized(account.operations, partyId);
|
|
183
112
|
|
|
184
|
-
|
|
185
|
-
status:
|
|
186
|
-
});
|
|
113
|
+
if (!isAuthorized) {
|
|
114
|
+
o.next({ status: AuthorizeStatus.PREPARE });
|
|
187
115
|
|
|
188
|
-
|
|
189
|
-
signer.signTransaction(derivationPath, preparedTransaction.hash),
|
|
190
|
-
);
|
|
116
|
+
const preparedTransaction = await preparePreApprovalTransaction(currency, partyId);
|
|
191
117
|
|
|
192
|
-
|
|
193
|
-
status: PreApprovalStatus.SUBMIT,
|
|
194
|
-
});
|
|
118
|
+
o.next({ status: AuthorizeStatus.SIGN });
|
|
195
119
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
preparedTransaction,
|
|
200
|
-
signature,
|
|
201
|
-
);
|
|
120
|
+
const signature = await signerContext(deviceId, signer =>
|
|
121
|
+
signer.signTransaction(account.freshAddressPath, preparedTransaction.hash),
|
|
122
|
+
);
|
|
202
123
|
|
|
203
|
-
|
|
204
|
-
status: PreApprovalStatus.SUCCESS,
|
|
205
|
-
});
|
|
124
|
+
o.next({ status: AuthorizeStatus.SUBMIT });
|
|
206
125
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
126
|
+
await submitPreApprovalTransaction(currency, partyId, preparedTransaction, signature);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
o.next({ isApproved: true }); // success
|
|
210
130
|
|
|
211
131
|
const handleTapRequest = async () => {
|
|
212
132
|
try {
|
|
213
|
-
const { serialized, hash } = await prepareTapRequest(currency, {
|
|
214
|
-
partyId,
|
|
215
|
-
});
|
|
133
|
+
const { serialized, hash } = await prepareTapRequest(currency, { partyId });
|
|
216
134
|
|
|
217
135
|
if (serialized && hash) {
|
|
218
|
-
|
|
219
|
-
status: PreApprovalStatus.SIGN,
|
|
220
|
-
});
|
|
136
|
+
o.next({ status: AuthorizeStatus.SIGN });
|
|
221
137
|
|
|
222
138
|
const signature = await signerContext(deviceId, signer =>
|
|
223
|
-
signer.signTransaction(
|
|
139
|
+
signer.signTransaction(account.freshAddressPath, hash),
|
|
224
140
|
);
|
|
225
141
|
|
|
226
|
-
|
|
227
|
-
status: PreApprovalStatus.SUBMIT,
|
|
228
|
-
});
|
|
142
|
+
o.next({ status: AuthorizeStatus.SUBMIT });
|
|
229
143
|
|
|
230
144
|
await submitTapRequest(currency, {
|
|
231
145
|
partyId,
|
|
232
146
|
serialized,
|
|
233
147
|
signature,
|
|
234
148
|
});
|
|
235
|
-
|
|
236
|
-
observer.next({
|
|
237
|
-
status: PreApprovalStatus.SUCCESS,
|
|
238
|
-
});
|
|
239
149
|
}
|
|
240
150
|
} catch (err) {
|
|
241
151
|
// Tap request failure should not break the pre-approval flow
|
|
242
152
|
}
|
|
243
153
|
};
|
|
244
154
|
await handleTapRequest();
|
|
245
|
-
|
|
246
|
-
observer.complete();
|
|
247
155
|
}
|
|
248
156
|
|
|
249
157
|
main().then(
|
|
250
|
-
() =>
|
|
158
|
+
() => o.complete(),
|
|
251
159
|
error => {
|
|
252
|
-
log("[
|
|
253
|
-
|
|
160
|
+
log("[canton:onboard] authorizePreapproval failed:", error);
|
|
161
|
+
o.error(error);
|
|
254
162
|
},
|
|
255
163
|
);
|
|
256
164
|
});
|
|
257
|
-
|
|
258
|
-
const createAccount = async ({
|
|
259
|
-
address,
|
|
260
|
-
partyId,
|
|
261
|
-
derivationPath,
|
|
262
|
-
currency,
|
|
263
|
-
derivationMode,
|
|
264
|
-
index = 0,
|
|
265
|
-
}: {
|
|
266
|
-
address: string;
|
|
267
|
-
derivationPath: string;
|
|
268
|
-
partyId: string;
|
|
269
|
-
currency: CryptoCurrency;
|
|
270
|
-
derivationMode: DerivationMode;
|
|
271
|
-
index?: number;
|
|
272
|
-
}): Promise<Partial<Account>> => {
|
|
273
|
-
const accountShape = await getAccountShape(
|
|
274
|
-
{
|
|
275
|
-
address,
|
|
276
|
-
currency,
|
|
277
|
-
derivationMode,
|
|
278
|
-
derivationPath,
|
|
279
|
-
index,
|
|
280
|
-
rest: {
|
|
281
|
-
cantonResources: {
|
|
282
|
-
partyId,
|
|
283
|
-
},
|
|
284
|
-
},
|
|
285
|
-
},
|
|
286
|
-
{ paginationConfig: {} },
|
|
287
|
-
);
|
|
288
|
-
|
|
289
|
-
const account: Partial<CantonAccount> = {
|
|
290
|
-
...accountShape,
|
|
291
|
-
type: "Account",
|
|
292
|
-
xpub: partyId.replace(/:/g, "_"),
|
|
293
|
-
index,
|
|
294
|
-
// operations: [],
|
|
295
|
-
currency,
|
|
296
|
-
derivationMode,
|
|
297
|
-
lastSyncDate: new Date(),
|
|
298
|
-
pendingOperations: [],
|
|
299
|
-
seedIdentifier: address,
|
|
300
|
-
balanceHistoryCache: emptyHistoryCache,
|
|
301
|
-
cantonResources: {
|
|
302
|
-
partyId,
|
|
303
|
-
},
|
|
304
|
-
};
|
|
305
|
-
|
|
306
|
-
return account;
|
|
307
|
-
};
|
|
308
|
-
|
|
309
|
-
const log = (message: string, ...rest: unknown[]) => {
|
|
310
|
-
// eslint-disable-next-line no-console
|
|
311
|
-
console.log(message, ...rest);
|
|
312
|
-
};
|
|
@@ -4,6 +4,7 @@ import { FeeNotLoaded } from "@ledgerhq/errors";
|
|
|
4
4
|
import { AccountBridge, Operation } from "@ledgerhq/types-live";
|
|
5
5
|
import { SignerContext } from "@ledgerhq/coin-framework/signer";
|
|
6
6
|
import { encodeOperationId } from "@ledgerhq/coin-framework/operation";
|
|
7
|
+
import { decodeAccountId } from "@ledgerhq/coin-framework/account";
|
|
7
8
|
import { combine, craftTransaction } from "../common-logic";
|
|
8
9
|
import { Transaction, CantonSigner } from "../types";
|
|
9
10
|
|
|
@@ -22,10 +23,8 @@ export const buildSignOperation =
|
|
|
22
23
|
});
|
|
23
24
|
|
|
24
25
|
const signature = await signerContext(deviceId, async signer => {
|
|
25
|
-
const { freshAddressPath: derivationPath } = account;
|
|
26
|
-
const
|
|
27
|
-
.cantonResources.partyId;
|
|
28
|
-
|
|
26
|
+
const { id, freshAddressPath: derivationPath, xpub } = account;
|
|
27
|
+
const address = xpub ?? decodeAccountId(id).xpubOrAddress;
|
|
29
28
|
const params: {
|
|
30
29
|
recipient?: string;
|
|
31
30
|
amount: BigNumber;
|
|
@@ -45,13 +44,13 @@ export const buildSignOperation =
|
|
|
45
44
|
const { hash, serializedTransaction } = await craftTransaction(
|
|
46
45
|
account.currency,
|
|
47
46
|
{
|
|
48
|
-
address
|
|
47
|
+
address,
|
|
49
48
|
},
|
|
50
49
|
params,
|
|
51
50
|
);
|
|
52
51
|
const transactionSignature = await signer.signTransaction(derivationPath, hash);
|
|
53
52
|
|
|
54
|
-
return combine(serializedTransaction, `${transactionSignature}__PARTY__${
|
|
53
|
+
return combine(serializedTransaction, `${transactionSignature}__PARTY__${address}`);
|
|
55
54
|
});
|
|
56
55
|
|
|
57
56
|
o.next({
|
|
@@ -9,7 +9,9 @@ import { getCryptoCurrencyById } from "@ledgerhq/cryptoassets/currencies";
|
|
|
9
9
|
import { Account, Operation } from "@ledgerhq/types-live";
|
|
10
10
|
import coinConfig from "../config";
|
|
11
11
|
import * as gateway from "../network/gateway";
|
|
12
|
-
import {
|
|
12
|
+
import { generateMockKeyPair, createMockSigner } from "../test/cantonTestUtils";
|
|
13
|
+
import { CantonAccount } from "../types";
|
|
14
|
+
import { makeGetAccountShape } from "./sync";
|
|
13
15
|
|
|
14
16
|
const TEST_ADDRESS =
|
|
15
17
|
"b6400f93ea1c74aea86be39b0ccc846fc5de01f12b2ad0d7c31848d6fb6eb6d9::1220c81315e2bf2524a9141bcc6cbf19b61c151e0dcaa95343c0ccf53aed7415c4ec";
|
|
@@ -22,14 +24,25 @@ const derivationPath = runDerivationScheme(
|
|
|
22
24
|
account: 0,
|
|
23
25
|
},
|
|
24
26
|
);
|
|
25
|
-
const
|
|
27
|
+
const xpub = TEST_ADDRESS;
|
|
28
|
+
const ACCOUNT_SHAPE_INFO: AccountShapeInfo<CantonAccount> = {
|
|
26
29
|
address: TEST_ADDRESS,
|
|
27
30
|
currency,
|
|
28
31
|
derivationMode,
|
|
29
32
|
derivationPath,
|
|
30
33
|
index: 0,
|
|
34
|
+
initialAccount: {
|
|
35
|
+
xpub,
|
|
36
|
+
} as CantonAccount,
|
|
31
37
|
};
|
|
32
38
|
|
|
39
|
+
// Mock signer context for testing
|
|
40
|
+
const keyPair = generateMockKeyPair();
|
|
41
|
+
const mockSigner = createMockSigner(keyPair);
|
|
42
|
+
const mockSignerContext = jest.fn().mockImplementation((deviceId, callback) => {
|
|
43
|
+
return callback(mockSigner);
|
|
44
|
+
});
|
|
45
|
+
|
|
33
46
|
describe("sync (devnet)", () => {
|
|
34
47
|
beforeAll(async () => {
|
|
35
48
|
coinConfig.setCoinConfig(() => ({
|
|
@@ -43,13 +56,14 @@ describe("sync (devnet)", () => {
|
|
|
43
56
|
}));
|
|
44
57
|
});
|
|
45
58
|
|
|
46
|
-
describe("
|
|
59
|
+
describe("makeGetAccountShape", () => {
|
|
47
60
|
it("should fetch account shape for a valid address", async () => {
|
|
61
|
+
const getAccountShape = makeGetAccountShape(mockSignerContext);
|
|
48
62
|
const result = await getAccountShape(ACCOUNT_SHAPE_INFO, { paginationConfig: {} });
|
|
49
63
|
|
|
50
64
|
expect(result).toBeDefined();
|
|
51
65
|
expect(result.id).toBeDefined();
|
|
52
|
-
expect(result.xpub).toBe(TEST_ADDRESS
|
|
66
|
+
expect(result.xpub).toBe(TEST_ADDRESS);
|
|
53
67
|
expect(result.blockHeight).toBeGreaterThan(0);
|
|
54
68
|
expect(result.balance).toBeDefined();
|
|
55
69
|
expect(result.spendableBalance).toBeDefined();
|
|
@@ -67,12 +81,15 @@ describe("sync (devnet)", () => {
|
|
|
67
81
|
});
|
|
68
82
|
|
|
69
83
|
it("should handle address with colons correctly", async () => {
|
|
84
|
+
const getAccountShape = makeGetAccountShape(mockSignerContext);
|
|
70
85
|
const result = await getAccountShape(ACCOUNT_SHAPE_INFO, { paginationConfig: {} });
|
|
71
86
|
|
|
72
|
-
expect(result.xpub).
|
|
87
|
+
expect(result.xpub).toContain("::");
|
|
73
88
|
});
|
|
74
89
|
|
|
75
90
|
it("should merge operations correctly with initial account", async () => {
|
|
91
|
+
const getAccountShape = makeGetAccountShape(mockSignerContext);
|
|
92
|
+
|
|
76
93
|
const operations: Operation[] = [
|
|
77
94
|
{
|
|
78
95
|
id: "test-op-1",
|
|
@@ -95,7 +112,7 @@ describe("sync (devnet)", () => {
|
|
|
95
112
|
{
|
|
96
113
|
...ACCOUNT_SHAPE_INFO,
|
|
97
114
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
98
|
-
initialAccount: { operations } as Account,
|
|
115
|
+
initialAccount: { xpub, operations } as Account,
|
|
99
116
|
},
|
|
100
117
|
{ paginationConfig: {} },
|
|
101
118
|
);
|
|
@@ -112,20 +129,26 @@ describe("sync (devnet)", () => {
|
|
|
112
129
|
mockGetBalance.mockResolvedValue([
|
|
113
130
|
{
|
|
114
131
|
instrument_id: "Amulet",
|
|
115
|
-
amount:
|
|
132
|
+
amount: "500",
|
|
133
|
+
locked: false,
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
instrument_id: "LockedAmulet",
|
|
137
|
+
amount: "1000000",
|
|
116
138
|
locked: true,
|
|
117
139
|
},
|
|
118
140
|
]);
|
|
119
141
|
|
|
142
|
+
const getAccountShape = makeGetAccountShape(mockSignerContext);
|
|
120
143
|
const result = await getAccountShape(ACCOUNT_SHAPE_INFO, { paginationConfig: {} });
|
|
121
144
|
|
|
122
|
-
expect(result.balance?.toNumber()).toBe(
|
|
123
|
-
expect(result.spendableBalance?.toNumber()).toBe(
|
|
145
|
+
expect(result.balance?.toNumber()).toBe(1000500);
|
|
146
|
+
expect(result.spendableBalance?.toNumber()).toBe(500);
|
|
124
147
|
|
|
125
148
|
mockGetBalance.mockRestore();
|
|
126
149
|
});
|
|
127
150
|
|
|
128
|
-
it("should call getOperations with correct cursor based
|
|
151
|
+
it("should call getOperations with correct cursor based on initial account", async () => {
|
|
129
152
|
const mockGetOperations = jest.spyOn(gateway, "getOperations");
|
|
130
153
|
const operation: Operation = {
|
|
131
154
|
id: "test-op-1",
|
|
@@ -144,8 +167,9 @@ describe("sync (devnet)", () => {
|
|
|
144
167
|
};
|
|
145
168
|
|
|
146
169
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
147
|
-
const initialAccount = { operations: [operation] } as Account;
|
|
170
|
+
const initialAccount = { xpub, operations: [operation] } as Account;
|
|
148
171
|
|
|
172
|
+
const getAccountShape = makeGetAccountShape(mockSignerContext);
|
|
149
173
|
const result = await getAccountShape(
|
|
150
174
|
{
|
|
151
175
|
...ACCOUNT_SHAPE_INFO,
|
|
@@ -154,7 +178,7 @@ describe("sync (devnet)", () => {
|
|
|
154
178
|
{ paginationConfig: {} },
|
|
155
179
|
);
|
|
156
180
|
|
|
157
|
-
expect(mockGetOperations).toHaveBeenCalledWith(TEST_ADDRESS, {
|
|
181
|
+
expect(mockGetOperations).toHaveBeenCalledWith(currency, TEST_ADDRESS, {
|
|
158
182
|
cursor: (operation.blockHeight || 0) + 1,
|
|
159
183
|
limit: 100,
|
|
160
184
|
});
|
|
@@ -165,12 +189,13 @@ describe("sync (devnet)", () => {
|
|
|
165
189
|
it("should call getOperations with cursor 0 when no initial account", async () => {
|
|
166
190
|
const mockGetOperations = jest.spyOn(gateway, "getOperations");
|
|
167
191
|
|
|
192
|
+
const getAccountShape = makeGetAccountShape(mockSignerContext);
|
|
168
193
|
const result = await getAccountShape(ACCOUNT_SHAPE_INFO, { paginationConfig: {} });
|
|
169
194
|
|
|
170
195
|
expect(result.operations).toBeDefined();
|
|
171
196
|
expect(result.operationsCount).toBeGreaterThanOrEqual(1);
|
|
172
197
|
|
|
173
|
-
expect(mockGetOperations).toHaveBeenCalledWith(TEST_ADDRESS, {
|
|
198
|
+
expect(mockGetOperations).toHaveBeenCalledWith(currency, TEST_ADDRESS, {
|
|
174
199
|
cursor: 0,
|
|
175
200
|
limit: 100,
|
|
176
201
|
});
|