@ledgerhq/live-common 21.17.1 → 21.19.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.
Files changed (76) hide show
  1. package/lib/account/addAccounts.d.ts.map +1 -1
  2. package/lib/account/addAccounts.js +3 -2
  3. package/lib/account/addAccounts.js.map +1 -1
  4. package/lib/bridge/jsHelpers.js +2 -2
  5. package/lib/bridge/jsHelpers.js.map +1 -1
  6. package/lib/families/bitcoin/getAccountNetworkInfo.d.ts.map +1 -1
  7. package/lib/families/bitcoin/getAccountNetworkInfo.js +12 -2
  8. package/lib/families/bitcoin/getAccountNetworkInfo.js.map +1 -1
  9. package/lib/families/bitcoin/types.d.ts +1 -0
  10. package/lib/families/bitcoin/types.d.ts.map +1 -1
  11. package/lib/families/bitcoin/types.js.map +1 -1
  12. package/lib/families/bitcoin/wallet-btc/explorer/index.d.ts +1 -0
  13. package/lib/families/bitcoin/wallet-btc/explorer/index.d.ts.map +1 -1
  14. package/lib/families/bitcoin/wallet-btc/explorer/index.js +19 -0
  15. package/lib/families/bitcoin/wallet-btc/explorer/index.js.map +1 -1
  16. package/lib/families/bitcoin/wallet-btc/explorer/types.d.ts +1 -0
  17. package/lib/families/bitcoin/wallet-btc/explorer/types.d.ts.map +1 -1
  18. package/lib/families/ethereum/cli-transaction.d.ts.map +1 -1
  19. package/lib/families/ethereum/cli-transaction.js +10 -0
  20. package/lib/families/ethereum/cli-transaction.js.map +1 -1
  21. package/lib/families/ethereum/modules/erc1155.d.ts +4 -0
  22. package/lib/families/ethereum/modules/erc1155.d.ts.map +1 -0
  23. package/lib/families/ethereum/modules/erc1155.js +117 -0
  24. package/lib/families/ethereum/modules/erc1155.js.map +1 -0
  25. package/lib/families/ethereum/modules/erc721.d.ts +4 -0
  26. package/lib/families/ethereum/modules/erc721.d.ts.map +1 -0
  27. package/lib/families/ethereum/modules/erc721.js +95 -0
  28. package/lib/families/ethereum/modules/erc721.js.map +1 -0
  29. package/lib/families/ethereum/modules/index.d.ts +3 -1
  30. package/lib/families/ethereum/modules/index.d.ts.map +1 -1
  31. package/lib/families/ethereum/modules/index.js +5 -1
  32. package/lib/families/ethereum/modules/index.js.map +1 -1
  33. package/lib/families/ethereum/nft.test.d.ts +2 -0
  34. package/lib/families/ethereum/nft.test.d.ts.map +1 -0
  35. package/lib/families/ethereum/nft.test.js +167 -0
  36. package/lib/families/ethereum/nft.test.js.map +1 -0
  37. package/lib/families/ethereum/signOperation.d.ts.map +1 -1
  38. package/lib/families/ethereum/signOperation.js +1 -9
  39. package/lib/families/ethereum/signOperation.js.map +1 -1
  40. package/lib/families/ethereum/synchronisation.test.js.map +1 -1
  41. package/lib/families/ethereum/transaction.d.ts.map +1 -1
  42. package/lib/families/ethereum/transaction.js +25 -8
  43. package/lib/families/ethereum/transaction.js.map +1 -1
  44. package/lib/families/ethereum/types.d.ts +6 -0
  45. package/lib/families/ethereum/types.d.ts.map +1 -1
  46. package/lib/families/ethereum/types.js.map +1 -1
  47. package/lib/hw/uninstallApp.d.ts.map +1 -1
  48. package/lib/hw/uninstallApp.js +1 -1
  49. package/lib/hw/uninstallApp.js.map +1 -1
  50. package/lib/nft/helpers.d.ts +2 -1
  51. package/lib/nft/helpers.d.ts.map +1 -1
  52. package/lib/nft/helpers.js +8 -1
  53. package/lib/nft/helpers.js.map +1 -1
  54. package/package.json +6 -6
  55. package/src/account/addAccounts.ts +3 -2
  56. package/src/bridge/jsHelpers.ts +2 -2
  57. package/src/currencies/__snapshots__/sortByMarketcap.test.ts.snap +2 -0
  58. package/src/families/bitcoin/getAccountNetworkInfo.ts +8 -1
  59. package/src/families/bitcoin/types.ts +1 -0
  60. package/src/families/bitcoin/wallet-btc/explorer/index.ts +7 -0
  61. package/src/families/bitcoin/wallet-btc/explorer/types.ts +1 -0
  62. package/src/families/ethereum/cli-transaction.ts +10 -0
  63. package/src/families/ethereum/modules/erc1155.ts +142 -0
  64. package/src/families/ethereum/modules/erc721.ts +108 -0
  65. package/src/families/ethereum/modules/index.ts +12 -1
  66. package/src/families/ethereum/nft.test.ts +90 -0
  67. package/src/families/ethereum/signOperation.ts +1 -13
  68. package/src/families/ethereum/synchronisation.test.ts +1 -0
  69. package/src/families/ethereum/transaction.ts +38 -8
  70. package/src/families/ethereum/types.ts +6 -0
  71. package/src/hw/uninstallApp.ts +13 -8
  72. package/src/nft/helpers.ts +9 -1
  73. package/src/types/account.ts +1 -1
  74. package/src/.DS_Store +0 -0
  75. package/src/__tests__/.DS_Store +0 -0
  76. package/src/currencies/.DS_Store +0 -0
@@ -25,7 +25,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
25
25
  return (mod && mod.__esModule) ? mod : { "default": mod };
26
26
  };
27
27
  exports.__esModule = true;
28
- exports.getNftKey = exports.nftsByCollections = exports.nftsFromOperations = void 0;
28
+ exports.isNftTransaction = exports.getNftKey = exports.nftsByCollections = exports.nftsFromOperations = void 0;
29
29
  var eip55_1 = __importDefault(require("eip55"));
30
30
  var bignumber_js_1 = __importDefault(require("bignumber.js"));
31
31
  var nftsFromOperations = function (ops) {
@@ -82,4 +82,11 @@ var getNftKey = function (contract, tokenId) {
82
82
  return contract + "-" + tokenId;
83
83
  };
84
84
  exports.getNftKey = getNftKey;
85
+ var isNftTransaction = function (transaction) {
86
+ if (transaction.family === "ethereum") {
87
+ return ["erc721.transfer", "erc1155.transfer"].includes(transaction.mode);
88
+ }
89
+ return false;
90
+ };
91
+ exports.isNftTransaction = isNftTransaction;
85
92
  //# sourceMappingURL=helpers.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"helpers.js","sourceRoot":"","sources":["../../src/nft/helpers.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"helpers.js","sourceRoot":"","sources":["../../src/nft/helpers.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -8,7 +8,7 @@
8
8
  "type": "git",
9
9
  "url": "https://github.com/LedgerHQ/ledger-live-common"
10
10
  },
11
- "version": "21.17.1",
11
+ "version": "21.19.0",
12
12
  "main": "lib/index.js",
13
13
  "types": "lib/index.d.ts",
14
14
  "license": "Apache-2.0",
@@ -44,13 +44,13 @@
44
44
  "dependencies": {
45
45
  "@crypto-com/chain-jslib": "0.0.19",
46
46
  "@ledgerhq/compressjs": "1.3.2",
47
- "@ledgerhq/cryptoassets": "6.16.0",
47
+ "@ledgerhq/cryptoassets": "6.18.0",
48
48
  "@ledgerhq/devices": "6.11.2",
49
49
  "@ledgerhq/errors": "6.10.0",
50
50
  "@ledgerhq/hw-app-algorand": "6.11.2",
51
- "@ledgerhq/hw-app-btc": "6.15.1",
51
+ "@ledgerhq/hw-app-btc": "6.17.1",
52
52
  "@ledgerhq/hw-app-cosmos": "6.11.2",
53
- "@ledgerhq/hw-app-eth": "6.16.2",
53
+ "@ledgerhq/hw-app-eth": "6.18.0",
54
54
  "@ledgerhq/hw-app-polkadot": "6.11.2",
55
55
  "@ledgerhq/hw-app-str": "6.11.2",
56
56
  "@ledgerhq/hw-app-tezos": "6.11.2",
@@ -62,8 +62,8 @@
62
62
  "@ledgerhq/json-bignumber": "^1.1.0",
63
63
  "@ledgerhq/live-app-sdk": "^0.2.0",
64
64
  "@ledgerhq/logs": "6.10.0",
65
- "@polkadot/types": "6.9.2",
66
- "@polkadot/types-known": "6.9.2",
65
+ "@polkadot/types": "6.8.1",
66
+ "@polkadot/types-known": "6.8.1",
67
67
  "@taquito/ledger-signer": "^10.2.1",
68
68
  "@taquito/taquito": "10.2.1",
69
69
  "@types/bchaddrjs": "^0.4.0",
@@ -74,7 +74,7 @@ export function groupAddAccounts(
74
74
  );
75
75
 
76
76
  if (existingAccount) {
77
- if (!acc.used) {
77
+ if (!acc.used && !alreadyEmptyAccount) {
78
78
  alreadyEmptyAccount = existingAccount;
79
79
  }
80
80
 
@@ -122,7 +122,8 @@ export function groupAddAccounts(
122
122
  ? creatableAccounts.filter(
123
123
  (a) =>
124
124
  context.preferredNewAccountSchemes &&
125
- context.preferredNewAccountSchemes.includes(a.derivationMode)
125
+ // Note: we could use a simple preferredNewAccountScheme param
126
+ a.derivationMode === context.preferredNewAccountSchemes[0]
126
127
  )
127
128
  : creatableAccounts,
128
129
  });
@@ -147,8 +147,8 @@ export const mergeNfts = (
147
147
  delete newNftsPerId[nft.id];
148
148
  }
149
149
 
150
- // Adding the rest of newNfts that was not already in oldNfts
151
- return nfts.concat(Object.values(newNftsPerId));
150
+ // Prepending newNfts to respect nfts's newest to oldest order
151
+ return Object.values(newNftsPerId).concat(nfts);
152
152
  };
153
153
 
154
154
  export const makeSync =
@@ -1331,11 +1331,13 @@ Array [
1331
1331
  "near",
1332
1332
  "avalanche",
1333
1333
  "banano",
1334
+ "celo",
1334
1335
  "dexon",
1335
1336
  "elrond",
1336
1337
  "fic",
1337
1338
  "flow",
1338
1339
  "LBRY",
1340
+ "moonriver",
1339
1341
  "musicoin",
1340
1342
  "songbird",
1341
1343
  "reosc",
@@ -20,6 +20,7 @@ export async function getAccountNetworkInfo(
20
20
  ): Promise<NetworkInfo> {
21
21
  const walletAccount = getWalletAccount(account);
22
22
  const rawFees = await walletAccount.xpub.explorer.getFees();
23
+ const relayFee = await walletAccount.xpub.explorer.getRelayFee();
23
24
  // Convoluted logic to convert from:
24
25
  // { "2": 2435, "3": 1241, "6": 1009, "last_updated": 1627973170 }
25
26
  // to:
@@ -44,7 +45,12 @@ export async function getAccountNetworkInfo(
44
45
  if (feesPerByte.length !== 3) {
45
46
  throw new Error("cardinality of feesPerByte should be exactly 3");
46
47
  }
47
-
48
+ // Fix fees if suggested fee is too low
49
+ if (feesPerByte[2].toNumber() < Math.ceil(relayFee * 100000)) {
50
+ feesPerByte[2] = new BigNumber(Math.ceil(relayFee * 100000)).plus(1);
51
+ feesPerByte[1] = feesPerByte[2].plus(1);
52
+ feesPerByte[0] = feesPerByte[1].plus(1);
53
+ }
48
54
  const feeItems = {
49
55
  items: feesPerByte.map((feePerByte, i) => ({
50
56
  key: String(i),
@@ -53,6 +59,7 @@ export async function getAccountNetworkInfo(
53
59
  })),
54
60
  defaultFeePerByte:
55
61
  feesPerByte[Math.floor(feesPerByte.length / 2)] || new BigNumber(0),
62
+ relayFee,
56
63
  };
57
64
  return {
58
65
  family: "bitcoin",
@@ -106,6 +106,7 @@ export type FeeItem = {
106
106
  export type FeeItems = {
107
107
  items: FeeItem[];
108
108
  defaultFeePerByte: BigNumber;
109
+ relayFee?: number;
109
110
  };
110
111
  export type FeeItemRaw = {
111
112
  key: string;
@@ -168,6 +168,13 @@ class BitcoinLikeExplorer extends EventEmitter implements IExplorer {
168
168
  return fees;
169
169
  }
170
170
 
171
+ async getRelayFee() {
172
+ const client = await this.client.acquire();
173
+ const fees = (await client.client.get(`/network`)).data;
174
+ await this.client.release(client);
175
+ return parseFloat(fees["relay_fee"]);
176
+ }
177
+
171
178
  async getPendings(address: Address, nbMax?: number) {
172
179
  const params: {
173
180
  no_token?: string;
@@ -9,6 +9,7 @@ export interface IExplorer {
9
9
  getTxHex(txId: string): Promise<string>;
10
10
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
11
11
  getFees(): Promise<any>;
12
+ getRelayFee(): Promise<number>;
12
13
  getCurrentBlock(): Promise<Block | null>;
13
14
  getBlockByHeight(height: number): Promise<Block | null>;
14
15
  getPendings(address: Address, nbMax?: number): Promise<TX[]>;
@@ -23,6 +23,16 @@ const options = [
23
23
  desc: "use an token account children of the account",
24
24
  multiple: true,
25
25
  },
26
+ {
27
+ name: "collection",
28
+ type: String,
29
+ desc: "determine the collection of an NFT (related to the --tokenId)",
30
+ },
31
+ {
32
+ name: "tokenId",
33
+ type: String,
34
+ desc: "determine the tokenId of an NFT (related to the --colection)",
35
+ },
26
36
  {
27
37
  name: "gasPrice",
28
38
  type: String,
@@ -0,0 +1,142 @@
1
+ import eip55 from "eip55";
2
+ import abi from "ethereumjs-abi";
3
+ import invariant from "invariant";
4
+ import BigNumber from "bignumber.js";
5
+ import {
6
+ createCustomErrorClass,
7
+ NotEnoughBalanceInParentAccount,
8
+ } from "@ledgerhq/errors";
9
+ import { validateRecipient } from "../transaction";
10
+ import type { ModeModule, Transaction } from "../types";
11
+ import type { Account } from "../../../types";
12
+
13
+ const notOwnedNft = createCustomErrorClass("NotOwnedNft");
14
+ const notEnoughNftOwned = createCustomErrorClass("NotEnoughNftOwned");
15
+ const notTokenIdsProvided = createCustomErrorClass("NotTokenIdsProvided");
16
+
17
+ export type Modes = "erc1155.transfer";
18
+
19
+ const erc1155Transfer: ModeModule = {
20
+ /**
21
+ * Tx data is filled during the buildEthereumTx
22
+ */
23
+ fillTransactionData(a, t, tx) {
24
+ const data = serializeTransactionData(a, t);
25
+ invariant(data, "serializeTransactionData provided no data");
26
+ tx.data = "0x" + (data as Buffer).toString("hex");
27
+ tx.to = t.collection;
28
+ tx.value = "0x00";
29
+ },
30
+
31
+ /**
32
+ * Tx status is filled after the buildEthereumTx
33
+ */
34
+ fillTransactionStatus: (a, t, result) => {
35
+ validateRecipient(a.currency, t.recipient, result);
36
+
37
+ if (!result.errors.recipient) {
38
+ result.totalSpent = result.estimatedFees;
39
+ result.amount = new BigNumber(t.amount);
40
+
41
+ if (result.estimatedFees.gt(a.spendableBalance)) {
42
+ result.errors.amount = new NotEnoughBalanceInParentAccount();
43
+ }
44
+
45
+ const enoughTokensOwned: true | Error =
46
+ t.tokenIds?.reduce((acc, tokenId, index) => {
47
+ if (acc instanceof Error) {
48
+ return acc;
49
+ }
50
+
51
+ const nft = a.nfts?.find((n) => n.tokenId === tokenId);
52
+ const transferQuantity = Number(t.quantities?.[index]);
53
+
54
+ if (!nft) {
55
+ return new notOwnedNft();
56
+ }
57
+
58
+ if (transferQuantity && !nft.amount.gte(transferQuantity)) {
59
+ return new notEnoughNftOwned();
60
+ }
61
+
62
+ return true;
63
+ }, true as true | Error) || new notTokenIdsProvided();
64
+
65
+ if (!enoughTokensOwned || enoughTokensOwned instanceof Error) {
66
+ result.errors.amount = enoughTokensOwned;
67
+ }
68
+ }
69
+ },
70
+
71
+ /**
72
+ * This will only be used by LLM & LLD, not the HW.
73
+ */
74
+ fillDeviceTransactionConfig(input, fields) {
75
+ fields.push({
76
+ type: "text",
77
+ label: "Type",
78
+ value: `ERC721.transfer`,
79
+ });
80
+
81
+ fields.push({
82
+ type: "text",
83
+ label: "Collection",
84
+ value: input.transaction.collection ?? "",
85
+ });
86
+
87
+ fields.push({
88
+ type: "text",
89
+ label: "Token IDs",
90
+ value: input.transaction.tokenIds?.join(",") ?? "",
91
+ });
92
+
93
+ fields.push({
94
+ type: "text",
95
+ label: "Quantities",
96
+ value: input.transaction.quantities?.join(",") ?? "",
97
+ });
98
+ },
99
+
100
+ /**
101
+ * Optimistic Operation is filled post signing
102
+ */
103
+ fillOptimisticOperation(a, t, op) {
104
+ op.type = "FEES";
105
+ op.extra = {
106
+ ...op.extra,
107
+ approving: true, // workaround to track the status ENABLING
108
+ };
109
+ },
110
+ };
111
+
112
+ function serializeTransactionData(
113
+ account: Account,
114
+ transaction: Transaction
115
+ ): Buffer | null | undefined {
116
+ const from = eip55.encode(account.freshAddress);
117
+ const to = eip55.encode(transaction.recipient);
118
+ const tokenIds = transaction.tokenIds || [];
119
+ const quantities = transaction.quantities?.map((q) => q.toFixed()) || [];
120
+
121
+ return tokenIds?.length > 1
122
+ ? abi.simpleEncode(
123
+ "safeBatchTransferFrom(address,address,uint256[],uint256[],bytes)",
124
+ from,
125
+ to,
126
+ tokenIds,
127
+ quantities,
128
+ "0x00"
129
+ )
130
+ : abi.simpleEncode(
131
+ "safeTransferFrom(address,address,uint256,uint256,bytes)",
132
+ from,
133
+ to,
134
+ tokenIds[0],
135
+ quantities[0],
136
+ "0x00"
137
+ );
138
+ }
139
+
140
+ export const modes: Record<Modes, ModeModule> = {
141
+ "erc1155.transfer": erc1155Transfer,
142
+ };
@@ -0,0 +1,108 @@
1
+ import eip55 from "eip55";
2
+ import abi from "ethereumjs-abi";
3
+ import invariant from "invariant";
4
+ import BigNumber from "bignumber.js";
5
+ import {
6
+ createCustomErrorClass,
7
+ NotEnoughBalanceInParentAccount,
8
+ } from "@ledgerhq/errors";
9
+ import { validateRecipient } from "../transaction";
10
+ import type { ModeModule, Transaction } from "../types";
11
+ import type { Account } from "../../../types";
12
+
13
+ const notOwnedNft = createCustomErrorClass("NotOwnedNft");
14
+
15
+ export type Modes = "erc721.transfer";
16
+
17
+ const erc721Transfer: ModeModule = {
18
+ /**
19
+ * Tx data is filled during the buildEthereumTx
20
+ */
21
+ fillTransactionData(a, t, tx) {
22
+ const data = serializeTransactionData(a, t);
23
+ invariant(data, "serializeTransactionData provided no data");
24
+ tx.data = "0x" + (data as Buffer).toString("hex");
25
+ tx.to = t.collection;
26
+ tx.value = "0x00";
27
+ },
28
+
29
+ /**
30
+ * Tx status is filled after the buildEthereumTx
31
+ */
32
+ fillTransactionStatus: (a, t, result) => {
33
+ validateRecipient(a.currency, t.recipient, result);
34
+
35
+ if (!result.errors.recipient) {
36
+ result.totalSpent = result.estimatedFees;
37
+ result.amount = new BigNumber(t.amount);
38
+
39
+ if (result.estimatedFees.gt(a.spendableBalance)) {
40
+ result.errors.amount = new NotEnoughBalanceInParentAccount();
41
+ }
42
+
43
+ if (
44
+ !a.nfts?.find?.(
45
+ (n) =>
46
+ n.tokenId === t.tokenIds?.[0] &&
47
+ n.collection.contract === t.collection
48
+ )
49
+ ) {
50
+ result.errors.amount = new notOwnedNft();
51
+ }
52
+ }
53
+ },
54
+
55
+ /**
56
+ * This will only be used by LLM & LLD, not the HW.
57
+ */
58
+ fillDeviceTransactionConfig(input, fields) {
59
+ fields.push({
60
+ type: "text",
61
+ label: "Type",
62
+ value: `ERC721.transfer`,
63
+ });
64
+
65
+ fields.push({
66
+ type: "text",
67
+ label: "Collection",
68
+ value: input.transaction.collection ?? "",
69
+ });
70
+
71
+ fields.push({
72
+ type: "text",
73
+ label: "Token ID",
74
+ value: input.transaction.tokenIds?.[0] ?? "",
75
+ });
76
+ },
77
+
78
+ /**
79
+ * Optimistic Operation is filled post signing
80
+ */
81
+ fillOptimisticOperation(a, t, op) {
82
+ op.type = "FEES";
83
+ op.extra = {
84
+ ...op.extra,
85
+ approving: true, // workaround to track the status ENABLING
86
+ };
87
+ },
88
+ };
89
+
90
+ function serializeTransactionData(
91
+ account: Account,
92
+ transaction: Transaction
93
+ ): Buffer | null | undefined {
94
+ const from = eip55.encode(account.freshAddress);
95
+ const to = eip55.encode(transaction.recipient);
96
+
97
+ return abi.simpleEncode(
98
+ "safeTransferFrom(address,address,uint256,bytes)",
99
+ from,
100
+ to,
101
+ transaction.tokenIds?.[0],
102
+ "0x00"
103
+ );
104
+ }
105
+
106
+ export const modes: Record<Modes, ModeModule> = {
107
+ "erc721.transfer": erc721Transfer,
108
+ };
@@ -13,15 +13,26 @@ import type { DeviceTransactionField } from "../../../transaction";
13
13
  import * as compound from "./compound";
14
14
  import * as erc20 from "./erc20";
15
15
  import * as send from "./send";
16
+ import * as erc721 from "./erc721";
17
+ import * as erc1155 from "./erc1155";
16
18
  import type { Modes as CompoundModes } from "./compound";
17
19
  import type { Modes as ERC20Modes } from "./erc20";
18
20
  import type { Modes as SendModes } from "./send";
21
+ import type { Modes as ERC721Modes } from "./erc721";
22
+ import type { Modes as ERC1155Modes } from "./erc1155";
19
23
  const modules = {
20
24
  erc20,
21
25
  compound,
22
26
  send,
27
+ erc721,
28
+ erc1155,
23
29
  };
24
- export type TransactionMode = CompoundModes | ERC20Modes | SendModes;
30
+ export type TransactionMode =
31
+ | CompoundModes
32
+ | ERC20Modes
33
+ | SendModes
34
+ | ERC721Modes
35
+ | ERC1155Modes;
25
36
 
26
37
  /**
27
38
  * A ModeModule enable a new transaction mode in Ethereum family
@@ -0,0 +1,90 @@
1
+ import "../../__tests__/test-helpers/setup";
2
+ import { reduce } from "rxjs/operators";
3
+ import { fromAccountRaw, toAccountRaw } from "../../account";
4
+ import type { Account, AccountRaw } from "../../types";
5
+ import { getAccountBridge } from "../../bridge";
6
+ import { makeBridgeCacheSystem } from "../../bridge/cache";
7
+ import { patchAccount } from "../../reconciliation";
8
+ import { setEnv } from "../../env";
9
+
10
+ const gaspardAccount: AccountRaw = {
11
+ id: "js:1:ethereum:0xb98d10d9f6d07ba283bfd21b2dfec050f9ae282a:",
12
+ seedIdentifier: "0xb98d10d9f6d07ba283bfd21b2dfec050f9ae282a",
13
+ name: "G4sp4rd",
14
+ derivationMode: "",
15
+ index: 0,
16
+ freshAddress: "0xb98d10d9f6d07ba283bfd21b2dfec050f9ae282a",
17
+ freshAddressPath: "44'/60'/0'/0/0",
18
+ freshAddresses: [],
19
+ pendingOperations: [],
20
+ operations: [],
21
+ currencyId: "ethereum",
22
+ unitMagnitude: 18,
23
+ balance: "",
24
+ blockHeight: 0,
25
+ lastSyncDate: "",
26
+ xpub: "",
27
+ };
28
+
29
+ describe("nft reconciliation", () => {
30
+ let account = fromAccountRaw(gaspardAccount);
31
+ const localCache = {};
32
+ const cache = makeBridgeCacheSystem({
33
+ saveData(c, d) {
34
+ localCache[c.id] = d;
35
+ return Promise.resolve();
36
+ },
37
+ getData(c) {
38
+ return Promise.resolve(localCache[c.id]);
39
+ },
40
+ });
41
+ const bridge = getAccountBridge(account);
42
+ async function sync(account) {
43
+ const blacklistedTokenIds = [];
44
+ setEnv("NFT", true);
45
+ const r = await bridge
46
+ .sync(account, {
47
+ paginationConfig: {},
48
+ blacklistedTokenIds,
49
+ })
50
+ .pipe(reduce((a, f: (arg0: Account) => Account) => f(a), account))
51
+ .toPromise();
52
+ setEnv("NFT", false);
53
+ return r;
54
+ }
55
+
56
+ test("first sync & have nfts", async () => {
57
+ await cache.prepareCurrency(account.currency);
58
+ account = await sync(account);
59
+ expect(account.nfts?.length || 0).not.toBe(0);
60
+ });
61
+
62
+ test("remove half NFTs will restore them with operations still here", async () => {
63
+ const copy = {
64
+ ...account,
65
+ nfts: account.nfts?.slice(Math.ceil((account.nfts?.length || 0) / 2)),
66
+ };
67
+ const resync = await sync(copy);
68
+ expect(resync.nfts).toEqual(account.nfts);
69
+ });
70
+
71
+ test("remove half NFTs will restore them with missing operations too", async () => {
72
+ const copy = {
73
+ ...account,
74
+ operations: [],
75
+ nfts: account.nfts?.slice(Math.ceil((account.nfts?.length || 0) / 2)),
76
+ };
77
+ const resync = await sync(copy);
78
+ expect(resync.nfts).toEqual(account.nfts);
79
+ });
80
+
81
+ test("patchAccount restore new NFTs correctly", async () => {
82
+ const copy = {
83
+ ...account,
84
+ operations: [],
85
+ nfts: account.nfts?.slice(Math.ceil((account.nfts?.length || 0) / 2)),
86
+ };
87
+ const newAccount = patchAccount(copy, toAccountRaw(account));
88
+ expect(newAccount.nfts).toEqual(account.nfts);
89
+ });
90
+ });
@@ -99,19 +99,7 @@ export const signOperation = ({
99
99
  type: "device-signature-granted",
100
100
  });
101
101
  // Second, we re-set some tx fields from the device signature
102
- let v = result.v;
103
-
104
- if (chainId > 0) {
105
- // EIP155 support. check/recalc signature v value.
106
- const rv = parseInt(v, 16);
107
- let cv = chainId * 2 + 35;
108
-
109
- if (rv !== cv && (rv & cv) !== rv) {
110
- cv += 1; // add signature v bit.
111
- }
112
-
113
- v = cv.toString(16);
114
- }
102
+ const v = result.v;
115
103
 
116
104
  tx.v = Buffer.from(v, "hex");
117
105
  tx.r = Buffer.from(result.r, "hex");
@@ -6,6 +6,7 @@ import type { Account, SubAccount } from "../../types";
6
6
  import { getAccountBridge } from "../../bridge";
7
7
  import { makeBridgeCacheSystem } from "../../bridge/cache";
8
8
  import { ethereum1 } from "./test-dataset";
9
+
9
10
  describe("blacklistedTokenIds functionality", () => {
10
11
  const account = fromAccountRaw(ethereum1);
11
12
  const localCache = {};
@@ -86,15 +86,38 @@ export const formatTransaction = (
86
86
  (t.subAccountId &&
87
87
  (mainAccount.subAccounts || []).find((a) => a.id === t.subAccountId)) ||
88
88
  mainAccount;
89
+
90
+ const header = (() => {
91
+ switch (t.mode) {
92
+ case "erc721.transfer":
93
+ return `${t.mode.toUpperCase()} Collection: ${t.collection} TokenId: ${
94
+ t.tokenIds?.[0]
95
+ }`;
96
+ case "erc1155.transfer":
97
+ return (
98
+ `${t.mode.toUpperCase()} Collection: ${t.collection}` +
99
+ t.tokenIds
100
+ ?.map((tokenId, index) => {
101
+ return `\n - TokenId: ${tokenId} Quantity: ${
102
+ t.quantities?.[index]?.toFixed() ?? 0
103
+ }`;
104
+ })
105
+ .join(",")
106
+ );
107
+ default:
108
+ return `${t.mode.toUpperCase()} ${
109
+ t.useAllAmount
110
+ ? "MAX"
111
+ : formatCurrencyUnit(getAccountUnit(account), t.amount, {
112
+ showCode: true,
113
+ disableRounding: true,
114
+ })
115
+ }`;
116
+ }
117
+ })();
118
+
89
119
  return `
90
- ${t.mode.toUpperCase()} ${
91
- t.useAllAmount
92
- ? "MAX"
93
- : formatCurrencyUnit(getAccountUnit(account), t.amount, {
94
- showCode: true,
95
- disableRounding: true,
96
- })
97
- }
120
+ ${header}
98
121
  TO ${t.recipient}
99
122
  with gasPrice=${formatCurrencyUnit(
100
123
  mainAccount.currency.units[1] || mainAccount.currency.units[0],
@@ -102,6 +125,7 @@ with gasPrice=${formatCurrencyUnit(
102
125
  )}
103
126
  with gasLimit=${gasLimit.toString()}`;
104
127
  };
128
+
105
129
  const defaultGasLimit = new BigNumber(0x5208);
106
130
  export const getGasLimit = (t: Transaction): BigNumber =>
107
131
  t.userGasLimit || t.estimatedGasLimit || defaultGasLimit;
@@ -127,6 +151,9 @@ export const fromTransactionRaw = (tr: TransactionRaw): Transaction => {
127
151
  },
128
152
  allowZeroAmount: tr.allowZeroAmount,
129
153
  feesStrategy: tr.feesStrategy,
154
+ tokenIds: tr.tokenIds,
155
+ collection: tr.collection,
156
+ quantities: tr.quantities?.map((q) => new BigNumber(q)),
130
157
  };
131
158
  };
132
159
  export const toTransactionRaw = (t: Transaction): TransactionRaw => {
@@ -151,6 +178,9 @@ export const toTransactionRaw = (t: Transaction): TransactionRaw => {
151
178
  },
152
179
  allowZeroAmount: t.allowZeroAmount,
153
180
  feesStrategy: t.feesStrategy,
181
+ tokenIds: t.tokenIds,
182
+ collection: t.collection,
183
+ quantities: t.quantities?.map((q) => q.toString()),
154
184
  };
155
185
  };
156
186