@ledgerhq/live-common 24.0.0-next.1 → 24.0.0-next.4
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/__tests__/families/bitcoin/wallet-btc/wallet.estimateMaxSpendable.test.js +4 -4
- package/lib/__tests__/families/bitcoin/wallet-btc/wallet.estimateMaxSpendable.test.js.map +1 -1
- package/lib/exchange/swap/getExchangeRates.test.js +0 -1
- package/lib/exchange/swap/getExchangeRates.test.js.map +1 -1
- package/lib/families/bitcoin/bridge/mock.d.ts.map +1 -1
- package/lib/families/bitcoin/bridge/mock.js +0 -1
- package/lib/families/bitcoin/bridge/mock.js.map +1 -1
- package/lib/families/bitcoin/cache.js +1 -1
- package/lib/families/bitcoin/cache.js.map +1 -1
- package/lib/families/bitcoin/cli-transaction.d.ts +6 -6
- package/lib/families/bitcoin/cli-transaction.d.ts.map +1 -1
- package/lib/families/bitcoin/cli-transaction.js +0 -6
- package/lib/families/bitcoin/cli-transaction.js.map +1 -1
- package/lib/families/bitcoin/datasets/bitcoin.d.ts.map +1 -1
- package/lib/families/bitcoin/datasets/bitcoin.js +0 -3
- package/lib/families/bitcoin/datasets/bitcoin.js.map +1 -1
- package/lib/families/bitcoin/datasets/digibyte.d.ts.map +1 -1
- package/lib/families/bitcoin/datasets/digibyte.js +0 -3
- package/lib/families/bitcoin/datasets/digibyte.js.map +1 -1
- package/lib/families/bitcoin/datasets/litecoin.d.ts.map +1 -1
- package/lib/families/bitcoin/datasets/litecoin.js +0 -3
- package/lib/families/bitcoin/datasets/litecoin.js.map +1 -1
- package/lib/families/bitcoin/js-buildTransaction.d.ts.map +1 -1
- package/lib/families/bitcoin/js-buildTransaction.js +1 -1
- package/lib/families/bitcoin/js-buildTransaction.js.map +1 -1
- package/lib/families/bitcoin/js-createTransaction.d.ts.map +1 -1
- package/lib/families/bitcoin/js-createTransaction.js +0 -1
- package/lib/families/bitcoin/js-createTransaction.js.map +1 -1
- package/lib/families/bitcoin/js-estimateMaxSpendable.d.ts.map +1 -1
- package/lib/families/bitcoin/js-estimateMaxSpendable.js +7 -7
- package/lib/families/bitcoin/js-estimateMaxSpendable.js.map +1 -1
- package/lib/families/bitcoin/js-synchronisation.d.ts.map +1 -1
- package/lib/families/bitcoin/js-synchronisation.js +3 -3
- package/lib/families/bitcoin/js-synchronisation.js.map +1 -1
- package/lib/families/bitcoin/logic.d.ts +1 -1
- package/lib/families/bitcoin/logic.d.ts.map +1 -1
- package/lib/families/bitcoin/logic.js +3 -8
- package/lib/families/bitcoin/logic.js.map +1 -1
- package/lib/families/bitcoin/specs.d.ts.map +1 -1
- package/lib/families/bitcoin/specs.js +1 -1
- package/lib/families/bitcoin/specs.js.map +1 -1
- package/lib/families/bitcoin/transaction.js +2 -2
- package/lib/families/bitcoin/transaction.js.map +1 -1
- package/lib/families/bitcoin/types.d.ts +0 -1
- package/lib/families/bitcoin/types.d.ts.map +1 -1
- package/lib/families/bitcoin/wallet-btc/wallet.d.ts +1 -1
- package/lib/families/bitcoin/wallet-btc/wallet.d.ts.map +1 -1
- package/lib/families/bitcoin/wallet-btc/wallet.js +9 -4
- package/lib/families/bitcoin/wallet-btc/wallet.js.map +1 -1
- package/lib/families/ethereum/modules/erc1155.d.ts.map +1 -1
- package/lib/families/ethereum/modules/erc1155.js +5 -1
- package/lib/families/ethereum/modules/erc1155.js.map +1 -1
- package/lib/families/ethereum/modules/erc721.d.ts.map +1 -1
- package/lib/families/ethereum/modules/erc721.js +5 -0
- package/lib/families/ethereum/modules/erc721.js.map +1 -1
- package/lib/families/ethereum/nft.unit.test.js +35 -15
- package/lib/families/ethereum/nft.unit.test.js.map +1 -1
- package/lib/families/ethereum/synchronisation.d.ts.map +1 -1
- package/lib/families/ethereum/synchronisation.js +17 -13
- package/lib/families/ethereum/synchronisation.js.map +1 -1
- package/lib/families/solana/api/chain/account/token.d.ts +1 -1
- package/lib/families/solana/api/chain/account/vote.d.ts +12 -12
- package/lib/families/solana/api/chain/instruction/system/types.d.ts +4 -4
- package/lib/featureFlags/defaultFeatures.d.ts.map +1 -1
- package/lib/featureFlags/defaultFeatures.js +3 -0
- package/lib/featureFlags/defaultFeatures.js.map +1 -1
- package/lib/featureFlags/types.d.ts +1 -1
- package/lib/featureFlags/types.d.ts.map +1 -1
- package/lib/generated/cli-transaction.d.ts +6 -6
- package/lib/nft/hook.d.ts +3 -0
- package/lib/nft/hook.d.ts.map +1 -0
- package/lib/nft/hook.js +73 -0
- package/lib/nft/hook.js.map +1 -0
- package/lib/nft/index.d.ts +1 -0
- package/lib/nft/index.d.ts.map +1 -1
- package/lib/nft/index.js +1 -0
- package/lib/nft/index.js.map +1 -1
- package/lib/nft/nftOperationId.d.ts +15 -0
- package/lib/nft/nftOperationId.d.ts.map +1 -0
- package/lib/nft/nftOperationId.js +50 -0
- package/lib/nft/nftOperationId.js.map +1 -0
- package/lib/operation.d.ts +1 -1
- package/lib/operation.d.ts.map +1 -1
- package/lib/operation.js +23 -1
- package/lib/operation.js.map +1 -1
- package/lib/types/nft.d.ts +4 -0
- package/lib/types/nft.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/families/bitcoin/wallet-btc/wallet.estimateMaxSpendable.test.ts +4 -12
- package/src/exchange/swap/getExchangeRates.test.ts +0 -1
- package/src/families/bitcoin/bridge/mock.ts +0 -1
- package/src/families/bitcoin/cache.ts +3 -3
- package/src/families/bitcoin/cli-transaction.ts +0 -6
- package/src/families/bitcoin/datasets/bitcoin.ts +0 -3
- package/src/families/bitcoin/datasets/digibyte.ts +0 -3
- package/src/families/bitcoin/datasets/litecoin.ts +0 -3
- package/src/families/bitcoin/js-buildTransaction.ts +0 -1
- package/src/families/bitcoin/js-createTransaction.ts +0 -1
- package/src/families/bitcoin/js-estimateMaxSpendable.ts +0 -1
- package/src/families/bitcoin/js-synchronisation.ts +6 -3
- package/src/families/bitcoin/logic.ts +4 -9
- package/src/families/bitcoin/specs.ts +0 -1
- package/src/families/bitcoin/transaction.ts +2 -2
- package/src/families/bitcoin/types.ts +0 -1
- package/src/families/bitcoin/wallet-btc/wallet.ts +8 -2
- package/src/families/ethereum/modules/erc1155.ts +12 -5
- package/src/families/ethereum/modules/erc721.ts +11 -0
- package/src/families/ethereum/nft.unit.test.ts +24 -17
- package/src/families/ethereum/synchronisation.ts +22 -18
- package/src/featureFlags/defaultFeatures.ts +3 -0
- package/src/featureFlags/types.ts +1 -0
- package/src/nft/hook.ts +29 -0
- package/src/nft/index.ts +1 -0
- package/src/nft/nftOperationId.ts +47 -0
- package/src/operation.ts +50 -1
- package/src/types/nft.ts +5 -0
|
@@ -15,9 +15,9 @@ const getCacheKeyForCalculateFees = ({
|
|
|
15
15
|
}) =>
|
|
16
16
|
`${a.id}_${a.blockHeight || 0}_${t.amount.toString()}_${String(
|
|
17
17
|
t.useAllAmount
|
|
18
|
-
)}_${t.recipient}_${t.feePerByte ? t.feePerByte.toString() : ""}_${
|
|
19
|
-
t.utxoStrategy.
|
|
20
|
-
}_${
|
|
18
|
+
)}_${t.recipient}_${t.feePerByte ? t.feePerByte.toString() : ""}_${0}_${
|
|
19
|
+
t.utxoStrategy.strategy
|
|
20
|
+
}_${String(t.rbf)}_${t.utxoStrategy.excludeUTXOs
|
|
21
21
|
.map(({ hash, outputIndex }) => `${hash}@${outputIndex}`)
|
|
22
22
|
.join("+")}`;
|
|
23
23
|
|
|
@@ -8,11 +8,6 @@ const options = [
|
|
|
8
8
|
type: String,
|
|
9
9
|
desc: "how much fee per byte",
|
|
10
10
|
},
|
|
11
|
-
{
|
|
12
|
-
name: "pickUnconfirmedRBF",
|
|
13
|
-
type: Boolean,
|
|
14
|
-
desc: "also pick unconfirmed replaceable txs",
|
|
15
|
-
},
|
|
16
11
|
{
|
|
17
12
|
name: "excludeUTXO",
|
|
18
13
|
alias: "E",
|
|
@@ -52,7 +47,6 @@ function inferTransactions(
|
|
|
52
47
|
rbf: opts.rbf || false,
|
|
53
48
|
utxoStrategy: {
|
|
54
49
|
strategy: bitcoinPickingStrategy[opts["bitcoin-pick-strategy"]] || 0,
|
|
55
|
-
pickUnconfirmedRBF: opts.pickUnconfirmedRBF || false,
|
|
56
50
|
excludeUTXOs: (opts.excludeUTXO || []).map((str) => {
|
|
57
51
|
const [hash, index] = str.split("@");
|
|
58
52
|
invariant(
|
|
@@ -47,7 +47,6 @@ const dataset: CurrenciesData<Transaction> = {
|
|
|
47
47
|
rbf: false,
|
|
48
48
|
utxoStrategy: {
|
|
49
49
|
strategy: 0,
|
|
50
|
-
pickUnconfirmedRBF: false,
|
|
51
50
|
excludeUTXOs: [],
|
|
52
51
|
},
|
|
53
52
|
}),
|
|
@@ -73,7 +72,6 @@ const dataset: CurrenciesData<Transaction> = {
|
|
|
73
72
|
rbf: false,
|
|
74
73
|
utxoStrategy: {
|
|
75
74
|
strategy: 0,
|
|
76
|
-
pickUnconfirmedRBF: false,
|
|
77
75
|
excludeUTXOs: [],
|
|
78
76
|
},
|
|
79
77
|
}),
|
|
@@ -99,7 +97,6 @@ const dataset: CurrenciesData<Transaction> = {
|
|
|
99
97
|
rbf: false,
|
|
100
98
|
utxoStrategy: {
|
|
101
99
|
strategy: 0,
|
|
102
|
-
pickUnconfirmedRBF: false,
|
|
103
100
|
excludeUTXOs: [],
|
|
104
101
|
},
|
|
105
102
|
}),
|
|
@@ -46,7 +46,6 @@ const dataset: CurrenciesData<Transaction> = {
|
|
|
46
46
|
rbf: false,
|
|
47
47
|
utxoStrategy: {
|
|
48
48
|
strategy: 0,
|
|
49
|
-
pickUnconfirmedRBF: false,
|
|
50
49
|
excludeUTXOs: [],
|
|
51
50
|
},
|
|
52
51
|
}),
|
|
@@ -69,7 +68,6 @@ const dataset: CurrenciesData<Transaction> = {
|
|
|
69
68
|
rbf: false,
|
|
70
69
|
utxoStrategy: {
|
|
71
70
|
strategy: 0,
|
|
72
|
-
pickUnconfirmedRBF: false,
|
|
73
71
|
excludeUTXOs: [],
|
|
74
72
|
},
|
|
75
73
|
}),
|
|
@@ -92,7 +90,6 @@ const dataset: CurrenciesData<Transaction> = {
|
|
|
92
90
|
rbf: false,
|
|
93
91
|
utxoStrategy: {
|
|
94
92
|
strategy: 0,
|
|
95
|
-
pickUnconfirmedRBF: false,
|
|
96
93
|
excludeUTXOs: [],
|
|
97
94
|
},
|
|
98
95
|
}),
|
|
@@ -47,7 +47,6 @@ const dataset: CurrenciesData<Transaction> = {
|
|
|
47
47
|
rbf: false,
|
|
48
48
|
utxoStrategy: {
|
|
49
49
|
strategy: 0,
|
|
50
|
-
pickUnconfirmedRBF: false,
|
|
51
50
|
excludeUTXOs: [],
|
|
52
51
|
},
|
|
53
52
|
}),
|
|
@@ -70,7 +69,6 @@ const dataset: CurrenciesData<Transaction> = {
|
|
|
70
69
|
rbf: false,
|
|
71
70
|
utxoStrategy: {
|
|
72
71
|
strategy: 0,
|
|
73
|
-
pickUnconfirmedRBF: false,
|
|
74
72
|
excludeUTXOs: [],
|
|
75
73
|
},
|
|
76
74
|
}),
|
|
@@ -93,7 +91,6 @@ const dataset: CurrenciesData<Transaction> = {
|
|
|
93
91
|
rbf: false,
|
|
94
92
|
utxoStrategy: {
|
|
95
93
|
strategy: 0,
|
|
96
|
-
pickUnconfirmedRBF: false,
|
|
97
94
|
excludeUTXOs: [],
|
|
98
95
|
},
|
|
99
96
|
}),
|
|
@@ -44,7 +44,6 @@ export const buildTransaction = async (
|
|
|
44
44
|
walletAccount,
|
|
45
45
|
transaction.feePerByte.toNumber(), //!\ wallet-btc handles fees as JS number
|
|
46
46
|
transaction.utxoStrategy.excludeUTXOs,
|
|
47
|
-
transaction.utxoStrategy.pickUnconfirmedRBF,
|
|
48
47
|
[transaction.recipient]
|
|
49
48
|
);
|
|
50
49
|
log("btcwallet", "building transaction", transaction);
|
|
@@ -30,7 +30,6 @@ const estimateMaxSpendable = async ({
|
|
|
30
30
|
walletAccount,
|
|
31
31
|
feePerByte.toNumber(), //!\ wallet-btc handles fees as JS number
|
|
32
32
|
transaction?.utxoStrategy?.excludeUTXOs || [],
|
|
33
|
-
transaction?.utxoStrategy?.pickUnconfirmedRBF || false,
|
|
34
33
|
transaction ? [transaction.recipient] : []
|
|
35
34
|
);
|
|
36
35
|
return maxSpendable.lt(0) ? new BigNumber(0) : maxSpendable;
|
|
@@ -53,7 +53,10 @@ const toWalletNetwork = (currencyId: string): "testnet" | "mainnet" => {
|
|
|
53
53
|
};
|
|
54
54
|
|
|
55
55
|
// Map wallet-btc's Output to LL's BitcoinOutput
|
|
56
|
-
const fromWalletUtxo = (
|
|
56
|
+
const fromWalletUtxo = (
|
|
57
|
+
utxo: WalletOutput,
|
|
58
|
+
changeAddresses: Set<string>
|
|
59
|
+
): BitcoinOutput => {
|
|
57
60
|
return {
|
|
58
61
|
hash: utxo.output_hash,
|
|
59
62
|
outputIndex: utxo.output_index,
|
|
@@ -61,7 +64,7 @@ const fromWalletUtxo = (utxo: WalletOutput): BitcoinOutput => {
|
|
|
61
64
|
address: utxo.address,
|
|
62
65
|
value: new BigNumber(utxo.value),
|
|
63
66
|
rbf: utxo.rbf,
|
|
64
|
-
isChange:
|
|
67
|
+
isChange: changeAddresses.has(utxo.address),
|
|
65
68
|
};
|
|
66
69
|
};
|
|
67
70
|
|
|
@@ -349,7 +352,7 @@ const getAccountShape: GetAccountShape = async (info) => {
|
|
|
349
352
|
const newUniqueOperations = deduplicateOperations(newOperations);
|
|
350
353
|
const operations = mergeOps(oldOperations, newUniqueOperations);
|
|
351
354
|
const rawUtxos = await wallet.getAccountUnspentUtxos(walletAccount);
|
|
352
|
-
const utxos = rawUtxos.map(fromWalletUtxo);
|
|
355
|
+
const utxos = rawUtxos.map((utxo) => fromWalletUtxo(utxo, changeAddresses));
|
|
353
356
|
return {
|
|
354
357
|
id: accountId,
|
|
355
358
|
xpub,
|
|
@@ -68,7 +68,7 @@ export const isValidRecipient = async (params: {
|
|
|
68
68
|
type UTXOStatus =
|
|
69
69
|
| {
|
|
70
70
|
excluded: true;
|
|
71
|
-
reason: "
|
|
71
|
+
reason: "pickPendingUtxo" | "userExclusion";
|
|
72
72
|
}
|
|
73
73
|
| {
|
|
74
74
|
excluded: false;
|
|
@@ -77,16 +77,11 @@ export const getUTXOStatus = (
|
|
|
77
77
|
utxo: BitcoinOutput,
|
|
78
78
|
utxoStrategy: UtxoStrategy
|
|
79
79
|
): UTXOStatus => {
|
|
80
|
-
if (!
|
|
80
|
+
if (!utxo.blockHeight && !utxo.isChange) {
|
|
81
|
+
// exclude pending and not change utxo
|
|
81
82
|
return {
|
|
82
83
|
excluded: true,
|
|
83
|
-
reason: "
|
|
84
|
-
};
|
|
85
|
-
}
|
|
86
|
-
if (!utxo.rbf && !utxo.blockHeight) {
|
|
87
|
-
return {
|
|
88
|
-
excluded: true,
|
|
89
|
-
reason: "pickPendingNonRBF",
|
|
84
|
+
reason: "pickPendingUtxo",
|
|
90
85
|
};
|
|
91
86
|
}
|
|
92
87
|
if (
|
|
@@ -80,7 +80,7 @@ const formatNetworkInfo = (
|
|
|
80
80
|
|
|
81
81
|
export const formatTransaction = (t: Transaction, account: Account): string => {
|
|
82
82
|
const n = getEnv("DEBUG_UTXO_DISPLAY");
|
|
83
|
-
const { excludeUTXOs, strategy
|
|
83
|
+
const { excludeUTXOs, strategy } = t.utxoStrategy;
|
|
84
84
|
const displayAll = excludeUTXOs.length <= n;
|
|
85
85
|
return `
|
|
86
86
|
SEND ${
|
|
@@ -99,7 +99,7 @@ ${[
|
|
|
99
99
|
Object.keys(bitcoinPickingStrategy).find(
|
|
100
100
|
(k) => bitcoinPickingStrategy[k] === strategy
|
|
101
101
|
),
|
|
102
|
-
|
|
102
|
+
"pick-unconfirmed",
|
|
103
103
|
t.rbf && "RBF-enabled",
|
|
104
104
|
]
|
|
105
105
|
.filter(Boolean)
|
|
@@ -126,7 +126,6 @@ export type BitcoinPickingStrategy =
|
|
|
126
126
|
typeof bitcoinPickingStrategy[keyof typeof bitcoinPickingStrategy];
|
|
127
127
|
export type UtxoStrategy = {
|
|
128
128
|
strategy: BitcoinPickingStrategy;
|
|
129
|
-
pickUnconfirmedRBF: boolean;
|
|
130
129
|
excludeUTXOs: Array<{
|
|
131
130
|
hash: string;
|
|
132
131
|
outputIndex: number;
|
|
@@ -117,10 +117,12 @@ class BitcoinLikeWallet {
|
|
|
117
117
|
account: Account,
|
|
118
118
|
feePerByte: number,
|
|
119
119
|
excludeUTXOs: Array<{ hash: string; outputIndex: number }>,
|
|
120
|
-
pickUnconfirmedRBF: boolean,
|
|
121
120
|
outputAddresses: string[] = []
|
|
122
121
|
) {
|
|
123
122
|
const addresses = await account.xpub.getXpubAddresses();
|
|
123
|
+
const changeAddresses = (await account.xpub.getAccountAddresses(1)).map(
|
|
124
|
+
(item) => item.address
|
|
125
|
+
);
|
|
124
126
|
const utxos = flatten(
|
|
125
127
|
await Promise.all(
|
|
126
128
|
addresses.map((address) =>
|
|
@@ -139,7 +141,11 @@ class BitcoinLikeWallet {
|
|
|
139
141
|
excludeUtxo.outputIndex === utxo.output_index
|
|
140
142
|
)
|
|
141
143
|
) {
|
|
142
|
-
|
|
144
|
+
// we can use either non pending utxo or change utxo
|
|
145
|
+
if (
|
|
146
|
+
changeAddresses.includes(utxo.address) ||
|
|
147
|
+
utxo.block_height !== null
|
|
148
|
+
) {
|
|
143
149
|
usableUtxoCount++;
|
|
144
150
|
balance = balance.plus(utxo.value);
|
|
145
151
|
}
|
|
@@ -8,8 +8,8 @@ import {
|
|
|
8
8
|
} from "@ledgerhq/errors";
|
|
9
9
|
import { validateRecipient } from "../transaction";
|
|
10
10
|
import type { ModeModule, Transaction } from "../types";
|
|
11
|
-
import type { Account } from "../../../types";
|
|
12
11
|
import { prepareTransaction } from "./erc721";
|
|
12
|
+
import { Account } from "../../../types";
|
|
13
13
|
|
|
14
14
|
const NotOwnedNft = createCustomErrorClass("NotOwnedNft");
|
|
15
15
|
const NotEnoughNftOwned = createCustomErrorClass("NotEnoughNftOwned");
|
|
@@ -124,10 +124,17 @@ const erc1155Transfer: ModeModule = {
|
|
|
124
124
|
*/
|
|
125
125
|
fillOptimisticOperation(a, t, op) {
|
|
126
126
|
op.type = "FEES";
|
|
127
|
-
op.
|
|
128
|
-
...op
|
|
129
|
-
|
|
130
|
-
|
|
127
|
+
op.nftOperations = t.tokenIds?.map((tokenId, i) => ({
|
|
128
|
+
...op,
|
|
129
|
+
id: "", // operation ID will be filled by patchOperationWithHash
|
|
130
|
+
type: "NFT_OUT",
|
|
131
|
+
senders: [eip55.encode(a.freshAddress)],
|
|
132
|
+
recipients: [eip55.encode(t.recipient)],
|
|
133
|
+
standard: "ERC1155",
|
|
134
|
+
contract: eip55.encode(t.collection ?? ""),
|
|
135
|
+
tokenId,
|
|
136
|
+
value: t.quantities?.[i] || new BigNumber(0),
|
|
137
|
+
}));
|
|
131
138
|
},
|
|
132
139
|
|
|
133
140
|
prepareTransaction,
|
|
@@ -118,6 +118,17 @@ const erc721Transfer: ModeModule = {
|
|
|
118
118
|
*/
|
|
119
119
|
fillOptimisticOperation(a, t, op) {
|
|
120
120
|
op.type = "FEES";
|
|
121
|
+
op.nftOperations = t.tokenIds?.map((tokenId, i) => ({
|
|
122
|
+
...op,
|
|
123
|
+
id: "", // operation ID will be filled by patchOperationWithHash
|
|
124
|
+
type: "NFT_OUT",
|
|
125
|
+
senders: [eip55.encode(a.freshAddress)],
|
|
126
|
+
recipients: [eip55.encode(t.recipient)],
|
|
127
|
+
standard: "ERC721",
|
|
128
|
+
contract: eip55.encode(t.collection ?? ""),
|
|
129
|
+
tokenId,
|
|
130
|
+
value: t.quantities?.[i] || new BigNumber(0),
|
|
131
|
+
}));
|
|
121
132
|
},
|
|
122
133
|
|
|
123
134
|
getResolutionConfig: () => ({ nft: true }),
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
nftsFromOperations,
|
|
14
14
|
} from "../../nft";
|
|
15
15
|
import { Transaction } from "./types";
|
|
16
|
+
import { encodeERC1155OperationId } from "../../nft/nftOperationId";
|
|
16
17
|
|
|
17
18
|
describe("nft merging", () => {
|
|
18
19
|
const makeNFT = (
|
|
@@ -92,18 +93,21 @@ describe("nft merging", () => {
|
|
|
92
93
|
describe("OpenSea lazy minting bs", () => {
|
|
93
94
|
test("should have a correct on-chain nft amount even with OpenSea lazy minting", () => {
|
|
94
95
|
const makeNftOperation = (
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
dateOrder: number
|
|
96
|
+
params: [Operation["type"], number],
|
|
97
|
+
index: number
|
|
98
98
|
): Operation => {
|
|
99
|
+
const [type, value] = params;
|
|
100
|
+
|
|
99
101
|
if (!["NFT_IN", "NFT_OUT"].includes(type)) {
|
|
100
102
|
return {} as Operation;
|
|
101
103
|
}
|
|
102
104
|
|
|
103
|
-
const
|
|
105
|
+
const now = Date.now() + index;
|
|
106
|
+
const currencyId = "polygon";
|
|
107
|
+
const accountId = encodeAccountId({
|
|
104
108
|
type: "type",
|
|
105
|
-
currencyId: "polygon",
|
|
106
109
|
xpubOrAddress: "0xbob",
|
|
110
|
+
currencyId,
|
|
107
111
|
derivationMode: "",
|
|
108
112
|
version: "1",
|
|
109
113
|
});
|
|
@@ -112,11 +116,12 @@ describe("OpenSea lazy minting bs", () => {
|
|
|
112
116
|
const contract = "0x0000000000000000000000000000000000000000";
|
|
113
117
|
const fee = new BigNumber(0);
|
|
114
118
|
const tokenId = "42069";
|
|
115
|
-
const hash = "FaKeHasH";
|
|
116
|
-
const date = new Date(
|
|
119
|
+
const hash = "FaKeHasH" + now;
|
|
120
|
+
const date = new Date(now);
|
|
121
|
+
const nftId = encodeNftId(accountId, contract, tokenId, currencyId);
|
|
117
122
|
|
|
118
123
|
return {
|
|
119
|
-
id,
|
|
124
|
+
id: encodeERC1155OperationId(nftId, hash, type),
|
|
120
125
|
hash,
|
|
121
126
|
senders: [sender],
|
|
122
127
|
recipients: [receiver],
|
|
@@ -126,20 +131,22 @@ describe("OpenSea lazy minting bs", () => {
|
|
|
126
131
|
tokenId,
|
|
127
132
|
value: new BigNumber(value),
|
|
128
133
|
type,
|
|
129
|
-
accountId
|
|
134
|
+
accountId,
|
|
130
135
|
date,
|
|
131
136
|
} as Operation;
|
|
132
137
|
};
|
|
133
138
|
|
|
134
139
|
// scenario with bob lazy minting 10 NFTs
|
|
135
|
-
const ops =
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
140
|
+
const ops = (
|
|
141
|
+
[
|
|
142
|
+
["NFT_OUT", 5], // lazy mint sending 5 NFT
|
|
143
|
+
["NFT_IN", 1], // receiving 1 of them back
|
|
144
|
+
["NFT_IN", 2], // receiving 2 of them back
|
|
145
|
+
["NFT_OUT", 2], // lazy mint sending 5 NFT (transformed by OpenSea in 2 txs) 1/2 (off-chain)
|
|
146
|
+
["NFT_OUT", 3], // lazy mint sending 5 NFT (transformed by OpenSea in 2 txs) 2/2 (on-chain)
|
|
147
|
+
["NFT_IN", 1], // receiving 1 back
|
|
148
|
+
] as [Operation["type"], number][]
|
|
149
|
+
).map((params, i) => makeNftOperation(params, i));
|
|
143
150
|
|
|
144
151
|
// What happened for bob:
|
|
145
152
|
//
|
|
@@ -20,6 +20,11 @@ import { API, apiForCurrency, Tx } from "../../api/Ethereum";
|
|
|
20
20
|
import { digestTokenAccounts, prepareTokenAccounts } from "./modules";
|
|
21
21
|
import { findTokenByAddressInCurrency } from "@ledgerhq/cryptoassets";
|
|
22
22
|
import { encodeNftId, isNFTActive, nftsFromOperations } from "../../nft";
|
|
23
|
+
import { encodeOperationId } from "../../operation";
|
|
24
|
+
import {
|
|
25
|
+
encodeERC1155OperationId,
|
|
26
|
+
encodeERC721OperationId,
|
|
27
|
+
} from "../../nft/nftOperationId";
|
|
23
28
|
|
|
24
29
|
export const getAccountShape: GetAccountShape = async (
|
|
25
30
|
infoInput,
|
|
@@ -167,7 +172,9 @@ export const getAccountShape: GetAccountShape = async (
|
|
|
167
172
|
const nfts = isNFTActive(currency)
|
|
168
173
|
? mergeNfts(
|
|
169
174
|
initialAccount?.nfts || [],
|
|
170
|
-
nftsFromOperations(
|
|
175
|
+
nftsFromOperations(
|
|
176
|
+
mergeOps(operations, initialAccount?.pendingOperations || [])
|
|
177
|
+
).filter((n) => n.amount.gt(0))
|
|
171
178
|
)
|
|
172
179
|
: undefined;
|
|
173
180
|
|
|
@@ -289,7 +296,7 @@ const txToOps =
|
|
|
289
296
|
if (sending) {
|
|
290
297
|
const type = "OUT";
|
|
291
298
|
all.push({
|
|
292
|
-
id:
|
|
299
|
+
id: encodeOperationId(accountId, hash, type),
|
|
293
300
|
hash,
|
|
294
301
|
type,
|
|
295
302
|
value,
|
|
@@ -308,7 +315,7 @@ const txToOps =
|
|
|
308
315
|
if (receiving) {
|
|
309
316
|
const type = "IN";
|
|
310
317
|
all.push({
|
|
311
|
-
id:
|
|
318
|
+
id: encodeOperationId(accountId, hash, type),
|
|
312
319
|
hash,
|
|
313
320
|
type,
|
|
314
321
|
value,
|
|
@@ -349,7 +356,7 @@ const txToOps =
|
|
|
349
356
|
if (sending) {
|
|
350
357
|
const type = "NFT_OUT";
|
|
351
358
|
all.push({
|
|
352
|
-
id:
|
|
359
|
+
id: encodeERC721OperationId(nftId, hash, type),
|
|
353
360
|
senders: [sender],
|
|
354
361
|
recipients: [receiver],
|
|
355
362
|
contract,
|
|
@@ -371,7 +378,7 @@ const txToOps =
|
|
|
371
378
|
if (receiving) {
|
|
372
379
|
const type = "NFT_IN";
|
|
373
380
|
all.push({
|
|
374
|
-
id:
|
|
381
|
+
id: encodeERC721OperationId(nftId, hash, type),
|
|
375
382
|
senders: [sender],
|
|
376
383
|
recipients: [receiver],
|
|
377
384
|
contract,
|
|
@@ -414,17 +421,12 @@ const txToOps =
|
|
|
414
421
|
event.transfers.forEach((transfer, j) => {
|
|
415
422
|
const tokenId = transfer.id;
|
|
416
423
|
const value = new BigNumber(transfer.value);
|
|
417
|
-
const nftId = encodeNftId(
|
|
418
|
-
id,
|
|
419
|
-
event.contract,
|
|
420
|
-
tokenId,
|
|
421
|
-
currency.id
|
|
422
|
-
);
|
|
424
|
+
const nftId = encodeNftId(id, contract, tokenId, currency.id);
|
|
423
425
|
|
|
424
426
|
if (sending) {
|
|
425
427
|
const type = "NFT_OUT";
|
|
426
428
|
all.push({
|
|
427
|
-
id:
|
|
429
|
+
id: encodeERC1155OperationId(nftId, hash, type, i, j),
|
|
428
430
|
senders: [sender],
|
|
429
431
|
recipients: [receiver],
|
|
430
432
|
contract,
|
|
@@ -447,7 +449,7 @@ const txToOps =
|
|
|
447
449
|
if (receiving) {
|
|
448
450
|
const type = "NFT_IN";
|
|
449
451
|
all.push({
|
|
450
|
-
id:
|
|
452
|
+
id: encodeERC1155OperationId(nftId, hash, type, i, j),
|
|
451
453
|
senders: [sender],
|
|
452
454
|
recipients: [receiver],
|
|
453
455
|
contract,
|
|
@@ -481,7 +483,7 @@ const txToOps =
|
|
|
481
483
|
if (sending) {
|
|
482
484
|
const type = value.eq(0) ? "FEES" : "OUT";
|
|
483
485
|
ops.push({
|
|
484
|
-
id:
|
|
486
|
+
id: encodeOperationId(id, hash, type),
|
|
485
487
|
hash,
|
|
486
488
|
type,
|
|
487
489
|
value: hasFailed ? new BigNumber(fee) : value.plus(fee),
|
|
@@ -502,10 +504,11 @@ const txToOps =
|
|
|
502
504
|
}
|
|
503
505
|
|
|
504
506
|
if (receiving) {
|
|
507
|
+
const type = "IN";
|
|
505
508
|
ops.push({
|
|
506
|
-
id:
|
|
509
|
+
id: encodeOperationId(id, hash, type),
|
|
507
510
|
hash: hash,
|
|
508
|
-
type
|
|
511
|
+
type,
|
|
509
512
|
value,
|
|
510
513
|
fee,
|
|
511
514
|
blockHeight,
|
|
@@ -531,10 +534,11 @@ const txToOps =
|
|
|
531
534
|
subOperations.length ||
|
|
532
535
|
nftOperations.length)
|
|
533
536
|
) {
|
|
537
|
+
const type = "NONE";
|
|
534
538
|
ops.push({
|
|
535
|
-
id:
|
|
539
|
+
id: encodeOperationId(id, hash, type),
|
|
536
540
|
hash: hash,
|
|
537
|
-
type
|
|
541
|
+
type,
|
|
538
542
|
value: new BigNumber(0),
|
|
539
543
|
fee,
|
|
540
544
|
blockHeight,
|
package/src/nft/hook.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { ProtoNFT, CryptoCurrency, FloorPrice } from "../types";
|
|
2
|
+
import { getEnv } from "../env";
|
|
3
|
+
import network from "../network";
|
|
4
|
+
|
|
5
|
+
const FLOOR_PRICE_CURRENCIES = new Set(["ethereum"]);
|
|
6
|
+
|
|
7
|
+
export const getFloorPrice = async (
|
|
8
|
+
nft: ProtoNFT,
|
|
9
|
+
currency: CryptoCurrency
|
|
10
|
+
): Promise<FloorPrice | null> => {
|
|
11
|
+
if (!FLOOR_PRICE_CURRENCIES.has(nft.currencyId)) {
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
try {
|
|
16
|
+
const { data } = await network({
|
|
17
|
+
method: "GET",
|
|
18
|
+
url: `${getEnv("NFT_ETH_METADATA_SERVICE")}/v1/marketdata/${
|
|
19
|
+
nft.currencyId
|
|
20
|
+
}/${currency?.ethereumLikeInfo?.chainId}/contract/${
|
|
21
|
+
nft.contract
|
|
22
|
+
}/floor-price`,
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
return data;
|
|
26
|
+
} catch (err: any) {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
};
|
package/src/nft/index.ts
CHANGED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export function encodeERC721OperationId(
|
|
2
|
+
nftId: string,
|
|
3
|
+
hash: string,
|
|
4
|
+
type: string
|
|
5
|
+
): string {
|
|
6
|
+
return `${nftId}-${hash}-${type}`;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function decodeERC721OperationId(id: string): {
|
|
10
|
+
nftId: string;
|
|
11
|
+
hash: string;
|
|
12
|
+
type: string;
|
|
13
|
+
} {
|
|
14
|
+
const [nftId, hash, type] = id.split("-");
|
|
15
|
+
return {
|
|
16
|
+
nftId,
|
|
17
|
+
hash,
|
|
18
|
+
type,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function encodeERC1155OperationId(
|
|
23
|
+
nftId: string,
|
|
24
|
+
hash: string,
|
|
25
|
+
type: string,
|
|
26
|
+
i = 0,
|
|
27
|
+
j = 0
|
|
28
|
+
): string {
|
|
29
|
+
return `${nftId}-${hash}-${type}-i${i}_${j}`;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function decodeERC1155OperationId(id: string): {
|
|
33
|
+
nftId: string;
|
|
34
|
+
hash: string;
|
|
35
|
+
type: string;
|
|
36
|
+
i: number;
|
|
37
|
+
j: number;
|
|
38
|
+
} {
|
|
39
|
+
const [nftId, hash, type, i, j] = id.split("-");
|
|
40
|
+
return {
|
|
41
|
+
nftId,
|
|
42
|
+
hash,
|
|
43
|
+
type,
|
|
44
|
+
i: Number(i),
|
|
45
|
+
j: Number(j),
|
|
46
|
+
};
|
|
47
|
+
}
|