@ledgerhq/live-common 21.16.4 → 21.18.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 (35) hide show
  1. package/lib/families/bitcoin/wallet-btc/crypto/zen.d.ts +2 -1
  2. package/lib/families/bitcoin/wallet-btc/crypto/zen.d.ts.map +1 -1
  3. package/lib/families/bitcoin/wallet-btc/crypto/zen.js +4 -2
  4. package/lib/families/bitcoin/wallet-btc/crypto/zen.js.map +1 -1
  5. package/lib/families/ethereum/cli-transaction.d.ts.map +1 -1
  6. package/lib/families/ethereum/cli-transaction.js +10 -0
  7. package/lib/families/ethereum/cli-transaction.js.map +1 -1
  8. package/lib/families/ethereum/modules/erc1155.d.ts +4 -0
  9. package/lib/families/ethereum/modules/erc1155.d.ts.map +1 -0
  10. package/lib/families/ethereum/modules/erc1155.js +117 -0
  11. package/lib/families/ethereum/modules/erc1155.js.map +1 -0
  12. package/lib/families/ethereum/modules/erc721.d.ts +4 -0
  13. package/lib/families/ethereum/modules/erc721.d.ts.map +1 -0
  14. package/lib/families/ethereum/modules/erc721.js +95 -0
  15. package/lib/families/ethereum/modules/erc721.js.map +1 -0
  16. package/lib/families/ethereum/modules/index.d.ts +3 -1
  17. package/lib/families/ethereum/modules/index.d.ts.map +1 -1
  18. package/lib/families/ethereum/modules/index.js +5 -1
  19. package/lib/families/ethereum/modules/index.js.map +1 -1
  20. package/lib/families/ethereum/transaction.d.ts.map +1 -1
  21. package/lib/families/ethereum/transaction.js +25 -8
  22. package/lib/families/ethereum/transaction.js.map +1 -1
  23. package/lib/families/ethereum/types.d.ts +6 -0
  24. package/lib/families/ethereum/types.d.ts.map +1 -1
  25. package/lib/families/ethereum/types.js.map +1 -1
  26. package/package.json +8 -8
  27. package/src/__tests__/__snapshots__/all.libcore.ts.snap +7 -7
  28. package/src/currencies/__snapshots__/sortByMarketcap.test.ts.snap +67 -1324
  29. package/src/families/bitcoin/wallet-btc/crypto/zen.ts +7 -2
  30. package/src/families/ethereum/cli-transaction.ts +10 -0
  31. package/src/families/ethereum/modules/erc1155.ts +142 -0
  32. package/src/families/ethereum/modules/erc721.ts +108 -0
  33. package/src/families/ethereum/modules/index.ts +12 -1
  34. package/src/families/ethereum/transaction.ts +38 -8
  35. package/src/families/ethereum/types.ts +6 -0
@@ -84,11 +84,16 @@ class Zen implements ICrypto {
84
84
  if (!this.validateAddress(address)) {
85
85
  throw new InvalidAddress();
86
86
  }
87
- // TODO find a better way to calculate the script from zen address instead of converting to bitcoin address
88
- return toOutputScript(
87
+ const outputScript = toOutputScript(
89
88
  Zen.toBitcoinAddr(address),
90
89
  coininfo.bitcoin.main.toBitcoinJS()
91
90
  );
91
+ // refer to https://github.com/LedgerHQ/lib-ledger-core/blob/fc9d762b83fc2b269d072b662065747a64ab2816/core/src/wallet/bitcoin/scripts/BitcoinLikeScript.cpp#L139 and https://github.com/LedgerHQ/lib-ledger-core/blob/fc9d762b83fc2b269d072b662065747a64ab2816/core/src/wallet/bitcoin/networks.cpp#L39 for bip115 Script and its network parameters
92
+ const bip115Script = Buffer.from(
93
+ "209ec9845acb02fab24e1c0368b3b517c1a4488fba97f0e3459ac053ea0100000003c01f02b4",
94
+ "hex"
95
+ );
96
+ return Buffer.concat([outputScript, bip115Script]);
92
97
  }
93
98
 
94
99
  // eslint-disable-next-line class-methods-use-this
@@ -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
@@ -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
 
@@ -37,6 +37,9 @@ export type Transaction = TransactionCommon & {
37
37
  feeCustomUnit: Unit | null | undefined;
38
38
  networkInfo: NetworkInfo | null | undefined;
39
39
  allowZeroAmount?: boolean;
40
+ collection?: string;
41
+ tokenIds?: string[];
42
+ quantities?: BigNumber[];
40
43
  };
41
44
  export type TransactionRaw = TransactionCommonRaw & {
42
45
  family: "ethereum";
@@ -49,6 +52,9 @@ export type TransactionRaw = TransactionCommonRaw & {
49
52
  feeCustomUnit: Unit | null | undefined;
50
53
  networkInfo: NetworkInfoRaw | null | undefined;
51
54
  allowZeroAmount?: boolean;
55
+ tokenIds?: string[];
56
+ collection?: string;
57
+ quantities?: string[];
52
58
  };
53
59
  export type TypedMessage = {
54
60
  types: {