@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.
- package/lib/families/bitcoin/wallet-btc/crypto/zen.d.ts +2 -1
- package/lib/families/bitcoin/wallet-btc/crypto/zen.d.ts.map +1 -1
- package/lib/families/bitcoin/wallet-btc/crypto/zen.js +4 -2
- package/lib/families/bitcoin/wallet-btc/crypto/zen.js.map +1 -1
- package/lib/families/ethereum/cli-transaction.d.ts.map +1 -1
- package/lib/families/ethereum/cli-transaction.js +10 -0
- package/lib/families/ethereum/cli-transaction.js.map +1 -1
- package/lib/families/ethereum/modules/erc1155.d.ts +4 -0
- package/lib/families/ethereum/modules/erc1155.d.ts.map +1 -0
- package/lib/families/ethereum/modules/erc1155.js +117 -0
- package/lib/families/ethereum/modules/erc1155.js.map +1 -0
- package/lib/families/ethereum/modules/erc721.d.ts +4 -0
- package/lib/families/ethereum/modules/erc721.d.ts.map +1 -0
- package/lib/families/ethereum/modules/erc721.js +95 -0
- package/lib/families/ethereum/modules/erc721.js.map +1 -0
- package/lib/families/ethereum/modules/index.d.ts +3 -1
- package/lib/families/ethereum/modules/index.d.ts.map +1 -1
- package/lib/families/ethereum/modules/index.js +5 -1
- package/lib/families/ethereum/modules/index.js.map +1 -1
- package/lib/families/ethereum/transaction.d.ts.map +1 -1
- package/lib/families/ethereum/transaction.js +25 -8
- package/lib/families/ethereum/transaction.js.map +1 -1
- package/lib/families/ethereum/types.d.ts +6 -0
- package/lib/families/ethereum/types.d.ts.map +1 -1
- package/lib/families/ethereum/types.js.map +1 -1
- package/package.json +8 -8
- package/src/__tests__/__snapshots__/all.libcore.ts.snap +7 -7
- package/src/currencies/__snapshots__/sortByMarketcap.test.ts.snap +67 -1324
- package/src/families/bitcoin/wallet-btc/crypto/zen.ts +7 -2
- package/src/families/ethereum/cli-transaction.ts +10 -0
- package/src/families/ethereum/modules/erc1155.ts +142 -0
- package/src/families/ethereum/modules/erc721.ts +108 -0
- package/src/families/ethereum/modules/index.ts +12 -1
- package/src/families/ethereum/transaction.ts +38 -8
- 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
|
-
|
|
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 =
|
|
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
|
-
${
|
|
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: {
|