@ledgerhq/coin-canton 0.7.0-nightly.2 → 0.7.0-nightly.3
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 +17 -0
- package/lib/api/lastBlock.integ.test.js +0 -15
- package/lib/api/lastBlock.integ.test.js.map +1 -1
- package/lib/bridge/index.d.ts.map +1 -1
- package/lib/bridge/index.js +2 -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 +31 -14
- 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.js +2 -2
- package/lib/common-logic/utils.js.map +1 -1
- package/lib/common-logic/utils.test.js +21 -6
- package/lib/common-logic/utils.test.js.map +1 -1
- package/lib/config.d.ts +1 -1
- package/lib/config.d.ts.map +1 -1
- package/lib/network/gateway.d.ts +9 -9
- 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 +33 -15
- package/lib/network/gateway.js.map +1 -1
- 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/lastBlock.integ.test.js +0 -15
- package/lib-es/api/lastBlock.integ.test.js.map +1 -1
- package/lib-es/bridge/index.d.ts.map +1 -1
- package/lib-es/bridge/index.js +3 -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 +26 -9
- 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.js +2 -2
- package/lib-es/common-logic/utils.js.map +1 -1
- package/lib-es/common-logic/utils.test.js +21 -6
- package/lib-es/common-logic/utils.test.js.map +1 -1
- package/lib-es/config.d.ts +1 -1
- package/lib-es/config.d.ts.map +1 -1
- package/lib-es/network/gateway.d.ts +9 -9
- 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 +33 -15
- package/lib-es/network/gateway.js.map +1 -1
- 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 +5 -4
- package/src/api/lastBlock.integ.test.ts +0 -18
- package/src/bridge/index.ts +3 -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 +30 -10
- package/src/bridge/sync.ts +87 -72
- package/src/common-logic/utils.test.ts +21 -6
- package/src/common-logic/utils.ts +2 -2
- package/src/config.ts +1 -1
- package/src/network/gateway.integ.test.ts +48 -21
- package/src/network/gateway.ts +49 -34
- 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,89 @@ const filterOperations = (
|
|
|
65
67
|
return transactions.map(txInfoToOperationAdapter(accountId, partyId));
|
|
66
68
|
};
|
|
67
69
|
|
|
68
|
-
export
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
const partyId =
|
|
76
|
-
rest?.cantonResources?.partyId ||
|
|
77
|
-
initialAccount?.cantonResources?.partyId ||
|
|
78
|
-
xpubOrAddress.replace(/_/g, ":");
|
|
79
|
-
|
|
80
|
-
const accountId = encodeAccountId({
|
|
81
|
-
type: "js",
|
|
82
|
-
version: "2",
|
|
83
|
-
currencyId: currency.id,
|
|
84
|
-
xpubOrAddress,
|
|
85
|
-
derivationMode,
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
// Account info retrieval + spendable balance calculation
|
|
89
|
-
// const accountInfo = await getAccountInfo(address);
|
|
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 || "";
|
|
99
77
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
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
|
+
}
|
|
92
|
+
|
|
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
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
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) : [];
|
|
103
|
+
const balanceData = balances.find(balance =>
|
|
104
|
+
balance.instrument_id.includes(nativeInstrumentId),
|
|
105
|
+
) || {
|
|
106
|
+
instrument_id: nativeInstrumentId,
|
|
107
|
+
amount: 0,
|
|
108
|
+
locked: false,
|
|
109
|
+
};
|
|
110
|
+
const balance = new BigNumber(balanceData.amount);
|
|
111
|
+
const reserveMin = new BigNumber(coinConfig.getCoinConfig(currency).minReserve || 0);
|
|
112
|
+
const lockedAmount = balanceData.locked ? balance : new BigNumber(0);
|
|
113
|
+
const spendableBalance = BigNumber.max(0, balance.minus(lockedAmount).minus(reserveMin));
|
|
138
114
|
|
|
139
|
-
|
|
140
|
-
|
|
115
|
+
let operations: Operation[] = [];
|
|
116
|
+
if (xpubOrAddress) {
|
|
117
|
+
const oldOperations = initialAccount?.operations || [];
|
|
118
|
+
const startAt = oldOperations.length ? (oldOperations[0].blockHeight || 0) + 1 : 0;
|
|
119
|
+
const transactionData = await getOperations(currency, xpubOrAddress, {
|
|
120
|
+
cursor: startAt,
|
|
121
|
+
limit: 100,
|
|
122
|
+
});
|
|
123
|
+
const newOperations = filterOperations(transactionData.operations, accountId, xpubOrAddress);
|
|
124
|
+
operations = mergeOps(oldOperations, newOperations);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const isAuthorized = await isAccountAuthorized(operations, xpubOrAddress);
|
|
128
|
+
const used = isAuthorized && balance.gt(0);
|
|
129
|
+
|
|
130
|
+
const blockHeight = await getLedgerEnd(currency);
|
|
131
|
+
|
|
132
|
+
const creationDate =
|
|
133
|
+
operations.length > 0
|
|
134
|
+
? new Date(Math.min(...operations.map(op => op.date.getTime())))
|
|
135
|
+
: new Date();
|
|
136
|
+
|
|
137
|
+
const shape = {
|
|
138
|
+
id: accountId,
|
|
139
|
+
type: "Account" as const,
|
|
140
|
+
balance,
|
|
141
|
+
blockHeight,
|
|
142
|
+
creationDate,
|
|
143
|
+
lastSyncDate: new Date(),
|
|
144
|
+
freshAddress: address,
|
|
145
|
+
seedIdentifier: address,
|
|
146
|
+
operations,
|
|
147
|
+
operationsCount: operations.length,
|
|
148
|
+
spendableBalance,
|
|
149
|
+
xpub: xpubOrAddress,
|
|
150
|
+
used,
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
return shape;
|
|
154
|
+
};
|
|
155
|
+
}
|
|
@@ -13,6 +13,10 @@ describe("utils", () => {
|
|
|
13
13
|
"user123::42",
|
|
14
14
|
"canton_1a2b3c4d5e6f7g8h9i0j1k2l3m4n5o6p7q8r9s0t1u2v3w4x5y6z::123",
|
|
15
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
|
|
16
20
|
];
|
|
17
21
|
|
|
18
22
|
validAddresses.forEach(address => {
|
|
@@ -24,20 +28,23 @@ describe("utils", () => {
|
|
|
24
28
|
const invalidAddresses = [
|
|
25
29
|
"", // empty string
|
|
26
30
|
"::123", // no characters before ::
|
|
27
|
-
"abc::", // no
|
|
31
|
+
"abc::", // no characters after ::
|
|
28
32
|
"abc:123", // single colon instead of double
|
|
29
|
-
"abc::abc", // letters instead of numbers after ::
|
|
30
33
|
"abc::", // empty after ::
|
|
31
34
|
"::", // only colons
|
|
32
35
|
"abc", // no colons
|
|
33
36
|
"123", // no colons
|
|
34
|
-
"abc::123abc", // mixed letters and numbers after ::
|
|
35
37
|
"abc::123.456", // decimal numbers
|
|
36
38
|
"abc::-123", // negative numbers
|
|
37
39
|
"abc::+123", // positive sign
|
|
38
|
-
"abc:: 123", // space before
|
|
39
|
-
"abc::123 ", // space after
|
|
40
|
+
"abc:: 123", // space before alphanumeric
|
|
41
|
+
"abc::123 ", // space after alphanumeric
|
|
40
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 ::
|
|
41
48
|
];
|
|
42
49
|
|
|
43
50
|
invalidAddresses.forEach(address => {
|
|
@@ -46,18 +53,26 @@ describe("utils", () => {
|
|
|
46
53
|
});
|
|
47
54
|
|
|
48
55
|
it("should handle edge cases", () => {
|
|
49
|
-
expect(isRecipientValid("a::1")).toBe(true); // minimum valid case
|
|
56
|
+
expect(isRecipientValid("a::1")).toBe(true); // minimum valid case with number
|
|
57
|
+
expect(isRecipientValid("a::a")).toBe(true); // minimum valid case with letter
|
|
50
58
|
expect(isRecipientValid("1::1")).toBe(true); // number before ::
|
|
59
|
+
expect(isRecipientValid("1::a")).toBe(true); // number before ::, letter after
|
|
51
60
|
expect(isRecipientValid("_::1")).toBe(true); // underscore before ::
|
|
61
|
+
expect(isRecipientValid("_::a")).toBe(true); // underscore before ::, letter after
|
|
52
62
|
expect(isRecipientValid("-::1")).toBe(true); // dash before ::
|
|
63
|
+
expect(isRecipientValid("-::a")).toBe(true); // dash before ::, letter after
|
|
53
64
|
expect(isRecipientValid(".::1")).toBe(true); // dot before ::
|
|
65
|
+
expect(isRecipientValid(".::a")).toBe(true); // dot before ::, letter after
|
|
54
66
|
});
|
|
55
67
|
|
|
56
68
|
it("should handle addresses with spaces and multiple colons", () => {
|
|
57
69
|
// These are valid according to our regex but might not be ideal Canton addresses
|
|
58
70
|
expect(isRecipientValid(" abc::123")).toBe(true); // space before address
|
|
71
|
+
expect(isRecipientValid(" abc::abc")).toBe(true); // space before address with letters
|
|
59
72
|
expect(isRecipientValid("abc ::123")).toBe(true); // space before ::
|
|
73
|
+
expect(isRecipientValid("abc ::abc")).toBe(true); // space before :: with letters
|
|
60
74
|
expect(isRecipientValid("abc::123::456")).toBe(true); // multiple ::
|
|
75
|
+
expect(isRecipientValid("abc::abc::def")).toBe(true); // multiple :: with letters
|
|
61
76
|
});
|
|
62
77
|
});
|
|
63
78
|
|
|
@@ -8,9 +8,9 @@ export const validateTag = (tag: BigNumber) => {
|
|
|
8
8
|
);
|
|
9
9
|
};
|
|
10
10
|
|
|
11
|
-
const CANTON_ADDRESS_REGEX =
|
|
11
|
+
const CANTON_ADDRESS_REGEX = /^.+::[a-zA-Z0-9]+$/;
|
|
12
12
|
|
|
13
13
|
export function isRecipientValid(recipient: string): boolean {
|
|
14
|
-
// Canton address format: at least 1 character :: at least 1
|
|
14
|
+
// Canton address format: at least 1 character :: at least 1 alphanumeric character
|
|
15
15
|
return CANTON_ADDRESS_REGEX.test(recipient);
|
|
16
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
|
});
|
package/src/network/gateway.ts
CHANGED
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
} from "../types/onboard";
|
|
12
12
|
import type { CryptoCurrency } from "@ledgerhq/types-cryptoassets";
|
|
13
13
|
|
|
14
|
-
type OnboardingPrepareResponse = {
|
|
14
|
+
export type OnboardingPrepareResponse = {
|
|
15
15
|
party_id: string;
|
|
16
16
|
party_name: string;
|
|
17
17
|
public_key_fingerprint: string;
|
|
@@ -274,7 +274,7 @@ export type OperationInfo =
|
|
|
274
274
|
|
|
275
275
|
const getGatewayUrl = (currency: CryptoCurrency) => coinConfig.getCoinConfig(currency).gatewayUrl;
|
|
276
276
|
const getNodeId = (currency: CryptoCurrency) =>
|
|
277
|
-
coinConfig.getCoinConfig(currency).nodeId || "ledger-devnet
|
|
277
|
+
coinConfig.getCoinConfig(currency).nodeId || "ledger-live-devnet";
|
|
278
278
|
const getNetworkType = (currency: CryptoCurrency) => coinConfig.getCoinConfig(currency).networkType;
|
|
279
279
|
|
|
280
280
|
const gatewayNetwork = <T, U = unknown>(req: LiveNetworkRequest<U>) => {
|
|
@@ -288,11 +288,7 @@ const gatewayNetwork = <T, U = unknown>(req: LiveNetworkRequest<U>) => {
|
|
|
288
288
|
});
|
|
289
289
|
};
|
|
290
290
|
|
|
291
|
-
export async function prepareOnboarding(
|
|
292
|
-
currency: CryptoCurrency,
|
|
293
|
-
pubKey: string,
|
|
294
|
-
pubKeyType: string,
|
|
295
|
-
): Promise<OnboardingPrepareResponse> {
|
|
291
|
+
export async function prepareOnboarding(currency: CryptoCurrency, pubKey: string) {
|
|
296
292
|
const gatewayUrl = getGatewayUrl(currency);
|
|
297
293
|
const nodeId = getNodeId(currency);
|
|
298
294
|
const fullUrl = `${gatewayUrl}/v1/node/${nodeId}/onboarding/prepare`;
|
|
@@ -302,29 +298,54 @@ export async function prepareOnboarding(
|
|
|
302
298
|
url: fullUrl,
|
|
303
299
|
data: {
|
|
304
300
|
public_key: pubKey,
|
|
305
|
-
public_key_type:
|
|
301
|
+
public_key_type: "ed25519",
|
|
306
302
|
},
|
|
307
303
|
});
|
|
308
304
|
|
|
309
305
|
return data;
|
|
310
306
|
}
|
|
311
307
|
|
|
308
|
+
type OnboardingSubmitError409 = {
|
|
309
|
+
partyId: string;
|
|
310
|
+
status: 409;
|
|
311
|
+
type: "PARTY_ALREADY_EXISTS";
|
|
312
|
+
message: string;
|
|
313
|
+
};
|
|
314
|
+
|
|
312
315
|
export async function submitOnboarding(
|
|
313
316
|
currency: CryptoCurrency,
|
|
314
|
-
|
|
317
|
+
publicKey: string,
|
|
315
318
|
prepareResponse: OnboardingPrepareResponse,
|
|
316
319
|
signature: string,
|
|
317
320
|
) {
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
321
|
+
try {
|
|
322
|
+
const { data } = await gatewayNetwork<OnboardingSubmitResponse, OnboardingSubmitRequest>({
|
|
323
|
+
method: "POST",
|
|
324
|
+
url: `${getGatewayUrl(currency)}/v1/node/${getNodeId(currency)}/onboarding/submit`,
|
|
325
|
+
data: {
|
|
326
|
+
prepare_request: {
|
|
327
|
+
public_key: publicKey,
|
|
328
|
+
public_key_type: "ed25519",
|
|
329
|
+
},
|
|
330
|
+
prepare_response: prepareResponse,
|
|
331
|
+
signature,
|
|
332
|
+
},
|
|
333
|
+
});
|
|
334
|
+
return data;
|
|
335
|
+
} catch (e) {
|
|
336
|
+
if (e instanceof Error && "type" in e && e.type === "PARTY_ALREADY_EXISTS") {
|
|
337
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
338
|
+
const { partyId } = e as unknown as OnboardingSubmitError409;
|
|
339
|
+
return {
|
|
340
|
+
party: {
|
|
341
|
+
party_id: partyId,
|
|
342
|
+
public_key: publicKey,
|
|
343
|
+
},
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
throw e;
|
|
348
|
+
}
|
|
328
349
|
}
|
|
329
350
|
|
|
330
351
|
export async function submit(
|
|
@@ -344,10 +365,7 @@ export async function submit(
|
|
|
344
365
|
return data;
|
|
345
366
|
}
|
|
346
367
|
|
|
347
|
-
export async function getBalance(
|
|
348
|
-
currency: CryptoCurrency,
|
|
349
|
-
partyId: string,
|
|
350
|
-
): Promise<InstrumentBalance[]> {
|
|
368
|
+
export async function getBalance(currency: CryptoCurrency, partyId: string) {
|
|
351
369
|
const { data } = await gatewayNetwork<InstrumentBalance[]>({
|
|
352
370
|
method: "GET",
|
|
353
371
|
url: `${getGatewayUrl(currency)}/v1/node/${getNodeId(currency)}/party/${partyId}/balance`,
|
|
@@ -421,7 +439,7 @@ enum TransactionType {
|
|
|
421
439
|
export async function prepareTapRequest(
|
|
422
440
|
currency: CryptoCurrency,
|
|
423
441
|
{ partyId, amount = 1000000 }: PrepareTapRequest,
|
|
424
|
-
)
|
|
442
|
+
) {
|
|
425
443
|
if (getNetworkType(currency) === "mainnet") {
|
|
426
444
|
return {
|
|
427
445
|
serialized: "",
|
|
@@ -429,11 +447,11 @@ export async function prepareTapRequest(
|
|
|
429
447
|
hash: "",
|
|
430
448
|
};
|
|
431
449
|
}
|
|
432
|
-
const { data } = await gatewayNetwork<PrepareTapResponse, { amount:
|
|
450
|
+
const { data } = await gatewayNetwork<PrepareTapResponse, { amount: string; type: string }>({
|
|
433
451
|
method: "POST",
|
|
434
452
|
url: `${getGatewayUrl(currency)}/v1/node/${getNodeId(currency)}/party/${partyId}/transaction/prepare`,
|
|
435
453
|
data: {
|
|
436
|
-
amount:
|
|
454
|
+
amount: amount.toString(),
|
|
437
455
|
type: TransactionType.TAP_REQUEST,
|
|
438
456
|
},
|
|
439
457
|
});
|
|
@@ -454,7 +472,7 @@ type SubmitTapRequestResponse = {
|
|
|
454
472
|
export async function submitTapRequest(
|
|
455
473
|
currency: CryptoCurrency,
|
|
456
474
|
{ partyId, serialized, signature }: SubmitTapRequestRequest,
|
|
457
|
-
)
|
|
475
|
+
) {
|
|
458
476
|
const { data } = await gatewayNetwork<
|
|
459
477
|
SubmitTapRequestResponse,
|
|
460
478
|
Omit<SubmitTapRequestRequest, "partyId">
|
|
@@ -473,7 +491,7 @@ export async function prepareTransferRequest(
|
|
|
473
491
|
currency: CryptoCurrency,
|
|
474
492
|
partyId: string,
|
|
475
493
|
params: PrepareTransferRequest,
|
|
476
|
-
)
|
|
494
|
+
) {
|
|
477
495
|
const { data } = await gatewayNetwork<PrepareTransferResponse, PrepareTransferRequest>({
|
|
478
496
|
method: "POST",
|
|
479
497
|
url: `${getGatewayUrl(currency)}/v1/node/${getNodeId(currency)}/party/${partyId}/transaction/prepare`,
|
|
@@ -490,10 +508,7 @@ export async function getLedgerEnd(currency: CryptoCurrency): Promise<number> {
|
|
|
490
508
|
return data;
|
|
491
509
|
}
|
|
492
510
|
|
|
493
|
-
export async function preparePreApprovalTransaction(
|
|
494
|
-
currency: CryptoCurrency,
|
|
495
|
-
partyId: string,
|
|
496
|
-
): Promise<PrepareTransactionResponse> {
|
|
511
|
+
export async function preparePreApprovalTransaction(currency: CryptoCurrency, partyId: string) {
|
|
497
512
|
const { data } = await gatewayNetwork<PrepareTransactionResponse, PrepareTransactionRequest>({
|
|
498
513
|
method: "POST",
|
|
499
514
|
url: `${getGatewayUrl(currency)}/v1/node/${getNodeId(currency)}/party/${partyId}/transaction/prepare`,
|
|
@@ -510,7 +525,7 @@ export async function submitPreApprovalTransaction(
|
|
|
510
525
|
partyId: string,
|
|
511
526
|
{ serialized }: PrepareTransactionResponse,
|
|
512
527
|
signature: string,
|
|
513
|
-
)
|
|
528
|
+
) {
|
|
514
529
|
const { data } = await gatewayNetwork<SubmitTransactionResponse, SubmitTransactionRequest>({
|
|
515
530
|
method: "POST",
|
|
516
531
|
url: `${getGatewayUrl(currency)}/v1/node/${getNodeId(currency)}/party/${partyId}/transaction/submit`,
|
|
@@ -524,5 +539,5 @@ export async function submitPreApprovalTransaction(
|
|
|
524
539
|
isApproved: true,
|
|
525
540
|
submissionId: data.submission_id,
|
|
526
541
|
updateId: data.update_id,
|
|
527
|
-
};
|
|
542
|
+
} satisfies PreApprovalResult;
|
|
528
543
|
}
|
package/src/types/bridge.ts
CHANGED
|
@@ -9,26 +9,26 @@ import type {
|
|
|
9
9
|
TransactionStatusCommon,
|
|
10
10
|
TransactionStatusCommonRaw,
|
|
11
11
|
} from "@ledgerhq/types-live";
|
|
12
|
+
import { CryptoCurrency } from "@ledgerhq/types-cryptoassets";
|
|
12
13
|
import type {
|
|
13
14
|
CantonOnboardProgress,
|
|
14
15
|
CantonOnboardResult,
|
|
15
|
-
|
|
16
|
-
|
|
16
|
+
CantonAuthorizeProgress,
|
|
17
|
+
CantonAuthorizeResult,
|
|
17
18
|
} from "./onboard";
|
|
18
|
-
import type { CryptoCurrency } from "@ledgerhq/types-cryptoassets";
|
|
19
19
|
|
|
20
20
|
export interface CantonCurrencyBridge extends CurrencyBridge {
|
|
21
21
|
onboardAccount: (
|
|
22
22
|
currency: CryptoCurrency,
|
|
23
23
|
deviceId: string,
|
|
24
|
-
|
|
24
|
+
creatableAccount: Account,
|
|
25
25
|
) => Observable<CantonOnboardProgress | CantonOnboardResult>;
|
|
26
26
|
authorizePreapproval: (
|
|
27
27
|
currency: CryptoCurrency,
|
|
28
28
|
deviceId: string,
|
|
29
|
-
|
|
29
|
+
creatableAccount: Account,
|
|
30
30
|
partyId: string,
|
|
31
|
-
) => Observable<
|
|
31
|
+
) => Observable<CantonAuthorizeProgress | CantonAuthorizeResult>;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
export type NetworkInfo = {
|
|
@@ -60,16 +60,5 @@ export type TransactionRaw = TransactionCommonRaw & {
|
|
|
60
60
|
export type TransactionStatus = TransactionStatusCommon;
|
|
61
61
|
export type TransactionStatusRaw = TransactionStatusCommonRaw;
|
|
62
62
|
|
|
63
|
-
export type
|
|
64
|
-
|
|
65
|
-
};
|
|
66
|
-
export type CantonResourcesRaw = {
|
|
67
|
-
partyId: string;
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
export type CantonAccount = Account & {
|
|
71
|
-
cantonResources?: CantonResources;
|
|
72
|
-
};
|
|
73
|
-
export type CantonAccountRaw = AccountRaw & {
|
|
74
|
-
cantonResources: CantonResourcesRaw;
|
|
75
|
-
};
|
|
63
|
+
export type CantonAccount = Account;
|
|
64
|
+
export type CantonAccountRaw = AccountRaw;
|