@ledgerhq/coin-hedera 1.6.7 → 1.7.0-next.0
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/.unimportedrc.json +1 -0
- package/CHANGELOG.md +17 -0
- package/lib/api/mirror.d.ts +16 -3
- package/lib/api/mirror.d.ts.map +1 -1
- package/lib/api/mirror.js +29 -10
- package/lib/api/mirror.js.map +1 -1
- package/lib/api/mirror.test.d.ts +2 -0
- package/lib/api/mirror.test.d.ts.map +1 -0
- package/lib/api/mirror.test.js +60 -0
- package/lib/api/mirror.test.js.map +1 -0
- package/lib/bridge/broadcast.d.ts.map +1 -1
- package/lib/bridge/broadcast.js +7 -1
- package/lib/bridge/broadcast.js.map +1 -1
- package/lib/bridge/synchronisation.js +3 -3
- package/lib/bridge/synchronisation.js.map +1 -1
- package/lib/bridge/utils.d.ts +3 -2
- package/lib/bridge/utils.d.ts.map +1 -1
- package/lib/bridge/utils.js +10 -1
- package/lib/bridge/utils.js.map +1 -1
- package/lib/logic.d.ts +5 -0
- package/lib/logic.d.ts.map +1 -0
- package/lib/logic.js +9 -0
- package/lib/logic.js.map +1 -0
- package/lib/logic.test.d.ts +2 -0
- package/lib/logic.test.d.ts.map +1 -0
- package/lib/logic.test.js +54 -0
- package/lib/logic.test.js.map +1 -0
- package/lib/types/bridge.d.ts +1 -3
- package/lib/types/bridge.d.ts.map +1 -1
- package/lib-es/api/mirror.d.ts +16 -3
- package/lib-es/api/mirror.d.ts.map +1 -1
- package/lib-es/api/mirror.js +27 -9
- package/lib-es/api/mirror.js.map +1 -1
- package/lib-es/api/mirror.test.d.ts +2 -0
- package/lib-es/api/mirror.test.d.ts.map +1 -0
- package/lib-es/api/mirror.test.js +55 -0
- package/lib-es/api/mirror.test.js.map +1 -0
- package/lib-es/bridge/broadcast.d.ts.map +1 -1
- package/lib-es/bridge/broadcast.js +8 -2
- package/lib-es/bridge/broadcast.js.map +1 -1
- package/lib-es/bridge/synchronisation.js +3 -3
- package/lib-es/bridge/synchronisation.js.map +1 -1
- package/lib-es/bridge/utils.d.ts +3 -2
- package/lib-es/bridge/utils.d.ts.map +1 -1
- package/lib-es/bridge/utils.js +8 -0
- package/lib-es/bridge/utils.js.map +1 -1
- package/lib-es/logic.d.ts +5 -0
- package/lib-es/logic.d.ts.map +1 -0
- package/lib-es/logic.js +6 -0
- package/lib-es/logic.js.map +1 -0
- package/lib-es/logic.test.d.ts +2 -0
- package/lib-es/logic.test.d.ts.map +1 -0
- package/lib-es/logic.test.js +52 -0
- package/lib-es/logic.test.js.map +1 -0
- package/lib-es/types/bridge.d.ts +1 -3
- package/lib-es/types/bridge.d.ts.map +1 -1
- package/package.json +6 -5
- package/src/api/mirror.test.ts +76 -0
- package/src/api/mirror.ts +43 -19
- package/src/bridge/broadcast.ts +11 -4
- package/src/bridge/synchronisation.ts +3 -3
- package/src/bridge/utils.ts +14 -2
- package/src/logic.test.ts +58 -0
- package/src/logic.ts +15 -0
- package/src/types/bridge.ts +1 -4
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import network from "@ledgerhq/live-network/network";
|
|
2
|
+
import { getAccountTransactions } from "./mirror";
|
|
3
|
+
jest.mock("@ledgerhq/live-network/network");
|
|
4
|
+
const mockedNetwork = jest.mocked(network);
|
|
5
|
+
const makeMockResponse = (data) => ({
|
|
6
|
+
data,
|
|
7
|
+
status: 200,
|
|
8
|
+
statusText: "OK",
|
|
9
|
+
headers: {},
|
|
10
|
+
config: {
|
|
11
|
+
headers: {},
|
|
12
|
+
},
|
|
13
|
+
});
|
|
14
|
+
describe("getAccountTransactions", () => {
|
|
15
|
+
beforeEach(() => {
|
|
16
|
+
jest.clearAllMocks();
|
|
17
|
+
});
|
|
18
|
+
it("should include 'account.id', 'limit=100' and 'order=desc' query params", async () => {
|
|
19
|
+
mockedNetwork.mockResolvedValueOnce(makeMockResponse({ transactions: [], links: { next: null } }));
|
|
20
|
+
await getAccountTransactions("0.0.1234", null);
|
|
21
|
+
const calledUrl = mockedNetwork.mock.calls[0][0].url;
|
|
22
|
+
expect(calledUrl).toContain("account.id=0.0.1234");
|
|
23
|
+
expect(calledUrl).toContain("limit=100");
|
|
24
|
+
expect(calledUrl).toContain("order=desc");
|
|
25
|
+
});
|
|
26
|
+
it("should break early if no transactions are returned", async () => {
|
|
27
|
+
mockedNetwork.mockResolvedValueOnce(makeMockResponse({
|
|
28
|
+
transactions: [],
|
|
29
|
+
links: { next: "/next-1" },
|
|
30
|
+
}));
|
|
31
|
+
const result = await getAccountTransactions("0.0.1234", null);
|
|
32
|
+
expect(mockedNetwork).toHaveBeenCalledTimes(1);
|
|
33
|
+
expect(result).toEqual([]);
|
|
34
|
+
});
|
|
35
|
+
it("should keep fetching if links.next is present and new transactions are returned", async () => {
|
|
36
|
+
mockedNetwork
|
|
37
|
+
.mockResolvedValueOnce(makeMockResponse({
|
|
38
|
+
transactions: [{ consensus_timestamp: "1" }],
|
|
39
|
+
links: { next: "/next-1" },
|
|
40
|
+
}))
|
|
41
|
+
.mockResolvedValueOnce(makeMockResponse({
|
|
42
|
+
transactions: [{ consensus_timestamp: "2" }],
|
|
43
|
+
links: { next: "/next-2" },
|
|
44
|
+
}))
|
|
45
|
+
.mockResolvedValueOnce(makeMockResponse({
|
|
46
|
+
transactions: [],
|
|
47
|
+
links: { next: "/next-3" },
|
|
48
|
+
}));
|
|
49
|
+
const result = await getAccountTransactions("0.0.1234", null);
|
|
50
|
+
expect(result).toHaveLength(2);
|
|
51
|
+
expect(result.map(tx => tx.consensus_timestamp)).toEqual(["1", "2"]);
|
|
52
|
+
expect(mockedNetwork).toHaveBeenCalledTimes(3);
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
//# sourceMappingURL=mirror.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mirror.test.js","sourceRoot":"","sources":["../../src/api/mirror.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"broadcast.d.ts","sourceRoot":"","sources":["../../src/bridge/broadcast.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,
|
|
1
|
+
{"version":3,"file":"broadcast.d.ts","sourceRoot":"","sources":["../../src/bridge/broadcast.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAa,MAAM,sBAAsB,CAAC;AAGhE,OAAO,EAAwB,WAAW,EAAE,MAAM,UAAU,CAAC;AAG7D,eAAO,MAAM,SAAS,EAAE,aAAa,CAAC,WAAW,CAAC,CAAC,WAAW,CAmB7D,CAAC"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Transaction as HederaSDKTransaction } from "@hashgraph/sdk";
|
|
2
2
|
import { patchOperationWithHash } from "@ledgerhq/coin-framework/operation";
|
|
3
|
-
import { base64ToUrlSafeBase64 } from "./utils";
|
|
3
|
+
import { base64ToUrlSafeBase64, patchOperationWithExtra } from "./utils";
|
|
4
4
|
import { broadcastTransaction } from "../api/network";
|
|
5
5
|
export const broadcast = async ({ signedOperation }) => {
|
|
6
6
|
const { signature, operation } = signedOperation;
|
|
@@ -9,6 +9,12 @@ export const broadcast = async ({ signedOperation }) => {
|
|
|
9
9
|
const response = await broadcastTransaction(hederaTransaction);
|
|
10
10
|
const base64Hash = Buffer.from(response.transactionHash).toString("base64");
|
|
11
11
|
const base64HashUrlSafe = base64ToUrlSafeBase64(base64Hash);
|
|
12
|
-
|
|
12
|
+
const extra = {
|
|
13
|
+
transactionId: response.transactionId.toString(),
|
|
14
|
+
};
|
|
15
|
+
let patchedOperation = operation;
|
|
16
|
+
patchedOperation = patchOperationWithHash(patchedOperation, base64HashUrlSafe);
|
|
17
|
+
patchedOperation = patchOperationWithExtra(patchedOperation, extra);
|
|
18
|
+
return patchedOperation;
|
|
13
19
|
};
|
|
14
20
|
//# sourceMappingURL=broadcast.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"broadcast.js","sourceRoot":"","sources":["../../src/bridge/broadcast.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,IAAI,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAErE,OAAO,EAAE,sBAAsB,EAAE,MAAM,oCAAoC,CAAC;AAC5E,OAAO,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"broadcast.js","sourceRoot":"","sources":["../../src/bridge/broadcast.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,IAAI,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAErE,OAAO,EAAE,sBAAsB,EAAE,MAAM,oCAAoC,CAAC;AAC5E,OAAO,EAAE,qBAAqB,EAAE,uBAAuB,EAAE,MAAM,SAAS,CAAC;AAEzE,OAAO,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAEtD,MAAM,CAAC,MAAM,SAAS,GAA4C,KAAK,EAAE,EAAE,eAAe,EAAE,EAAE,EAAE;IAC9F,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,GAAG,eAAe,CAAC;IAEjD,oFAAoF;IACpF,MAAM,iBAAiB,GAAG,oBAAoB,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC,CAAC;IAE3F,MAAM,QAAQ,GAAG,MAAM,oBAAoB,CAAC,iBAAiB,CAAC,CAAC;IAE/D,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAC5E,MAAM,iBAAiB,GAAG,qBAAqB,CAAC,UAAU,CAAC,CAAC;IAC5D,MAAM,KAAK,GAAyB;QAClC,aAAa,EAAE,QAAQ,CAAC,aAAa,CAAC,QAAQ,EAAE;KACjD,CAAC;IAEF,IAAI,gBAAgB,GAAc,SAAS,CAAC;IAC5C,gBAAgB,GAAG,sBAAsB,CAAC,gBAAgB,EAAE,iBAAiB,CAAC,CAAC;IAC/E,gBAAgB,GAAG,uBAAuB,CAAC,gBAAgB,EAAE,KAAK,CAAC,CAAC;IAEpE,OAAO,gBAAgB,CAAC;AAC1B,CAAC,CAAC"}
|
|
@@ -20,10 +20,10 @@ export const getAccountShape = async (info) => {
|
|
|
20
20
|
// grab latest operation's consensus timestamp for incremental sync
|
|
21
21
|
const oldOperations = initialAccount?.operations ?? [];
|
|
22
22
|
const latestOperationTimestamp = oldOperations[0]
|
|
23
|
-
? Math.floor(oldOperations[0].date.getTime() / 1000)
|
|
24
|
-
:
|
|
23
|
+
? new BigNumber(Math.floor(oldOperations[0].date.getTime() / 1000))
|
|
24
|
+
: null;
|
|
25
25
|
// merge new operations w/ previously synced ones
|
|
26
|
-
const newOperations = await getOperationsForAccount(liveAccountId, address,
|
|
26
|
+
const newOperations = await getOperationsForAccount(liveAccountId, address, latestOperationTimestamp ? latestOperationTimestamp.toString() : null);
|
|
27
27
|
const operations = mergeOps(oldOperations, newOperations);
|
|
28
28
|
return {
|
|
29
29
|
id: liveAccountId,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"synchronisation.js","sourceRoot":"","sources":["../../src/bridge/synchronisation.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,WAAW,CAAC;AAClC,OAAO,EACL,mBAAmB,EAEnB,mBAAmB,GACpB,MAAM,qCAAqC,CAAC;AAC7C,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzC,OAAO,EAAE,uBAAuB,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AACjF,OAAO,EAGL,QAAQ,GACT,MAAM,2CAA2C,CAAC;AACnD,OAAO,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AACnE,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAEnD,MAAM,CAAC,MAAM,eAAe,GAA6B,KAAK,EAC5D,IAAS,EACkB,EAAE;IAC7B,MAAM,EAAE,QAAQ,EAAE,cAAc,EAAE,OAAO,EAAE,cAAc,EAAE,GAAG,IAAI,CAAC;IAEnE,SAAS,CAAC,OAAO,EAAE,+BAA+B,CAAC,CAAC;IAEpD,MAAM,aAAa,GAAG,eAAe,CAAC;QACpC,IAAI,EAAE,IAAI;QACV,OAAO,EAAE,GAAG;QACZ,UAAU,EAAE,QAAQ,CAAC,EAAE;QACvB,aAAa,EAAE,OAAO;QACtB,cAAc;KACf,CAAC,CAAC;IAEH,8BAA8B;IAC9B,MAAM,cAAc,GAAG,MAAM,iBAAiB,CAAC,OAAO,CAAC,CAAC;IAExD,mEAAmE;IACnE,MAAM,aAAa,GAAG,cAAc,EAAE,UAAU,IAAI,EAAE,CAAC;IACvD,MAAM,wBAAwB,GAAG,aAAa,CAAC,CAAC,CAAC;QAC/C,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC
|
|
1
|
+
{"version":3,"file":"synchronisation.js","sourceRoot":"","sources":["../../src/bridge/synchronisation.ts"],"names":[],"mappings":"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"}
|
package/lib-es/bridge/utils.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import BigNumber from "bignumber.js";
|
|
2
|
-
import type { Account } from "@ledgerhq/types-live";
|
|
3
|
-
import type { Transaction } from "../types";
|
|
2
|
+
import type { Account, Operation } from "@ledgerhq/types-live";
|
|
3
|
+
import type { HederaOperationExtra, Transaction } from "../types";
|
|
4
4
|
export declare const estimatedFeeSafetyRate = 2;
|
|
5
5
|
export declare function getEstimatedFees(account: Account): Promise<BigNumber>;
|
|
6
6
|
export declare function calculateAmount({ account, transaction, }: {
|
|
@@ -11,4 +11,5 @@ export declare function calculateAmount({ account, transaction, }: {
|
|
|
11
11
|
totalSpent: BigNumber;
|
|
12
12
|
}>;
|
|
13
13
|
export declare function base64ToUrlSafeBase64(data: string): string;
|
|
14
|
+
export declare function patchOperationWithExtra(operation: Operation, extra: HederaOperationExtra): Operation;
|
|
14
15
|
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/bridge/utils.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,cAAc,CAAC;AACrC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/bridge/utils.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,cAAc,CAAC;AACrC,OAAO,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAI/D,OAAO,KAAK,EAAE,oBAAoB,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAElE,eAAO,MAAM,sBAAsB,IAAI,CAAC;AAExC,wBAAsB,gBAAgB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,SAAS,CAAC,CAsB3E;AAED,wBAAsB,eAAe,CAAC,EACpC,OAAO,EACP,WAAW,GACZ,EAAE;IACD,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,WAAW,CAAC;CAC1B,GAAG,OAAO,CAAC;IACV,MAAM,EAAE,SAAS,CAAC;IAClB,UAAU,EAAE,SAAS,CAAC;CACvB,CAAC,CASD;AAGD,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAM1D;AAED,wBAAgB,uBAAuB,CACrC,SAAS,EAAE,SAAS,EACpB,KAAK,EAAE,oBAAoB,GAC1B,SAAS,CAOX"}
|
package/lib-es/bridge/utils.js
CHANGED
|
@@ -41,4 +41,12 @@ export function base64ToUrlSafeBase64(data) {
|
|
|
41
41
|
// Buffer.from(data, "base64").toString("base64url");
|
|
42
42
|
return data.replace(/\//g, "_").replace(/\+/g, "-");
|
|
43
43
|
}
|
|
44
|
+
export function patchOperationWithExtra(operation, extra) {
|
|
45
|
+
return {
|
|
46
|
+
...operation,
|
|
47
|
+
extra,
|
|
48
|
+
subOperations: (operation.subOperations ?? []).map(op => ({ ...op, extra })),
|
|
49
|
+
nftOperations: (operation.nftOperations ?? []).map(op => ({ ...op, extra })),
|
|
50
|
+
};
|
|
51
|
+
}
|
|
44
52
|
//# sourceMappingURL=utils.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/bridge/utils.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,cAAc,CAAC;AAErC,OAAO,MAAM,MAAM,wCAAwC,CAAC;AAC5D,OAAO,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AACjE,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAG9D,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,CAAC;AAExC,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,OAAgB;IACrD,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,WAAW,CAAC;YACpC;gBACE,IAAI,EAAE,OAAO,CAAC,QAAQ;gBACtB,EAAE,EAAE,uBAAuB,CAAC,KAAK,CAAC;gBAClC,SAAS,EAAE,IAAI,IAAI,EAAE;aACtB;SACF,CAAC,CAAC;QAEH,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;YACZ,OAAO,IAAI,SAAS,CAAC,KAAK,CAAC;iBACxB,SAAS,CAAC,IAAI,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;iBACjC,YAAY,CAAC,SAAS,CAAC,UAAU,CAAC;iBAClC,YAAY,CAAC,sBAAsB,CAAC,CAAC;QAC1C,CAAC;QACD,oCAAoC;IACtC,CAAC;IAAC,MAAM,CAAC,CAAA,CAAC;IAEV,sDAAsD;IACtD,oEAAoE;IACpE,OAAO,IAAI,SAAS,CAAC,QAAQ,CAAC,CAAC,YAAY,CAAC,sBAAsB,CAAC,CAAC,CAAC,gCAAgC;AACvG,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,EACpC,OAAO,EACP,WAAW,GAIZ;IAIC,MAAM,MAAM,GAAG,WAAW,CAAC,YAAY;QACrC,CAAC,CAAC,MAAM,oBAAoB,CAAC,EAAE,OAAO,EAAE,CAAC;QACzC,CAAC,CAAC,WAAW,CAAC,MAAM,CAAC;IAEvB,OAAO;QACL,MAAM;QACN,UAAU,EAAE,MAAM,CAAC,IAAI,CAAC,MAAM,gBAAgB,CAAC,OAAO,CAAC,CAAC;KACzD,CAAC;AACJ,CAAC;AAED,yGAAyG;AACzG,MAAM,UAAU,qBAAqB,CAAC,IAAY;IAChD,8EAA8E;IAC9E,uDAAuD;IACvD,qDAAqD;IAErD,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;AACtD,CAAC"}
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/bridge/utils.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,cAAc,CAAC;AAErC,OAAO,MAAM,MAAM,wCAAwC,CAAC;AAC5D,OAAO,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AACjE,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAG9D,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,CAAC;AAExC,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,OAAgB;IACrD,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,WAAW,CAAC;YACpC;gBACE,IAAI,EAAE,OAAO,CAAC,QAAQ;gBACtB,EAAE,EAAE,uBAAuB,CAAC,KAAK,CAAC;gBAClC,SAAS,EAAE,IAAI,IAAI,EAAE;aACtB;SACF,CAAC,CAAC;QAEH,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;YACZ,OAAO,IAAI,SAAS,CAAC,KAAK,CAAC;iBACxB,SAAS,CAAC,IAAI,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;iBACjC,YAAY,CAAC,SAAS,CAAC,UAAU,CAAC;iBAClC,YAAY,CAAC,sBAAsB,CAAC,CAAC;QAC1C,CAAC;QACD,oCAAoC;IACtC,CAAC;IAAC,MAAM,CAAC,CAAA,CAAC;IAEV,sDAAsD;IACtD,oEAAoE;IACpE,OAAO,IAAI,SAAS,CAAC,QAAQ,CAAC,CAAC,YAAY,CAAC,sBAAsB,CAAC,CAAC,CAAC,gCAAgC;AACvG,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,EACpC,OAAO,EACP,WAAW,GAIZ;IAIC,MAAM,MAAM,GAAG,WAAW,CAAC,YAAY;QACrC,CAAC,CAAC,MAAM,oBAAoB,CAAC,EAAE,OAAO,EAAE,CAAC;QACzC,CAAC,CAAC,WAAW,CAAC,MAAM,CAAC;IAEvB,OAAO;QACL,MAAM;QACN,UAAU,EAAE,MAAM,CAAC,IAAI,CAAC,MAAM,gBAAgB,CAAC,OAAO,CAAC,CAAC;KACzD,CAAC;AACJ,CAAC;AAED,yGAAyG;AACzG,MAAM,UAAU,qBAAqB,CAAC,IAAY;IAChD,8EAA8E;IAC9E,uDAAuD;IACvD,qDAAqD;IAErD,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;AACtD,CAAC;AAED,MAAM,UAAU,uBAAuB,CACrC,SAAoB,EACpB,KAA2B;IAE3B,OAAO;QACL,GAAG,SAAS;QACZ,KAAK;QACL,aAAa,EAAE,CAAC,SAAS,CAAC,aAAa,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;QAC5E,aAAa,EAAE,CAAC,SAAS,CAAC,aAAa,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;KAC7E,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { ExplorerView } from "@ledgerhq/types-cryptoassets";
|
|
2
|
+
import { Operation } from "@ledgerhq/types-live";
|
|
3
|
+
declare const getTransactionExplorer: (explorerView: ExplorerView | null | undefined, operation: Operation) => string | undefined;
|
|
4
|
+
export { getTransactionExplorer };
|
|
5
|
+
//# sourceMappingURL=logic.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logic.d.ts","sourceRoot":"","sources":["../src/logic.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAC5D,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAIjD,QAAA,MAAM,sBAAsB,iBACZ,YAAY,GAAG,IAAI,GAAG,SAAS,aAClC,SAAS,KACnB,MAAM,GAAG,SAIX,CAAC;AAEF,OAAO,EAAE,sBAAsB,EAAE,CAAC"}
|
package/lib-es/logic.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
const getTransactionExplorer = (explorerView, operation) => {
|
|
2
|
+
const extra = operation.extra;
|
|
3
|
+
return explorerView?.tx?.replace("$hash", extra.consensusTimestamp ?? extra.transactionId ?? "0");
|
|
4
|
+
};
|
|
5
|
+
export { getTransactionExplorer };
|
|
6
|
+
//# sourceMappingURL=logic.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logic.js","sourceRoot":"","sources":["../src/logic.ts"],"names":[],"mappings":"AAKA,MAAM,sBAAsB,GAAG,CAC7B,YAA6C,EAC7C,SAAoB,EACA,EAAE;IACtB,MAAM,KAAK,GAAG,SAAS,CAAC,KAA6B,CAAC;IAEtD,OAAO,YAAY,EAAE,EAAE,EAAE,OAAO,CAAC,OAAO,EAAE,KAAK,CAAC,kBAAkB,IAAI,KAAK,CAAC,aAAa,IAAI,GAAG,CAAC,CAAC;AACpG,CAAC,CAAC;AAEF,OAAO,EAAE,sBAAsB,EAAE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logic.test.d.ts","sourceRoot":"","sources":["../src/logic.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { BigNumber } from "bignumber.js";
|
|
2
|
+
import { getCryptoCurrencyById } from "@ledgerhq/cryptoassets";
|
|
3
|
+
import { getTransactionExplorer } from "./logic";
|
|
4
|
+
describe("getTransactionExplorer", () => {
|
|
5
|
+
test("Tx explorer URL is converted from hash to consensus timestamp", async () => {
|
|
6
|
+
const explorerView = getCryptoCurrencyById("hedera").explorerViews[0];
|
|
7
|
+
expect(explorerView).toBeDefined();
|
|
8
|
+
expect(explorerView.tx).toBeDefined();
|
|
9
|
+
const mockOperation = {
|
|
10
|
+
extra: {
|
|
11
|
+
consensusTimestamp: "1.2.3.4",
|
|
12
|
+
},
|
|
13
|
+
id: "",
|
|
14
|
+
hash: "",
|
|
15
|
+
type: "IN",
|
|
16
|
+
value: new BigNumber(0),
|
|
17
|
+
fee: new BigNumber(0),
|
|
18
|
+
senders: [],
|
|
19
|
+
recipients: [],
|
|
20
|
+
blockHeight: undefined,
|
|
21
|
+
blockHash: undefined,
|
|
22
|
+
accountId: "",
|
|
23
|
+
date: new Date(),
|
|
24
|
+
};
|
|
25
|
+
const newUrl = getTransactionExplorer(explorerView, mockOperation);
|
|
26
|
+
expect(newUrl).toBe("https://hashscan.io/mainnet/transaction/1.2.3.4");
|
|
27
|
+
});
|
|
28
|
+
test("Tx explorer URL is based on transaction id if consensus timestamp is not available", async () => {
|
|
29
|
+
const explorerView = getCryptoCurrencyById("hedera").explorerViews[0];
|
|
30
|
+
expect(explorerView).toBeDefined();
|
|
31
|
+
expect(explorerView.tx).toBeDefined();
|
|
32
|
+
const mockOperation = {
|
|
33
|
+
extra: {
|
|
34
|
+
transactionId: "0.0.1234567-123-123",
|
|
35
|
+
},
|
|
36
|
+
id: "",
|
|
37
|
+
hash: "",
|
|
38
|
+
type: "IN",
|
|
39
|
+
value: new BigNumber(0),
|
|
40
|
+
fee: new BigNumber(0),
|
|
41
|
+
senders: [],
|
|
42
|
+
recipients: [],
|
|
43
|
+
blockHeight: undefined,
|
|
44
|
+
blockHash: undefined,
|
|
45
|
+
accountId: "",
|
|
46
|
+
date: new Date(),
|
|
47
|
+
};
|
|
48
|
+
const newUrl = getTransactionExplorer(explorerView, mockOperation);
|
|
49
|
+
expect(newUrl).toBe("https://hashscan.io/mainnet/transaction/0.0.1234567-123-123");
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
//# sourceMappingURL=logic.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logic.test.js","sourceRoot":"","sources":["../src/logic.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzC,OAAO,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,EAAE,sBAAsB,EAAE,MAAM,SAAS,CAAC;AAEjD,QAAQ,CAAC,wBAAwB,EAAE,GAAG,EAAE;IACtC,IAAI,CAAC,+DAA+D,EAAE,KAAK,IAAI,EAAE;QAC/E,MAAM,YAAY,GAAG,qBAAqB,CAAC,QAAQ,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;QACtE,MAAM,CAAC,YAAY,CAAC,CAAC,WAAW,EAAE,CAAC;QACnC,MAAM,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;QAEtC,MAAM,aAAa,GAAc;YAC/B,KAAK,EAAE;gBACL,kBAAkB,EAAE,SAAS;aAC9B;YACD,EAAE,EAAE,EAAE;YACN,IAAI,EAAE,EAAE;YACR,IAAI,EAAE,IAAI;YACV,KAAK,EAAE,IAAI,SAAS,CAAC,CAAC,CAAC;YACvB,GAAG,EAAE,IAAI,SAAS,CAAC,CAAC,CAAC;YACrB,OAAO,EAAE,EAAE;YACX,UAAU,EAAE,EAAE;YACd,WAAW,EAAE,SAAS;YACtB,SAAS,EAAE,SAAS;YACpB,SAAS,EAAE,EAAE;YACb,IAAI,EAAE,IAAI,IAAI,EAAE;SACjB,CAAC;QAEF,MAAM,MAAM,GAAG,sBAAsB,CAAC,YAAY,EAAE,aAAa,CAAC,CAAC;QACnE,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,iDAAiD,CAAC,CAAC;IACzE,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,oFAAoF,EAAE,KAAK,IAAI,EAAE;QACpG,MAAM,YAAY,GAAG,qBAAqB,CAAC,QAAQ,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;QACtE,MAAM,CAAC,YAAY,CAAC,CAAC,WAAW,EAAE,CAAC;QACnC,MAAM,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;QAEtC,MAAM,aAAa,GAAc;YAC/B,KAAK,EAAE;gBACL,aAAa,EAAE,qBAAqB;aACrC;YACD,EAAE,EAAE,EAAE;YACN,IAAI,EAAE,EAAE;YACR,IAAI,EAAE,IAAI;YACV,KAAK,EAAE,IAAI,SAAS,CAAC,CAAC,CAAC;YACvB,GAAG,EAAE,IAAI,SAAS,CAAC,CAAC,CAAC;YACrB,OAAO,EAAE,EAAE;YACX,UAAU,EAAE,EAAE;YACd,WAAW,EAAE,SAAS;YACtB,SAAS,EAAE,SAAS;YACpB,SAAS,EAAE,EAAE;YACb,IAAI,EAAE,IAAI,IAAI,EAAE;SACjB,CAAC;QAEF,MAAM,MAAM,GAAG,sBAAsB,CAAC,YAAY,EAAE,aAAa,CAAC,CAAC;QACnE,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,6DAA6D,CAAC,CAAC;IACrF,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
package/lib-es/types/bridge.d.ts
CHANGED
|
@@ -17,8 +17,6 @@ export type TransactionStatus = TransactionStatusCommon;
|
|
|
17
17
|
export type TransactionStatusRaw = TransactionStatusCommonRaw;
|
|
18
18
|
export type HederaOperationExtra = {
|
|
19
19
|
consensusTimestamp?: string;
|
|
20
|
-
|
|
21
|
-
export type HederaOperationExtraRaw = {
|
|
22
|
-
consensusTimestamp?: string;
|
|
20
|
+
transactionId?: string;
|
|
23
21
|
};
|
|
24
22
|
//# sourceMappingURL=bridge.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bridge.d.ts","sourceRoot":"","sources":["../../src/types/bridge.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,iBAAiB,EACjB,oBAAoB,EACpB,uBAAuB,EACvB,0BAA0B,EAC3B,MAAM,sBAAsB,CAAC;AAE9B,MAAM,MAAM,WAAW,GAAG;IACxB,MAAM,EAAE,QAAQ,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,MAAM,EAAE,QAAQ,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG,iBAAiB,GAAG;IAC5C,MAAM,EAAE,QAAQ,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC3B,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG,oBAAoB,GAAG;IAClD,MAAM,EAAE,QAAQ,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC3B,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG,uBAAuB,CAAC;AAExD,MAAM,MAAM,oBAAoB,GAAG,0BAA0B,CAAC;AAE9D,MAAM,MAAM,oBAAoB,GAAG;IACjC,kBAAkB,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"bridge.d.ts","sourceRoot":"","sources":["../../src/types/bridge.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,iBAAiB,EACjB,oBAAoB,EACpB,uBAAuB,EACvB,0BAA0B,EAC3B,MAAM,sBAAsB,CAAC;AAE9B,MAAM,MAAM,WAAW,GAAG;IACxB,MAAM,EAAE,QAAQ,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,MAAM,EAAE,QAAQ,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG,iBAAiB,GAAG;IAC5C,MAAM,EAAE,QAAQ,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC3B,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG,oBAAoB,GAAG;IAClD,MAAM,EAAE,QAAQ,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC3B,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG,uBAAuB,CAAC;AAExD,MAAM,MAAM,oBAAoB,GAAG,0BAA0B,CAAC;AAE9D,MAAM,MAAM,oBAAoB,GAAG;IACjC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ledgerhq/coin-hedera",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.0-next.0",
|
|
4
4
|
"description": "Ledger Hedera Coin integration",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"Ledger",
|
|
@@ -81,14 +81,14 @@
|
|
|
81
81
|
"invariant": "^2.2.2",
|
|
82
82
|
"lodash": "^4.17.21",
|
|
83
83
|
"rxjs": "^7.8.1",
|
|
84
|
-
"@ledgerhq/coin-framework": "^5.
|
|
84
|
+
"@ledgerhq/coin-framework": "^5.2.0-next.0",
|
|
85
85
|
"@ledgerhq/cryptoassets": "^13.18.0",
|
|
86
86
|
"@ledgerhq/devices": "8.4.5",
|
|
87
87
|
"@ledgerhq/errors": "^6.21.0",
|
|
88
|
-
"@ledgerhq/live-countervalues": "^0.5.
|
|
88
|
+
"@ledgerhq/live-countervalues": "^0.5.8-next.0",
|
|
89
89
|
"@ledgerhq/live-env": "^2.9.0",
|
|
90
90
|
"@ledgerhq/live-network": "^2.0.8",
|
|
91
|
-
"@ledgerhq/types-live": "^6.
|
|
91
|
+
"@ledgerhq/types-live": "^6.72.0-next.0"
|
|
92
92
|
},
|
|
93
93
|
"devDependencies": {
|
|
94
94
|
"@types/invariant": "^2.2.2",
|
|
@@ -97,7 +97,8 @@
|
|
|
97
97
|
"jest": "^29.7.0",
|
|
98
98
|
"jest-sonar": "0.2.16",
|
|
99
99
|
"ts-jest": "^29.1.1",
|
|
100
|
-
"@ledgerhq/disable-network-setup": "^0.0.0"
|
|
100
|
+
"@ledgerhq/disable-network-setup": "^0.0.0",
|
|
101
|
+
"@ledgerhq/types-cryptoassets": "^7.23.0"
|
|
101
102
|
},
|
|
102
103
|
"scripts": {
|
|
103
104
|
"clean": "rimraf lib lib-es",
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import network from "@ledgerhq/live-network/network";
|
|
2
|
+
import { getAccountTransactions } from "./mirror";
|
|
3
|
+
|
|
4
|
+
jest.mock("@ledgerhq/live-network/network");
|
|
5
|
+
const mockedNetwork = jest.mocked(network);
|
|
6
|
+
|
|
7
|
+
const makeMockResponse = (data: any): Awaited<ReturnType<typeof network>> => ({
|
|
8
|
+
data,
|
|
9
|
+
status: 200,
|
|
10
|
+
statusText: "OK",
|
|
11
|
+
headers: {},
|
|
12
|
+
config: {
|
|
13
|
+
headers: {} as any,
|
|
14
|
+
},
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
describe("getAccountTransactions", () => {
|
|
18
|
+
beforeEach(() => {
|
|
19
|
+
jest.clearAllMocks();
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it("should include 'account.id', 'limit=100' and 'order=desc' query params", async () => {
|
|
23
|
+
mockedNetwork.mockResolvedValueOnce(
|
|
24
|
+
makeMockResponse({ transactions: [], links: { next: null } }),
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
await getAccountTransactions("0.0.1234", null);
|
|
28
|
+
|
|
29
|
+
const calledUrl = mockedNetwork.mock.calls[0][0].url;
|
|
30
|
+
expect(calledUrl).toContain("account.id=0.0.1234");
|
|
31
|
+
expect(calledUrl).toContain("limit=100");
|
|
32
|
+
expect(calledUrl).toContain("order=desc");
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it("should break early if no transactions are returned", async () => {
|
|
36
|
+
mockedNetwork.mockResolvedValueOnce(
|
|
37
|
+
makeMockResponse({
|
|
38
|
+
transactions: [],
|
|
39
|
+
links: { next: "/next-1" },
|
|
40
|
+
}),
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
const result = await getAccountTransactions("0.0.1234", null);
|
|
44
|
+
|
|
45
|
+
expect(mockedNetwork).toHaveBeenCalledTimes(1);
|
|
46
|
+
expect(result).toEqual([]);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it("should keep fetching if links.next is present and new transactions are returned", async () => {
|
|
50
|
+
mockedNetwork
|
|
51
|
+
.mockResolvedValueOnce(
|
|
52
|
+
makeMockResponse({
|
|
53
|
+
transactions: [{ consensus_timestamp: "1" }],
|
|
54
|
+
links: { next: "/next-1" },
|
|
55
|
+
}),
|
|
56
|
+
)
|
|
57
|
+
.mockResolvedValueOnce(
|
|
58
|
+
makeMockResponse({
|
|
59
|
+
transactions: [{ consensus_timestamp: "2" }],
|
|
60
|
+
links: { next: "/next-2" },
|
|
61
|
+
}),
|
|
62
|
+
)
|
|
63
|
+
.mockResolvedValueOnce(
|
|
64
|
+
makeMockResponse({
|
|
65
|
+
transactions: [],
|
|
66
|
+
links: { next: "/next-3" },
|
|
67
|
+
}),
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
const result = await getAccountTransactions("0.0.1234", null);
|
|
71
|
+
|
|
72
|
+
expect(result).toHaveLength(2);
|
|
73
|
+
expect(result.map(tx => tx.consensus_timestamp)).toEqual(["1", "2"]);
|
|
74
|
+
expect(mockedNetwork).toHaveBeenCalledTimes(3);
|
|
75
|
+
});
|
|
76
|
+
});
|
package/src/api/mirror.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { getEnv } from "@ledgerhq/live-env";
|
|
|
6
6
|
import { encodeOperationId } from "@ledgerhq/coin-framework/operation";
|
|
7
7
|
import { getAccountBalance } from "./network";
|
|
8
8
|
import { base64ToUrlSafeBase64 } from "../bridge/utils";
|
|
9
|
+
import { HederaOperationExtra } from "../types";
|
|
9
10
|
|
|
10
11
|
const getMirrorApiUrl = (): string => getEnv("API_HEDERA_MIRROR");
|
|
11
12
|
|
|
@@ -16,12 +17,12 @@ const fetch = (path: string) => {
|
|
|
16
17
|
});
|
|
17
18
|
};
|
|
18
19
|
|
|
19
|
-
|
|
20
|
+
interface HederaMirrorAccount {
|
|
20
21
|
accountId: AccountId;
|
|
21
22
|
balance: BigNumber;
|
|
22
23
|
}
|
|
23
24
|
|
|
24
|
-
export async function getAccountsForPublicKey(publicKey: string): Promise<
|
|
25
|
+
export async function getAccountsForPublicKey(publicKey: string): Promise<HederaMirrorAccount[]> {
|
|
25
26
|
let r;
|
|
26
27
|
try {
|
|
27
28
|
r = await fetch(`/api/v1/accounts?account.publicKey=${publicKey}&balance=false`);
|
|
@@ -30,7 +31,7 @@ export async function getAccountsForPublicKey(publicKey: string): Promise<Accoun
|
|
|
30
31
|
throw e;
|
|
31
32
|
}
|
|
32
33
|
const rawAccounts = r.data.accounts;
|
|
33
|
-
const accounts:
|
|
34
|
+
const accounts: HederaMirrorAccount[] = [];
|
|
34
35
|
|
|
35
36
|
for (const raw of rawAccounts) {
|
|
36
37
|
const accountBalance = await getAccountBalance(raw.account);
|
|
@@ -44,37 +45,57 @@ export async function getAccountsForPublicKey(publicKey: string): Promise<Accoun
|
|
|
44
45
|
return accounts;
|
|
45
46
|
}
|
|
46
47
|
|
|
48
|
+
interface HederaMirrorTransfer {
|
|
49
|
+
account: string;
|
|
50
|
+
amount: number;
|
|
51
|
+
}
|
|
52
|
+
|
|
47
53
|
interface HederaMirrorTransaction {
|
|
48
54
|
transfers: HederaMirrorTransfer[];
|
|
49
55
|
charged_tx_fee: string;
|
|
50
56
|
transaction_hash: string;
|
|
51
57
|
consensus_timestamp: string;
|
|
58
|
+
transaction_id: string;
|
|
52
59
|
}
|
|
53
60
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
61
|
+
export async function getAccountTransactions(
|
|
62
|
+
address: string,
|
|
63
|
+
since: string | null,
|
|
64
|
+
): Promise<HederaMirrorTransaction[]> {
|
|
65
|
+
const transactions: HederaMirrorTransaction[] = [];
|
|
66
|
+
const params = new URLSearchParams({
|
|
67
|
+
"account.id": address,
|
|
68
|
+
order: "desc",
|
|
69
|
+
limit: "100",
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
if (since) {
|
|
73
|
+
params.append("timestamp", `gt:${since}`);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
let nextUrl = `/api/v1/transactions?${params.toString()}`;
|
|
77
|
+
|
|
78
|
+
while (nextUrl) {
|
|
79
|
+
const res = await fetch(nextUrl);
|
|
80
|
+
const newTransactions = res.data.transactions as HederaMirrorTransaction[];
|
|
81
|
+
if (newTransactions.length === 0) break;
|
|
82
|
+
transactions.push(...newTransactions);
|
|
83
|
+
nextUrl = res.data.links.next;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return transactions;
|
|
57
87
|
}
|
|
58
88
|
|
|
59
89
|
export async function getOperationsForAccount(
|
|
60
90
|
ledgerAccountId: string,
|
|
61
91
|
address: string,
|
|
62
|
-
latestOperationTimestamp: string,
|
|
92
|
+
latestOperationTimestamp: string | null,
|
|
63
93
|
): Promise<Operation[]> {
|
|
94
|
+
const rawOperations = await getAccountTransactions(address, latestOperationTimestamp);
|
|
64
95
|
const operations: Operation[] = [];
|
|
65
|
-
let r = await fetch(
|
|
66
|
-
`/api/v1/transactions?account.id=${address}×tamp=gt:${latestOperationTimestamp}`,
|
|
67
|
-
);
|
|
68
|
-
const rawOperations = r.data.transactions as HederaMirrorTransaction[];
|
|
69
|
-
|
|
70
|
-
while (r.data.links.next) {
|
|
71
|
-
r = await fetch(r.data.links.next);
|
|
72
|
-
const newOperations = r.data.transactions as HederaMirrorTransaction[];
|
|
73
|
-
rawOperations.push(...newOperations);
|
|
74
|
-
}
|
|
75
96
|
|
|
76
97
|
for (const raw of rawOperations) {
|
|
77
|
-
const { consensus_timestamp } = raw;
|
|
98
|
+
const { consensus_timestamp, transaction_id } = raw;
|
|
78
99
|
const timestamp = new Date(parseInt(consensus_timestamp.split(".")[0], 10) * 1000);
|
|
79
100
|
const senders: string[] = [];
|
|
80
101
|
const recipients: string[] = [];
|
|
@@ -131,7 +152,10 @@ export async function getOperationsForAccount(
|
|
|
131
152
|
// Set a value just so that it's considered confirmed according to isConfirmedOperation
|
|
132
153
|
blockHeight: 5,
|
|
133
154
|
blockHash: null,
|
|
134
|
-
extra: {
|
|
155
|
+
extra: {
|
|
156
|
+
consensusTimestamp: consensus_timestamp,
|
|
157
|
+
transactionId: transaction_id,
|
|
158
|
+
} satisfies HederaOperationExtra,
|
|
135
159
|
fee,
|
|
136
160
|
hash,
|
|
137
161
|
recipients,
|
package/src/bridge/broadcast.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Transaction as HederaSDKTransaction } from "@hashgraph/sdk";
|
|
2
|
-
import { AccountBridge } from "@ledgerhq/types-live";
|
|
2
|
+
import { AccountBridge, Operation } from "@ledgerhq/types-live";
|
|
3
3
|
import { patchOperationWithHash } from "@ledgerhq/coin-framework/operation";
|
|
4
|
-
import { base64ToUrlSafeBase64 } from "./utils";
|
|
5
|
-
import { Transaction } from "../types";
|
|
4
|
+
import { base64ToUrlSafeBase64, patchOperationWithExtra } from "./utils";
|
|
5
|
+
import { HederaOperationExtra, Transaction } from "../types";
|
|
6
6
|
import { broadcastTransaction } from "../api/network";
|
|
7
7
|
|
|
8
8
|
export const broadcast: AccountBridge<Transaction>["broadcast"] = async ({ signedOperation }) => {
|
|
@@ -15,6 +15,13 @@ export const broadcast: AccountBridge<Transaction>["broadcast"] = async ({ signe
|
|
|
15
15
|
|
|
16
16
|
const base64Hash = Buffer.from(response.transactionHash).toString("base64");
|
|
17
17
|
const base64HashUrlSafe = base64ToUrlSafeBase64(base64Hash);
|
|
18
|
+
const extra: HederaOperationExtra = {
|
|
19
|
+
transactionId: response.transactionId.toString(),
|
|
20
|
+
};
|
|
18
21
|
|
|
19
|
-
|
|
22
|
+
let patchedOperation: Operation = operation;
|
|
23
|
+
patchedOperation = patchOperationWithHash(patchedOperation, base64HashUrlSafe);
|
|
24
|
+
patchedOperation = patchOperationWithExtra(patchedOperation, extra);
|
|
25
|
+
|
|
26
|
+
return patchedOperation;
|
|
20
27
|
};
|
|
@@ -36,14 +36,14 @@ export const getAccountShape: GetAccountShape<Account> = async (
|
|
|
36
36
|
// grab latest operation's consensus timestamp for incremental sync
|
|
37
37
|
const oldOperations = initialAccount?.operations ?? [];
|
|
38
38
|
const latestOperationTimestamp = oldOperations[0]
|
|
39
|
-
? Math.floor(oldOperations[0].date.getTime() / 1000)
|
|
40
|
-
:
|
|
39
|
+
? new BigNumber(Math.floor(oldOperations[0].date.getTime() / 1000))
|
|
40
|
+
: null;
|
|
41
41
|
|
|
42
42
|
// merge new operations w/ previously synced ones
|
|
43
43
|
const newOperations = await getOperationsForAccount(
|
|
44
44
|
liveAccountId,
|
|
45
45
|
address,
|
|
46
|
-
|
|
46
|
+
latestOperationTimestamp ? latestOperationTimestamp.toString() : null,
|
|
47
47
|
);
|
|
48
48
|
const operations = mergeOps(oldOperations, newOperations);
|
|
49
49
|
|
package/src/bridge/utils.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import BigNumber from "bignumber.js";
|
|
2
|
-
import type { Account } from "@ledgerhq/types-live";
|
|
2
|
+
import type { Account, Operation } from "@ledgerhq/types-live";
|
|
3
3
|
import cvsApi from "@ledgerhq/live-countervalues/api/index";
|
|
4
4
|
import { getFiatCurrencyByTicker } from "@ledgerhq/cryptoassets";
|
|
5
5
|
import { estimateMaxSpendable } from "./estimateMaxSpendable";
|
|
6
|
-
import type { Transaction } from "../types";
|
|
6
|
+
import type { HederaOperationExtra, Transaction } from "../types";
|
|
7
7
|
|
|
8
8
|
export const estimatedFeeSafetyRate = 2;
|
|
9
9
|
|
|
@@ -59,3 +59,15 @@ export function base64ToUrlSafeBase64(data: string): string {
|
|
|
59
59
|
|
|
60
60
|
return data.replace(/\//g, "_").replace(/\+/g, "-");
|
|
61
61
|
}
|
|
62
|
+
|
|
63
|
+
export function patchOperationWithExtra(
|
|
64
|
+
operation: Operation,
|
|
65
|
+
extra: HederaOperationExtra,
|
|
66
|
+
): Operation {
|
|
67
|
+
return {
|
|
68
|
+
...operation,
|
|
69
|
+
extra,
|
|
70
|
+
subOperations: (operation.subOperations ?? []).map(op => ({ ...op, extra })),
|
|
71
|
+
nftOperations: (operation.nftOperations ?? []).map(op => ({ ...op, extra })),
|
|
72
|
+
};
|
|
73
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { BigNumber } from "bignumber.js";
|
|
2
|
+
import { Operation } from "@ledgerhq/types-live";
|
|
3
|
+
import { getCryptoCurrencyById } from "@ledgerhq/cryptoassets";
|
|
4
|
+
import { getTransactionExplorer } from "./logic";
|
|
5
|
+
|
|
6
|
+
describe("getTransactionExplorer", () => {
|
|
7
|
+
test("Tx explorer URL is converted from hash to consensus timestamp", async () => {
|
|
8
|
+
const explorerView = getCryptoCurrencyById("hedera").explorerViews[0];
|
|
9
|
+
expect(explorerView).toBeDefined();
|
|
10
|
+
expect(explorerView.tx).toBeDefined();
|
|
11
|
+
|
|
12
|
+
const mockOperation: Operation = {
|
|
13
|
+
extra: {
|
|
14
|
+
consensusTimestamp: "1.2.3.4",
|
|
15
|
+
},
|
|
16
|
+
id: "",
|
|
17
|
+
hash: "",
|
|
18
|
+
type: "IN",
|
|
19
|
+
value: new BigNumber(0),
|
|
20
|
+
fee: new BigNumber(0),
|
|
21
|
+
senders: [],
|
|
22
|
+
recipients: [],
|
|
23
|
+
blockHeight: undefined,
|
|
24
|
+
blockHash: undefined,
|
|
25
|
+
accountId: "",
|
|
26
|
+
date: new Date(),
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const newUrl = getTransactionExplorer(explorerView, mockOperation);
|
|
30
|
+
expect(newUrl).toBe("https://hashscan.io/mainnet/transaction/1.2.3.4");
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test("Tx explorer URL is based on transaction id if consensus timestamp is not available", async () => {
|
|
34
|
+
const explorerView = getCryptoCurrencyById("hedera").explorerViews[0];
|
|
35
|
+
expect(explorerView).toBeDefined();
|
|
36
|
+
expect(explorerView.tx).toBeDefined();
|
|
37
|
+
|
|
38
|
+
const mockOperation: Operation = {
|
|
39
|
+
extra: {
|
|
40
|
+
transactionId: "0.0.1234567-123-123",
|
|
41
|
+
},
|
|
42
|
+
id: "",
|
|
43
|
+
hash: "",
|
|
44
|
+
type: "IN",
|
|
45
|
+
value: new BigNumber(0),
|
|
46
|
+
fee: new BigNumber(0),
|
|
47
|
+
senders: [],
|
|
48
|
+
recipients: [],
|
|
49
|
+
blockHeight: undefined,
|
|
50
|
+
blockHash: undefined,
|
|
51
|
+
accountId: "",
|
|
52
|
+
date: new Date(),
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const newUrl = getTransactionExplorer(explorerView, mockOperation);
|
|
56
|
+
expect(newUrl).toBe("https://hashscan.io/mainnet/transaction/0.0.1234567-123-123");
|
|
57
|
+
});
|
|
58
|
+
});
|