@ledgerhq/coin-hedera 1.13.0-nightly.2 → 1.13.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/.unimportedrc.json +1 -1
- package/CHANGELOG.md +14 -0
- package/jest.config.js +1 -1
- package/jest.integ.config.js +8 -0
- package/lib/api/index.d.ts +4 -0
- package/lib/api/index.d.ts.map +1 -0
- package/lib/api/index.js +119 -0
- package/lib/api/index.js.map +1 -0
- package/lib/bridge/broadcast.d.ts +1 -1
- package/lib/bridge/broadcast.d.ts.map +1 -1
- package/lib/bridge/broadcast.js +6 -9
- package/lib/bridge/broadcast.js.map +1 -1
- package/lib/bridge/buildOptimisticOperation.d.ts.map +1 -1
- package/lib/bridge/buildOptimisticOperation.js +14 -11
- package/lib/bridge/buildOptimisticOperation.js.map +1 -1
- package/lib/bridge/createTransaction.d.ts +1 -1
- package/lib/bridge/createTransaction.d.ts.map +1 -1
- package/lib/bridge/createTransaction.js +2 -0
- package/lib/bridge/createTransaction.js.map +1 -1
- package/lib/bridge/estimateMaxSpendable.d.ts.map +1 -1
- package/lib/bridge/estimateMaxSpendable.js +2 -2
- package/lib/bridge/estimateMaxSpendable.js.map +1 -1
- package/lib/bridge/getTransactionStatus.d.ts.map +1 -1
- package/lib/bridge/getTransactionStatus.js +11 -10
- package/lib/bridge/getTransactionStatus.js.map +1 -1
- package/lib/bridge/prepareTransaction.d.ts.map +1 -1
- package/lib/bridge/prepareTransaction.js +6 -5
- package/lib/bridge/prepareTransaction.js.map +1 -1
- package/lib/bridge/signOperation.d.ts +1 -1
- package/lib/bridge/signOperation.d.ts.map +1 -1
- package/lib/bridge/signOperation.js +53 -14
- package/lib/bridge/signOperation.js.map +1 -1
- package/lib/bridge/synchronisation.d.ts.map +1 -1
- package/lib/bridge/synchronisation.js +24 -11
- package/lib/bridge/synchronisation.js.map +1 -1
- package/lib/bridge/utils.d.ts +3 -13
- package/lib/bridge/utils.d.ts.map +1 -1
- package/lib/bridge/utils.js +10 -119
- package/lib/bridge/utils.js.map +1 -1
- package/lib/config.d.ts +8 -0
- package/lib/config.d.ts.map +1 -0
- package/lib/config.js +9 -0
- package/lib/config.js.map +1 -0
- package/lib/constants.d.ts +11 -8
- package/lib/constants.d.ts.map +1 -1
- package/lib/constants.js +17 -13
- package/lib/constants.js.map +1 -1
- package/lib/deviceTransactionConfig.d.ts +1 -1
- package/lib/deviceTransactionConfig.d.ts.map +1 -1
- package/lib/deviceTransactionConfig.js +3 -3
- package/lib/deviceTransactionConfig.js.map +1 -1
- package/lib/logic/broadcast.d.ts +3 -0
- package/lib/logic/broadcast.d.ts.map +1 -0
- package/lib/logic/broadcast.js +11 -0
- package/lib/logic/broadcast.js.map +1 -0
- package/lib/logic/combine.d.ts +2 -0
- package/lib/logic/combine.d.ts.map +1 -0
- package/lib/logic/combine.js +19 -0
- package/lib/logic/combine.js.map +1 -0
- package/lib/logic/craftTransaction.d.ts +8 -0
- package/lib/logic/craftTransaction.d.ts.map +1 -0
- package/lib/logic/craftTransaction.js +107 -0
- package/lib/logic/craftTransaction.js.map +1 -0
- package/lib/logic/estimateFees.d.ts +5 -0
- package/lib/logic/estimateFees.d.ts.map +1 -0
- package/lib/logic/estimateFees.js +25 -0
- package/lib/logic/estimateFees.js.map +1 -0
- package/lib/logic/getAssetFromToken.d.ts +4 -0
- package/lib/logic/getAssetFromToken.d.ts.map +1 -0
- package/lib/logic/getAssetFromToken.js +14 -0
- package/lib/logic/getAssetFromToken.js.map +1 -0
- package/lib/logic/getBalance.d.ts +4 -0
- package/lib/logic/getBalance.d.ts.map +1 -0
- package/lib/logic/getBalance.js +36 -0
- package/lib/logic/getBalance.js.map +1 -0
- package/lib/logic/getTokenFromAsset.d.ts +4 -0
- package/lib/logic/getTokenFromAsset.d.ts.map +1 -0
- package/lib/logic/getTokenFromAsset.js +13 -0
- package/lib/logic/getTokenFromAsset.js.map +1 -0
- package/lib/logic/index.d.ts +10 -0
- package/lib/logic/index.d.ts.map +1 -0
- package/lib/logic/index.js +22 -0
- package/lib/logic/index.js.map +1 -0
- package/lib/logic/lastBlock.d.ts +12 -0
- package/lib/logic/lastBlock.d.ts.map +1 -0
- package/lib/logic/lastBlock.js +25 -0
- package/lib/logic/lastBlock.js.map +1 -0
- package/lib/logic/listOperations.d.ts +19 -0
- package/lib/logic/listOperations.d.ts.map +1 -0
- package/lib/logic/listOperations.js +179 -0
- package/lib/logic/listOperations.js.map +1 -0
- package/lib/logic/utils.d.ts +55 -0
- package/lib/logic/utils.d.ts.map +1 -0
- package/lib/logic/utils.js +197 -0
- package/lib/logic/utils.js.map +1 -0
- package/lib/network/api.d.ts +24 -0
- package/lib/network/api.d.ts.map +1 -0
- package/lib/network/api.js +119 -0
- package/lib/network/api.js.map +1 -0
- package/lib/network/rpc.d.ts +12 -0
- package/lib/network/rpc.d.ts.map +1 -0
- package/lib/network/rpc.js +22 -0
- package/lib/network/rpc.js.map +1 -0
- package/lib/{api → network}/utils.d.ts +1 -5
- package/lib/network/utils.d.ts.map +1 -0
- package/lib/network/utils.js +52 -0
- package/lib/network/utils.js.map +1 -0
- package/lib/test/bridgeDatasetTest.d.ts.map +1 -1
- package/lib/test/bridgeDatasetTest.js +5 -1
- package/lib/test/bridgeDatasetTest.js.map +1 -1
- package/lib/test/fixtures/account.fixture.d.ts +17 -0
- package/lib/test/fixtures/account.fixture.d.ts.map +1 -1
- package/lib/test/fixtures/account.fixture.js +18 -1
- package/lib/test/fixtures/account.fixture.js.map +1 -1
- package/lib/test/fixtures/currency.fixture.d.ts.map +1 -1
- package/lib/test/fixtures/currency.fixture.js +1 -1
- package/lib/test/fixtures/currency.fixture.js.map +1 -1
- package/lib/test/fixtures/mirror.fixture.d.ts +1 -1
- package/lib/test/fixtures/mirror.fixture.d.ts.map +1 -1
- package/lib/test/fixtures/network.fixture.d.ts +3 -0
- package/lib/test/fixtures/network.fixture.d.ts.map +1 -0
- package/lib/test/fixtures/network.fixture.js +9 -0
- package/lib/test/fixtures/network.fixture.js.map +1 -0
- package/lib/test/fixtures/transaction.fixture.d.ts.map +1 -1
- package/lib/test/fixtures/transaction.fixture.js +3 -0
- package/lib/test/fixtures/transaction.fixture.js.map +1 -1
- package/lib/transaction.d.ts +1 -1
- package/lib/transaction.d.ts.map +1 -1
- package/lib/transaction.js +35 -6
- package/lib/transaction.js.map +1 -1
- package/lib/types/alpaca.d.ts +3 -0
- package/lib/types/alpaca.d.ts.map +1 -0
- package/lib/{api/types.js → types/alpaca.js} +1 -1
- package/lib/types/alpaca.js.map +1 -0
- package/lib/types/bridge.d.ts +28 -9
- package/lib/types/bridge.d.ts.map +1 -1
- package/lib/types/index.d.ts +2 -0
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/index.js +2 -0
- package/lib/types/index.js.map +1 -1
- package/lib/{api/types.d.ts → types/mirror.d.ts} +21 -2
- package/lib/types/mirror.d.ts.map +1 -0
- package/lib/types/mirror.js +3 -0
- package/lib/types/mirror.js.map +1 -0
- package/lib-es/api/index.d.ts +4 -0
- package/lib-es/api/index.d.ts.map +1 -0
- package/lib-es/api/index.js +112 -0
- package/lib-es/api/index.js.map +1 -0
- package/lib-es/bridge/broadcast.d.ts +1 -1
- package/lib-es/bridge/broadcast.d.ts.map +1 -1
- package/lib-es/bridge/broadcast.js +4 -7
- package/lib-es/bridge/broadcast.js.map +1 -1
- package/lib-es/bridge/buildOptimisticOperation.d.ts.map +1 -1
- package/lib-es/bridge/buildOptimisticOperation.js +12 -9
- package/lib-es/bridge/buildOptimisticOperation.js.map +1 -1
- package/lib-es/bridge/createTransaction.d.ts +1 -1
- package/lib-es/bridge/createTransaction.d.ts.map +1 -1
- package/lib-es/bridge/createTransaction.js +2 -0
- package/lib-es/bridge/createTransaction.js.map +1 -1
- package/lib-es/bridge/estimateMaxSpendable.d.ts.map +1 -1
- package/lib-es/bridge/estimateMaxSpendable.js +2 -2
- package/lib-es/bridge/estimateMaxSpendable.js.map +1 -1
- package/lib-es/bridge/getTransactionStatus.d.ts.map +1 -1
- package/lib-es/bridge/getTransactionStatus.js +7 -6
- package/lib-es/bridge/getTransactionStatus.js.map +1 -1
- package/lib-es/bridge/prepareTransaction.d.ts.map +1 -1
- package/lib-es/bridge/prepareTransaction.js +4 -3
- package/lib-es/bridge/prepareTransaction.js.map +1 -1
- package/lib-es/bridge/signOperation.d.ts +1 -1
- package/lib-es/bridge/signOperation.d.ts.map +1 -1
- package/lib-es/bridge/signOperation.js +53 -14
- 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 +24 -11
- package/lib-es/bridge/synchronisation.js.map +1 -1
- package/lib-es/bridge/utils.d.ts +3 -13
- package/lib-es/bridge/utils.d.ts.map +1 -1
- package/lib-es/bridge/utils.js +7 -113
- package/lib-es/bridge/utils.js.map +1 -1
- package/lib-es/config.d.ts +8 -0
- package/lib-es/config.d.ts.map +1 -0
- package/lib-es/config.js +4 -0
- package/lib-es/config.js.map +1 -0
- package/lib-es/constants.d.ts +11 -8
- package/lib-es/constants.d.ts.map +1 -1
- package/lib-es/constants.js +13 -9
- package/lib-es/constants.js.map +1 -1
- package/lib-es/deviceTransactionConfig.d.ts +1 -1
- package/lib-es/deviceTransactionConfig.d.ts.map +1 -1
- package/lib-es/deviceTransactionConfig.js +1 -1
- package/lib-es/deviceTransactionConfig.js.map +1 -1
- package/lib-es/logic/broadcast.d.ts +3 -0
- package/lib-es/logic/broadcast.d.ts.map +1 -0
- package/lib-es/logic/broadcast.js +7 -0
- package/lib-es/logic/broadcast.js.map +1 -0
- package/lib-es/logic/combine.d.ts +2 -0
- package/lib-es/logic/combine.d.ts.map +1 -0
- package/lib-es/logic/combine.js +12 -0
- package/lib-es/logic/combine.js.map +1 -0
- package/lib-es/logic/craftTransaction.d.ts +8 -0
- package/lib-es/logic/craftTransaction.d.ts.map +1 -0
- package/lib-es/logic/craftTransaction.js +100 -0
- package/lib-es/logic/craftTransaction.js.map +1 -0
- package/lib-es/logic/estimateFees.d.ts +5 -0
- package/lib-es/logic/estimateFees.d.ts.map +1 -0
- package/lib-es/logic/estimateFees.js +18 -0
- package/lib-es/logic/estimateFees.js.map +1 -0
- package/lib-es/logic/getAssetFromToken.d.ts +4 -0
- package/lib-es/logic/getAssetFromToken.d.ts.map +1 -0
- package/lib-es/logic/getAssetFromToken.js +10 -0
- package/lib-es/logic/getAssetFromToken.js.map +1 -0
- package/lib-es/logic/getBalance.d.ts +4 -0
- package/lib-es/logic/getBalance.d.ts.map +1 -0
- package/lib-es/logic/getBalance.js +32 -0
- package/lib-es/logic/getBalance.js.map +1 -0
- package/lib-es/logic/getTokenFromAsset.d.ts +4 -0
- package/lib-es/logic/getTokenFromAsset.d.ts.map +1 -0
- package/lib-es/logic/getTokenFromAsset.js +9 -0
- package/lib-es/logic/getTokenFromAsset.js.map +1 -0
- package/lib-es/logic/index.d.ts +10 -0
- package/lib-es/logic/index.d.ts.map +1 -0
- package/lib-es/logic/index.js +10 -0
- package/lib-es/logic/index.js.map +1 -0
- package/lib-es/logic/lastBlock.d.ts +12 -0
- package/lib-es/logic/lastBlock.d.ts.map +1 -0
- package/lib-es/logic/lastBlock.js +21 -0
- package/lib-es/logic/lastBlock.js.map +1 -0
- package/lib-es/logic/listOperations.d.ts +19 -0
- package/lib-es/logic/listOperations.d.ts.map +1 -0
- package/lib-es/logic/listOperations.js +172 -0
- package/lib-es/logic/listOperations.js.map +1 -0
- package/lib-es/logic/utils.d.ts +55 -0
- package/lib-es/logic/utils.d.ts.map +1 -0
- package/lib-es/logic/utils.js +174 -0
- package/lib-es/logic/utils.js.map +1 -0
- package/lib-es/network/api.d.ts +24 -0
- package/lib-es/network/api.d.ts.map +1 -0
- package/lib-es/network/api.js +113 -0
- package/lib-es/network/api.js.map +1 -0
- package/lib-es/network/rpc.d.ts +12 -0
- package/lib-es/network/rpc.d.ts.map +1 -0
- package/lib-es/network/rpc.js +19 -0
- package/lib-es/network/rpc.js.map +1 -0
- package/lib-es/{api → network}/utils.d.ts +1 -5
- package/lib-es/network/utils.d.ts.map +1 -0
- package/lib-es/network/utils.js +45 -0
- package/lib-es/network/utils.js.map +1 -0
- package/lib-es/test/bridgeDatasetTest.d.ts.map +1 -1
- package/lib-es/test/bridgeDatasetTest.js +5 -1
- package/lib-es/test/bridgeDatasetTest.js.map +1 -1
- package/lib-es/test/fixtures/account.fixture.d.ts +17 -0
- package/lib-es/test/fixtures/account.fixture.d.ts.map +1 -1
- package/lib-es/test/fixtures/account.fixture.js +17 -0
- package/lib-es/test/fixtures/account.fixture.js.map +1 -1
- package/lib-es/test/fixtures/currency.fixture.d.ts.map +1 -1
- package/lib-es/test/fixtures/currency.fixture.js +1 -1
- package/lib-es/test/fixtures/currency.fixture.js.map +1 -1
- package/lib-es/test/fixtures/mirror.fixture.d.ts +1 -1
- package/lib-es/test/fixtures/mirror.fixture.d.ts.map +1 -1
- package/lib-es/test/fixtures/network.fixture.d.ts +3 -0
- package/lib-es/test/fixtures/network.fixture.d.ts.map +1 -0
- package/lib-es/test/fixtures/network.fixture.js +5 -0
- package/lib-es/test/fixtures/network.fixture.js.map +1 -0
- package/lib-es/test/fixtures/transaction.fixture.d.ts.map +1 -1
- package/lib-es/test/fixtures/transaction.fixture.js +3 -0
- package/lib-es/test/fixtures/transaction.fixture.js.map +1 -1
- package/lib-es/transaction.d.ts +1 -1
- package/lib-es/transaction.d.ts.map +1 -1
- package/lib-es/transaction.js +35 -6
- 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 -0
- package/lib-es/types/alpaca.js +2 -0
- package/lib-es/types/alpaca.js.map +1 -0
- package/lib-es/types/bridge.d.ts +28 -9
- package/lib-es/types/bridge.d.ts.map +1 -1
- package/lib-es/types/index.d.ts +2 -0
- package/lib-es/types/index.d.ts.map +1 -1
- package/lib-es/types/index.js +2 -0
- package/lib-es/types/index.js.map +1 -1
- package/lib-es/{api/types.d.ts → types/mirror.d.ts} +21 -2
- package/lib-es/types/mirror.d.ts.map +1 -0
- package/lib-es/types/mirror.js +2 -0
- package/lib-es/types/mirror.js.map +1 -0
- package/package.json +4 -3
- package/src/api/index.integ.test.ts +401 -0
- package/src/api/index.test.ts +30 -0
- package/src/api/index.ts +149 -0
- package/src/bridge/broadcast.ts +5 -10
- package/src/bridge/buildOptimisticOperation.integration.test.ts +8 -8
- package/src/bridge/buildOptimisticOperation.ts +13 -10
- package/src/bridge/createTransaction.ts +3 -1
- package/src/bridge/estimateMaxSpendable.ts +6 -3
- package/src/bridge/getTransactionStatus.test.ts +11 -10
- package/src/bridge/getTransactionStatus.ts +12 -11
- package/src/bridge/js-estimateMaxSpendable.integration.test.ts +6 -3
- package/src/bridge/prepareTransaction.test.ts +9 -17
- package/src/bridge/prepareTransaction.ts +5 -4
- package/src/bridge/serialization.test.ts +6 -6
- package/src/bridge/signOperation.ts +69 -16
- package/src/bridge/synchronisation.ts +22 -14
- package/src/bridge/utils.integration.test.ts +19 -248
- package/src/bridge/utils.ts +14 -160
- package/src/config.ts +7 -0
- package/src/constants.ts +15 -9
- package/src/deviceTransactionConfig.ts +2 -2
- package/src/logic/broadcast.test.ts +58 -0
- package/src/logic/broadcast.ts +8 -0
- package/src/logic/combine.test.ts +119 -0
- package/src/logic/combine.ts +14 -0
- package/src/logic/craftTransaction.test.ts +215 -0
- package/src/logic/craftTransaction.ts +175 -0
- package/src/logic/estimateFees.test.ts +99 -0
- package/src/logic/estimateFees.ts +28 -0
- package/src/logic/getAssetFromToken.test.ts +27 -0
- package/src/logic/getAssetFromToken.ts +12 -0
- package/src/logic/getBalance.test.ts +200 -0
- package/src/logic/getBalance.ts +39 -0
- package/src/logic/getTokenFromAsset.test.ts +22 -0
- package/src/logic/getTokenFromAsset.ts +17 -0
- package/src/logic/index.ts +9 -0
- package/src/logic/lastBlock.test.ts +23 -0
- package/src/logic/lastBlock.ts +23 -0
- package/src/logic/listOperations.test.ts +388 -0
- package/src/logic/listOperations.ts +247 -0
- package/src/logic/utils.test.ts +432 -0
- package/src/logic/utils.ts +255 -0
- package/src/{api/mirror.test.ts → network/api.test.ts} +81 -35
- package/src/network/api.ts +159 -0
- package/src/network/rpc.test.ts +68 -0
- package/src/network/rpc.ts +25 -0
- package/src/network/utils.test.ts +175 -0
- package/src/network/utils.ts +58 -0
- package/src/test/bridgeDatasetTest.ts +6 -2
- package/src/test/fixtures/account.fixture.ts +18 -0
- package/src/test/fixtures/currency.fixture.ts +1 -1
- package/src/test/fixtures/mirror.fixture.ts +1 -1
- package/src/test/fixtures/network.fixture.ts +6 -0
- package/src/test/fixtures/transaction.fixture.ts +5 -2
- package/src/transaction.ts +40 -9
- package/src/types/alpaca.ts +3 -0
- package/src/types/bridge.ts +36 -10
- package/src/types/index.ts +2 -0
- package/src/{api/types.ts → types/mirror.ts} +23 -1
- package/lib/api/mirror.d.ts +0 -6
- package/lib/api/mirror.d.ts.map +0 -1
- package/lib/api/mirror.js +0 -84
- package/lib/api/mirror.js.map +0 -1
- package/lib/api/network.d.ts +0 -11
- package/lib/api/network.d.ts.map +0 -1
- package/lib/api/network.js +0 -80
- package/lib/api/network.js.map +0 -1
- package/lib/api/types.d.ts.map +0 -1
- package/lib/api/types.js.map +0 -1
- package/lib/api/utils.d.ts.map +0 -1
- package/lib/api/utils.js +0 -132
- package/lib/api/utils.js.map +0 -1
- package/lib/logic.d.ts +0 -11
- package/lib/logic.d.ts.map +0 -1
- package/lib/logic.js +0 -37
- package/lib/logic.js.map +0 -1
- package/lib-es/api/mirror.d.ts +0 -6
- package/lib-es/api/mirror.d.ts.map +0 -1
- package/lib-es/api/mirror.js +0 -74
- package/lib-es/api/mirror.js.map +0 -1
- package/lib-es/api/network.d.ts +0 -11
- package/lib-es/api/network.d.ts.map +0 -1
- package/lib-es/api/network.js +0 -71
- package/lib-es/api/network.js.map +0 -1
- package/lib-es/api/types.d.ts.map +0 -1
- package/lib-es/api/types.js +0 -2
- package/lib-es/api/types.js.map +0 -1
- package/lib-es/api/utils.d.ts.map +0 -1
- package/lib-es/api/utils.js +0 -124
- package/lib-es/api/utils.js.map +0 -1
- package/lib-es/logic.d.ts +0 -11
- package/lib-es/logic.d.ts.map +0 -1
- package/lib-es/logic.js +0 -29
- package/lib-es/logic.js.map +0 -1
- package/src/api/mirror.ts +0 -91
- package/src/api/network.test.ts +0 -49
- package/src/api/network.ts +0 -125
- package/src/api/utils.ts +0 -150
- package/src/logic.test.ts +0 -152
- package/src/logic.ts +0 -66
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { broadcast } from "./broadcast";
|
|
2
|
+
import { rpcClient } from "../network/rpc";
|
|
3
|
+
import { deserializeTransaction } from "./utils";
|
|
4
|
+
|
|
5
|
+
jest.mock("../network/rpc");
|
|
6
|
+
jest.mock("./utils");
|
|
7
|
+
|
|
8
|
+
describe("broadcast", () => {
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
jest.clearAllMocks();
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it("should deserialize and broadcast a transaction successfully", async () => {
|
|
14
|
+
const txWithSignature = "serialized-transaction-with-signature";
|
|
15
|
+
const mockDeserializedTx = { someProperty: "mockTransaction" } as any;
|
|
16
|
+
const mockResponse = { transactionId: "mock-transaction-id" } as any;
|
|
17
|
+
|
|
18
|
+
(deserializeTransaction as jest.Mock).mockReturnValue(mockDeserializedTx);
|
|
19
|
+
(rpcClient.broadcastTransaction as jest.Mock).mockResolvedValue(mockResponse);
|
|
20
|
+
|
|
21
|
+
const result = await broadcast(txWithSignature);
|
|
22
|
+
|
|
23
|
+
expect(deserializeTransaction).toHaveBeenCalledTimes(1);
|
|
24
|
+
expect(deserializeTransaction).toHaveBeenCalledWith(txWithSignature);
|
|
25
|
+
expect(rpcClient.broadcastTransaction).toHaveBeenCalledTimes(1);
|
|
26
|
+
expect(rpcClient.broadcastTransaction).toHaveBeenCalledWith(mockDeserializedTx);
|
|
27
|
+
expect(result).toBe(mockResponse);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it("should propagate errors from deserializeTransaction", async () => {
|
|
31
|
+
const txWithSignature = "serialized-transaction-with-signature";
|
|
32
|
+
const error = new Error("Deserialization error");
|
|
33
|
+
|
|
34
|
+
(deserializeTransaction as jest.Mock).mockImplementation(() => {
|
|
35
|
+
throw error;
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
await expect(broadcast(txWithSignature)).rejects.toThrow(error);
|
|
39
|
+
expect(deserializeTransaction).toHaveBeenCalledTimes(1);
|
|
40
|
+
expect(deserializeTransaction).toHaveBeenCalledWith(txWithSignature);
|
|
41
|
+
expect(rpcClient.broadcastTransaction).not.toHaveBeenCalled();
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it("should propagate errors from broadcastTransaction", async () => {
|
|
45
|
+
const txWithSignature = "serialized-transaction-with-signature";
|
|
46
|
+
const mockDeserializedTx = { someProperty: "mockTransaction" } as any;
|
|
47
|
+
const error = new Error("Network error");
|
|
48
|
+
|
|
49
|
+
(deserializeTransaction as jest.Mock).mockReturnValue(mockDeserializedTx);
|
|
50
|
+
(rpcClient.broadcastTransaction as jest.Mock).mockRejectedValue(error);
|
|
51
|
+
|
|
52
|
+
await expect(broadcast(txWithSignature)).rejects.toThrow(error);
|
|
53
|
+
expect(deserializeTransaction).toHaveBeenCalledTimes(1);
|
|
54
|
+
expect(deserializeTransaction).toHaveBeenCalledWith(txWithSignature);
|
|
55
|
+
expect(rpcClient.broadcastTransaction).toHaveBeenCalledTimes(1);
|
|
56
|
+
expect(rpcClient.broadcastTransaction).toHaveBeenCalledWith(mockDeserializedTx);
|
|
57
|
+
});
|
|
58
|
+
});
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { TransactionResponse } from "@hashgraph/sdk";
|
|
2
|
+
import { rpcClient } from "../network/rpc";
|
|
3
|
+
import { deserializeTransaction } from "./utils";
|
|
4
|
+
|
|
5
|
+
export const broadcast = async (txWithSignature: string): Promise<TransactionResponse> => {
|
|
6
|
+
const hederaTransaction = deserializeTransaction(txWithSignature);
|
|
7
|
+
return await rpcClient.broadcastTransaction(hederaTransaction);
|
|
8
|
+
};
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { PublicKey } from "@hashgraph/sdk";
|
|
2
|
+
import { combine } from "./combine";
|
|
3
|
+
import { deserializeSignature, deserializeTransaction, serializeTransaction } from "./utils";
|
|
4
|
+
|
|
5
|
+
jest.mock("./utils");
|
|
6
|
+
jest.mock("@hashgraph/sdk");
|
|
7
|
+
|
|
8
|
+
describe("combine", () => {
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
jest.clearAllMocks();
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it("should combine transaction with signature successfully", () => {
|
|
14
|
+
const tx = "serialized-transaction";
|
|
15
|
+
const signature = "serialized-signature";
|
|
16
|
+
const publicKey = "public-key-string";
|
|
17
|
+
|
|
18
|
+
const mockDeserializedTx = { addSignature: jest.fn() } as any;
|
|
19
|
+
const mockBufferSignature = Buffer.from("mock-signature");
|
|
20
|
+
const mockBufferPublicKey = { key: "mock-public-key" } as any;
|
|
21
|
+
const mockSerializedTxWithSignature = "serialized-transaction-with-signature";
|
|
22
|
+
|
|
23
|
+
(deserializeTransaction as jest.Mock).mockReturnValue(mockDeserializedTx);
|
|
24
|
+
(deserializeSignature as jest.Mock).mockReturnValue(mockBufferSignature);
|
|
25
|
+
(PublicKey.fromString as jest.Mock).mockReturnValue(mockBufferPublicKey);
|
|
26
|
+
(serializeTransaction as jest.Mock).mockReturnValue(mockSerializedTxWithSignature);
|
|
27
|
+
|
|
28
|
+
const result = combine(tx, signature, publicKey);
|
|
29
|
+
|
|
30
|
+
expect(deserializeTransaction).toHaveBeenCalledTimes(1);
|
|
31
|
+
expect(deserializeTransaction).toHaveBeenCalledWith(tx);
|
|
32
|
+
expect(deserializeSignature).toHaveBeenCalledTimes(1);
|
|
33
|
+
expect(deserializeSignature).toHaveBeenCalledWith(signature);
|
|
34
|
+
expect(PublicKey.fromString).toHaveBeenCalledTimes(1);
|
|
35
|
+
expect(PublicKey.fromString).toHaveBeenCalledWith(publicKey);
|
|
36
|
+
expect(mockDeserializedTx.addSignature).toHaveBeenCalledTimes(1);
|
|
37
|
+
expect(mockDeserializedTx.addSignature).toHaveBeenCalledWith(
|
|
38
|
+
mockBufferPublicKey,
|
|
39
|
+
mockBufferSignature,
|
|
40
|
+
);
|
|
41
|
+
expect(serializeTransaction).toHaveBeenCalledTimes(1);
|
|
42
|
+
expect(serializeTransaction).toHaveBeenCalledWith(mockDeserializedTx);
|
|
43
|
+
expect(result).toBe(mockSerializedTxWithSignature);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it("should throw an error when public key is not provided", () => {
|
|
47
|
+
const tx = "serialized-transaction";
|
|
48
|
+
const signature = "serialized-signature";
|
|
49
|
+
|
|
50
|
+
expect(() => combine(tx, signature, undefined)).toThrow();
|
|
51
|
+
expect(deserializeTransaction).not.toHaveBeenCalled();
|
|
52
|
+
expect(deserializeSignature).not.toHaveBeenCalled();
|
|
53
|
+
expect(PublicKey.fromString).not.toHaveBeenCalled();
|
|
54
|
+
expect(serializeTransaction).not.toHaveBeenCalled();
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it("should propagate errors from deserializeTransaction", () => {
|
|
58
|
+
const tx = "serialized-transaction";
|
|
59
|
+
const signature = "serialized-signature";
|
|
60
|
+
const publicKey = "public-key-string";
|
|
61
|
+
const error = new Error("Deserialization error");
|
|
62
|
+
|
|
63
|
+
(deserializeTransaction as jest.Mock).mockImplementation(() => {
|
|
64
|
+
throw error;
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
expect(() => combine(tx, signature, publicKey)).toThrow(error);
|
|
68
|
+
expect(deserializeTransaction).toHaveBeenCalledTimes(1);
|
|
69
|
+
expect(deserializeTransaction).toHaveBeenCalledWith(tx);
|
|
70
|
+
expect(deserializeSignature).not.toHaveBeenCalled();
|
|
71
|
+
expect(PublicKey.fromString).not.toHaveBeenCalled();
|
|
72
|
+
expect(serializeTransaction).not.toHaveBeenCalled();
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it("should propagate errors from deserializeSignature", () => {
|
|
76
|
+
const tx = "serialized-transaction";
|
|
77
|
+
const signature = "serialized-signature";
|
|
78
|
+
const publicKey = "public-key-string";
|
|
79
|
+
const mockDeserializedTx = { addSignature: jest.fn() } as any;
|
|
80
|
+
const error = new Error("Signature deserialization error");
|
|
81
|
+
|
|
82
|
+
(deserializeTransaction as jest.Mock).mockReturnValue(mockDeserializedTx);
|
|
83
|
+
(deserializeSignature as jest.Mock).mockImplementation(() => {
|
|
84
|
+
throw error;
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
expect(() => combine(tx, signature, publicKey)).toThrow(error);
|
|
88
|
+
expect(deserializeTransaction).toHaveBeenCalledTimes(1);
|
|
89
|
+
expect(deserializeTransaction).toHaveBeenCalledWith(tx);
|
|
90
|
+
expect(deserializeSignature).toHaveBeenCalledTimes(1);
|
|
91
|
+
expect(deserializeSignature).toHaveBeenCalledWith(signature);
|
|
92
|
+
expect(PublicKey.fromString).not.toHaveBeenCalled();
|
|
93
|
+
expect(serializeTransaction).not.toHaveBeenCalled();
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it("should propagate errors from PublicKey.fromString", () => {
|
|
97
|
+
const tx = "serialized-transaction";
|
|
98
|
+
const signature = "serialized-signature";
|
|
99
|
+
const publicKey = "invalid-public-key";
|
|
100
|
+
const mockDeserializedTx = { addSignature: jest.fn() } as any;
|
|
101
|
+
const mockBufferSignature = Buffer.from("mock-signature");
|
|
102
|
+
const error = new Error("Invalid public key");
|
|
103
|
+
|
|
104
|
+
(deserializeTransaction as jest.Mock).mockReturnValue(mockDeserializedTx);
|
|
105
|
+
(deserializeSignature as jest.Mock).mockReturnValue(mockBufferSignature);
|
|
106
|
+
(PublicKey.fromString as jest.Mock).mockImplementation(() => {
|
|
107
|
+
throw error;
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
expect(() => combine(tx, signature, publicKey)).toThrow(error);
|
|
111
|
+
expect(deserializeTransaction).toHaveBeenCalledTimes(1);
|
|
112
|
+
expect(deserializeTransaction).toHaveBeenCalledWith(tx);
|
|
113
|
+
expect(deserializeSignature).toHaveBeenCalledTimes(1);
|
|
114
|
+
expect(deserializeSignature).toHaveBeenCalledWith(signature);
|
|
115
|
+
expect(PublicKey.fromString).toHaveBeenCalledTimes(1);
|
|
116
|
+
expect(PublicKey.fromString).toHaveBeenCalledWith(publicKey);
|
|
117
|
+
expect(serializeTransaction).not.toHaveBeenCalled();
|
|
118
|
+
});
|
|
119
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import invariant from "invariant";
|
|
2
|
+
import { PublicKey } from "@hashgraph/sdk";
|
|
3
|
+
import { deserializeSignature, deserializeTransaction, serializeTransaction } from "./utils";
|
|
4
|
+
|
|
5
|
+
export function combine(tx: string, signature: string, publicKey?: string): string {
|
|
6
|
+
invariant(publicKey, "hedera: public key is required to combine the transaction");
|
|
7
|
+
|
|
8
|
+
const hederaTransaction = deserializeTransaction(tx);
|
|
9
|
+
const bufferSignature = deserializeSignature(signature);
|
|
10
|
+
const bufferPublicKey = PublicKey.fromString(publicKey);
|
|
11
|
+
hederaTransaction.addSignature(bufferPublicKey, bufferSignature);
|
|
12
|
+
|
|
13
|
+
return serializeTransaction(hederaTransaction);
|
|
14
|
+
}
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import invariant from "invariant";
|
|
2
|
+
import * as sdk from "@hashgraph/sdk";
|
|
3
|
+
import type { FeeEstimation, TransactionIntent } from "@ledgerhq/coin-framework/api/index";
|
|
4
|
+
import { HEDERA_TRANSACTION_MODES, TINYBAR_SCALE } from "../constants";
|
|
5
|
+
import { craftTransaction } from "./craftTransaction";
|
|
6
|
+
import type { HederaMemo } from "../types";
|
|
7
|
+
import { serializeTransaction } from "./utils";
|
|
8
|
+
|
|
9
|
+
jest.mock("./utils");
|
|
10
|
+
|
|
11
|
+
describe("craftTransaction", () => {
|
|
12
|
+
beforeEach(() => {
|
|
13
|
+
jest.clearAllMocks();
|
|
14
|
+
(serializeTransaction as jest.Mock).mockReturnValue("serialized-transaction");
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it("should craft a native HBAR transfer transaction", async () => {
|
|
18
|
+
const txIntent = {
|
|
19
|
+
intentType: "transaction",
|
|
20
|
+
type: HEDERA_TRANSACTION_MODES.Send,
|
|
21
|
+
amount: BigInt(1 * 10 ** TINYBAR_SCALE),
|
|
22
|
+
recipient: "0.0.12345",
|
|
23
|
+
sender: "0.0.54321",
|
|
24
|
+
asset: {
|
|
25
|
+
type: "native",
|
|
26
|
+
},
|
|
27
|
+
memo: {
|
|
28
|
+
kind: "text",
|
|
29
|
+
type: "string",
|
|
30
|
+
value: "Hbar transfer",
|
|
31
|
+
},
|
|
32
|
+
} satisfies TransactionIntent<HederaMemo>;
|
|
33
|
+
|
|
34
|
+
const result = await craftTransaction(txIntent);
|
|
35
|
+
|
|
36
|
+
expect(result.tx).toBeInstanceOf(sdk.TransferTransaction);
|
|
37
|
+
invariant(result.tx instanceof sdk.TransferTransaction, "TransferTransaction type guard");
|
|
38
|
+
|
|
39
|
+
const senderTransfer = result.tx.hbarTransfers?.get(txIntent.sender);
|
|
40
|
+
const recipientTransfer = result.tx.hbarTransfers?.get(txIntent.recipient);
|
|
41
|
+
|
|
42
|
+
expect(senderTransfer).toEqual(sdk.Hbar.fromTinybars(-txIntent.amount));
|
|
43
|
+
expect(recipientTransfer).toEqual(sdk.Hbar.fromTinybars(txIntent.amount));
|
|
44
|
+
expect(result.tx.transactionMemo).toBe(txIntent.memo.value);
|
|
45
|
+
expect(serializeTransaction).toHaveBeenCalled();
|
|
46
|
+
expect(result).toEqual({
|
|
47
|
+
tx: expect.any(Object),
|
|
48
|
+
serializedTx: "serialized-transaction",
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it("should craft a token transfer transaction", async () => {
|
|
53
|
+
const txIntent = {
|
|
54
|
+
intentType: "transaction",
|
|
55
|
+
type: HEDERA_TRANSACTION_MODES.Send,
|
|
56
|
+
amount: BigInt(1000),
|
|
57
|
+
recipient: "0.0.12345",
|
|
58
|
+
sender: "0.0.54321",
|
|
59
|
+
asset: {
|
|
60
|
+
type: "hts",
|
|
61
|
+
assetReference: "0.0.7890",
|
|
62
|
+
},
|
|
63
|
+
memo: {
|
|
64
|
+
kind: "text",
|
|
65
|
+
type: "string",
|
|
66
|
+
value: "Token transfer",
|
|
67
|
+
},
|
|
68
|
+
} satisfies TransactionIntent<HederaMemo>;
|
|
69
|
+
|
|
70
|
+
const result = await craftTransaction(txIntent);
|
|
71
|
+
|
|
72
|
+
expect(result.tx).toBeInstanceOf(sdk.TransferTransaction);
|
|
73
|
+
invariant(result.tx instanceof sdk.TransferTransaction, "TransferTransaction type guard");
|
|
74
|
+
|
|
75
|
+
const tokenTransfers = result.tx.tokenTransfers.get(txIntent.asset.assetReference);
|
|
76
|
+
const senderTransfer = tokenTransfers?.get(txIntent.sender);
|
|
77
|
+
const recipientTransfer = tokenTransfers?.get(txIntent.recipient);
|
|
78
|
+
|
|
79
|
+
// .toString() is used because tokenTransfers values are Long objects
|
|
80
|
+
// this is internal dependency of @hashgraph/sdk, re-exported in newer version
|
|
81
|
+
expect(senderTransfer?.toString()).toEqual((-txIntent.amount).toString());
|
|
82
|
+
expect(recipientTransfer?.toString()).toEqual(txIntent.amount.toString());
|
|
83
|
+
expect(result.tx.transactionMemo).toBe("Token transfer");
|
|
84
|
+
expect(serializeTransaction).toHaveBeenCalled();
|
|
85
|
+
expect(result).toEqual({
|
|
86
|
+
tx: expect.any(Object),
|
|
87
|
+
serializedTx: "serialized-transaction",
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it("should craft a token associate transaction", async () => {
|
|
92
|
+
const txIntent = {
|
|
93
|
+
intentType: "transaction",
|
|
94
|
+
type: HEDERA_TRANSACTION_MODES.TokenAssociate,
|
|
95
|
+
amount: BigInt(0),
|
|
96
|
+
recipient: "",
|
|
97
|
+
sender: "0.0.54321",
|
|
98
|
+
asset: {
|
|
99
|
+
type: "hts",
|
|
100
|
+
assetReference: "0.0.7890",
|
|
101
|
+
},
|
|
102
|
+
memo: {
|
|
103
|
+
kind: "text",
|
|
104
|
+
type: "string",
|
|
105
|
+
value: "Token association",
|
|
106
|
+
},
|
|
107
|
+
} satisfies TransactionIntent<HederaMemo>;
|
|
108
|
+
|
|
109
|
+
const result = await craftTransaction(txIntent);
|
|
110
|
+
|
|
111
|
+
expect(result.tx).toBeInstanceOf(sdk.TokenAssociateTransaction);
|
|
112
|
+
invariant(
|
|
113
|
+
result.tx instanceof sdk.TokenAssociateTransaction,
|
|
114
|
+
"TokenAssociateTransaction type guard",
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
expect(result.tx.accountId).toEqual(sdk.AccountId.fromString(txIntent.sender));
|
|
118
|
+
expect(result.tx.tokenIds).toEqual([sdk.TokenId.fromString(txIntent.asset.assetReference)]);
|
|
119
|
+
expect(result.tx.transactionMemo).toBe("Token association");
|
|
120
|
+
expect(serializeTransaction).toHaveBeenCalled();
|
|
121
|
+
expect(result).toEqual({
|
|
122
|
+
tx: expect.any(Object),
|
|
123
|
+
serializedTx: "serialized-transaction",
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it("should apply custom fees when provided", async () => {
|
|
128
|
+
const customFees: FeeEstimation = {
|
|
129
|
+
value: BigInt(50000),
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
const result = await craftTransaction(
|
|
133
|
+
{
|
|
134
|
+
intentType: "transaction",
|
|
135
|
+
type: HEDERA_TRANSACTION_MODES.Send,
|
|
136
|
+
amount: BigInt(1000000),
|
|
137
|
+
recipient: "0.0.12345",
|
|
138
|
+
sender: "0.0.54321",
|
|
139
|
+
asset: {
|
|
140
|
+
type: "native",
|
|
141
|
+
},
|
|
142
|
+
memo: {
|
|
143
|
+
kind: "text",
|
|
144
|
+
type: "string",
|
|
145
|
+
value: "Test memo with custom fee",
|
|
146
|
+
},
|
|
147
|
+
},
|
|
148
|
+
customFees,
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
expect(result.tx).toBeInstanceOf(sdk.TransferTransaction);
|
|
152
|
+
invariant(result.tx instanceof sdk.TransferTransaction, "TransferTransaction type guard");
|
|
153
|
+
expect(result.tx.maxTransactionFee).toEqual(sdk.Hbar.fromTinybars(customFees.value));
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it("should throw error when token associate transaction has invalid asset type", async () => {
|
|
157
|
+
const txIntent = {
|
|
158
|
+
intentType: "transaction",
|
|
159
|
+
type: HEDERA_TRANSACTION_MODES.TokenAssociate,
|
|
160
|
+
amount: BigInt(0),
|
|
161
|
+
recipient: "",
|
|
162
|
+
sender: "0.0.54321",
|
|
163
|
+
asset: {
|
|
164
|
+
type: "native",
|
|
165
|
+
},
|
|
166
|
+
memo: {
|
|
167
|
+
kind: "text",
|
|
168
|
+
type: "string",
|
|
169
|
+
value: "Invalid token association",
|
|
170
|
+
},
|
|
171
|
+
} satisfies TransactionIntent<HederaMemo>;
|
|
172
|
+
|
|
173
|
+
await expect(craftTransaction(txIntent)).rejects.toThrow();
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
it("should throw error when token associate transaction has missing assetReference", async () => {
|
|
177
|
+
const txIntent = {
|
|
178
|
+
intentType: "transaction",
|
|
179
|
+
type: HEDERA_TRANSACTION_MODES.TokenAssociate,
|
|
180
|
+
amount: BigInt(0),
|
|
181
|
+
recipient: "",
|
|
182
|
+
sender: "0.0.54321",
|
|
183
|
+
asset: {
|
|
184
|
+
type: "hts",
|
|
185
|
+
},
|
|
186
|
+
memo: {
|
|
187
|
+
kind: "text",
|
|
188
|
+
type: "string",
|
|
189
|
+
value: "Missing asset reference",
|
|
190
|
+
},
|
|
191
|
+
} satisfies TransactionIntent<HederaMemo>;
|
|
192
|
+
|
|
193
|
+
await expect(craftTransaction(txIntent)).rejects.toThrow();
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
it("should throw error when token transfer transaction has missing assetReference", async () => {
|
|
197
|
+
const txIntent = {
|
|
198
|
+
intentType: "transaction",
|
|
199
|
+
type: HEDERA_TRANSACTION_MODES.Send,
|
|
200
|
+
amount: BigInt(1000),
|
|
201
|
+
recipient: "0.0.12345",
|
|
202
|
+
sender: "0.0.54321",
|
|
203
|
+
asset: {
|
|
204
|
+
type: "hts",
|
|
205
|
+
},
|
|
206
|
+
memo: {
|
|
207
|
+
kind: "text",
|
|
208
|
+
type: "string",
|
|
209
|
+
value: "Missing token reference",
|
|
210
|
+
},
|
|
211
|
+
} satisfies TransactionIntent<HederaMemo>;
|
|
212
|
+
|
|
213
|
+
await expect(craftTransaction(txIntent)).rejects.toThrow();
|
|
214
|
+
});
|
|
215
|
+
});
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import BigNumber from "bignumber.js";
|
|
2
|
+
import invariant from "invariant";
|
|
3
|
+
import {
|
|
4
|
+
AccountId,
|
|
5
|
+
Hbar,
|
|
6
|
+
TokenAssociateTransaction,
|
|
7
|
+
TransactionId,
|
|
8
|
+
TransferTransaction,
|
|
9
|
+
} from "@hashgraph/sdk";
|
|
10
|
+
import type { FeeEstimation, TransactionIntent } from "@ledgerhq/coin-framework/api/index";
|
|
11
|
+
import { HEDERA_TRANSACTION_MODES } from "../constants";
|
|
12
|
+
import type { HederaMemo } from "../types";
|
|
13
|
+
import { serializeTransaction } from "./utils";
|
|
14
|
+
|
|
15
|
+
// avoid "sign" prompt loop by having only one node (one transaction)
|
|
16
|
+
// https://github.com/LedgerHQ/ledger-live/pull/72/commits/1e942687d4301660e43e0c4b5419fcfa2733b290
|
|
17
|
+
// changing this will break `getHederaTransactionBodyBytes` from logic/utils.ts
|
|
18
|
+
const nodeAccountIds: AccountId[] = [new AccountId(3)];
|
|
19
|
+
|
|
20
|
+
interface BuilderOperator {
|
|
21
|
+
accountId: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
interface BuilderCommonTransactionFields {
|
|
25
|
+
maxFee: BigNumber | undefined;
|
|
26
|
+
memo: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
interface BuilderCoinTransferTransaction extends BuilderCommonTransactionFields {
|
|
30
|
+
type: HEDERA_TRANSACTION_MODES.Send;
|
|
31
|
+
amount: BigNumber;
|
|
32
|
+
recipient: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
interface BuilderTokenTransferTransaction extends BuilderCommonTransactionFields {
|
|
36
|
+
type: HEDERA_TRANSACTION_MODES.Send;
|
|
37
|
+
tokenId: string;
|
|
38
|
+
amount: BigNumber;
|
|
39
|
+
recipient: string;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
interface BuilderTokenAssociateTransaction extends BuilderCommonTransactionFields {
|
|
43
|
+
type: HEDERA_TRANSACTION_MODES.TokenAssociate;
|
|
44
|
+
tokenId: string;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async function buildUnsignedCoinTransaction({
|
|
48
|
+
account,
|
|
49
|
+
transaction,
|
|
50
|
+
}: {
|
|
51
|
+
account: BuilderOperator;
|
|
52
|
+
transaction: BuilderCoinTransferTransaction;
|
|
53
|
+
}): Promise<TransferTransaction> {
|
|
54
|
+
const accountId = account.accountId;
|
|
55
|
+
const hbarAmount = Hbar.fromTinybars(transaction.amount);
|
|
56
|
+
|
|
57
|
+
const tx = new TransferTransaction()
|
|
58
|
+
.setNodeAccountIds(nodeAccountIds)
|
|
59
|
+
.setTransactionId(TransactionId.generate(accountId))
|
|
60
|
+
.setTransactionMemo(transaction.memo)
|
|
61
|
+
.addHbarTransfer(accountId, hbarAmount.negated())
|
|
62
|
+
.addHbarTransfer(transaction.recipient, hbarAmount);
|
|
63
|
+
|
|
64
|
+
if (transaction.maxFee) {
|
|
65
|
+
tx.setMaxTransactionFee(Hbar.fromTinybars(transaction.maxFee.toNumber()));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return tx.freeze();
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async function buildUnsignedTokenTransaction({
|
|
72
|
+
account,
|
|
73
|
+
transaction,
|
|
74
|
+
}: {
|
|
75
|
+
account: BuilderOperator;
|
|
76
|
+
transaction: BuilderTokenTransferTransaction;
|
|
77
|
+
}): Promise<TransferTransaction> {
|
|
78
|
+
const accountId = account.accountId;
|
|
79
|
+
const tokenId = transaction.tokenId;
|
|
80
|
+
|
|
81
|
+
const tx = new TransferTransaction()
|
|
82
|
+
.setNodeAccountIds(nodeAccountIds)
|
|
83
|
+
.setTransactionId(TransactionId.generate(accountId))
|
|
84
|
+
.setTransactionMemo(transaction.memo)
|
|
85
|
+
.addTokenTransfer(tokenId, accountId, transaction.amount.negated().toNumber())
|
|
86
|
+
.addTokenTransfer(tokenId, transaction.recipient, transaction.amount.toNumber());
|
|
87
|
+
|
|
88
|
+
if (transaction.maxFee) {
|
|
89
|
+
tx.setMaxTransactionFee(Hbar.fromTinybars(transaction.maxFee.toNumber()));
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return tx.freeze();
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
async function buildTokenAssociateTransaction({
|
|
96
|
+
account,
|
|
97
|
+
transaction,
|
|
98
|
+
}: {
|
|
99
|
+
account: BuilderOperator;
|
|
100
|
+
transaction: BuilderTokenAssociateTransaction;
|
|
101
|
+
}): Promise<TokenAssociateTransaction> {
|
|
102
|
+
const accountId = account.accountId;
|
|
103
|
+
|
|
104
|
+
const tx = new TokenAssociateTransaction()
|
|
105
|
+
.setNodeAccountIds(nodeAccountIds)
|
|
106
|
+
.setTransactionId(TransactionId.generate(accountId))
|
|
107
|
+
.setTransactionMemo(transaction.memo)
|
|
108
|
+
.setAccountId(accountId)
|
|
109
|
+
.setTokenIds([transaction.tokenId]);
|
|
110
|
+
|
|
111
|
+
if (transaction.maxFee) {
|
|
112
|
+
tx.setMaxTransactionFee(Hbar.fromTinybars(transaction.maxFee.toNumber()));
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return tx.freeze();
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export async function craftTransaction(
|
|
119
|
+
txIntent: TransactionIntent<HederaMemo>,
|
|
120
|
+
customFees?: FeeEstimation,
|
|
121
|
+
) {
|
|
122
|
+
const account = {
|
|
123
|
+
accountId: txIntent.sender,
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
let tx;
|
|
127
|
+
|
|
128
|
+
if (txIntent.type === HEDERA_TRANSACTION_MODES.TokenAssociate) {
|
|
129
|
+
invariant(txIntent.asset.type !== "native", "hedera: invalid asset type");
|
|
130
|
+
invariant("assetReference" in txIntent.asset, "hedera: assetReference is missing");
|
|
131
|
+
|
|
132
|
+
tx = await buildTokenAssociateTransaction({
|
|
133
|
+
account,
|
|
134
|
+
transaction: {
|
|
135
|
+
type: txIntent.type,
|
|
136
|
+
tokenId: txIntent.asset.assetReference,
|
|
137
|
+
memo: txIntent.memo.value,
|
|
138
|
+
maxFee: customFees ? new BigNumber(customFees.value.toString()) : undefined,
|
|
139
|
+
},
|
|
140
|
+
});
|
|
141
|
+
} else if (txIntent.type === HEDERA_TRANSACTION_MODES.Send && txIntent.asset.type !== "native") {
|
|
142
|
+
invariant("assetReference" in txIntent.asset, "hedera: no assetReference in token transfer");
|
|
143
|
+
|
|
144
|
+
const amount = new BigNumber(txIntent.amount.toString());
|
|
145
|
+
|
|
146
|
+
tx = await buildUnsignedTokenTransaction({
|
|
147
|
+
account,
|
|
148
|
+
transaction: {
|
|
149
|
+
type: txIntent.type,
|
|
150
|
+
tokenId: txIntent.asset.assetReference,
|
|
151
|
+
amount,
|
|
152
|
+
recipient: txIntent.recipient,
|
|
153
|
+
memo: txIntent.memo.value,
|
|
154
|
+
maxFee: customFees ? new BigNumber(customFees.value.toString()) : undefined,
|
|
155
|
+
},
|
|
156
|
+
});
|
|
157
|
+
} else {
|
|
158
|
+
const amount = new BigNumber(txIntent.amount.toString());
|
|
159
|
+
|
|
160
|
+
tx = await buildUnsignedCoinTransaction({
|
|
161
|
+
account,
|
|
162
|
+
transaction: {
|
|
163
|
+
type: HEDERA_TRANSACTION_MODES.Send,
|
|
164
|
+
amount,
|
|
165
|
+
recipient: txIntent.recipient,
|
|
166
|
+
memo: txIntent.memo.value,
|
|
167
|
+
maxFee: customFees ? new BigNumber(customFees.value.toString()) : undefined,
|
|
168
|
+
},
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const serializedTx = serializeTransaction(tx);
|
|
173
|
+
|
|
174
|
+
return { tx, serializedTx };
|
|
175
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import BigNumber from "bignumber.js";
|
|
2
|
+
import cvsApi from "@ledgerhq/live-countervalues/api/index";
|
|
3
|
+
import {
|
|
4
|
+
DEFAULT_TINYBAR_FEE,
|
|
5
|
+
ESTIMATED_FEE_SAFETY_RATE,
|
|
6
|
+
HEDERA_OPERATION_TYPES,
|
|
7
|
+
TINYBAR_SCALE,
|
|
8
|
+
} from "../constants";
|
|
9
|
+
import { estimateFees } from "./estimateFees";
|
|
10
|
+
import { getMockedAccount } from "../test/fixtures/account.fixture";
|
|
11
|
+
import { getCurrencyToUSDRate } from "./utils";
|
|
12
|
+
|
|
13
|
+
jest.mock("@ledgerhq/live-countervalues/api/index");
|
|
14
|
+
|
|
15
|
+
describe("getEstimatedFees", () => {
|
|
16
|
+
const mockedAccount = getMockedAccount();
|
|
17
|
+
|
|
18
|
+
beforeEach(() => {
|
|
19
|
+
jest.clearAllMocks();
|
|
20
|
+
// reset LRU cache to make sure all tests receive correct mocks from mockedFetchLatest
|
|
21
|
+
getCurrencyToUSDRate.clear(mockedAccount.currency.ticker);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test("returns estimated fee based on USD rate for CryptoTransfer", async () => {
|
|
25
|
+
const usdRate = 1; // 1 HBAR = 1 USD
|
|
26
|
+
(cvsApi.fetchLatest as jest.Mock).mockResolvedValueOnce([usdRate]);
|
|
27
|
+
|
|
28
|
+
const result = await estimateFees(
|
|
29
|
+
mockedAccount.currency,
|
|
30
|
+
HEDERA_OPERATION_TYPES.CryptoTransfer,
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
const baseFeeTinybar = 0.0001 * 10 ** TINYBAR_SCALE;
|
|
34
|
+
const expectedFee = new BigNumber(baseFeeTinybar)
|
|
35
|
+
.div(usdRate)
|
|
36
|
+
.integerValue(BigNumber.ROUND_CEIL)
|
|
37
|
+
.multipliedBy(ESTIMATED_FEE_SAFETY_RATE);
|
|
38
|
+
|
|
39
|
+
expect(result).toEqual(expectedFee);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test("returns estimated fee based on USD rate for TokenTransfer", async () => {
|
|
43
|
+
const usdRate = 0.5; // 1 HBAR = 0.5 USD
|
|
44
|
+
(cvsApi.fetchLatest as jest.Mock).mockResolvedValueOnce([usdRate]);
|
|
45
|
+
|
|
46
|
+
const result = await estimateFees(mockedAccount.currency, HEDERA_OPERATION_TYPES.TokenTransfer);
|
|
47
|
+
|
|
48
|
+
const baseFeeTinybar = 0.001 * 10 ** TINYBAR_SCALE;
|
|
49
|
+
const expectedFee = new BigNumber(baseFeeTinybar)
|
|
50
|
+
.div(usdRate)
|
|
51
|
+
.integerValue(BigNumber.ROUND_CEIL)
|
|
52
|
+
.multipliedBy(ESTIMATED_FEE_SAFETY_RATE);
|
|
53
|
+
|
|
54
|
+
expect(result).toEqual(expectedFee);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
test("returns estimated fee based on USD rate for TokenAssociate", async () => {
|
|
58
|
+
const usdRate = 2; // 1 HBAR = 2 USD
|
|
59
|
+
(cvsApi.fetchLatest as jest.Mock).mockResolvedValueOnce([usdRate]);
|
|
60
|
+
|
|
61
|
+
const result = await estimateFees(
|
|
62
|
+
mockedAccount.currency,
|
|
63
|
+
HEDERA_OPERATION_TYPES.TokenAssociate,
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
const baseFeeTinybar = 0.05 * 10 ** TINYBAR_SCALE;
|
|
67
|
+
const expectedFee = new BigNumber(baseFeeTinybar)
|
|
68
|
+
.div(usdRate)
|
|
69
|
+
.integerValue(BigNumber.ROUND_CEIL)
|
|
70
|
+
.multipliedBy(ESTIMATED_FEE_SAFETY_RATE);
|
|
71
|
+
|
|
72
|
+
expect(result).toEqual(expectedFee);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
test("falls back to default estimate when cvs api returns null", async () => {
|
|
76
|
+
const usdRate = null;
|
|
77
|
+
(cvsApi.fetchLatest as jest.Mock).mockResolvedValueOnce([usdRate]);
|
|
78
|
+
|
|
79
|
+
const result = await estimateFees(
|
|
80
|
+
mockedAccount.currency,
|
|
81
|
+
HEDERA_OPERATION_TYPES.CryptoTransfer,
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
const expected = new BigNumber(DEFAULT_TINYBAR_FEE).multipliedBy(ESTIMATED_FEE_SAFETY_RATE);
|
|
85
|
+
expect(result).toEqual(expected);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test("falls back to default estimate on cvs api failure", async () => {
|
|
89
|
+
(cvsApi.fetchLatest as jest.Mock).mockRejectedValueOnce(new Error("Network error"));
|
|
90
|
+
|
|
91
|
+
const result = await estimateFees(
|
|
92
|
+
mockedAccount.currency,
|
|
93
|
+
HEDERA_OPERATION_TYPES.CryptoTransfer,
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
const expected = new BigNumber(DEFAULT_TINYBAR_FEE).multipliedBy(ESTIMATED_FEE_SAFETY_RATE);
|
|
97
|
+
expect(result).toEqual(expected);
|
|
98
|
+
});
|
|
99
|
+
});
|