@ledgerhq/coin-canton 0.7.0 → 0.8.0-nightly.1
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/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +26 -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 +8 -7
- 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/sync.ts
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import BigNumber from "bignumber.js";
|
|
2
2
|
import { Operation, OperationType } from "@ledgerhq/types-live";
|
|
3
|
-
import {
|
|
3
|
+
import { encodeAccountId } from "@ledgerhq/coin-framework/account/index";
|
|
4
4
|
import { GetAccountShape, mergeOps } from "@ledgerhq/coin-framework/bridge/jsHelpers";
|
|
5
5
|
import { encodeOperationId } from "@ledgerhq/coin-framework/operation";
|
|
6
|
+
import { SignerContext } from "@ledgerhq/coin-framework/signer";
|
|
6
7
|
import { getBalance, getLedgerEnd, getOperations, type OperationInfo } from "../network/gateway";
|
|
7
|
-
import { CantonAccount } from "../types";
|
|
8
8
|
import coinConfig from "../config";
|
|
9
|
+
import resolver from "../signer";
|
|
10
|
+
import { CantonAccount, CantonSigner } from "../types";
|
|
11
|
+
import { isAccountOnboarded, isAccountAuthorized } from "./onboard";
|
|
9
12
|
|
|
10
13
|
const txInfoToOperationAdapter =
|
|
11
14
|
(accountId: string, partyId: string) =>
|
|
@@ -30,7 +33,6 @@ const txInfoToOperationAdapter =
|
|
|
30
33
|
} else if (txInfo.type === "Initialize") {
|
|
31
34
|
type = "PRE_APPROVAL";
|
|
32
35
|
}
|
|
33
|
-
|
|
34
36
|
const value = new BigNumber(transferValue);
|
|
35
37
|
const feeValue = new BigNumber(fee);
|
|
36
38
|
const memo = details.metadata.reason;
|
|
@@ -65,76 +67,92 @@ const filterOperations = (
|
|
|
65
67
|
return transactions.map(txInfoToOperationAdapter(accountId, partyId));
|
|
66
68
|
};
|
|
67
69
|
|
|
68
|
-
export
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
const balances = await getBalance(currency, partyId);
|
|
91
|
-
|
|
92
|
-
const balanceData = balances.find(
|
|
93
|
-
balance => balance.instrument_id === coinConfig.getCoinConfig(currency).nativeInstrumentId,
|
|
94
|
-
) || {
|
|
95
|
-
instrument_id: coinConfig.getCoinConfig(currency).nativeInstrumentId,
|
|
96
|
-
amount: 0,
|
|
97
|
-
locked: false,
|
|
98
|
-
};
|
|
70
|
+
export function makeGetAccountShape(
|
|
71
|
+
signerContext: SignerContext<CantonSigner>,
|
|
72
|
+
): GetAccountShape<CantonAccount> {
|
|
73
|
+
return async info => {
|
|
74
|
+
const { address, currency, derivationMode, derivationPath, initialAccount } = info;
|
|
75
|
+
|
|
76
|
+
let xpubOrAddress = initialAccount?.xpub || "";
|
|
77
|
+
|
|
78
|
+
if (!xpubOrAddress) {
|
|
79
|
+
const getAddress = resolver(signerContext);
|
|
80
|
+
const { publicKey } = await getAddress(info.deviceId || "", {
|
|
81
|
+
path: derivationPath,
|
|
82
|
+
currency: currency,
|
|
83
|
+
derivationMode: derivationMode,
|
|
84
|
+
verify: false,
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
const { isOnboarded, partyId } = await isAccountOnboarded(currency, publicKey);
|
|
88
|
+
if (isOnboarded && partyId) {
|
|
89
|
+
xpubOrAddress = partyId;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
99
92
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
);
|
|
107
|
-
|
|
108
|
-
let operations: Operation[] = [];
|
|
109
|
-
// Tx history fetching if xpubOrAddress is not empty
|
|
110
|
-
if (xpubOrAddress) {
|
|
111
|
-
const oldOperations = initialAccount?.operations || [];
|
|
112
|
-
const startAt = oldOperations.length ? (oldOperations[0].blockHeight || 0) + 1 : 0;
|
|
113
|
-
const transactionData = await getOperations(currency, partyId, {
|
|
114
|
-
cursor: startAt,
|
|
115
|
-
limit: 100,
|
|
93
|
+
const accountId = encodeAccountId({
|
|
94
|
+
type: "js",
|
|
95
|
+
version: "2",
|
|
96
|
+
currencyId: currency.id,
|
|
97
|
+
xpubOrAddress: xpubOrAddress,
|
|
98
|
+
derivationMode,
|
|
116
99
|
});
|
|
117
100
|
|
|
118
|
-
const
|
|
119
|
-
|
|
120
|
-
}
|
|
121
|
-
// blockheight retrieval
|
|
122
|
-
const blockHeight = await getLedgerEnd(currency);
|
|
123
|
-
// We return the new account shape
|
|
124
|
-
const shape = {
|
|
125
|
-
id: accountId,
|
|
126
|
-
xpub: xpubOrAddress,
|
|
127
|
-
blockHeight,
|
|
128
|
-
balance,
|
|
129
|
-
spendableBalance,
|
|
130
|
-
operations,
|
|
131
|
-
operationsCount: operations.length,
|
|
132
|
-
freshAddress: address,
|
|
133
|
-
freshAddressPath: derivationPath,
|
|
134
|
-
cantonResources: {
|
|
135
|
-
partyId,
|
|
136
|
-
},
|
|
137
|
-
};
|
|
101
|
+
const { nativeInstrumentId } = coinConfig.getCoinConfig(currency);
|
|
102
|
+
const balances = xpubOrAddress ? await getBalance(currency, xpubOrAddress) : [];
|
|
138
103
|
|
|
139
|
-
|
|
140
|
-
}
|
|
104
|
+
const balancesData = (balances || []).reduce(
|
|
105
|
+
(acc, { amount, instrument_id, locked }) => {
|
|
106
|
+
acc[instrument_id] = { amount, locked };
|
|
107
|
+
return acc;
|
|
108
|
+
},
|
|
109
|
+
{} as Record<string, { amount: string; locked: boolean }>,
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
const unlockedAmount = new BigNumber(balancesData[nativeInstrumentId]?.amount || "0");
|
|
113
|
+
const lockedAmount = new BigNumber(balancesData[`Locked${nativeInstrumentId}`]?.amount || "0");
|
|
114
|
+
const totalBalance = unlockedAmount.plus(lockedAmount);
|
|
115
|
+
const reserveMin = new BigNumber(coinConfig.getCoinConfig(currency).minReserve || 0);
|
|
116
|
+
const spendableBalance = BigNumber.max(0, unlockedAmount.minus(reserveMin));
|
|
117
|
+
|
|
118
|
+
let operations: Operation[] = [];
|
|
119
|
+
if (xpubOrAddress) {
|
|
120
|
+
const oldOperations = initialAccount?.operations || [];
|
|
121
|
+
const startAt = oldOperations.length ? (oldOperations[0].blockHeight || 0) + 1 : 0;
|
|
122
|
+
const transactionData = await getOperations(currency, xpubOrAddress, {
|
|
123
|
+
cursor: startAt,
|
|
124
|
+
limit: 100,
|
|
125
|
+
});
|
|
126
|
+
const newOperations = filterOperations(transactionData.operations, accountId, xpubOrAddress);
|
|
127
|
+
operations = mergeOps(oldOperations, newOperations);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const isAuthorized = await isAccountAuthorized(operations, xpubOrAddress);
|
|
131
|
+
const used = isAuthorized && totalBalance.gt(0);
|
|
132
|
+
|
|
133
|
+
const blockHeight = await getLedgerEnd(currency);
|
|
134
|
+
|
|
135
|
+
const creationDate =
|
|
136
|
+
operations.length > 0
|
|
137
|
+
? new Date(Math.min(...operations.map(op => op.date.getTime())))
|
|
138
|
+
: new Date();
|
|
139
|
+
|
|
140
|
+
const shape = {
|
|
141
|
+
id: accountId,
|
|
142
|
+
type: "Account" as const,
|
|
143
|
+
balance: totalBalance,
|
|
144
|
+
blockHeight,
|
|
145
|
+
creationDate,
|
|
146
|
+
lastSyncDate: new Date(),
|
|
147
|
+
freshAddress: address,
|
|
148
|
+
seedIdentifier: address,
|
|
149
|
+
operations,
|
|
150
|
+
operationsCount: operations.length,
|
|
151
|
+
spendableBalance,
|
|
152
|
+
xpub: xpubOrAddress,
|
|
153
|
+
used,
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
return shape;
|
|
157
|
+
};
|
|
158
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { isRecipientValid, validateTag } from "./utils";
|
|
2
|
+
import BigNumber from "bignumber.js";
|
|
3
|
+
|
|
4
|
+
describe("utils", () => {
|
|
5
|
+
describe("isRecipientValid", () => {
|
|
6
|
+
it("should return true for valid Canton addresses", () => {
|
|
7
|
+
const validAddresses = [
|
|
8
|
+
"abc::123",
|
|
9
|
+
"hello::1",
|
|
10
|
+
"test123::456",
|
|
11
|
+
"a::0",
|
|
12
|
+
"party::999",
|
|
13
|
+
"user123::42",
|
|
14
|
+
"canton_1a2b3c4d5e6f7g8h9i0j1k2l3m4n5o6p7q8r9s0t1u2v3w4x5y6z::123",
|
|
15
|
+
"test::123456789",
|
|
16
|
+
"abc::abc", // letters after ::
|
|
17
|
+
"test::ABC123", // mixed case letters and numbers
|
|
18
|
+
"user::a1b2c3", // alphanumeric after ::
|
|
19
|
+
"contract::XyZ789", // mixed case with numbers
|
|
20
|
+
];
|
|
21
|
+
|
|
22
|
+
validAddresses.forEach(address => {
|
|
23
|
+
expect(isRecipientValid(address)).toBe(true);
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it("should return false for invalid Canton addresses", () => {
|
|
28
|
+
const invalidAddresses = [
|
|
29
|
+
"", // empty string
|
|
30
|
+
"::123", // no characters before ::
|
|
31
|
+
"abc::", // no characters after ::
|
|
32
|
+
"abc:123", // single colon instead of double
|
|
33
|
+
"abc::", // empty after ::
|
|
34
|
+
"::", // only colons
|
|
35
|
+
"abc", // no colons
|
|
36
|
+
"123", // no colons
|
|
37
|
+
"abc::123.456", // decimal numbers
|
|
38
|
+
"abc::-123", // negative numbers
|
|
39
|
+
"abc::+123", // positive sign
|
|
40
|
+
"abc:: 123", // space before alphanumeric
|
|
41
|
+
"abc::123 ", // space after alphanumeric
|
|
42
|
+
"abc:: 123", // space after ::
|
|
43
|
+
"abc::123-abc", // dash in alphanumeric part
|
|
44
|
+
"abc::123_abc", // underscore in alphanumeric part
|
|
45
|
+
"abc::123.abc", // dot in alphanumeric part
|
|
46
|
+
"abc::123 abc", // space in alphanumeric part
|
|
47
|
+
"abc::", // empty after ::
|
|
48
|
+
];
|
|
49
|
+
|
|
50
|
+
invalidAddresses.forEach(address => {
|
|
51
|
+
expect(isRecipientValid(address)).toBe(false);
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it("should handle edge cases", () => {
|
|
56
|
+
expect(isRecipientValid("a::1")).toBe(true); // minimum valid case with number
|
|
57
|
+
expect(isRecipientValid("a::a")).toBe(true); // minimum valid case with letter
|
|
58
|
+
expect(isRecipientValid("1::1")).toBe(true); // number before ::
|
|
59
|
+
expect(isRecipientValid("1::a")).toBe(true); // number before ::, letter after
|
|
60
|
+
expect(isRecipientValid("_::1")).toBe(true); // underscore before ::
|
|
61
|
+
expect(isRecipientValid("_::a")).toBe(true); // underscore before ::, letter after
|
|
62
|
+
expect(isRecipientValid("-::1")).toBe(true); // dash before ::
|
|
63
|
+
expect(isRecipientValid("-::a")).toBe(true); // dash before ::, letter after
|
|
64
|
+
expect(isRecipientValid(".::1")).toBe(true); // dot before ::
|
|
65
|
+
expect(isRecipientValid(".::a")).toBe(true); // dot before ::, letter after
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it("should handle addresses with spaces and multiple colons", () => {
|
|
69
|
+
// These are valid according to our regex but might not be ideal Canton addresses
|
|
70
|
+
expect(isRecipientValid(" abc::123")).toBe(true); // space before address
|
|
71
|
+
expect(isRecipientValid(" abc::abc")).toBe(true); // space before address with letters
|
|
72
|
+
expect(isRecipientValid("abc ::123")).toBe(true); // space before ::
|
|
73
|
+
expect(isRecipientValid("abc ::abc")).toBe(true); // space before :: with letters
|
|
74
|
+
expect(isRecipientValid("abc::123::456")).toBe(true); // multiple ::
|
|
75
|
+
expect(isRecipientValid("abc::abc::def")).toBe(true); // multiple :: with letters
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
describe("validateTag", () => {
|
|
80
|
+
it("should return true for valid tags", () => {
|
|
81
|
+
const validTags = [
|
|
82
|
+
new BigNumber(1),
|
|
83
|
+
new BigNumber(42),
|
|
84
|
+
new BigNumber(4294967295), // UINT32_MAX
|
|
85
|
+
new BigNumber(0),
|
|
86
|
+
];
|
|
87
|
+
|
|
88
|
+
validTags.forEach(tag => {
|
|
89
|
+
expect(validateTag(tag)).toBe(true);
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it("should return false for invalid tags", () => {
|
|
94
|
+
const invalidTags = [
|
|
95
|
+
new BigNumber(-1), // negative
|
|
96
|
+
new BigNumber(4294967296), // greater than UINT32_MAX
|
|
97
|
+
new BigNumber(1.5), // decimal
|
|
98
|
+
new BigNumber(NaN), // NaN
|
|
99
|
+
new BigNumber(Infinity), // Infinity
|
|
100
|
+
new BigNumber(-Infinity), // -Infinity
|
|
101
|
+
];
|
|
102
|
+
|
|
103
|
+
invalidTags.forEach(tag => {
|
|
104
|
+
expect(validateTag(tag)).toBe(false);
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
});
|
|
@@ -8,6 +8,9 @@ export const validateTag = (tag: BigNumber) => {
|
|
|
8
8
|
);
|
|
9
9
|
};
|
|
10
10
|
|
|
11
|
+
const CANTON_ADDRESS_REGEX = /^.+::[a-zA-Z0-9]+$/;
|
|
12
|
+
|
|
11
13
|
export function isRecipientValid(recipient: string): boolean {
|
|
12
|
-
|
|
14
|
+
// Canton address format: at least 1 character :: at least 1 alphanumeric character
|
|
15
|
+
return CANTON_ADDRESS_REGEX.test(recipient);
|
|
13
16
|
}
|
package/src/config.ts
CHANGED
|
@@ -7,7 +7,7 @@ export type CantonConfig = {
|
|
|
7
7
|
gatewayUrl?: string;
|
|
8
8
|
// TODELETE
|
|
9
9
|
minReserve?: number;
|
|
10
|
-
networkType: "mainnet" | "devnet" | "localnet";
|
|
10
|
+
networkType: "mainnet" | "devnet" | "testnet" | "localnet";
|
|
11
11
|
useGateway?: boolean;
|
|
12
12
|
nativeInstrumentId: string;
|
|
13
13
|
fee?: number;
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
submitTapRequest,
|
|
13
13
|
preparePreApprovalTransaction,
|
|
14
14
|
submitPreApprovalTransaction,
|
|
15
|
+
type OnboardingPrepareResponse,
|
|
15
16
|
} from "./gateway";
|
|
16
17
|
import type { CryptoCurrency } from "@ledgerhq/types-cryptoassets";
|
|
17
18
|
|
|
@@ -25,6 +26,8 @@ describe("gateway (devnet)", () => {
|
|
|
25
26
|
partyId: string;
|
|
26
27
|
} | null = null;
|
|
27
28
|
|
|
29
|
+
let prepareResponse: OnboardingPrepareResponse | null = null;
|
|
30
|
+
|
|
28
31
|
beforeAll(async () => {
|
|
29
32
|
coinConfig.setCoinConfig(() => ({
|
|
30
33
|
gatewayUrl: "https://canton-gateway.api.live.ledger-test.com",
|
|
@@ -35,7 +38,7 @@ describe("gateway (devnet)", () => {
|
|
|
35
38
|
type: "active",
|
|
36
39
|
},
|
|
37
40
|
}));
|
|
38
|
-
}
|
|
41
|
+
});
|
|
39
42
|
|
|
40
43
|
const getOnboardedAccount = () => {
|
|
41
44
|
if (!onboardedAccount) {
|
|
@@ -56,7 +59,7 @@ describe("gateway (devnet)", () => {
|
|
|
56
59
|
};
|
|
57
60
|
|
|
58
61
|
// WHEN
|
|
59
|
-
const response = await prepareOnboarding(mockCurrency, keyPair.publicKeyHex
|
|
62
|
+
const response = await prepareOnboarding(mockCurrency, keyPair.publicKeyHex);
|
|
60
63
|
|
|
61
64
|
// THEN
|
|
62
65
|
expect(response).toHaveProperty("party_id");
|
|
@@ -68,30 +71,26 @@ describe("gateway (devnet)", () => {
|
|
|
68
71
|
expect(typeof response.party_name).toBe("string");
|
|
69
72
|
|
|
70
73
|
expect(response.public_key_fingerprint).toBe(keyPair.fingerprint);
|
|
71
|
-
}
|
|
74
|
+
});
|
|
72
75
|
});
|
|
73
76
|
|
|
74
77
|
describe("submitOnboarding", () => {
|
|
75
78
|
it("should submit onboarding with proper signature", async () => {
|
|
76
79
|
// GIVEN
|
|
77
80
|
const { keyPair } = getOnboardedAccount();
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
mockCurrency,
|
|
81
|
-
keyPair.publicKeyHex,
|
|
82
|
-
"ed25519",
|
|
83
|
-
);
|
|
81
|
+
// Save prepare response for next test
|
|
82
|
+
prepareResponse = await prepareOnboarding(mockCurrency, keyPair.publicKeyHex);
|
|
84
83
|
const signature = keyPair.sign(prepareResponse.transactions.combined_hash);
|
|
85
84
|
|
|
86
85
|
// WHEN
|
|
87
86
|
const response = await submitOnboarding(
|
|
88
87
|
mockCurrency,
|
|
89
|
-
|
|
88
|
+
keyPair.publicKeyHex,
|
|
90
89
|
prepareResponse,
|
|
91
90
|
signature,
|
|
92
91
|
);
|
|
93
92
|
|
|
94
|
-
// Save onboarded account for
|
|
93
|
+
// Save onboarded account for next tests that need a valid party ID
|
|
95
94
|
onboardedAccount = {
|
|
96
95
|
keyPair,
|
|
97
96
|
partyId: response.party.party_id,
|
|
@@ -103,6 +102,31 @@ describe("gateway (devnet)", () => {
|
|
|
103
102
|
expect(response.party).toHaveProperty("public_key");
|
|
104
103
|
expect(response.party.public_key).toBe(keyPair.publicKeyHex);
|
|
105
104
|
}, 30000);
|
|
105
|
+
|
|
106
|
+
const testIfPrepared = prepareResponse ? it.skip : it;
|
|
107
|
+
testIfPrepared(
|
|
108
|
+
"should not throw when already onboarded",
|
|
109
|
+
async () => {
|
|
110
|
+
// GIVEN
|
|
111
|
+
const { keyPair } = getOnboardedAccount();
|
|
112
|
+
const signature = keyPair.sign(prepareResponse!.transactions.combined_hash);
|
|
113
|
+
|
|
114
|
+
// WHEN
|
|
115
|
+
const response = await submitOnboarding(
|
|
116
|
+
mockCurrency,
|
|
117
|
+
keyPair.publicKeyHex,
|
|
118
|
+
prepareResponse!,
|
|
119
|
+
signature,
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
// THEN
|
|
123
|
+
expect(response).toHaveProperty("party");
|
|
124
|
+
expect(response.party).toHaveProperty("party_id");
|
|
125
|
+
expect(response.party).toHaveProperty("public_key");
|
|
126
|
+
expect(response.party.public_key).toBe(keyPair.publicKeyHex);
|
|
127
|
+
},
|
|
128
|
+
30000,
|
|
129
|
+
);
|
|
106
130
|
});
|
|
107
131
|
|
|
108
132
|
describe("getLedgerEnd", () => {
|
|
@@ -125,17 +149,20 @@ describe("gateway (devnet)", () => {
|
|
|
125
149
|
});
|
|
126
150
|
|
|
127
151
|
describe("getPartyById", () => {
|
|
128
|
-
it
|
|
129
|
-
const party = await getPartyById(
|
|
152
|
+
it("should return party info", async () => {
|
|
153
|
+
const party = await getPartyById(
|
|
154
|
+
mockCurrency,
|
|
155
|
+
"ldg::12208b12fa34be8a079bcbb68bba828e58313046c4208855b39885fab48661322e68",
|
|
156
|
+
);
|
|
130
157
|
expect(party).toBeDefined();
|
|
131
158
|
});
|
|
132
159
|
});
|
|
133
160
|
|
|
134
161
|
describe("getPartyByPubKey", () => {
|
|
135
|
-
it
|
|
162
|
+
it("should return party info", async () => {
|
|
136
163
|
const party = await getPartyByPubKey(
|
|
137
164
|
mockCurrency,
|
|
138
|
-
"
|
|
165
|
+
"c5cdb19624833f9a929a0125978c886ec4297320c14cea6bf667dc1d23a8e650",
|
|
139
166
|
);
|
|
140
167
|
expect(party).toBeDefined();
|
|
141
168
|
});
|
|
@@ -153,7 +180,7 @@ describe("gateway (devnet)", () => {
|
|
|
153
180
|
});
|
|
154
181
|
|
|
155
182
|
describe("prepareTapRequest", () => {
|
|
156
|
-
it("should prepare tap request for onboarded party", async () => {
|
|
183
|
+
it.skip("should prepare tap request for onboarded party", async () => {
|
|
157
184
|
// GIVEN
|
|
158
185
|
const { partyId } = getOnboardedAccount();
|
|
159
186
|
const amount = 1000;
|
|
@@ -166,11 +193,11 @@ describe("gateway (devnet)", () => {
|
|
|
166
193
|
expect(response).toHaveProperty("hash");
|
|
167
194
|
expect(typeof response.serialized).toBe("string");
|
|
168
195
|
expect(typeof response.hash).toBe("string");
|
|
169
|
-
}
|
|
196
|
+
});
|
|
170
197
|
});
|
|
171
198
|
|
|
172
199
|
describe("submitTapRequest", () => {
|
|
173
|
-
it("should submit tap request with proper signature", async () => {
|
|
200
|
+
it.skip("should submit tap request with proper signature", async () => {
|
|
174
201
|
// GIVEN
|
|
175
202
|
const { keyPair, partyId } = getOnboardedAccount();
|
|
176
203
|
const tapPrepareResponse = await prepareTapRequest(mockCurrency, {
|
|
@@ -191,7 +218,7 @@ describe("gateway (devnet)", () => {
|
|
|
191
218
|
expect(response).toHaveProperty("update_id");
|
|
192
219
|
expect(typeof response.submission_id).toBe("string");
|
|
193
220
|
expect(typeof response.update_id).toBe("string");
|
|
194
|
-
}
|
|
221
|
+
});
|
|
195
222
|
});
|
|
196
223
|
|
|
197
224
|
describe("preparePreApprovalTransaction", () => {
|
|
@@ -207,7 +234,7 @@ describe("gateway (devnet)", () => {
|
|
|
207
234
|
expect(response).toHaveProperty("hash");
|
|
208
235
|
expect(typeof response.serialized).toBe("string");
|
|
209
236
|
expect(typeof response.hash).toBe("string");
|
|
210
|
-
}
|
|
237
|
+
});
|
|
211
238
|
});
|
|
212
239
|
|
|
213
240
|
describe("submitPreApprovalTransaction", () => {
|
|
@@ -232,6 +259,6 @@ describe("gateway (devnet)", () => {
|
|
|
232
259
|
expect(response.isApproved).toBe(true);
|
|
233
260
|
expect(typeof response.submissionId).toBe("string");
|
|
234
261
|
expect(typeof response.updateId).toBe("string");
|
|
235
|
-
}
|
|
262
|
+
});
|
|
236
263
|
});
|
|
237
264
|
});
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { getBalance, type GetBalanceResponse, type InstrumentBalance } from "./gateway";
|
|
2
|
+
import type { CryptoCurrency } from "@ledgerhq/types-cryptoassets";
|
|
3
|
+
import coinConfig from "../config";
|
|
4
|
+
|
|
5
|
+
jest.mock("@ledgerhq/live-network", () => ({
|
|
6
|
+
__esModule: true,
|
|
7
|
+
default: jest.fn(),
|
|
8
|
+
}));
|
|
9
|
+
|
|
10
|
+
import network from "@ledgerhq/live-network";
|
|
11
|
+
|
|
12
|
+
const mockBalances: InstrumentBalance[] = [
|
|
13
|
+
{
|
|
14
|
+
instrument_id: "Amulet",
|
|
15
|
+
amount: "10000000000000000000000000000000000000000",
|
|
16
|
+
locked: false,
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
instrument_id: "LockedAmulet",
|
|
20
|
+
amount: "5000000000000000000000000000000000000000",
|
|
21
|
+
locked: true,
|
|
22
|
+
},
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
describe("getBalance", () => {
|
|
26
|
+
const mockCurrency = {
|
|
27
|
+
id: "canton_network",
|
|
28
|
+
} as unknown as CryptoCurrency;
|
|
29
|
+
|
|
30
|
+
const mockNetwork = network as jest.MockedFunction<typeof network>;
|
|
31
|
+
|
|
32
|
+
beforeAll(() => {
|
|
33
|
+
coinConfig.setCoinConfig(() => ({
|
|
34
|
+
gatewayUrl: "https://canton-gateway.api.live.ledger-test.com",
|
|
35
|
+
useGateway: true,
|
|
36
|
+
networkType: "devnet",
|
|
37
|
+
nativeInstrumentId: "Amulet",
|
|
38
|
+
status: {
|
|
39
|
+
type: "active",
|
|
40
|
+
},
|
|
41
|
+
}));
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
beforeEach(() => {
|
|
45
|
+
jest.clearAllMocks();
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it("should return an array of balances (backwards compatibility)", async () => {
|
|
49
|
+
mockNetwork.mockResolvedValue({ data: mockBalances, status: 200 });
|
|
50
|
+
const result = await getBalance(mockCurrency, "test-party-id");
|
|
51
|
+
|
|
52
|
+
expect(result).toEqual(mockBalances);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it("should return and object with balances property", async () => {
|
|
56
|
+
const mockResponse: GetBalanceResponse = {
|
|
57
|
+
at_round: 123,
|
|
58
|
+
balances: mockBalances,
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
mockNetwork.mockResolvedValue({ data: mockResponse, status: 200 });
|
|
62
|
+
const result = await getBalance(mockCurrency, "test-party-id");
|
|
63
|
+
|
|
64
|
+
expect(result).toEqual(mockResponse.balances);
|
|
65
|
+
});
|
|
66
|
+
});
|