@ledgerhq/coin-canton 0.9.0-nightly.3 → 0.9.0-nightly.4
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/.unimportedrc.json +2 -1
- package/CHANGELOG.md +17 -0
- package/lib/bridge/estimateMaxSpendable.d.ts +2 -2
- package/lib/bridge/estimateMaxSpendable.d.ts.map +1 -1
- package/lib/bridge/estimateMaxSpendable.js +2 -3
- package/lib/bridge/estimateMaxSpendable.js.map +1 -1
- package/lib/bridge/getTransactionStatus.d.ts +5 -3
- package/lib/bridge/getTransactionStatus.d.ts.map +1 -1
- package/lib/bridge/getTransactionStatus.js +26 -5
- package/lib/bridge/getTransactionStatus.js.map +1 -1
- package/lib/bridge/index.d.ts +2 -2
- package/lib/bridge/index.d.ts.map +1 -1
- package/lib/bridge/index.js +3 -3
- package/lib/bridge/index.js.map +1 -1
- package/lib/bridge/prepareTransaction.js +1 -1
- package/lib/bridge/prepareTransaction.js.map +1 -1
- package/lib/bridge/serialization.d.ts +4 -0
- package/lib/bridge/serialization.d.ts.map +1 -0
- package/lib/bridge/serialization.js +36 -0
- package/lib/bridge/serialization.js.map +1 -0
- package/lib/bridge/sync.d.ts.map +1 -1
- package/lib/bridge/sync.js +13 -5
- package/lib/bridge/sync.js.map +1 -1
- package/lib/bridge/transaction.js +1 -1
- package/lib/bridge/transaction.js.map +1 -1
- package/lib/common-logic/account/getBalance.d.ts +5 -1
- package/lib/common-logic/account/getBalance.d.ts.map +1 -1
- package/lib/common-logic/account/getBalance.js +2 -0
- package/lib/common-logic/account/getBalance.js.map +1 -1
- package/lib/common-logic/transaction/craftTransaction.js +1 -1
- package/lib/common-logic/transaction/craftTransaction.js.map +1 -1
- package/lib/network/gateway.d.ts +1 -0
- package/lib/network/gateway.d.ts.map +1 -1
- package/lib/network/gateway.js.map +1 -1
- package/lib/test/fixtures.d.ts +5 -0
- package/lib/test/fixtures.d.ts.map +1 -0
- package/lib/test/fixtures.js +57 -0
- package/lib/test/fixtures.js.map +1 -0
- package/lib/types/bridge.d.ts +14 -2
- package/lib/types/bridge.d.ts.map +1 -1
- package/lib/types/errors.d.ts +6 -0
- package/lib/types/errors.d.ts.map +1 -1
- package/lib/types/errors.js +3 -1
- package/lib/types/errors.js.map +1 -1
- package/lib/types/index.d.ts +1 -0
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/index.js +1 -0
- package/lib/types/index.js.map +1 -1
- package/lib-es/bridge/estimateMaxSpendable.d.ts +2 -2
- package/lib-es/bridge/estimateMaxSpendable.d.ts.map +1 -1
- package/lib-es/bridge/estimateMaxSpendable.js +2 -3
- package/lib-es/bridge/estimateMaxSpendable.js.map +1 -1
- package/lib-es/bridge/getTransactionStatus.d.ts +5 -3
- package/lib-es/bridge/getTransactionStatus.d.ts.map +1 -1
- package/lib-es/bridge/getTransactionStatus.js +26 -5
- package/lib-es/bridge/getTransactionStatus.js.map +1 -1
- package/lib-es/bridge/index.d.ts +2 -2
- package/lib-es/bridge/index.d.ts.map +1 -1
- package/lib-es/bridge/index.js +3 -3
- package/lib-es/bridge/index.js.map +1 -1
- package/lib-es/bridge/prepareTransaction.js +1 -1
- package/lib-es/bridge/prepareTransaction.js.map +1 -1
- package/lib-es/bridge/serialization.d.ts +4 -0
- package/lib-es/bridge/serialization.d.ts.map +1 -0
- package/lib-es/bridge/serialization.js +32 -0
- package/lib-es/bridge/serialization.js.map +1 -0
- package/lib-es/bridge/sync.d.ts.map +1 -1
- package/lib-es/bridge/sync.js +13 -5
- package/lib-es/bridge/sync.js.map +1 -1
- package/lib-es/bridge/transaction.js +1 -1
- package/lib-es/bridge/transaction.js.map +1 -1
- package/lib-es/common-logic/account/getBalance.d.ts +5 -1
- package/lib-es/common-logic/account/getBalance.d.ts.map +1 -1
- package/lib-es/common-logic/account/getBalance.js +2 -0
- package/lib-es/common-logic/account/getBalance.js.map +1 -1
- package/lib-es/common-logic/transaction/craftTransaction.js +1 -1
- package/lib-es/common-logic/transaction/craftTransaction.js.map +1 -1
- package/lib-es/network/gateway.d.ts +1 -0
- package/lib-es/network/gateway.d.ts.map +1 -1
- package/lib-es/network/gateway.js.map +1 -1
- package/lib-es/test/fixtures.d.ts +5 -0
- package/lib-es/test/fixtures.d.ts.map +1 -0
- package/lib-es/test/fixtures.js +49 -0
- package/lib-es/test/fixtures.js.map +1 -0
- package/lib-es/types/bridge.d.ts +14 -2
- package/lib-es/types/bridge.d.ts.map +1 -1
- package/lib-es/types/errors.d.ts +6 -0
- package/lib-es/types/errors.d.ts.map +1 -1
- package/lib-es/types/errors.js +2 -0
- package/lib-es/types/errors.js.map +1 -1
- package/lib-es/types/index.d.ts +1 -0
- package/lib-es/types/index.d.ts.map +1 -1
- package/lib-es/types/index.js +1 -0
- package/lib-es/types/index.js.map +1 -1
- package/package.json +6 -6
- package/src/bridge/estimateMaxSpendable.ts +6 -8
- package/src/bridge/getTransactionStatus.test.ts +84 -146
- package/src/bridge/getTransactionStatus.ts +42 -7
- package/src/bridge/index.ts +6 -5
- package/src/bridge/onboard.integ.test.ts +8 -31
- package/src/bridge/prepareTransaction.ts +1 -1
- package/src/bridge/serialization.ts +44 -0
- package/src/bridge/signOperation.test.ts +3 -9
- package/src/bridge/sync.test.ts +4 -0
- package/src/bridge/sync.ts +17 -6
- package/src/bridge/transaction.ts +1 -1
- package/src/common-logic/account/getBalance.ts +12 -2
- package/src/common-logic/account/getBalance.unit.test.ts +7 -1
- package/src/common-logic/transaction/craftTransaction.ts +1 -1
- package/src/network/gateway.ts +1 -0
- package/src/test/fixtures.ts +53 -0
- package/src/types/bridge.ts +15 -2
- package/src/types/errors.ts +3 -0
- package/src/types/index.ts +1 -0
|
@@ -4,21 +4,30 @@ import {
|
|
|
4
4
|
FeeRequired,
|
|
5
5
|
FeeTooHigh,
|
|
6
6
|
InvalidAddress,
|
|
7
|
-
InvalidAddressBecauseDestinationIsAlsoSource,
|
|
8
7
|
NotEnoughBalanceBecauseDestinationNotCreated,
|
|
9
8
|
NotEnoughSpendableBalance,
|
|
10
9
|
RecipientRequired,
|
|
11
10
|
} from "@ledgerhq/errors";
|
|
12
11
|
import BigNumber from "bignumber.js";
|
|
13
|
-
import {
|
|
12
|
+
import { AccountBridge } from "@ledgerhq/types-live";
|
|
14
13
|
import { formatCurrencyUnit } from "@ledgerhq/coin-framework/currencies/index";
|
|
15
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
Transaction,
|
|
16
|
+
TransactionStatus,
|
|
17
|
+
CantonAccount,
|
|
18
|
+
TooManyUtxosCritical,
|
|
19
|
+
TooManyUtxosWarning,
|
|
20
|
+
} from "../types";
|
|
16
21
|
import { isRecipientValid } from "../common-logic/utils";
|
|
17
22
|
import coinConfig from "../config";
|
|
23
|
+
import { getAbandonSeedAddress } from "@ledgerhq/cryptoassets/abandonseed";
|
|
24
|
+
|
|
25
|
+
export const TO_MANY_UTXOS_CRITICAL_COUNT = 100;
|
|
26
|
+
export const TO_MANY_UTXOS_WARNING_COUNT = 10;
|
|
18
27
|
|
|
19
28
|
export const getTransactionStatus: AccountBridge<
|
|
20
29
|
Transaction,
|
|
21
|
-
|
|
30
|
+
CantonAccount,
|
|
22
31
|
TransactionStatus
|
|
23
32
|
>["getTransactionStatus"] = async (account, transaction) => {
|
|
24
33
|
const errors: Record<string, Error> = {};
|
|
@@ -63,9 +72,6 @@ export const getTransactionStatus: AccountBridge<
|
|
|
63
72
|
|
|
64
73
|
if (!transaction.recipient) {
|
|
65
74
|
errors.recipient = new RecipientRequired("");
|
|
66
|
-
} else if (account.freshAddress === transaction.recipient) {
|
|
67
|
-
// we want to prevent user from sending to themselves (even if it's technically feasible)
|
|
68
|
-
errors.recipient = new InvalidAddressBecauseDestinationIsAlsoSource();
|
|
69
75
|
} else if (!isRecipientValid(transaction.recipient)) {
|
|
70
76
|
// We want to prevent user from sending to an invalid address
|
|
71
77
|
errors.recipient = new InvalidAddress("", {
|
|
@@ -78,6 +84,8 @@ export const getTransactionStatus: AccountBridge<
|
|
|
78
84
|
errors.amount = new AmountRequired();
|
|
79
85
|
}
|
|
80
86
|
|
|
87
|
+
validateUtxoCount(account, transaction, warnings);
|
|
88
|
+
|
|
81
89
|
return {
|
|
82
90
|
errors,
|
|
83
91
|
warnings,
|
|
@@ -86,3 +94,30 @@ export const getTransactionStatus: AccountBridge<
|
|
|
86
94
|
totalSpent,
|
|
87
95
|
};
|
|
88
96
|
};
|
|
97
|
+
|
|
98
|
+
function validateUtxoCount(
|
|
99
|
+
account: CantonAccount,
|
|
100
|
+
transaction: Transaction,
|
|
101
|
+
warnings: Record<string, Error>,
|
|
102
|
+
): void {
|
|
103
|
+
const abandonSeedAddress = getAbandonSeedAddress(account.currency.id);
|
|
104
|
+
const isAbandonSeedAddress = transaction.recipient?.includes(abandonSeedAddress);
|
|
105
|
+
|
|
106
|
+
// UTXO count validation - only validate if recipient is valid and not equal to sender
|
|
107
|
+
// Skip validation for abandon seed addresses
|
|
108
|
+
if (
|
|
109
|
+
account?.cantonResources?.instrumentUtxoCounts &&
|
|
110
|
+
transaction.recipient &&
|
|
111
|
+
isRecipientValid(transaction.recipient) &&
|
|
112
|
+
account.xpub !== transaction.recipient &&
|
|
113
|
+
!isAbandonSeedAddress
|
|
114
|
+
) {
|
|
115
|
+
const { instrumentUtxoCounts } = account.cantonResources;
|
|
116
|
+
const instrumentUtxoCount = instrumentUtxoCounts[transaction.tokenId] || 0;
|
|
117
|
+
if (instrumentUtxoCount > TO_MANY_UTXOS_CRITICAL_COUNT) {
|
|
118
|
+
warnings.tooManyUtxos = new TooManyUtxosCritical();
|
|
119
|
+
} else if (instrumentUtxoCount > TO_MANY_UTXOS_WARNING_COUNT) {
|
|
120
|
+
warnings.tooManyUtxos = new TooManyUtxosWarning("families.canton.tooManyUtxos.warning");
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
package/src/bridge/index.ts
CHANGED
|
@@ -10,7 +10,7 @@ import { SignerContext } from "@ledgerhq/coin-framework/signer";
|
|
|
10
10
|
import type { AccountBridge } from "@ledgerhq/types-live";
|
|
11
11
|
import cantonCoinConfig, { type CantonCoinConfig } from "../config";
|
|
12
12
|
import resolver from "../signer";
|
|
13
|
-
import { CantonCurrencyBridge, CantonSigner } from "../types";
|
|
13
|
+
import { CantonCurrencyBridge, CantonSigner, CantonAccount } from "../types";
|
|
14
14
|
import type { Transaction } from "../types";
|
|
15
15
|
import { broadcast } from "./broadcast";
|
|
16
16
|
import { createTransaction } from "./createTransaction";
|
|
@@ -21,6 +21,7 @@ import { buildSignOperation } from "./signOperation";
|
|
|
21
21
|
import { makeGetAccountShape } from "./sync";
|
|
22
22
|
import { updateTransaction } from "./updateTransaction";
|
|
23
23
|
import { buildOnboardAccount, buildAuthorizePreapproval } from "./onboard";
|
|
24
|
+
import { assignToAccountRaw, assignFromAccountRaw } from "./serialization";
|
|
24
25
|
|
|
25
26
|
export function createBridges(
|
|
26
27
|
signerContext: SignerContext<CantonSigner>,
|
|
@@ -49,13 +50,11 @@ export function createBridges(
|
|
|
49
50
|
|
|
50
51
|
const signOperation = buildSignOperation(signerContext);
|
|
51
52
|
const sync = makeSync({ getAccountShape: makeGetAccountShape(signerContext) });
|
|
52
|
-
|
|
53
|
-
const accountBridge: AccountBridge<Transaction> = {
|
|
53
|
+
|
|
54
|
+
const accountBridge: AccountBridge<Transaction, CantonAccount> = {
|
|
54
55
|
broadcast,
|
|
55
56
|
createTransaction,
|
|
56
57
|
updateTransaction,
|
|
57
|
-
// NOTE: use updateTransaction: defaultUpdateTransaction<Transaction>,
|
|
58
|
-
// if you don't need to update the transaction patch object
|
|
59
58
|
prepareTransaction,
|
|
60
59
|
getTransactionStatus,
|
|
61
60
|
estimateMaxSpendable,
|
|
@@ -65,6 +64,8 @@ export function createBridges(
|
|
|
65
64
|
signRawOperation: () => {
|
|
66
65
|
throw new Error("signRawOperation is not supported");
|
|
67
66
|
},
|
|
67
|
+
assignToAccountRaw,
|
|
68
|
+
assignFromAccountRaw,
|
|
68
69
|
getSerializedAddressParameters,
|
|
69
70
|
};
|
|
70
71
|
|
|
@@ -1,45 +1,22 @@
|
|
|
1
|
-
import BigNumber from "bignumber.js";
|
|
2
|
-
import { firstValueFrom, toArray } from "rxjs";
|
|
3
1
|
import { CryptoCurrency } from "@ledgerhq/types-cryptoassets";
|
|
4
|
-
import {
|
|
5
|
-
import
|
|
2
|
+
import { firstValueFrom, toArray } from "rxjs";
|
|
3
|
+
import coinConfig from "../config";
|
|
4
|
+
import { createMockSigner, generateMockKeyPair } from "../test/cantonTestUtils";
|
|
5
|
+
import { createMockAccount, createMockCantonCurrency } from "../test/fixtures";
|
|
6
6
|
import {
|
|
7
7
|
AuthorizeStatus,
|
|
8
|
-
OnboardStatus,
|
|
9
8
|
CantonAuthorizeProgress,
|
|
10
9
|
CantonAuthorizeResult,
|
|
11
10
|
CantonOnboardProgress,
|
|
12
11
|
CantonOnboardResult,
|
|
12
|
+
OnboardStatus,
|
|
13
13
|
} from "../types/onboard";
|
|
14
|
-
import
|
|
15
|
-
import { buildOnboardAccount, isAccountOnboarded, buildAuthorizePreapproval } from "./onboard";
|
|
14
|
+
import { buildAuthorizePreapproval, buildOnboardAccount, isAccountOnboarded } from "./onboard";
|
|
16
15
|
|
|
17
16
|
describe("onboard (devnet)", () => {
|
|
18
17
|
const mockDeviceId = "test-device-id";
|
|
19
|
-
const mockCurrency =
|
|
20
|
-
|
|
21
|
-
} as unknown as CryptoCurrency;
|
|
22
|
-
const mockAccount = {
|
|
23
|
-
type: "Account" as const,
|
|
24
|
-
id: "js:2:canton_network:canton_3f5c9d9a:canton",
|
|
25
|
-
seedIdentifier: "canton_3f5c9d9a",
|
|
26
|
-
derivationMode: "canton" as const,
|
|
27
|
-
index: 0,
|
|
28
|
-
freshAddress: "canton_3f5c9d9a",
|
|
29
|
-
freshAddressPath: "44'/6767'/0'/0'/0'",
|
|
30
|
-
used: false,
|
|
31
|
-
balance: BigNumber(10000),
|
|
32
|
-
spendableBalance: BigNumber(10000),
|
|
33
|
-
creationDate: new Date(),
|
|
34
|
-
blockHeight: 1,
|
|
35
|
-
currency: mockCurrency,
|
|
36
|
-
operationsCount: 0,
|
|
37
|
-
operations: [],
|
|
38
|
-
pendingOperations: [],
|
|
39
|
-
lastSyncDate: new Date(),
|
|
40
|
-
balanceHistoryCache: emptyHistoryCache,
|
|
41
|
-
swapHistory: [],
|
|
42
|
-
};
|
|
18
|
+
const mockCurrency = createMockCantonCurrency();
|
|
19
|
+
const mockAccount = createMockAccount();
|
|
43
20
|
|
|
44
21
|
let onboardedAccount: {
|
|
45
22
|
keyPair: ReturnType<typeof generateMockKeyPair>;
|
|
@@ -11,7 +11,7 @@ export const prepareTransaction: AccountBridge<Transaction>["prepareTransaction"
|
|
|
11
11
|
) => {
|
|
12
12
|
const amount = transaction.amount || BigNumber(0);
|
|
13
13
|
const fee = BigNumber(
|
|
14
|
-
(await estimateFees(account.currency, BigInt(amount.
|
|
14
|
+
(await estimateFees(account.currency, BigInt(amount.toFixed()))).toString(),
|
|
15
15
|
);
|
|
16
16
|
|
|
17
17
|
if (!transaction.tokenId) {
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { Account, AccountRaw } from "@ledgerhq/types-live";
|
|
2
|
+
import {
|
|
3
|
+
type CantonAccount,
|
|
4
|
+
type CantonAccountRaw,
|
|
5
|
+
type CantonResources,
|
|
6
|
+
type CantonResourcesRaw,
|
|
7
|
+
} from "../types";
|
|
8
|
+
|
|
9
|
+
function isCantonAccount(account: Account): account is CantonAccount {
|
|
10
|
+
return "cantonResources" in account;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function isCantonAccountRaw(accountRaw: AccountRaw): accountRaw is CantonAccountRaw {
|
|
14
|
+
return "cantonResources" in accountRaw;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function toResourcesRaw(r: CantonResources): CantonResourcesRaw {
|
|
18
|
+
const { instrumentUtxoCounts } = r;
|
|
19
|
+
return {
|
|
20
|
+
instrumentUtxoCounts,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function fromResourcesRaw(r: CantonResourcesRaw): CantonResources {
|
|
25
|
+
return {
|
|
26
|
+
instrumentUtxoCounts: r.instrumentUtxoCounts,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function assignToAccountRaw(account: Account, accountRaw: AccountRaw): void {
|
|
31
|
+
if (isCantonAccount(account) && isCantonAccountRaw(accountRaw)) {
|
|
32
|
+
if (account.cantonResources) {
|
|
33
|
+
accountRaw.cantonResources = toResourcesRaw(account.cantonResources);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function assignFromAccountRaw(accountRaw: AccountRaw, account: Account): void {
|
|
39
|
+
if (isCantonAccountRaw(accountRaw) && isCantonAccount(account)) {
|
|
40
|
+
if (accountRaw.cantonResources) {
|
|
41
|
+
account.cantonResources = fromResourcesRaw(accountRaw.cantonResources);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -9,6 +9,7 @@ import { Transaction } from "../types";
|
|
|
9
9
|
import { craftTransaction } from "../common-logic";
|
|
10
10
|
import prepareTransferMock from "../test/prepare-transfer.json";
|
|
11
11
|
import { buildSignOperation } from "./signOperation";
|
|
12
|
+
import { createMockAccount } from "../test/fixtures";
|
|
12
13
|
|
|
13
14
|
jest.mock("../common-logic", () => {
|
|
14
15
|
const actual = jest.requireActual("../common-logic");
|
|
@@ -54,25 +55,18 @@ class MockCantonSigner implements CantonSigner {
|
|
|
54
55
|
describe("buildSignOperation", () => {
|
|
55
56
|
const mockDeviceId = "test-device-id";
|
|
56
57
|
const mockDerivationPath = "44'/6767'/0'/0'/0'";
|
|
57
|
-
const mockPartyId = "alice::1220d466a5d96a3509736c821e25fe81fc8a73f226d92e57e94a65170e58b07fc08e";
|
|
58
58
|
|
|
59
59
|
beforeEach(() => {
|
|
60
60
|
jest.clearAllMocks();
|
|
61
61
|
mockCraftTransaction.mockReset();
|
|
62
62
|
});
|
|
63
63
|
|
|
64
|
-
const mockAccount = {
|
|
64
|
+
const mockAccount = createMockAccount({
|
|
65
65
|
id: "js:2:canton_network:test-party-id:",
|
|
66
66
|
freshAddress: "test-address",
|
|
67
67
|
freshAddressPath: mockDerivationPath,
|
|
68
68
|
xpub: "test-party-id",
|
|
69
|
-
|
|
70
|
-
id: "canton_network",
|
|
71
|
-
},
|
|
72
|
-
cantonResources: {
|
|
73
|
-
partyId: mockPartyId,
|
|
74
|
-
},
|
|
75
|
-
} as any;
|
|
69
|
+
});
|
|
76
70
|
|
|
77
71
|
const mockTransaction: Transaction = {
|
|
78
72
|
family: "canton",
|
package/src/bridge/sync.test.ts
CHANGED
|
@@ -52,6 +52,7 @@ describe("makeGetAccountShape", () => {
|
|
|
52
52
|
mockedCoinConfig.mockReturnValue({
|
|
53
53
|
nativeInstrumentId: "Native",
|
|
54
54
|
minReserve: "0",
|
|
55
|
+
useGateway: true,
|
|
55
56
|
});
|
|
56
57
|
|
|
57
58
|
mockedIsAuthorized.mockResolvedValue(true);
|
|
@@ -64,6 +65,7 @@ describe("makeGetAccountShape", () => {
|
|
|
64
65
|
instrument_id: "Native",
|
|
65
66
|
amount: "1000",
|
|
66
67
|
locked: false,
|
|
68
|
+
utxo_count: 1,
|
|
67
69
|
},
|
|
68
70
|
]);
|
|
69
71
|
mockedGetOperations.mockResolvedValue({
|
|
@@ -113,11 +115,13 @@ describe("makeGetAccountShape", () => {
|
|
|
113
115
|
instrument_id: "LockedNative",
|
|
114
116
|
amount: "1000",
|
|
115
117
|
locked: true,
|
|
118
|
+
utxo_count: 1,
|
|
116
119
|
},
|
|
117
120
|
{
|
|
118
121
|
instrument_id: "Native",
|
|
119
122
|
amount: "10",
|
|
120
123
|
locked: false,
|
|
124
|
+
utxo_count: 1,
|
|
121
125
|
},
|
|
122
126
|
]);
|
|
123
127
|
|
package/src/bridge/sync.ts
CHANGED
|
@@ -4,7 +4,8 @@ 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
6
|
import { SignerContext } from "@ledgerhq/coin-framework/signer";
|
|
7
|
-
import {
|
|
7
|
+
import { getLedgerEnd, getOperations, type OperationInfo } from "../network/gateway";
|
|
8
|
+
import { getBalance, type CantonBalance } from "../common-logic/account/getBalance";
|
|
8
9
|
import coinConfig from "../config";
|
|
9
10
|
import resolver from "../signer";
|
|
10
11
|
import { CantonAccount, CantonSigner } from "../types";
|
|
@@ -108,19 +109,26 @@ export function makeGetAccountShape(
|
|
|
108
109
|
const balances = xpubOrAddress ? await getBalance(currency, xpubOrAddress) : [];
|
|
109
110
|
|
|
110
111
|
const balancesData = (balances || []).reduce(
|
|
111
|
-
(acc,
|
|
112
|
-
acc[
|
|
112
|
+
(acc, balance) => {
|
|
113
|
+
acc[balance.instrumentId] = balance;
|
|
113
114
|
return acc;
|
|
114
115
|
},
|
|
115
|
-
{} as Record<string,
|
|
116
|
+
{} as Record<string, CantonBalance>,
|
|
116
117
|
);
|
|
117
118
|
|
|
118
|
-
const unlockedAmount = new BigNumber(balancesData[nativeInstrumentId]?.
|
|
119
|
-
const lockedAmount = new BigNumber(
|
|
119
|
+
const unlockedAmount = new BigNumber(balancesData[nativeInstrumentId]?.value.toString() || "0");
|
|
120
|
+
const lockedAmount = new BigNumber(
|
|
121
|
+
balancesData[`Locked${nativeInstrumentId}`]?.value.toString() || "0",
|
|
122
|
+
);
|
|
120
123
|
const totalBalance = unlockedAmount.plus(lockedAmount);
|
|
121
124
|
const reserveMin = new BigNumber(coinConfig.getCoinConfig(currency).minReserve || 0);
|
|
122
125
|
const spendableBalance = BigNumber.max(0, unlockedAmount.minus(reserveMin));
|
|
123
126
|
|
|
127
|
+
const instrumentUtxoCounts: Record<string, number> = {};
|
|
128
|
+
for (const [instrumentId, balance] of Object.entries(balancesData)) {
|
|
129
|
+
instrumentUtxoCounts[instrumentId] = balance.utxoCount;
|
|
130
|
+
}
|
|
131
|
+
|
|
124
132
|
let operations: Operation[] = [];
|
|
125
133
|
if (xpubOrAddress) {
|
|
126
134
|
const oldOperations = initialAccount?.operations || [];
|
|
@@ -159,6 +167,9 @@ export function makeGetAccountShape(
|
|
|
159
167
|
spendableBalance,
|
|
160
168
|
xpub: xpubOrAddress,
|
|
161
169
|
used,
|
|
170
|
+
cantonResources: {
|
|
171
|
+
instrumentUtxoCounts,
|
|
172
|
+
},
|
|
162
173
|
};
|
|
163
174
|
|
|
164
175
|
return shape;
|
|
@@ -3,12 +3,17 @@ import { getBalance as gatewayGetBalance, type InstrumentBalance } from "../../n
|
|
|
3
3
|
import coinConfig from "../../config";
|
|
4
4
|
import { CryptoCurrency } from "@ledgerhq/types-cryptoassets";
|
|
5
5
|
|
|
6
|
+
export type CantonBalance = Balance & {
|
|
7
|
+
utxoCount: number;
|
|
8
|
+
instrumentId: string;
|
|
9
|
+
};
|
|
10
|
+
|
|
6
11
|
const useGateway = (currency: CryptoCurrency) =>
|
|
7
12
|
coinConfig.getCoinConfig(currency).useGateway === true;
|
|
8
13
|
const getNativeId = (currency: CryptoCurrency) =>
|
|
9
14
|
coinConfig.getCoinConfig(currency).nativeInstrumentId;
|
|
10
15
|
|
|
11
|
-
function adaptInstrument(currency: CryptoCurrency, instrument: InstrumentBalance):
|
|
16
|
+
function adaptInstrument(currency: CryptoCurrency, instrument: InstrumentBalance): CantonBalance {
|
|
12
17
|
return {
|
|
13
18
|
value: BigInt(instrument.amount),
|
|
14
19
|
locked: instrument.locked === true ? BigInt(instrument.amount) : BigInt(0),
|
|
@@ -16,10 +21,15 @@ function adaptInstrument(currency: CryptoCurrency, instrument: InstrumentBalance
|
|
|
16
21
|
getNativeId(currency) === instrument.instrument_id
|
|
17
22
|
? { type: "native" }
|
|
18
23
|
: { type: "token", assetReference: instrument.instrument_id },
|
|
24
|
+
utxoCount: instrument.utxo_count,
|
|
25
|
+
instrumentId: instrument.instrument_id,
|
|
19
26
|
};
|
|
20
27
|
}
|
|
21
28
|
|
|
22
|
-
export async function getBalance(
|
|
29
|
+
export async function getBalance(
|
|
30
|
+
currency: CryptoCurrency,
|
|
31
|
+
partyId: string,
|
|
32
|
+
): Promise<CantonBalance[]> {
|
|
23
33
|
if (useGateway(currency))
|
|
24
34
|
return (await gatewayGetBalance(currency, partyId)).map(instrument =>
|
|
25
35
|
adaptInstrument(currency, instrument),
|
|
@@ -29,11 +29,13 @@ describe("getBalance", () => {
|
|
|
29
29
|
{
|
|
30
30
|
instrument_id: "native-id",
|
|
31
31
|
amount: "1000",
|
|
32
|
+
utxo_count: 1,
|
|
32
33
|
locked: false,
|
|
33
34
|
},
|
|
34
35
|
{
|
|
35
36
|
instrument_id: "token-123",
|
|
36
37
|
amount: "5000",
|
|
38
|
+
utxo_count: 1,
|
|
37
39
|
locked: true,
|
|
38
40
|
},
|
|
39
41
|
];
|
|
@@ -43,16 +45,20 @@ describe("getBalance", () => {
|
|
|
43
45
|
const result = await getBalance(mockCurrency, "party-id");
|
|
44
46
|
|
|
45
47
|
expect(getBalanceFromNetwork).toHaveBeenCalledWith(mockCurrency, "party-id");
|
|
46
|
-
expect(result).toEqual
|
|
48
|
+
expect(result).toEqual([
|
|
47
49
|
{
|
|
48
50
|
value: BigInt(1000),
|
|
49
51
|
locked: BigInt(0),
|
|
50
52
|
asset: { type: "native" },
|
|
53
|
+
utxoCount: 1,
|
|
54
|
+
instrumentId: "native-id",
|
|
51
55
|
},
|
|
52
56
|
{
|
|
53
57
|
value: BigInt(5000),
|
|
54
58
|
locked: BigInt(5000),
|
|
55
59
|
asset: { type: "token", assetReference: "token-123" },
|
|
60
|
+
utxoCount: 1,
|
|
61
|
+
instrumentId: "token-123",
|
|
56
62
|
},
|
|
57
63
|
]);
|
|
58
64
|
});
|
|
@@ -27,7 +27,7 @@ export async function craftTransaction(
|
|
|
27
27
|
}> {
|
|
28
28
|
const params: PrepareTransferRequest = {
|
|
29
29
|
recipient: transaction.recipient || "",
|
|
30
|
-
amount: transaction.amount.
|
|
30
|
+
amount: transaction.amount.toFixed(),
|
|
31
31
|
type: "token-transfer-request" as const,
|
|
32
32
|
execute_before_secs: transaction.expireInSeconds,
|
|
33
33
|
instrument_id: transaction.tokenId,
|
package/src/network/gateway.ts
CHANGED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import BigNumber from "bignumber.js";
|
|
2
|
+
import { Account } from "@ledgerhq/types-live";
|
|
3
|
+
import { CryptoCurrency } from "@ledgerhq/types-cryptoassets";
|
|
4
|
+
import { getDerivationScheme, runDerivationScheme } from "@ledgerhq/coin-framework/derivation";
|
|
5
|
+
import { createEmptyHistoryCache } from "@ledgerhq/coin-framework/account";
|
|
6
|
+
|
|
7
|
+
export const createMockCantonCurrency = (): CryptoCurrency => {
|
|
8
|
+
const mockCurrency = {
|
|
9
|
+
id: "canton_network",
|
|
10
|
+
name: "Canton",
|
|
11
|
+
type: "CryptoCurrency",
|
|
12
|
+
family: "canton",
|
|
13
|
+
units: [{ name: "Canton", code: "CANTON", magnitude: 38 }],
|
|
14
|
+
ticker: "CANTON",
|
|
15
|
+
scheme: "canton",
|
|
16
|
+
color: "#000000",
|
|
17
|
+
managerAppName: "Canton",
|
|
18
|
+
coinType: 6767,
|
|
19
|
+
explorerViews: [],
|
|
20
|
+
} satisfies CryptoCurrency;
|
|
21
|
+
return mockCurrency;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export const createMockAccount = (overrides: Partial<Account> = {}): Account => {
|
|
25
|
+
const currency = createMockCantonCurrency();
|
|
26
|
+
const derivationMode = "canton" as const;
|
|
27
|
+
const scheme = getDerivationScheme({ derivationMode, currency });
|
|
28
|
+
const freshAddressPath = runDerivationScheme(scheme, currency, { account: 0 });
|
|
29
|
+
|
|
30
|
+
return {
|
|
31
|
+
id: "js:2:canton_network:test-address:canton",
|
|
32
|
+
type: "Account",
|
|
33
|
+
used: true,
|
|
34
|
+
currency,
|
|
35
|
+
derivationMode,
|
|
36
|
+
index: 0,
|
|
37
|
+
freshAddress: "test_address",
|
|
38
|
+
freshAddressPath,
|
|
39
|
+
creationDate: new Date(),
|
|
40
|
+
lastSyncDate: new Date(),
|
|
41
|
+
balance: new BigNumber(0),
|
|
42
|
+
spendableBalance: new BigNumber(0),
|
|
43
|
+
seedIdentifier: "test_seed",
|
|
44
|
+
blockHeight: 0,
|
|
45
|
+
operationsCount: 0,
|
|
46
|
+
operations: [],
|
|
47
|
+
pendingOperations: [],
|
|
48
|
+
balanceHistoryCache: createEmptyHistoryCache(),
|
|
49
|
+
swapHistory: [],
|
|
50
|
+
subAccounts: [],
|
|
51
|
+
...overrides,
|
|
52
|
+
};
|
|
53
|
+
};
|
package/src/types/bridge.ts
CHANGED
|
@@ -48,6 +48,7 @@ export type Transaction = TransactionCommon & {
|
|
|
48
48
|
fee: BigNumber | null | undefined;
|
|
49
49
|
memo?: string;
|
|
50
50
|
tokenId: string;
|
|
51
|
+
expireInSeconds?: number;
|
|
51
52
|
};
|
|
52
53
|
|
|
53
54
|
export type TransactionRaw = TransactionCommonRaw & {
|
|
@@ -55,10 +56,22 @@ export type TransactionRaw = TransactionCommonRaw & {
|
|
|
55
56
|
fee: string | null | undefined;
|
|
56
57
|
memo?: string;
|
|
57
58
|
tokenId: string;
|
|
59
|
+
expireInSeconds?: number;
|
|
58
60
|
};
|
|
59
61
|
|
|
60
62
|
export type TransactionStatus = TransactionStatusCommon;
|
|
61
63
|
export type TransactionStatusRaw = TransactionStatusCommonRaw;
|
|
62
64
|
|
|
63
|
-
export type
|
|
64
|
-
|
|
65
|
+
export type CantonResources = {
|
|
66
|
+
instrumentUtxoCounts: Record<string, number>;
|
|
67
|
+
};
|
|
68
|
+
export type CantonResourcesRaw = {
|
|
69
|
+
instrumentUtxoCounts: Record<string, number>;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
export type CantonAccount = Account & {
|
|
73
|
+
cantonResources: CantonResources;
|
|
74
|
+
};
|
|
75
|
+
export type CantonAccountRaw = AccountRaw & {
|
|
76
|
+
cantonResources: CantonResourcesRaw;
|
|
77
|
+
};
|
package/src/types/errors.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
1
|
import { createCustomErrorClass } from "@ledgerhq/errors";
|
|
2
2
|
|
|
3
3
|
export const SimulationError = createCustomErrorClass("SimulationError");
|
|
4
|
+
|
|
5
|
+
export const TooManyUtxosCritical = createCustomErrorClass("TooManyUtxosCritical");
|
|
6
|
+
export const TooManyUtxosWarning = createCustomErrorClass("TooManyUtxosWarning");
|
package/src/types/index.ts
CHANGED