@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.
Files changed (116) hide show
  1. package/lib/__tests__/families/bitcoin/wallet-btc/wallet.estimateMaxSpendable.test.js +4 -4
  2. package/lib/__tests__/families/bitcoin/wallet-btc/wallet.estimateMaxSpendable.test.js.map +1 -1
  3. package/lib/exchange/swap/getExchangeRates.test.js +0 -1
  4. package/lib/exchange/swap/getExchangeRates.test.js.map +1 -1
  5. package/lib/families/bitcoin/bridge/mock.d.ts.map +1 -1
  6. package/lib/families/bitcoin/bridge/mock.js +0 -1
  7. package/lib/families/bitcoin/bridge/mock.js.map +1 -1
  8. package/lib/families/bitcoin/cache.js +1 -1
  9. package/lib/families/bitcoin/cache.js.map +1 -1
  10. package/lib/families/bitcoin/cli-transaction.d.ts +6 -6
  11. package/lib/families/bitcoin/cli-transaction.d.ts.map +1 -1
  12. package/lib/families/bitcoin/cli-transaction.js +0 -6
  13. package/lib/families/bitcoin/cli-transaction.js.map +1 -1
  14. package/lib/families/bitcoin/datasets/bitcoin.d.ts.map +1 -1
  15. package/lib/families/bitcoin/datasets/bitcoin.js +0 -3
  16. package/lib/families/bitcoin/datasets/bitcoin.js.map +1 -1
  17. package/lib/families/bitcoin/datasets/digibyte.d.ts.map +1 -1
  18. package/lib/families/bitcoin/datasets/digibyte.js +0 -3
  19. package/lib/families/bitcoin/datasets/digibyte.js.map +1 -1
  20. package/lib/families/bitcoin/datasets/litecoin.d.ts.map +1 -1
  21. package/lib/families/bitcoin/datasets/litecoin.js +0 -3
  22. package/lib/families/bitcoin/datasets/litecoin.js.map +1 -1
  23. package/lib/families/bitcoin/js-buildTransaction.d.ts.map +1 -1
  24. package/lib/families/bitcoin/js-buildTransaction.js +1 -1
  25. package/lib/families/bitcoin/js-buildTransaction.js.map +1 -1
  26. package/lib/families/bitcoin/js-createTransaction.d.ts.map +1 -1
  27. package/lib/families/bitcoin/js-createTransaction.js +0 -1
  28. package/lib/families/bitcoin/js-createTransaction.js.map +1 -1
  29. package/lib/families/bitcoin/js-estimateMaxSpendable.d.ts.map +1 -1
  30. package/lib/families/bitcoin/js-estimateMaxSpendable.js +7 -7
  31. package/lib/families/bitcoin/js-estimateMaxSpendable.js.map +1 -1
  32. package/lib/families/bitcoin/js-synchronisation.d.ts.map +1 -1
  33. package/lib/families/bitcoin/js-synchronisation.js +3 -3
  34. package/lib/families/bitcoin/js-synchronisation.js.map +1 -1
  35. package/lib/families/bitcoin/logic.d.ts +1 -1
  36. package/lib/families/bitcoin/logic.d.ts.map +1 -1
  37. package/lib/families/bitcoin/logic.js +3 -8
  38. package/lib/families/bitcoin/logic.js.map +1 -1
  39. package/lib/families/bitcoin/specs.d.ts.map +1 -1
  40. package/lib/families/bitcoin/specs.js +1 -1
  41. package/lib/families/bitcoin/specs.js.map +1 -1
  42. package/lib/families/bitcoin/transaction.js +2 -2
  43. package/lib/families/bitcoin/transaction.js.map +1 -1
  44. package/lib/families/bitcoin/types.d.ts +0 -1
  45. package/lib/families/bitcoin/types.d.ts.map +1 -1
  46. package/lib/families/bitcoin/wallet-btc/wallet.d.ts +1 -1
  47. package/lib/families/bitcoin/wallet-btc/wallet.d.ts.map +1 -1
  48. package/lib/families/bitcoin/wallet-btc/wallet.js +9 -4
  49. package/lib/families/bitcoin/wallet-btc/wallet.js.map +1 -1
  50. package/lib/families/ethereum/modules/erc1155.d.ts.map +1 -1
  51. package/lib/families/ethereum/modules/erc1155.js +5 -1
  52. package/lib/families/ethereum/modules/erc1155.js.map +1 -1
  53. package/lib/families/ethereum/modules/erc721.d.ts.map +1 -1
  54. package/lib/families/ethereum/modules/erc721.js +5 -0
  55. package/lib/families/ethereum/modules/erc721.js.map +1 -1
  56. package/lib/families/ethereum/nft.unit.test.js +35 -15
  57. package/lib/families/ethereum/nft.unit.test.js.map +1 -1
  58. package/lib/families/ethereum/synchronisation.d.ts.map +1 -1
  59. package/lib/families/ethereum/synchronisation.js +17 -13
  60. package/lib/families/ethereum/synchronisation.js.map +1 -1
  61. package/lib/families/solana/api/chain/account/token.d.ts +1 -1
  62. package/lib/families/solana/api/chain/account/vote.d.ts +12 -12
  63. package/lib/families/solana/api/chain/instruction/system/types.d.ts +4 -4
  64. package/lib/featureFlags/defaultFeatures.d.ts.map +1 -1
  65. package/lib/featureFlags/defaultFeatures.js +3 -0
  66. package/lib/featureFlags/defaultFeatures.js.map +1 -1
  67. package/lib/featureFlags/types.d.ts +1 -1
  68. package/lib/featureFlags/types.d.ts.map +1 -1
  69. package/lib/generated/cli-transaction.d.ts +6 -6
  70. package/lib/nft/hook.d.ts +3 -0
  71. package/lib/nft/hook.d.ts.map +1 -0
  72. package/lib/nft/hook.js +73 -0
  73. package/lib/nft/hook.js.map +1 -0
  74. package/lib/nft/index.d.ts +1 -0
  75. package/lib/nft/index.d.ts.map +1 -1
  76. package/lib/nft/index.js +1 -0
  77. package/lib/nft/index.js.map +1 -1
  78. package/lib/nft/nftOperationId.d.ts +15 -0
  79. package/lib/nft/nftOperationId.d.ts.map +1 -0
  80. package/lib/nft/nftOperationId.js +50 -0
  81. package/lib/nft/nftOperationId.js.map +1 -0
  82. package/lib/operation.d.ts +1 -1
  83. package/lib/operation.d.ts.map +1 -1
  84. package/lib/operation.js +23 -1
  85. package/lib/operation.js.map +1 -1
  86. package/lib/types/nft.d.ts +4 -0
  87. package/lib/types/nft.d.ts.map +1 -1
  88. package/package.json +1 -1
  89. package/src/__tests__/families/bitcoin/wallet-btc/wallet.estimateMaxSpendable.test.ts +4 -12
  90. package/src/exchange/swap/getExchangeRates.test.ts +0 -1
  91. package/src/families/bitcoin/bridge/mock.ts +0 -1
  92. package/src/families/bitcoin/cache.ts +3 -3
  93. package/src/families/bitcoin/cli-transaction.ts +0 -6
  94. package/src/families/bitcoin/datasets/bitcoin.ts +0 -3
  95. package/src/families/bitcoin/datasets/digibyte.ts +0 -3
  96. package/src/families/bitcoin/datasets/litecoin.ts +0 -3
  97. package/src/families/bitcoin/js-buildTransaction.ts +0 -1
  98. package/src/families/bitcoin/js-createTransaction.ts +0 -1
  99. package/src/families/bitcoin/js-estimateMaxSpendable.ts +0 -1
  100. package/src/families/bitcoin/js-synchronisation.ts +6 -3
  101. package/src/families/bitcoin/logic.ts +4 -9
  102. package/src/families/bitcoin/specs.ts +0 -1
  103. package/src/families/bitcoin/transaction.ts +2 -2
  104. package/src/families/bitcoin/types.ts +0 -1
  105. package/src/families/bitcoin/wallet-btc/wallet.ts +8 -2
  106. package/src/families/ethereum/modules/erc1155.ts +12 -5
  107. package/src/families/ethereum/modules/erc721.ts +11 -0
  108. package/src/families/ethereum/nft.unit.test.ts +24 -17
  109. package/src/families/ethereum/synchronisation.ts +22 -18
  110. package/src/featureFlags/defaultFeatures.ts +3 -0
  111. package/src/featureFlags/types.ts +1 -0
  112. package/src/nft/hook.ts +29 -0
  113. package/src/nft/index.ts +1 -0
  114. package/src/nft/nftOperationId.ts +47 -0
  115. package/src/operation.ts +50 -1
  116. package/src/types/nft.ts +5 -0
@@ -60,7 +60,6 @@ const transaction: Transaction = {
60
60
  family: "bitcoin",
61
61
  utxoStrategy: {
62
62
  strategy: 0,
63
- pickUnconfirmedRBF: false,
64
63
  excludeUTXOs: [],
65
64
  },
66
65
  rbf: true,
@@ -32,7 +32,6 @@ const createTransaction = (): Transaction => ({
32
32
  rbf: false,
33
33
  utxoStrategy: {
34
34
  strategy: 0,
35
- pickUnconfirmedRBF: false,
36
35
  excludeUTXOs: [],
37
36
  },
38
37
  });
@@ -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.pickUnconfirmedRBF ? 1 : 0
20
- }_${t.utxoStrategy.strategy}_${String(t.rbf)}_${t.utxoStrategy.excludeUTXOs
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);
@@ -12,7 +12,6 @@ const createTransaction = (): Transaction => {
12
12
  amount: new BigNumber(0),
13
13
  utxoStrategy: {
14
14
  strategy: 0,
15
- pickUnconfirmedRBF: false,
16
15
  excludeUTXOs: [],
17
16
  },
18
17
  recipient: "",
@@ -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 = (utxo: WalletOutput): BitcoinOutput => {
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: false, // wallet-btc limitation: doesn't provide it
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: "pickUnconfirmedRBF" | "pickPendingNonRBF" | "userExclusion";
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 (!utxoStrategy.pickUnconfirmedRBF && utxo.rbf && !utxo.blockHeight) {
80
+ if (!utxo.blockHeight && !utxo.isChange) {
81
+ // exclude pending and not change utxo
81
82
  return {
82
83
  excluded: true,
83
- reason: "pickUnconfirmedRBF",
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 (
@@ -248,7 +248,6 @@ const bitcoinLikeMutations = ({
248
248
  {
249
249
  utxoStrategy: {
250
250
  ...transaction.utxoStrategy,
251
- pickUnconfirmedRBF: true,
252
251
  },
253
252
  },
254
253
  {
@@ -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, pickUnconfirmedRBF } = t.utxoStrategy;
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
- pickUnconfirmedRBF && "pick-unconfirmed",
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
- if ((pickUnconfirmedRBF && utxo.rbf) || utxo.block_height !== null) {
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.extra = {
128
- ...op.extra,
129
- approving: true, // workaround to track the status ENABLING
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
- type: Operation["type"],
96
- value: string | number,
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 id = encodeAccountId({
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(Date.now() + dateOrder ?? 0);
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: id,
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
- makeNftOperation("NFT_OUT", 5, 0), // lazy mint sending 5 NFT
137
- makeNftOperation("NFT_IN", 1, 1), // receiving 1 of them back
138
- makeNftOperation("NFT_IN", 2, 2), // receiving 2 of them back
139
- makeNftOperation("NFT_OUT", 2, 3), // lazy mint sending 5 NFT (transformed by OpenSea in 2 txs) 1/2 (off-chain)
140
- makeNftOperation("NFT_OUT", 3, 4), // lazy mint sending 5 NFT (transformed by OpenSea in 2 txs) 2/2 (on-chain)
141
- makeNftOperation("NFT_IN", 1, 5), // receiving 1 back
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(operations).filter((n) => n.amount.gt(0))
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: `${accountId}-${hash}-${type}`,
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: `${accountId}-${hash}-${type}`,
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: `${nftId}-${hash}-${type}`,
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: `${nftId}-${hash}-${type}`,
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: `${nftId}-${hash}-${type}-i${i}_${j}`,
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: `${nftId}-${hash}-${type}-i${i}_${j}`,
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: `${id}-${hash}-${type}`,
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: `${id}-${hash}-IN`,
509
+ id: encodeOperationId(id, hash, type),
507
510
  hash: hash,
508
- type: "IN",
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: `${id}-${hash}-NONE`,
539
+ id: encodeOperationId(id, hash, type),
536
540
  hash: hash,
537
- type: "NONE",
541
+ type,
538
542
  value: new BigNumber(0),
539
543
  fee,
540
544
  blockHeight,
@@ -61,4 +61,7 @@ export const defaultFeatures: DefaultFeatures = {
61
61
  support_email: "support@ledger.com",
62
62
  },
63
63
  },
64
+ counterValue: {
65
+ enabled: false,
66
+ },
64
67
  };
@@ -4,6 +4,7 @@ export type FeatureId =
4
4
  | "pushNotifications"
5
5
  | "llmUsbFirmwareUpdate"
6
6
  | "ratings"
7
+ | "counterValue"
7
8
  | "buyDeviceFromLive"
8
9
  | string;
9
10
 
@@ -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
@@ -1,4 +1,5 @@
1
1
  export * from "./nftId";
2
2
  export * from "./helpers";
3
3
  export * from "./support";
4
+ export * from "./hook";
4
5
  export * from "./NftMetadataProvider";
@@ -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
+ }