@ledgerhq/coin-hedera 1.15.0 → 1.16.0-nightly.20251210120335
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 +24 -0
- package/lib/bridge/buildOptimisticOperation.d.ts.map +1 -1
- package/lib/bridge/buildOptimisticOperation.js +33 -0
- package/lib/bridge/buildOptimisticOperation.js.map +1 -1
- package/lib/bridge/getTransactionStatus.d.ts.map +1 -1
- package/lib/bridge/getTransactionStatus.js +54 -0
- package/lib/bridge/getTransactionStatus.js.map +1 -1
- package/lib/bridge/index.d.ts.map +1 -1
- package/lib/bridge/index.js +4 -2
- package/lib/bridge/index.js.map +1 -1
- package/lib/bridge/prepareTransaction.d.ts.map +1 -1
- package/lib/bridge/prepareTransaction.js +16 -0
- package/lib/bridge/prepareTransaction.js.map +1 -1
- package/lib/bridge/serialization.d.ts.map +1 -1
- package/lib/bridge/serialization.js +20 -0
- package/lib/bridge/serialization.js.map +1 -1
- package/lib/bridge/signOperation.d.ts +4 -4
- package/lib/bridge/signOperation.d.ts.map +1 -1
- package/lib/bridge/signOperation.js +10 -0
- package/lib/bridge/signOperation.js.map +1 -1
- package/lib/bridge/synchronisation.d.ts.map +1 -1
- package/lib/bridge/synchronisation.js +8 -0
- package/lib/bridge/synchronisation.js.map +1 -1
- package/lib/constants.d.ts +21 -1
- package/lib/constants.d.ts.map +1 -1
- package/lib/constants.js +22 -1
- package/lib/constants.js.map +1 -1
- package/lib/deviceTransactionConfig.d.ts.map +1 -1
- package/lib/deviceTransactionConfig.js +30 -0
- package/lib/deviceTransactionConfig.js.map +1 -1
- package/lib/errors.d.ts +9 -0
- package/lib/errors.d.ts.map +1 -1
- package/lib/errors.js +4 -1
- package/lib/errors.js.map +1 -1
- package/lib/logic/craftTransaction.d.ts +2 -2
- package/lib/logic/craftTransaction.d.ts.map +1 -1
- package/lib/logic/craftTransaction.js +42 -8
- package/lib/logic/craftTransaction.js.map +1 -1
- package/lib/logic/getBlock.d.ts.map +1 -1
- package/lib/logic/getBlock.js +1 -0
- package/lib/logic/getBlock.js.map +1 -1
- package/lib/logic/listOperations.d.ts.map +1 -1
- package/lib/logic/listOperations.js +39 -7
- package/lib/logic/listOperations.js.map +1 -1
- package/lib/logic/utils.d.ts +61 -3
- package/lib/logic/utils.d.ts.map +1 -1
- package/lib/logic/utils.js +117 -4
- package/lib/logic/utils.js.map +1 -1
- package/lib/network/api.d.ts +3 -1
- package/lib/network/api.d.ts.map +1 -1
- package/lib/network/api.js +19 -0
- package/lib/network/api.js.map +1 -1
- package/lib/preload-data.d.ts +7 -0
- package/lib/preload-data.d.ts.map +1 -0
- package/lib/preload-data.js +37 -0
- package/lib/preload-data.js.map +1 -0
- package/lib/preload.d.ts +8 -0
- package/lib/preload.d.ts.map +1 -0
- package/lib/preload.js +76 -0
- package/lib/preload.js.map +1 -0
- package/lib/test/fixtures/account.fixture.d.ts +1 -1
- package/lib/test/fixtures/account.fixture.d.ts.map +1 -1
- package/lib/test/fixtures/account.fixture.js +2 -0
- package/lib/test/fixtures/account.fixture.js.map +1 -1
- package/lib/transaction.d.ts.map +1 -1
- package/lib/transaction.js +34 -0
- package/lib/transaction.js.map +1 -1
- package/lib/types/alpaca.d.ts +3 -0
- package/lib/types/alpaca.d.ts.map +1 -1
- package/lib/types/bridge.d.ts +87 -3
- package/lib/types/bridge.d.ts.map +1 -1
- package/lib/types/logic.d.ts +5 -1
- package/lib/types/logic.d.ts.map +1 -1
- package/lib/types/mirror.d.ts +19 -0
- package/lib/types/mirror.d.ts.map +1 -1
- package/lib-es/bridge/buildOptimisticOperation.d.ts.map +1 -1
- package/lib-es/bridge/buildOptimisticOperation.js +34 -1
- package/lib-es/bridge/buildOptimisticOperation.js.map +1 -1
- package/lib-es/bridge/getTransactionStatus.d.ts.map +1 -1
- package/lib-es/bridge/getTransactionStatus.js +57 -3
- package/lib-es/bridge/getTransactionStatus.js.map +1 -1
- package/lib-es/bridge/index.d.ts.map +1 -1
- package/lib-es/bridge/index.js +4 -2
- package/lib-es/bridge/index.js.map +1 -1
- package/lib-es/bridge/prepareTransaction.d.ts.map +1 -1
- package/lib-es/bridge/prepareTransaction.js +15 -2
- package/lib-es/bridge/prepareTransaction.js.map +1 -1
- package/lib-es/bridge/serialization.d.ts.map +1 -1
- package/lib-es/bridge/serialization.js +17 -0
- package/lib-es/bridge/serialization.js.map +1 -1
- package/lib-es/bridge/signOperation.d.ts +4 -4
- package/lib-es/bridge/signOperation.d.ts.map +1 -1
- package/lib-es/bridge/signOperation.js +11 -1
- package/lib-es/bridge/signOperation.js.map +1 -1
- package/lib-es/bridge/synchronisation.d.ts.map +1 -1
- package/lib-es/bridge/synchronisation.js +8 -0
- package/lib-es/bridge/synchronisation.js.map +1 -1
- package/lib-es/constants.d.ts +21 -1
- package/lib-es/constants.d.ts.map +1 -1
- package/lib-es/constants.js +21 -0
- package/lib-es/constants.js.map +1 -1
- package/lib-es/deviceTransactionConfig.d.ts.map +1 -1
- package/lib-es/deviceTransactionConfig.js +31 -1
- package/lib-es/deviceTransactionConfig.js.map +1 -1
- package/lib-es/errors.d.ts +9 -0
- package/lib-es/errors.d.ts.map +1 -1
- package/lib-es/errors.js +3 -0
- package/lib-es/errors.js.map +1 -1
- package/lib-es/logic/craftTransaction.d.ts +2 -2
- package/lib-es/logic/craftTransaction.d.ts.map +1 -1
- package/lib-es/logic/craftTransaction.js +44 -10
- package/lib-es/logic/craftTransaction.js.map +1 -1
- package/lib-es/logic/getBlock.d.ts.map +1 -1
- package/lib-es/logic/getBlock.js +2 -1
- package/lib-es/logic/getBlock.js.map +1 -1
- package/lib-es/logic/listOperations.d.ts.map +1 -1
- package/lib-es/logic/listOperations.js +39 -7
- package/lib-es/logic/listOperations.js.map +1 -1
- package/lib-es/logic/utils.d.ts +61 -3
- package/lib-es/logic/utils.d.ts.map +1 -1
- package/lib-es/logic/utils.js +107 -4
- package/lib-es/logic/utils.js.map +1 -1
- package/lib-es/network/api.d.ts +3 -1
- package/lib-es/network/api.d.ts.map +1 -1
- package/lib-es/network/api.js +19 -0
- package/lib-es/network/api.js.map +1 -1
- package/lib-es/preload-data.d.ts +7 -0
- package/lib-es/preload-data.d.ts.map +1 -0
- package/lib-es/preload-data.js +31 -0
- package/lib-es/preload-data.js.map +1 -0
- package/lib-es/preload.d.ts +8 -0
- package/lib-es/preload.d.ts.map +1 -0
- package/lib-es/preload.js +67 -0
- package/lib-es/preload.js.map +1 -0
- package/lib-es/test/fixtures/account.fixture.d.ts +1 -1
- package/lib-es/test/fixtures/account.fixture.d.ts.map +1 -1
- package/lib-es/test/fixtures/account.fixture.js +2 -0
- package/lib-es/test/fixtures/account.fixture.js.map +1 -1
- package/lib-es/transaction.d.ts.map +1 -1
- package/lib-es/transaction.js +34 -0
- package/lib-es/transaction.js.map +1 -1
- package/lib-es/types/alpaca.d.ts +3 -0
- package/lib-es/types/alpaca.d.ts.map +1 -1
- package/lib-es/types/bridge.d.ts +87 -3
- package/lib-es/types/bridge.d.ts.map +1 -1
- package/lib-es/types/logic.d.ts +5 -1
- package/lib-es/types/logic.d.ts.map +1 -1
- package/lib-es/types/mirror.d.ts +19 -0
- package/lib-es/types/mirror.d.ts.map +1 -1
- package/package.json +13 -12
- package/src/api/index.integ.test.ts +11 -1
- package/src/bridge/buildOptimisticOperation.integration.test.ts +159 -4
- package/src/bridge/buildOptimisticOperation.ts +50 -2
- package/src/bridge/getTransactionStatus.test.ts +191 -21
- package/src/bridge/getTransactionStatus.ts +75 -1
- package/src/bridge/index.ts +4 -2
- package/src/bridge/prepareTransaction.test.ts +112 -8
- package/src/bridge/prepareTransaction.ts +20 -2
- package/src/bridge/serialization.ts +17 -0
- package/src/bridge/signOperation.ts +15 -5
- package/src/bridge/synchronisation.ts +9 -0
- package/src/bridge/utils.integration.test.ts +3 -10
- package/src/constants.ts +22 -0
- package/src/deviceTransactionConfig.test.ts +315 -0
- package/src/deviceTransactionConfig.ts +37 -1
- package/src/errors.ts +7 -0
- package/src/logic/craftTransaction.ts +70 -13
- package/src/logic/getBalance.test.ts +15 -16
- package/src/logic/getBlock.ts +2 -1
- package/src/logic/listOperations.test.ts +86 -29
- package/src/logic/listOperations.ts +46 -6
- package/src/logic/utils.test.ts +362 -8
- package/src/logic/utils.ts +158 -4
- package/src/network/api.test.ts +58 -6
- package/src/network/api.ts +25 -0
- package/src/network/thirdweb.test.ts +2 -2
- package/src/network/utils.test.ts +4 -6
- package/src/preload-data.ts +38 -0
- package/src/preload.test.ts +64 -0
- package/src/preload.ts +80 -0
- package/src/test/fixtures/account.fixture.ts +3 -1
- package/src/transaction.ts +42 -0
- package/src/types/alpaca.ts +4 -0
- package/src/types/bridge.ts +108 -3
- package/src/types/logic.ts +6 -1
- package/src/types/mirror.ts +21 -0
|
@@ -2,6 +2,7 @@ import BigNumber from "bignumber.js";
|
|
|
2
2
|
import invariant from "invariant";
|
|
3
3
|
import {
|
|
4
4
|
AccountId,
|
|
5
|
+
AccountUpdateTransaction,
|
|
5
6
|
ContractExecuteTransaction,
|
|
6
7
|
ContractFunctionParameters,
|
|
7
8
|
ContractId,
|
|
@@ -13,7 +14,7 @@ import {
|
|
|
13
14
|
import type { FeeEstimation, TransactionIntent } from "@ledgerhq/coin-framework/api/index";
|
|
14
15
|
import { DEFAULT_GAS_LIMIT, HEDERA_TRANSACTION_MODES } from "../constants";
|
|
15
16
|
import type { HederaMemo, HederaTxData } from "../types";
|
|
16
|
-
import { serializeTransaction } from "./utils";
|
|
17
|
+
import { hasSpecificIntentData, serializeTransaction } from "./utils";
|
|
17
18
|
|
|
18
19
|
// avoid "sign" prompt loop by having only one node (one transaction)
|
|
19
20
|
// https://github.com/LedgerHQ/ledger-live/pull/72/commits/1e942687d4301660e43e0c4b5419fcfa2733b290
|
|
@@ -55,6 +56,14 @@ interface BuilderTokenAssociateTransaction extends BuilderCommonTransactionField
|
|
|
55
56
|
tokenId: string;
|
|
56
57
|
}
|
|
57
58
|
|
|
59
|
+
interface BuilderUpdateAccountTransaction extends BuilderCommonTransactionFields {
|
|
60
|
+
type:
|
|
61
|
+
| HEDERA_TRANSACTION_MODES.Delegate
|
|
62
|
+
| HEDERA_TRANSACTION_MODES.Undelegate
|
|
63
|
+
| HEDERA_TRANSACTION_MODES.Redelegate;
|
|
64
|
+
stakingNodeId: number | null | undefined;
|
|
65
|
+
}
|
|
66
|
+
|
|
58
67
|
async function buildUnsignedCoinTransaction({
|
|
59
68
|
account,
|
|
60
69
|
transaction,
|
|
@@ -159,13 +168,42 @@ async function buildTokenAssociateTransaction({
|
|
|
159
168
|
return tx.freeze();
|
|
160
169
|
}
|
|
161
170
|
|
|
171
|
+
async function buildUnsignedUpdateAccountTransaction({
|
|
172
|
+
account,
|
|
173
|
+
transaction,
|
|
174
|
+
}: {
|
|
175
|
+
account: BuilderOperator;
|
|
176
|
+
transaction: BuilderUpdateAccountTransaction;
|
|
177
|
+
}): Promise<AccountUpdateTransaction> {
|
|
178
|
+
const accountId = account.accountId;
|
|
179
|
+
|
|
180
|
+
const tx = new AccountUpdateTransaction()
|
|
181
|
+
.setNodeAccountIds([new AccountId(3)])
|
|
182
|
+
.setTransactionId(TransactionId.generate(accountId))
|
|
183
|
+
.setTransactionMemo(transaction.memo ?? "")
|
|
184
|
+
.setAccountId(accountId);
|
|
185
|
+
|
|
186
|
+
if (transaction.maxFee) {
|
|
187
|
+
tx.setMaxTransactionFee(Hbar.fromTinybars(transaction.maxFee.toNumber()));
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (typeof transaction.stakingNodeId === "number") {
|
|
191
|
+
tx.setStakedNodeId(transaction.stakingNodeId);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (transaction.stakingNodeId === null) {
|
|
195
|
+
tx.clearStakedNodeId();
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return tx.freeze();
|
|
199
|
+
}
|
|
200
|
+
|
|
162
201
|
export async function craftTransaction(
|
|
163
202
|
txIntent: TransactionIntent<HederaMemo, HederaTxData>,
|
|
164
203
|
customFees?: FeeEstimation,
|
|
165
204
|
) {
|
|
166
|
-
const account = {
|
|
167
|
-
|
|
168
|
-
};
|
|
205
|
+
const account = { accountId: txIntent.sender };
|
|
206
|
+
const maxFee = customFees ? new BigNumber(customFees.value.toString()) : undefined;
|
|
169
207
|
|
|
170
208
|
let tx;
|
|
171
209
|
|
|
@@ -179,7 +217,7 @@ export async function craftTransaction(
|
|
|
179
217
|
type: txIntent.type,
|
|
180
218
|
tokenId: txIntent.asset.assetReference,
|
|
181
219
|
memo: txIntent.memo.value,
|
|
182
|
-
maxFee
|
|
220
|
+
maxFee,
|
|
183
221
|
},
|
|
184
222
|
});
|
|
185
223
|
} else if (txIntent.type === HEDERA_TRANSACTION_MODES.Send && txIntent.asset.type === "hts") {
|
|
@@ -195,17 +233,16 @@ export async function craftTransaction(
|
|
|
195
233
|
amount,
|
|
196
234
|
recipient: txIntent.recipient,
|
|
197
235
|
memo: txIntent.memo.value,
|
|
198
|
-
maxFee
|
|
236
|
+
maxFee,
|
|
199
237
|
},
|
|
200
238
|
});
|
|
201
239
|
} else if (txIntent.type === HEDERA_TRANSACTION_MODES.Send && txIntent.asset.type === "erc20") {
|
|
202
240
|
invariant("assetReference" in txIntent.asset, "hedera: no assetReference in token transfer");
|
|
203
241
|
|
|
204
242
|
const amount = new BigNumber(txIntent.amount.toString());
|
|
205
|
-
const gasLimit =
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
: DEFAULT_GAS_LIMIT;
|
|
243
|
+
const gasLimit = hasSpecificIntentData(txIntent, "erc20")
|
|
244
|
+
? new BigNumber(txIntent.data.gasLimit.toString())
|
|
245
|
+
: DEFAULT_GAS_LIMIT;
|
|
209
246
|
|
|
210
247
|
tx = await buildUnsignedERC20TokenTransaction({
|
|
211
248
|
account,
|
|
@@ -215,11 +252,31 @@ export async function craftTransaction(
|
|
|
215
252
|
amount,
|
|
216
253
|
recipient: txIntent.recipient,
|
|
217
254
|
memo: txIntent.memo.value,
|
|
218
|
-
maxFee
|
|
255
|
+
maxFee,
|
|
219
256
|
gasLimit,
|
|
220
257
|
},
|
|
221
258
|
});
|
|
222
|
-
} else
|
|
259
|
+
} else if (
|
|
260
|
+
txIntent.type === HEDERA_TRANSACTION_MODES.Redelegate ||
|
|
261
|
+
txIntent.type === HEDERA_TRANSACTION_MODES.Undelegate ||
|
|
262
|
+
txIntent.type === HEDERA_TRANSACTION_MODES.Delegate
|
|
263
|
+
) {
|
|
264
|
+
const stakingNodeId = hasSpecificIntentData(txIntent, "staking")
|
|
265
|
+
? txIntent.data.stakingNodeId
|
|
266
|
+
: undefined;
|
|
267
|
+
|
|
268
|
+
tx = await buildUnsignedUpdateAccountTransaction({
|
|
269
|
+
account,
|
|
270
|
+
transaction: {
|
|
271
|
+
type: txIntent.type,
|
|
272
|
+
memo: txIntent.memo.value,
|
|
273
|
+
maxFee,
|
|
274
|
+
stakingNodeId,
|
|
275
|
+
},
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
// HEDERA_TRANSACTION_MODES.ClaimRewards is just a coin transfer that triggers staking rewards claim
|
|
279
|
+
else {
|
|
223
280
|
const amount = new BigNumber(txIntent.amount.toString());
|
|
224
281
|
|
|
225
282
|
tx = await buildUnsignedCoinTransaction({
|
|
@@ -229,7 +286,7 @@ export async function craftTransaction(
|
|
|
229
286
|
amount,
|
|
230
287
|
recipient: txIntent.recipient,
|
|
231
288
|
memo: txIntent.memo.value,
|
|
232
|
-
maxFee
|
|
289
|
+
maxFee,
|
|
233
290
|
},
|
|
234
291
|
});
|
|
235
292
|
}
|
|
@@ -29,7 +29,6 @@ describe("getBalance", () => {
|
|
|
29
29
|
expect(apiClient.getAccount).toHaveBeenCalledWith(address);
|
|
30
30
|
expect(apiClient.getAccountTokens).toHaveBeenCalledTimes(1);
|
|
31
31
|
expect(apiClient.getAccountTokens).toHaveBeenCalledWith(address);
|
|
32
|
-
expect(result).toHaveLength(1);
|
|
33
32
|
expect(result).toEqual([
|
|
34
33
|
{
|
|
35
34
|
asset: { type: "native" },
|
|
@@ -80,7 +79,6 @@ describe("getBalance", () => {
|
|
|
80
79
|
expect(apiClient.getAccountTokens).toHaveBeenCalledWith(address);
|
|
81
80
|
expect(findTokenByAddressInCurrencyMock).toHaveBeenCalledTimes(1);
|
|
82
81
|
expect(findTokenByAddressInCurrencyMock).toHaveBeenCalledWith("0.0.7890", "hedera");
|
|
83
|
-
expect(result).toHaveLength(2);
|
|
84
82
|
expect(result).toEqual(
|
|
85
83
|
expect.arrayContaining([
|
|
86
84
|
{
|
|
@@ -150,21 +148,22 @@ describe("getBalance", () => {
|
|
|
150
148
|
|
|
151
149
|
const result = await getBalance(mockCurrency, address);
|
|
152
150
|
|
|
153
|
-
expect(result).
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
});
|
|
158
|
-
expect(result[1]).toEqual({
|
|
159
|
-
value: BigInt("5000"),
|
|
160
|
-
asset: {
|
|
161
|
-
type: mockTokenHTS.tokenType,
|
|
162
|
-
assetReference: mockTokenHTS.contractAddress,
|
|
163
|
-
assetOwner: address,
|
|
164
|
-
name: mockTokenHTS.name,
|
|
165
|
-
unit: mockTokenHTS.units[0],
|
|
151
|
+
expect(result).toEqual([
|
|
152
|
+
{
|
|
153
|
+
asset: { type: "native" },
|
|
154
|
+
value: BigInt("1000000000"),
|
|
166
155
|
},
|
|
167
|
-
|
|
156
|
+
{
|
|
157
|
+
value: BigInt("5000"),
|
|
158
|
+
asset: {
|
|
159
|
+
type: mockTokenHTS.tokenType,
|
|
160
|
+
assetReference: mockTokenHTS.contractAddress,
|
|
161
|
+
assetOwner: address,
|
|
162
|
+
name: mockTokenHTS.name,
|
|
163
|
+
unit: mockTokenHTS.units[0],
|
|
164
|
+
},
|
|
165
|
+
},
|
|
166
|
+
]);
|
|
168
167
|
});
|
|
169
168
|
|
|
170
169
|
it("should throw when failing to getAccount data", async () => {
|
package/src/logic/getBlock.ts
CHANGED
|
@@ -7,7 +7,7 @@ import type {
|
|
|
7
7
|
import { getBlockInfo } from "./getBlockInfo";
|
|
8
8
|
import { apiClient } from "../network/api";
|
|
9
9
|
import type { HederaMirrorCoinTransfer, HederaMirrorTokenTransfer } from "../types";
|
|
10
|
-
import { getTimestampRangeFromBlockHeight } from "./utils";
|
|
10
|
+
import { getMemoFromBase64, getTimestampRangeFromBlockHeight } from "./utils";
|
|
11
11
|
|
|
12
12
|
function toHederaAsset(
|
|
13
13
|
mirrorTransfer: HederaMirrorCoinTransfer | HederaMirrorTokenTransfer,
|
|
@@ -64,6 +64,7 @@ export async function getBlock(height: number): Promise<Block> {
|
|
|
64
64
|
operations,
|
|
65
65
|
fees: BigInt(tx.charged_tx_fee),
|
|
66
66
|
feesPayer: payerAccount,
|
|
67
|
+
details: { memo: getMemoFromBase64(tx.memo_base64) },
|
|
67
68
|
};
|
|
68
69
|
});
|
|
69
70
|
|
|
@@ -1,11 +1,14 @@
|
|
|
1
|
+
import BigNumber from "bignumber.js";
|
|
2
|
+
import { setupMockCryptoAssetsStore } from "@ledgerhq/cryptoassets/cal-client/test-helpers";
|
|
1
3
|
import { encodeTokenAccountId } from "@ledgerhq/coin-framework/account/accountId";
|
|
2
4
|
import { encodeOperationId } from "@ledgerhq/coin-framework/operation";
|
|
3
5
|
import type { Pagination } from "@ledgerhq/coin-framework/api/types";
|
|
6
|
+
import { getEnv } from "@ledgerhq/live-env";
|
|
4
7
|
import { listOperations } from "./listOperations";
|
|
5
8
|
import { apiClient } from "../network/api";
|
|
6
9
|
import { getMockedCurrency } from "../test/fixtures/currency.fixture";
|
|
10
|
+
import type { HederaMirrorTransaction } from "../types";
|
|
7
11
|
import * as utils from "./utils";
|
|
8
|
-
import { setupMockCryptoAssetsStore } from "@ledgerhq/cryptoassets/cal-client/test-helpers";
|
|
9
12
|
|
|
10
13
|
setupMockCryptoAssetsStore();
|
|
11
14
|
jest.mock("@ledgerhq/coin-framework/account/accountId");
|
|
@@ -73,7 +76,7 @@ describe("listOperations", () => {
|
|
|
73
76
|
order: "desc",
|
|
74
77
|
};
|
|
75
78
|
|
|
76
|
-
const mockTransactions = [
|
|
79
|
+
const mockTransactions: Partial<HederaMirrorTransaction>[] = [
|
|
77
80
|
{
|
|
78
81
|
consensus_timestamp: "1625097600.000000000",
|
|
79
82
|
transaction_hash: "hash1",
|
|
@@ -81,9 +84,10 @@ describe("listOperations", () => {
|
|
|
81
84
|
result: "SUCCESS",
|
|
82
85
|
memo_base64: "test-memo",
|
|
83
86
|
token_transfers: [],
|
|
87
|
+
staking_reward_transfers: [],
|
|
84
88
|
transfers: [
|
|
85
|
-
{ account: address, amount:
|
|
86
|
-
{ account: "0.0.67890", amount:
|
|
89
|
+
{ account: address, amount: -1000000 },
|
|
90
|
+
{ account: "0.0.67890", amount: 1000000 },
|
|
87
91
|
],
|
|
88
92
|
name: "CRYPTOTRANSFER",
|
|
89
93
|
},
|
|
@@ -105,9 +109,8 @@ describe("listOperations", () => {
|
|
|
105
109
|
useSyntheticBlocks: false,
|
|
106
110
|
});
|
|
107
111
|
|
|
108
|
-
expect(result.coinOperations).toHaveLength(1);
|
|
109
112
|
expect(result.tokenOperations).toEqual([]);
|
|
110
|
-
|
|
113
|
+
expect(result.coinOperations).toHaveLength(1);
|
|
111
114
|
expect(result.coinOperations).toMatchObject([
|
|
112
115
|
{
|
|
113
116
|
type: "OUT",
|
|
@@ -144,16 +147,17 @@ describe("listOperations", () => {
|
|
|
144
147
|
units: [{ name: "TT", code: "tt", magnitude: 6 }],
|
|
145
148
|
};
|
|
146
149
|
|
|
147
|
-
const mockTransactions = [
|
|
150
|
+
const mockTransactions: Partial<HederaMirrorTransaction>[] = [
|
|
148
151
|
{
|
|
149
152
|
consensus_timestamp: "1625097600.000000000",
|
|
150
153
|
transaction_hash: "hash1",
|
|
151
154
|
charged_tx_fee: 500000,
|
|
152
155
|
result: "SUCCESS",
|
|
153
156
|
token_transfers: [
|
|
154
|
-
{ token_id: tokenId, account: address, amount:
|
|
155
|
-
{ token_id: tokenId, account: "0.0.67890", amount:
|
|
157
|
+
{ token_id: tokenId, account: address, amount: -1000 },
|
|
158
|
+
{ token_id: tokenId, account: "0.0.67890", amount: 1000 },
|
|
156
159
|
],
|
|
160
|
+
staking_reward_transfers: [],
|
|
157
161
|
transfers: [],
|
|
158
162
|
name: "CRYPTOTRANSFER",
|
|
159
163
|
},
|
|
@@ -180,16 +184,12 @@ describe("listOperations", () => {
|
|
|
180
184
|
useSyntheticBlocks: false,
|
|
181
185
|
});
|
|
182
186
|
|
|
183
|
-
expect(result.coinOperations).toHaveLength(1);
|
|
184
|
-
expect(result.tokenOperations).toHaveLength(1);
|
|
185
|
-
|
|
186
187
|
expect(result.coinOperations).toMatchObject([
|
|
187
188
|
{
|
|
188
189
|
type: "FEES",
|
|
189
190
|
fee: expect.any(Object),
|
|
190
191
|
},
|
|
191
192
|
]);
|
|
192
|
-
|
|
193
193
|
expect(result.tokenOperations).toMatchObject([
|
|
194
194
|
{
|
|
195
195
|
type: "OUT",
|
|
@@ -216,14 +216,15 @@ describe("listOperations", () => {
|
|
|
216
216
|
order: "desc",
|
|
217
217
|
};
|
|
218
218
|
|
|
219
|
-
const mockTransactions = [
|
|
219
|
+
const mockTransactions: Partial<HederaMirrorTransaction>[] = [
|
|
220
220
|
{
|
|
221
221
|
consensus_timestamp: "1625097600.000000000",
|
|
222
222
|
transaction_hash: "hash1",
|
|
223
223
|
charged_tx_fee: 500000,
|
|
224
224
|
result: "SUCCESS",
|
|
225
225
|
token_transfers: [],
|
|
226
|
-
|
|
226
|
+
staking_reward_transfers: [],
|
|
227
|
+
transfers: [{ account: address, amount: -500000 }],
|
|
227
228
|
name: "TOKENASSOCIATE",
|
|
228
229
|
},
|
|
229
230
|
];
|
|
@@ -244,9 +245,7 @@ describe("listOperations", () => {
|
|
|
244
245
|
useSyntheticBlocks: false,
|
|
245
246
|
});
|
|
246
247
|
|
|
247
|
-
expect(result.
|
|
248
|
-
expect(result.tokenOperations).toHaveLength(0);
|
|
249
|
-
|
|
248
|
+
expect(result.tokenOperations).toEqual([]);
|
|
250
249
|
expect(result.coinOperations).toMatchObject([
|
|
251
250
|
{
|
|
252
251
|
type: "ASSOCIATE_TOKEN",
|
|
@@ -273,16 +272,17 @@ describe("listOperations", () => {
|
|
|
273
272
|
order: "desc",
|
|
274
273
|
};
|
|
275
274
|
|
|
276
|
-
const mockTransactions = [
|
|
275
|
+
const mockTransactions: Partial<HederaMirrorTransaction>[] = [
|
|
277
276
|
{
|
|
278
277
|
consensus_timestamp: "1625097600.000000000",
|
|
279
278
|
transaction_hash: "hash1",
|
|
280
279
|
charged_tx_fee: 500000,
|
|
281
280
|
result: "SUCCESS",
|
|
282
281
|
token_transfers: [
|
|
283
|
-
{ token_id: tokenId, account: address, amount:
|
|
284
|
-
{ token_id: tokenId, account: "0.0.67890", amount:
|
|
282
|
+
{ token_id: tokenId, account: address, amount: -1000 },
|
|
283
|
+
{ token_id: tokenId, account: "0.0.67890", amount: 1000 },
|
|
285
284
|
],
|
|
285
|
+
staking_reward_transfers: [],
|
|
286
286
|
transfers: [],
|
|
287
287
|
name: "CRYPTOTRANSFER",
|
|
288
288
|
},
|
|
@@ -309,8 +309,8 @@ describe("listOperations", () => {
|
|
|
309
309
|
useSyntheticBlocks: false,
|
|
310
310
|
});
|
|
311
311
|
|
|
312
|
-
expect(result.coinOperations).
|
|
313
|
-
expect(result.tokenOperations).
|
|
312
|
+
expect(result.coinOperations).toEqual([]);
|
|
313
|
+
expect(result.tokenOperations).toEqual([]);
|
|
314
314
|
});
|
|
315
315
|
|
|
316
316
|
it("should use pagination parameters correctly", async () => {
|
|
@@ -358,17 +358,18 @@ describe("listOperations", () => {
|
|
|
358
358
|
order: "desc",
|
|
359
359
|
};
|
|
360
360
|
|
|
361
|
-
const mockTransactions = [
|
|
361
|
+
const mockTransactions: Partial<HederaMirrorTransaction>[] = [
|
|
362
362
|
{
|
|
363
363
|
consensus_timestamp: "1625097600.000000000",
|
|
364
364
|
transaction_hash: "hash1",
|
|
365
365
|
charged_tx_fee: 500000,
|
|
366
366
|
result: "INVALID_SIGNATURE",
|
|
367
|
-
memo_base64:
|
|
367
|
+
memo_base64: "",
|
|
368
368
|
token_transfers: [],
|
|
369
|
+
staking_reward_transfers: [],
|
|
369
370
|
transfers: [
|
|
370
|
-
{ account: address, amount:
|
|
371
|
-
{ account: "0.0.67890", amount:
|
|
371
|
+
{ account: address, amount: -1000000 },
|
|
372
|
+
{ account: "0.0.67890", amount: 1000000 },
|
|
372
373
|
],
|
|
373
374
|
name: "CRYPTOTRANSFER",
|
|
374
375
|
},
|
|
@@ -390,7 +391,63 @@ describe("listOperations", () => {
|
|
|
390
391
|
useSyntheticBlocks: false,
|
|
391
392
|
});
|
|
392
393
|
|
|
393
|
-
expect(result.coinOperations).
|
|
394
|
-
|
|
394
|
+
expect(result.coinOperations).toMatchObject([{ hasFailed: true }]);
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
it("should create REWARD operation when staking rewards are present", async () => {
|
|
398
|
+
const address = "0.0.1234567";
|
|
399
|
+
const mockCurrency = getMockedCurrency();
|
|
400
|
+
const pagination: Pagination = {
|
|
401
|
+
minHeight: 0,
|
|
402
|
+
limit: 10,
|
|
403
|
+
order: "desc",
|
|
404
|
+
};
|
|
405
|
+
const mockTransaction: Partial<HederaMirrorTransaction> = {
|
|
406
|
+
consensus_timestamp: "1625097600.000000000",
|
|
407
|
+
transaction_hash: "hash1",
|
|
408
|
+
charged_tx_fee: 500000,
|
|
409
|
+
result: "SUCCESS",
|
|
410
|
+
memo_base64: "",
|
|
411
|
+
token_transfers: [],
|
|
412
|
+
staking_reward_transfers: [{ account: address, amount: 1000000 }],
|
|
413
|
+
transfers: [{ account: address, amount: -500000 }],
|
|
414
|
+
name: "CRYPTOTRANSFER",
|
|
415
|
+
};
|
|
416
|
+
|
|
417
|
+
(apiClient.getAccountTransactions as jest.Mock).mockResolvedValue({
|
|
418
|
+
transactions: [mockTransaction],
|
|
419
|
+
nextCursor: null,
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
const result = await listOperations({
|
|
423
|
+
currency: mockCurrency,
|
|
424
|
+
address,
|
|
425
|
+
pagination,
|
|
426
|
+
mirrorTokens: [],
|
|
427
|
+
fetchAllPages: true,
|
|
428
|
+
skipFeesForTokenOperations: false,
|
|
429
|
+
useEncodedHash: false,
|
|
430
|
+
useSyntheticBlocks: false,
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
const rewardTimestamp = result.coinOperations[0].date.getTime();
|
|
434
|
+
const mainTimestamp = result.coinOperations[1].date.getTime();
|
|
435
|
+
|
|
436
|
+
expect(result.tokenOperations).toEqual([]);
|
|
437
|
+
expect(rewardTimestamp).toBe(mainTimestamp + 1);
|
|
438
|
+
expect(result.coinOperations).toMatchObject([
|
|
439
|
+
{
|
|
440
|
+
type: "REWARD",
|
|
441
|
+
hash: `${mockTransaction.transaction_hash}-staking-reward`,
|
|
442
|
+
value: new BigNumber(1000000),
|
|
443
|
+
fee: new BigNumber(0),
|
|
444
|
+
senders: [getEnv("HEDERA_STAKING_REWARD_ACCOUNT_ID")],
|
|
445
|
+
recipients: [address],
|
|
446
|
+
},
|
|
447
|
+
{
|
|
448
|
+
type: "OUT",
|
|
449
|
+
hash: mockTransaction.transaction_hash,
|
|
450
|
+
},
|
|
451
|
+
]);
|
|
395
452
|
});
|
|
396
453
|
});
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import BigNumber from "bignumber.js";
|
|
2
|
+
import { getEnv } from "@ledgerhq/live-env";
|
|
2
3
|
import type { Operation, OperationType } from "@ledgerhq/types-live";
|
|
3
4
|
import type { CryptoCurrency } from "@ledgerhq/types-cryptoassets";
|
|
4
5
|
import type { Pagination } from "@ledgerhq/coin-framework/api/types";
|
|
@@ -13,6 +14,7 @@ import { base64ToUrlSafeBase64, getMemoFromBase64, getSyntheticBlock } from "./u
|
|
|
13
14
|
const txNameToCustomOperationType: Record<string, OperationType> = {
|
|
14
15
|
TOKENASSOCIATE: "ASSOCIATE_TOKEN",
|
|
15
16
|
CONTRACTCALL: "CONTRACT_CALL",
|
|
17
|
+
CRYPTOUPDATEACCOUNT: "UPDATE_ACCOUNT",
|
|
16
18
|
};
|
|
17
19
|
|
|
18
20
|
function getCommonOperationData(
|
|
@@ -133,14 +135,27 @@ function processTransfers({
|
|
|
133
135
|
ledgerAccountId: string;
|
|
134
136
|
commonData: ReturnType<typeof getCommonOperationData>;
|
|
135
137
|
mirrorTokens: HederaMirrorToken[];
|
|
136
|
-
}): Operation<HederaOperationExtra>
|
|
138
|
+
}): Operation<HederaOperationExtra>[] {
|
|
139
|
+
const coinOperations: Operation<HederaOperationExtra>[] = [];
|
|
137
140
|
const transfers = rawTx.transfers ?? [];
|
|
138
|
-
|
|
141
|
+
|
|
142
|
+
if (transfers.length === 0) {
|
|
143
|
+
return [];
|
|
144
|
+
}
|
|
139
145
|
|
|
140
146
|
const { type, value, senders, recipients } = parseTransfers(transfers, address);
|
|
141
147
|
const { hash, fee, timestamp, blockHeight, blockHash, hasFailed } = commonData;
|
|
142
148
|
const extra = { ...commonData.extra };
|
|
143
149
|
const operationType = txNameToCustomOperationType[rawTx.name] ?? type;
|
|
150
|
+
const stakingReward = rawTx.staking_reward_transfers.reduce((acc, transfer) => {
|
|
151
|
+
const transferAmount = new BigNumber(transfer.amount);
|
|
152
|
+
|
|
153
|
+
if (transfer.account === address) {
|
|
154
|
+
acc = acc.plus(transferAmount);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return acc;
|
|
158
|
+
}, new BigNumber(0));
|
|
144
159
|
|
|
145
160
|
// try to enrich ASSOCIATE_TOKEN operation with extra.associatedTokenId
|
|
146
161
|
// this value is used by custom OperationDetails components in Hedera family
|
|
@@ -155,7 +170,30 @@ function processTransfers({
|
|
|
155
170
|
}
|
|
156
171
|
}
|
|
157
172
|
|
|
158
|
-
|
|
173
|
+
// add REWARD operation representing staking reward transfers
|
|
174
|
+
if (stakingReward.gt(0)) {
|
|
175
|
+
const stakingRewardHash = `${hash}-staking-reward`;
|
|
176
|
+
const stakingRewardType: OperationType = "REWARD";
|
|
177
|
+
// offset timestamp by +1ms to ensure it appears just before the operation that triggered it
|
|
178
|
+
const stakingRewardTimestamp = new Date(timestamp.getTime() + 1);
|
|
179
|
+
|
|
180
|
+
coinOperations.push({
|
|
181
|
+
id: encodeOperationId(ledgerAccountId, stakingRewardHash, stakingRewardType),
|
|
182
|
+
accountId: ledgerAccountId,
|
|
183
|
+
type: stakingRewardType,
|
|
184
|
+
value: stakingReward,
|
|
185
|
+
recipients: [address],
|
|
186
|
+
senders: [getEnv("HEDERA_STAKING_REWARD_ACCOUNT_ID")],
|
|
187
|
+
hash: stakingRewardHash,
|
|
188
|
+
fee: new BigNumber(0),
|
|
189
|
+
date: stakingRewardTimestamp,
|
|
190
|
+
blockHeight,
|
|
191
|
+
blockHash,
|
|
192
|
+
extra,
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
coinOperations.push({
|
|
159
197
|
id: encodeOperationId(ledgerAccountId, hash, operationType),
|
|
160
198
|
accountId: ledgerAccountId,
|
|
161
199
|
type: operationType,
|
|
@@ -169,7 +207,9 @@ function processTransfers({
|
|
|
169
207
|
blockHash,
|
|
170
208
|
hasFailed,
|
|
171
209
|
extra,
|
|
172
|
-
};
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
return coinOperations;
|
|
173
213
|
}
|
|
174
214
|
|
|
175
215
|
export async function listOperations({
|
|
@@ -231,7 +271,7 @@ export async function listOperations({
|
|
|
231
271
|
|
|
232
272
|
// process regular transfers only if there were no token transfers
|
|
233
273
|
if (!tokenResult) {
|
|
234
|
-
const
|
|
274
|
+
const newCoinOperations = processTransfers({
|
|
235
275
|
rawTx,
|
|
236
276
|
address,
|
|
237
277
|
ledgerAccountId,
|
|
@@ -239,7 +279,7 @@ export async function listOperations({
|
|
|
239
279
|
mirrorTokens,
|
|
240
280
|
});
|
|
241
281
|
|
|
242
|
-
|
|
282
|
+
coinOperations.push(...newCoinOperations);
|
|
243
283
|
}
|
|
244
284
|
}
|
|
245
285
|
|