@ledgerhq/live-common 21.14.1 → 21.16.2
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/utils.test.js +85 -0
- package/lib/__tests__/families/bitcoin/wallet-btc/utils.test.js.map +1 -1
- package/lib/__tests__/families/bitcoin/wallet-btc/wallet.estimateMaxSpendable.test.js +1 -1
- package/lib/__tests__/families/bitcoin/wallet-btc/wallet.estimateMaxSpendable.test.js.map +1 -1
- package/lib/__tests__/families/bitcoin/wallet-btc/wallet.integration.test.js +2 -2
- package/lib/__tests__/families/bitcoin/wallet-btc/wallet.integration.test.js.map +1 -1
- package/lib/__tests__/families/bitcoin/wallet-btc/xpub.pickingStrategies.integration.test.js +20 -10
- package/lib/__tests__/families/bitcoin/wallet-btc/xpub.pickingStrategies.integration.test.js.map +1 -1
- package/lib/__tests__/families/bitcoin/wallet-btc/xpub.txs.NP2WPKH.integration.test.js +1 -1
- package/lib/__tests__/families/bitcoin/wallet-btc/xpub.txs.NP2WPKH.integration.test.js.map +1 -1
- package/lib/__tests__/families/bitcoin/wallet-btc/xpub.txs.P2WPKH.integration.test.js +1 -1
- package/lib/__tests__/families/bitcoin/wallet-btc/xpub.txs.P2WPKH.integration.test.js.map +1 -1
- package/lib/__tests__/families/bitcoin/wallet-btc/xpub.txs.integration.test.js +2 -2
- package/lib/__tests__/families/bitcoin/wallet-btc/xpub.txs.integration.test.js.map +1 -1
- package/lib/account/formatters.d.ts.map +1 -1
- package/lib/account/formatters.js +33 -1
- package/lib/account/formatters.js.map +1 -1
- package/lib/account/groupOperations.js +1 -1
- package/lib/account/groupOperations.js.map +1 -1
- package/lib/account/pending.d.ts +1 -0
- package/lib/account/pending.d.ts.map +1 -1
- package/lib/account/serialization.d.ts +6 -4
- package/lib/account/serialization.d.ts.map +1 -1
- package/lib/account/serialization.js +47 -9
- package/lib/account/serialization.js.map +1 -1
- package/lib/api/Ethereum.d.ts +22 -1
- package/lib/api/Ethereum.d.ts.map +1 -1
- package/lib/api/Ethereum.js +21 -1
- package/lib/api/Ethereum.js.map +1 -1
- package/lib/apps/polyfill.d.ts.map +1 -1
- package/lib/apps/polyfill.js +3 -0
- package/lib/apps/polyfill.js.map +1 -1
- package/lib/bridge/jsHelpers.d.ts +2 -1
- package/lib/bridge/jsHelpers.d.ts.map +1 -1
- package/lib/bridge/jsHelpers.js +27 -1
- package/lib/bridge/jsHelpers.js.map +1 -1
- package/lib/csvExport.d.ts.map +1 -1
- package/lib/csvExport.js +3 -1
- package/lib/csvExport.js.map +1 -1
- package/lib/currencies/sortByMarketcap.d.ts.map +1 -1
- package/lib/currencies/sortByMarketcap.js +8 -1
- package/lib/currencies/sortByMarketcap.js.map +1 -1
- package/lib/currencies/support.d.ts.map +1 -1
- package/lib/currencies/support.js.map +1 -1
- package/lib/env.d.ts +15 -5
- package/lib/env.d.ts.map +1 -1
- package/lib/env.js +11 -0
- package/lib/env.js.map +1 -1
- package/lib/env.react.d.ts +1 -1
- package/lib/env.react.d.ts.map +1 -1
- package/lib/families/bitcoin/datasets/bitcoin.d.ts.map +1 -1
- package/lib/families/bitcoin/datasets/bitcoin.js +4 -0
- package/lib/families/bitcoin/datasets/bitcoin.js.map +1 -1
- package/lib/families/bitcoin/datasets/bitcoin_cash.d.ts.map +1 -1
- package/lib/families/bitcoin/datasets/bitcoin_cash.js +4 -0
- package/lib/families/bitcoin/datasets/bitcoin_cash.js.map +1 -1
- package/lib/families/bitcoin/datasets/bitcoin_gold.d.ts.map +1 -1
- package/lib/families/bitcoin/datasets/bitcoin_gold.js +4 -0
- package/lib/families/bitcoin/datasets/bitcoin_gold.js.map +1 -1
- package/lib/families/bitcoin/datasets/dash.d.ts.map +1 -1
- package/lib/families/bitcoin/datasets/dash.js +4 -0
- package/lib/families/bitcoin/datasets/dash.js.map +1 -1
- package/lib/families/bitcoin/datasets/decred.d.ts.map +1 -1
- package/lib/families/bitcoin/datasets/decred.js +4 -0
- package/lib/families/bitcoin/datasets/decred.js.map +1 -1
- package/lib/families/bitcoin/datasets/digibyte.d.ts.map +1 -1
- package/lib/families/bitcoin/datasets/digibyte.js +4 -0
- package/lib/families/bitcoin/datasets/digibyte.js.map +1 -1
- package/lib/families/bitcoin/datasets/dogecoin.d.ts.map +1 -1
- package/lib/families/bitcoin/datasets/dogecoin.js +4 -0
- package/lib/families/bitcoin/datasets/dogecoin.js.map +1 -1
- package/lib/families/bitcoin/datasets/komodo.d.ts.map +1 -1
- package/lib/families/bitcoin/datasets/komodo.js +4 -0
- package/lib/families/bitcoin/datasets/komodo.js.map +1 -1
- package/lib/families/bitcoin/datasets/litecoin.d.ts.map +1 -1
- package/lib/families/bitcoin/datasets/litecoin.js +4 -0
- package/lib/families/bitcoin/datasets/litecoin.js.map +1 -1
- package/lib/families/bitcoin/datasets/peercoin.d.ts.map +1 -1
- package/lib/families/bitcoin/datasets/peercoin.js +4 -0
- package/lib/families/bitcoin/datasets/peercoin.js.map +1 -1
- package/lib/families/bitcoin/datasets/pivx.d.ts.map +1 -1
- package/lib/families/bitcoin/datasets/pivx.js +4 -0
- package/lib/families/bitcoin/datasets/pivx.js.map +1 -1
- package/lib/families/bitcoin/datasets/qtum.d.ts.map +1 -1
- package/lib/families/bitcoin/datasets/qtum.js +4 -0
- package/lib/families/bitcoin/datasets/qtum.js.map +1 -1
- package/lib/families/bitcoin/datasets/stakenet.d.ts.map +1 -1
- package/lib/families/bitcoin/datasets/stakenet.js +4 -0
- package/lib/families/bitcoin/datasets/stakenet.js.map +1 -1
- package/lib/families/bitcoin/datasets/vertcoin.d.ts.map +1 -1
- package/lib/families/bitcoin/datasets/vertcoin.js +4 -0
- package/lib/families/bitcoin/datasets/vertcoin.js.map +1 -1
- package/lib/families/bitcoin/datasets/viacoin.d.ts.map +1 -1
- package/lib/families/bitcoin/datasets/viacoin.js +4 -0
- package/lib/families/bitcoin/datasets/viacoin.js.map +1 -1
- package/lib/families/bitcoin/datasets/zcash.d.ts.map +1 -1
- package/lib/families/bitcoin/datasets/zcash.js +4 -0
- package/lib/families/bitcoin/datasets/zcash.js.map +1 -1
- package/lib/families/bitcoin/datasets/zencash.d.ts.map +1 -1
- package/lib/families/bitcoin/datasets/zencash.js +4 -0
- package/lib/families/bitcoin/datasets/zencash.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-estimateMaxSpendable.d.ts.map +1 -1
- package/lib/families/bitcoin/js-estimateMaxSpendable.js +1 -1
- 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 +26 -14
- 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 +6 -0
- 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 +2 -1
- package/lib/families/bitcoin/specs.js.map +1 -1
- package/lib/families/bitcoin/test-dataset.js +2 -2
- package/lib/families/bitcoin/test-dataset.js.map +1 -1
- package/lib/families/bitcoin/wallet-btc/pickingstrategies/CoinSelect.d.ts +2 -1
- package/lib/families/bitcoin/wallet-btc/pickingstrategies/CoinSelect.d.ts.map +1 -1
- package/lib/families/bitcoin/wallet-btc/pickingstrategies/CoinSelect.js +16 -12
- package/lib/families/bitcoin/wallet-btc/pickingstrategies/CoinSelect.js.map +1 -1
- package/lib/families/bitcoin/wallet-btc/pickingstrategies/DeepFirst.d.ts +2 -1
- package/lib/families/bitcoin/wallet-btc/pickingstrategies/DeepFirst.d.ts.map +1 -1
- package/lib/families/bitcoin/wallet-btc/pickingstrategies/DeepFirst.js +12 -7
- package/lib/families/bitcoin/wallet-btc/pickingstrategies/DeepFirst.js.map +1 -1
- package/lib/families/bitcoin/wallet-btc/pickingstrategies/Merge.d.ts +2 -1
- package/lib/families/bitcoin/wallet-btc/pickingstrategies/Merge.d.ts.map +1 -1
- package/lib/families/bitcoin/wallet-btc/pickingstrategies/Merge.js +12 -7
- package/lib/families/bitcoin/wallet-btc/pickingstrategies/Merge.js.map +1 -1
- package/lib/families/bitcoin/wallet-btc/pickingstrategies/types.d.ts +2 -1
- package/lib/families/bitcoin/wallet-btc/pickingstrategies/types.d.ts.map +1 -1
- package/lib/families/bitcoin/wallet-btc/pickingstrategies/types.js.map +1 -1
- package/lib/families/bitcoin/wallet-btc/utils.d.ts +38 -1
- package/lib/families/bitcoin/wallet-btc/utils.d.ts.map +1 -1
- package/lib/families/bitcoin/wallet-btc/utils.js +104 -32
- package/lib/families/bitcoin/wallet-btc/utils.js.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 +6 -3
- package/lib/families/bitcoin/wallet-btc/wallet.js.map +1 -1
- package/lib/families/bitcoin/wallet-btc/xpub.js +2 -2
- package/lib/families/bitcoin/wallet-btc/xpub.js.map +1 -1
- package/lib/families/ethereum/signOperation.d.ts.map +1 -1
- package/lib/families/ethereum/signOperation.js +7 -0
- package/lib/families/ethereum/signOperation.js.map +1 -1
- package/lib/families/ethereum/synchronisation.d.ts.map +1 -1
- package/lib/families/ethereum/synchronisation.js +175 -15
- package/lib/families/ethereum/synchronisation.js.map +1 -1
- package/lib/families/ethereum/versions.d.ts +2 -0
- package/lib/families/ethereum/versions.d.ts.map +1 -0
- package/lib/families/ethereum/versions.js +5 -0
- package/lib/families/ethereum/versions.js.map +1 -0
- package/lib/nft/NftMetadataProvider/index.d.ts +17 -0
- package/lib/nft/NftMetadataProvider/index.d.ts.map +1 -0
- package/lib/nft/NftMetadataProvider/index.js +244 -0
- package/lib/nft/NftMetadataProvider/index.js.map +1 -0
- package/lib/nft/NftMetadataProvider/logic.d.ts +3 -0
- package/lib/nft/NftMetadataProvider/logic.d.ts.map +1 -0
- package/lib/nft/NftMetadataProvider/logic.js +22 -0
- package/lib/nft/NftMetadataProvider/logic.js.map +1 -0
- package/lib/nft/NftMetadataProvider/types.d.ts +44 -0
- package/lib/nft/NftMetadataProvider/types.d.ts.map +1 -0
- package/lib/nft/NftMetadataProvider/types.js +3 -0
- package/lib/nft/NftMetadataProvider/types.js.map +1 -0
- package/lib/nft/helpers.d.ts +10 -0
- package/lib/nft/helpers.d.ts.map +1 -0
- package/lib/nft/helpers.js +85 -0
- package/lib/nft/helpers.js.map +1 -0
- package/lib/nft/index.d.ts +4 -0
- package/lib/nft/index.d.ts.map +1 -0
- package/lib/nft/index.js +16 -0
- package/lib/nft/index.js.map +1 -0
- package/lib/nft/nftId.d.ts +3 -0
- package/lib/nft/nftId.d.ts.map +1 -0
- package/lib/nft/nftId.js +33 -0
- package/lib/nft/nftId.js.map +1 -0
- package/lib/operation.d.ts +1 -1
- package/lib/operation.d.ts.map +1 -1
- package/lib/operation.js +19 -5
- package/lib/operation.js.map +1 -1
- package/lib/reconciliation.d.ts.map +1 -1
- package/lib/reconciliation.js +6 -0
- package/lib/reconciliation.js.map +1 -1
- package/lib/types/account.d.ts +3 -0
- package/lib/types/account.d.ts.map +1 -1
- package/lib/types/index.d.ts +1 -0
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/index.js +1 -0
- package/lib/types/index.js.map +1 -1
- package/lib/types/nft.d.ts +29 -0
- package/lib/types/nft.d.ts.map +1 -0
- package/lib/types/nft.js +3 -0
- package/lib/types/nft.js.map +1 -0
- package/lib/types/operation.d.ts +12 -1
- package/lib/types/operation.d.ts.map +1 -1
- package/package.json +13 -13
- package/src/.DS_Store +0 -0
- package/src/__tests__/__snapshots__/all.libcore.ts.snap +11643 -127265
- package/src/__tests__/families/bitcoin/wallet-btc/utils.test.ts +105 -0
- package/src/__tests__/families/bitcoin/wallet-btc/wallet.estimateMaxSpendable.test.ts +3 -2
- package/src/__tests__/families/bitcoin/wallet-btc/wallet.integration.test.ts +2 -2
- package/src/__tests__/families/bitcoin/wallet-btc/xpub.pickingStrategies.integration.test.ts +35 -31
- package/src/__tests__/families/bitcoin/wallet-btc/xpub.txs.NP2WPKH.integration.test.ts +3 -2
- package/src/__tests__/families/bitcoin/wallet-btc/xpub.txs.P2WPKH.integration.test.ts +3 -2
- package/src/__tests__/families/bitcoin/wallet-btc/xpub.txs.integration.test.ts +6 -4
- package/src/account/formatters.ts +43 -0
- package/src/account/groupOperations.ts +2 -2
- package/src/account/serialization.ts +52 -0
- package/src/api/Ethereum.ts +40 -2
- package/src/apps/polyfill.ts +3 -0
- package/src/bridge/jsHelpers.ts +35 -0
- package/src/csvExport.ts +3 -2
- package/src/currencies/.DS_Store +0 -0
- package/src/currencies/__snapshots__/sortByMarketcap.test.ts.snap +1354 -1
- package/src/currencies/sortByMarketcap.ts +9 -1
- package/src/currencies/support.ts +1 -0
- package/src/env.ts +11 -0
- package/src/families/bitcoin/datasets/bitcoin.ts +4 -0
- package/src/families/bitcoin/datasets/bitcoin_cash.ts +4 -0
- package/src/families/bitcoin/datasets/bitcoin_gold.ts +4 -0
- package/src/families/bitcoin/datasets/dash.ts +4 -0
- package/src/families/bitcoin/datasets/decred.ts +4 -0
- package/src/families/bitcoin/datasets/digibyte.ts +4 -0
- package/src/families/bitcoin/datasets/dogecoin.ts +4 -0
- package/src/families/bitcoin/datasets/komodo.ts +4 -0
- package/src/families/bitcoin/datasets/litecoin.ts +4 -0
- package/src/families/bitcoin/datasets/peercoin.ts +4 -0
- package/src/families/bitcoin/datasets/pivx.ts +4 -0
- package/src/families/bitcoin/datasets/qtum.ts +4 -0
- package/src/families/bitcoin/datasets/stakenet.ts +4 -0
- package/src/families/bitcoin/datasets/vertcoin.ts +4 -0
- package/src/families/bitcoin/datasets/viacoin.ts +4 -0
- package/src/families/bitcoin/datasets/zcash.ts +4 -0
- package/src/families/bitcoin/datasets/zencash.ts +4 -0
- package/src/families/bitcoin/js-buildTransaction.ts +2 -1
- package/src/families/bitcoin/js-estimateMaxSpendable.ts +2 -1
- package/src/families/bitcoin/js-synchronisation.ts +30 -16
- package/src/families/bitcoin/logic.ts +7 -2
- package/src/families/bitcoin/specs.ts +6 -1
- package/src/families/bitcoin/test-dataset.ts +2 -2
- package/src/families/bitcoin/wallet-btc/pickingstrategies/CoinSelect.ts +34 -22
- package/src/families/bitcoin/wallet-btc/pickingstrategies/DeepFirst.ts +30 -9
- package/src/families/bitcoin/wallet-btc/pickingstrategies/Merge.ts +30 -9
- package/src/families/bitcoin/wallet-btc/pickingstrategies/types.ts +3 -3
- package/src/families/bitcoin/wallet-btc/utils.ts +118 -35
- package/src/families/bitcoin/wallet-btc/wallet.ts +9 -4
- package/src/families/bitcoin/wallet-btc/xpub.ts +5 -5
- package/src/families/ethereum/modules/__snapshots__/compound.resilience.test.ts.snap +2876 -0
- package/src/families/ethereum/signOperation.ts +8 -0
- package/src/families/ethereum/synchronisation.ts +185 -6
- package/src/families/ethereum/versions.ts +1 -0
- package/src/nft/NftMetadataProvider/index.tsx +205 -0
- package/src/nft/NftMetadataProvider/logic.ts +20 -0
- package/src/nft/NftMetadataProvider/types.ts +60 -0
- package/src/nft/helpers.ts +78 -0
- package/src/nft/index.ts +3 -0
- package/src/nft/nftId.ts +17 -0
- package/src/operation.ts +22 -3
- package/src/reconciliation.ts +7 -0
- package/src/types/account.ts +4 -0
- package/src/types/index.ts +1 -0
- package/src/types/nft.ts +38 -0
- package/src/types/operation.ts +23 -1
|
@@ -13,6 +13,7 @@ import { getGasLimit, buildEthereumTx } from "./transaction";
|
|
|
13
13
|
import { apiForCurrency } from "../../api/Ethereum";
|
|
14
14
|
import { withDevice } from "../../hw/deviceAccess";
|
|
15
15
|
import { modes } from "./modules";
|
|
16
|
+
import { getEnv } from "../../env";
|
|
16
17
|
export const signOperation = ({
|
|
17
18
|
account,
|
|
18
19
|
deviceId,
|
|
@@ -61,6 +62,13 @@ export const signOperation = ({
|
|
|
61
62
|
"0x" + (tx.value.toString("hex") || "0")
|
|
62
63
|
);
|
|
63
64
|
const eth = new Eth(transport);
|
|
65
|
+
if (getEnv("NFT")) {
|
|
66
|
+
eth.setLoadConfig({
|
|
67
|
+
// FIXME drop this after LL-8001
|
|
68
|
+
nftExplorerBaseURL:
|
|
69
|
+
"https://nft.staging.aws.ledger.fr/v1/ethereum",
|
|
70
|
+
});
|
|
71
|
+
}
|
|
64
72
|
// FIXME this part is still required for compound to correctly display info on the device
|
|
65
73
|
const addrs =
|
|
66
74
|
(fillTransactionDataResult &&
|
|
@@ -4,7 +4,7 @@ import throttle from "lodash/throttle";
|
|
|
4
4
|
import flatMap from "lodash/flatMap";
|
|
5
5
|
import eip55 from "eip55";
|
|
6
6
|
import { log } from "@ledgerhq/logs";
|
|
7
|
-
import { mergeOps } from "../../bridge/jsHelpers";
|
|
7
|
+
import { mergeNfts, mergeOps } from "../../bridge/jsHelpers";
|
|
8
8
|
import type { GetAccountShape } from "../../bridge/jsHelpers";
|
|
9
9
|
import {
|
|
10
10
|
encodeTokenAccountId,
|
|
@@ -15,10 +15,14 @@ import {
|
|
|
15
15
|
} from "../../account";
|
|
16
16
|
import { listTokensForCryptoCurrency } from "../../currencies";
|
|
17
17
|
import { encodeAccountId } from "../../account";
|
|
18
|
-
import type { Operation, TokenAccount, Account } from "../../types";
|
|
18
|
+
import type { Operation, TokenAccount, Account, NFT } from "../../types";
|
|
19
19
|
import { API, apiForCurrency, Tx } from "../../api/Ethereum";
|
|
20
20
|
import { digestTokenAccounts, prepareTokenAccounts } from "./modules";
|
|
21
21
|
import { findTokenByAddressInCurrency } from "@ledgerhq/cryptoassets";
|
|
22
|
+
import { encodeNftId, nftsFromOperations } from "../../nft";
|
|
23
|
+
import { NFT_VERSION } from "./versions";
|
|
24
|
+
import { getEnv } from "../../env";
|
|
25
|
+
|
|
22
26
|
export const getAccountShape: GetAccountShape = async (
|
|
23
27
|
infoInput,
|
|
24
28
|
{ blacklistedTokenIds }
|
|
@@ -47,11 +51,15 @@ export const getAccountShape: GetAccountShape = async (
|
|
|
47
51
|
withDelisted: true,
|
|
48
52
|
}).length;
|
|
49
53
|
const outdatedBlacklist = initialAccount?.syncHash !== syncHash;
|
|
54
|
+
const firstNftSync =
|
|
55
|
+
getEnv("NFT") && typeof initialAccount?.nfts === "undefined";
|
|
50
56
|
const pullFromBlockHash =
|
|
51
57
|
initialAccount &&
|
|
52
58
|
areAllOperationsLoaded(initialAccount) &&
|
|
53
59
|
mostRecentStableOperation &&
|
|
54
|
-
!outdatedBlacklist
|
|
60
|
+
!outdatedBlacklist &&
|
|
61
|
+
!firstNftSync &&
|
|
62
|
+
NFT_VERSION
|
|
55
63
|
? mostRecentStableOperation.blockHash
|
|
56
64
|
: undefined;
|
|
57
65
|
const txsP = fetchAllTransactions(api, address, pullFromBlockHash);
|
|
@@ -75,8 +83,10 @@ export const getAccountShape: GetAccountShape = async (
|
|
|
75
83
|
let newOps = flatMap(txs, txToOps({ address, id: accountId, currency }));
|
|
76
84
|
// extracting out the sub operations by token account
|
|
77
85
|
const perTokenAccountIdOperations = {};
|
|
86
|
+
// extracting and concat all nft operations for an account
|
|
87
|
+
const flatNftOps: Operation[] = [];
|
|
78
88
|
newOps.forEach((op) => {
|
|
79
|
-
const { subOperations } = op;
|
|
89
|
+
const { subOperations, nftOperations } = op;
|
|
80
90
|
|
|
81
91
|
if (subOperations?.length) {
|
|
82
92
|
subOperations.forEach((sop) => {
|
|
@@ -87,6 +97,10 @@ export const getAccountShape: GetAccountShape = async (
|
|
|
87
97
|
perTokenAccountIdOperations[sop.accountId].push(sop);
|
|
88
98
|
});
|
|
89
99
|
}
|
|
100
|
+
|
|
101
|
+
if (nftOperations?.length) {
|
|
102
|
+
nftOperations.forEach((nop) => flatNftOps.push(nop));
|
|
103
|
+
}
|
|
90
104
|
});
|
|
91
105
|
const subAccountsExisting = {};
|
|
92
106
|
initialAccount?.subAccounts?.forEach((a) => {
|
|
@@ -158,6 +172,11 @@ export const getAccountShape: GetAccountShape = async (
|
|
|
158
172
|
subOperations: inferSubOperations(o.hash, subAccounts),
|
|
159
173
|
}));
|
|
160
174
|
const operations = mergeOps(initialStableOperations, newOps);
|
|
175
|
+
|
|
176
|
+
const nfts = getEnv("NFT")
|
|
177
|
+
? mergeNfts(initialAccount?.nfts, await getNfts(flatNftOps))
|
|
178
|
+
: undefined;
|
|
179
|
+
|
|
161
180
|
const accountShape: Partial<Account> = {
|
|
162
181
|
id: accountId,
|
|
163
182
|
operations,
|
|
@@ -168,6 +187,7 @@ export const getAccountShape: GetAccountShape = async (
|
|
|
168
187
|
lastSyncDate: new Date(),
|
|
169
188
|
balanceHistory: undefined,
|
|
170
189
|
syncHash,
|
|
190
|
+
nfts,
|
|
171
191
|
};
|
|
172
192
|
return accountShape;
|
|
173
193
|
};
|
|
@@ -188,7 +208,14 @@ const txToOps =
|
|
|
188
208
|
(tx: Tx): Operation[] => {
|
|
189
209
|
// workaround bugs in our explorer that don't treat partial/optimistic operation really well
|
|
190
210
|
if (!tx.gas_used) return [];
|
|
191
|
-
const {
|
|
211
|
+
const {
|
|
212
|
+
hash,
|
|
213
|
+
block,
|
|
214
|
+
actions,
|
|
215
|
+
transfer_events,
|
|
216
|
+
erc721_transfer_events,
|
|
217
|
+
erc1155_transfer_events,
|
|
218
|
+
} = tx;
|
|
192
219
|
const addr = address;
|
|
193
220
|
const from = safeEncodeEIP55(tx.from);
|
|
194
221
|
const to = safeEncodeEIP55(tx.to);
|
|
@@ -305,6 +332,148 @@ const txToOps =
|
|
|
305
332
|
|
|
306
333
|
return all;
|
|
307
334
|
});
|
|
335
|
+
|
|
336
|
+
// Creating NFTOps from transfer events related to ERC721 only
|
|
337
|
+
const erc721Operations = !erc721_transfer_events
|
|
338
|
+
? []
|
|
339
|
+
: flatMap(erc721_transfer_events, (event) => {
|
|
340
|
+
const sender = safeEncodeEIP55(event.sender);
|
|
341
|
+
const receiver = safeEncodeEIP55(event.receiver);
|
|
342
|
+
const contract = safeEncodeEIP55(event.contract);
|
|
343
|
+
const tokenId = event.token_id;
|
|
344
|
+
const nftId = encodeNftId(id, event.contract, tokenId);
|
|
345
|
+
const sending = addr === sender;
|
|
346
|
+
const receiving = addr === receiver;
|
|
347
|
+
|
|
348
|
+
if (!sending && !receiving) {
|
|
349
|
+
return [];
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
const all: Operation[] = [];
|
|
353
|
+
|
|
354
|
+
if (sending) {
|
|
355
|
+
const type = "NFT_OUT";
|
|
356
|
+
all.push({
|
|
357
|
+
id: `${nftId}-${hash}-${type}`,
|
|
358
|
+
senders: [sender],
|
|
359
|
+
recipients: [receiver],
|
|
360
|
+
contract,
|
|
361
|
+
fee,
|
|
362
|
+
standard: "ERC721",
|
|
363
|
+
tokenId,
|
|
364
|
+
value: new BigNumber(1),
|
|
365
|
+
hash,
|
|
366
|
+
type,
|
|
367
|
+
blockHeight,
|
|
368
|
+
blockHash,
|
|
369
|
+
date,
|
|
370
|
+
transactionSequenceNumber,
|
|
371
|
+
accountId: id,
|
|
372
|
+
extra: {},
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
if (receiving) {
|
|
377
|
+
const type = "NFT_IN";
|
|
378
|
+
all.push({
|
|
379
|
+
id: `${nftId}-${hash}-${type}`,
|
|
380
|
+
senders: [sender],
|
|
381
|
+
recipients: [receiver],
|
|
382
|
+
contract,
|
|
383
|
+
fee,
|
|
384
|
+
standard: "ERC721",
|
|
385
|
+
tokenId,
|
|
386
|
+
value: new BigNumber(1),
|
|
387
|
+
hash,
|
|
388
|
+
type,
|
|
389
|
+
blockHeight,
|
|
390
|
+
blockHash,
|
|
391
|
+
date,
|
|
392
|
+
transactionSequenceNumber,
|
|
393
|
+
accountId: id,
|
|
394
|
+
extra: {},
|
|
395
|
+
});
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
return all;
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
// Creating NFTOps from transfer events related to ERC1155 only
|
|
402
|
+
const erc1155Operations = !erc1155_transfer_events
|
|
403
|
+
? []
|
|
404
|
+
: flatMap(erc1155_transfer_events, (event) => {
|
|
405
|
+
const sender = safeEncodeEIP55(event.sender);
|
|
406
|
+
const receiver = safeEncodeEIP55(event.receiver);
|
|
407
|
+
const contract = safeEncodeEIP55(event.contract);
|
|
408
|
+
const operator = safeEncodeEIP55(event.operator);
|
|
409
|
+
const sending = addr === sender;
|
|
410
|
+
const receiving = addr === receiver;
|
|
411
|
+
|
|
412
|
+
if (!sending && !receiving) {
|
|
413
|
+
return [];
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
const all: Operation[] = [];
|
|
417
|
+
|
|
418
|
+
event.transfers.forEach((transfer) => {
|
|
419
|
+
const tokenId = transfer.id;
|
|
420
|
+
const value = new BigNumber(transfer.value);
|
|
421
|
+
const nftId = encodeNftId(id, event.contract, tokenId);
|
|
422
|
+
|
|
423
|
+
if (sending) {
|
|
424
|
+
const type = "NFT_OUT";
|
|
425
|
+
all.push({
|
|
426
|
+
id: `${nftId}-${hash}-${type}`,
|
|
427
|
+
senders: [sender],
|
|
428
|
+
recipients: [receiver],
|
|
429
|
+
contract,
|
|
430
|
+
fee,
|
|
431
|
+
operator,
|
|
432
|
+
standard: "ERC1155",
|
|
433
|
+
tokenId,
|
|
434
|
+
value,
|
|
435
|
+
hash,
|
|
436
|
+
type,
|
|
437
|
+
blockHeight,
|
|
438
|
+
blockHash,
|
|
439
|
+
date,
|
|
440
|
+
transactionSequenceNumber,
|
|
441
|
+
accountId: id,
|
|
442
|
+
extra: {},
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
if (receiving) {
|
|
447
|
+
const type = "NFT_IN";
|
|
448
|
+
all.push({
|
|
449
|
+
id: `${nftId}-${hash}-${type}`,
|
|
450
|
+
senders: [sender],
|
|
451
|
+
recipients: [receiver],
|
|
452
|
+
contract,
|
|
453
|
+
fee,
|
|
454
|
+
operator,
|
|
455
|
+
standard: "ERC1155",
|
|
456
|
+
tokenId,
|
|
457
|
+
value,
|
|
458
|
+
hash,
|
|
459
|
+
type,
|
|
460
|
+
blockHeight,
|
|
461
|
+
blockHash,
|
|
462
|
+
date,
|
|
463
|
+
transactionSequenceNumber,
|
|
464
|
+
accountId: id,
|
|
465
|
+
extra: {},
|
|
466
|
+
});
|
|
467
|
+
}
|
|
468
|
+
});
|
|
469
|
+
|
|
470
|
+
return all;
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
const nftOperations = erc721Operations
|
|
474
|
+
.concat(erc1155Operations)
|
|
475
|
+
/** @warning is this necessary ? Do we need the operations to be chronologically organised for LLD/LLM ? */
|
|
476
|
+
.sort((a, b) => b.date.getTime() - a.date.getTime());
|
|
308
477
|
const ops: Operation[] = [];
|
|
309
478
|
|
|
310
479
|
if (sending) {
|
|
@@ -325,6 +494,7 @@ const txToOps =
|
|
|
325
494
|
hasFailed,
|
|
326
495
|
internalOperations: internalOperations,
|
|
327
496
|
subOperations,
|
|
497
|
+
nftOperations,
|
|
328
498
|
transactionSequenceNumber,
|
|
329
499
|
});
|
|
330
500
|
}
|
|
@@ -347,6 +517,7 @@ const txToOps =
|
|
|
347
517
|
internalOperations: sending ? [] : internalOperations,
|
|
348
518
|
// if it was already in sending, we don't add twice
|
|
349
519
|
subOperations: sending ? [] : subOperations,
|
|
520
|
+
nftOperations,
|
|
350
521
|
transactionSequenceNumber,
|
|
351
522
|
});
|
|
352
523
|
}
|
|
@@ -354,7 +525,9 @@ const txToOps =
|
|
|
354
525
|
if (
|
|
355
526
|
!sending &&
|
|
356
527
|
!receiving &&
|
|
357
|
-
(internalOperations.length ||
|
|
528
|
+
(internalOperations.length ||
|
|
529
|
+
subOperations.length ||
|
|
530
|
+
nftOperations.length)
|
|
358
531
|
) {
|
|
359
532
|
ops.push({
|
|
360
533
|
id: `${id}-${hash}-NONE`,
|
|
@@ -371,6 +544,7 @@ const txToOps =
|
|
|
371
544
|
extra: {},
|
|
372
545
|
internalOperations,
|
|
373
546
|
subOperations,
|
|
547
|
+
nftOperations,
|
|
374
548
|
transactionSequenceNumber,
|
|
375
549
|
});
|
|
376
550
|
}
|
|
@@ -445,6 +619,11 @@ async function loadERC20Balances(tokenAccounts, address, api) {
|
|
|
445
619
|
.filter(Boolean);
|
|
446
620
|
}
|
|
447
621
|
|
|
622
|
+
function getNfts(nftOperations: Operation[]): NFT[] {
|
|
623
|
+
const nfts: NFT[] = nftsFromOperations(nftOperations);
|
|
624
|
+
return nfts.filter((nft) => nft.amount.gt(0));
|
|
625
|
+
}
|
|
626
|
+
|
|
448
627
|
const SAFE_REORG_THRESHOLD = 80;
|
|
449
628
|
|
|
450
629
|
function stableOperations(a) {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const NFT_VERSION = 1;
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import React, {
|
|
2
|
+
createContext,
|
|
3
|
+
useContext,
|
|
4
|
+
useMemo,
|
|
5
|
+
useState,
|
|
6
|
+
useEffect,
|
|
7
|
+
} from "react";
|
|
8
|
+
import { Currency, findCryptoCurrencyById } from "@ledgerhq/cryptoassets";
|
|
9
|
+
import { API, apiForCurrency } from "../../api/Ethereum";
|
|
10
|
+
import { NFTMetadataResponse } from "../../types";
|
|
11
|
+
import { getNftKey } from "../helpers";
|
|
12
|
+
import {
|
|
13
|
+
Batch,
|
|
14
|
+
BatchElement,
|
|
15
|
+
NFTMetadataContextAPI,
|
|
16
|
+
NFTMetadataContextState,
|
|
17
|
+
NFTMetadataContextType,
|
|
18
|
+
NFTResource,
|
|
19
|
+
} from "./types";
|
|
20
|
+
import { isOutdated } from "./logic";
|
|
21
|
+
|
|
22
|
+
const currency: Currency = findCryptoCurrencyById("ethereum")!;
|
|
23
|
+
const ethApi: API = apiForCurrency(currency);
|
|
24
|
+
|
|
25
|
+
const NftMetadataContext = createContext<NFTMetadataContextType>({
|
|
26
|
+
cache: {},
|
|
27
|
+
loadNFTMetadata: () => Promise.resolve(),
|
|
28
|
+
clearCache: () => {},
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
export const metadataCallBatcher = (() => {
|
|
32
|
+
const batch: BatchElement[] = [];
|
|
33
|
+
|
|
34
|
+
let debounce;
|
|
35
|
+
const timeoutBatchCall = () => {
|
|
36
|
+
// Clear the previous scheduled call if it was existing
|
|
37
|
+
clearTimeout(debounce);
|
|
38
|
+
|
|
39
|
+
// Schedule a new call with the whole batch
|
|
40
|
+
debounce = setTimeout(() => {
|
|
41
|
+
// Seperate each batch element properties into arrays by type and index
|
|
42
|
+
const { couples, resolvers, rejecters } = batch.reduce(
|
|
43
|
+
(acc, { couple, resolve, reject }) => {
|
|
44
|
+
acc.couples.push(couple);
|
|
45
|
+
acc.resolvers.push(resolve);
|
|
46
|
+
acc.rejecters.push(reject);
|
|
47
|
+
|
|
48
|
+
return acc;
|
|
49
|
+
},
|
|
50
|
+
{ couples: [], resolvers: [], rejecters: [] } as Batch
|
|
51
|
+
);
|
|
52
|
+
// Empty the batch
|
|
53
|
+
batch.length = 0;
|
|
54
|
+
|
|
55
|
+
// Make the call with all the couples of contract and tokenId at once
|
|
56
|
+
ethApi
|
|
57
|
+
.getNFTMetadata(couples)
|
|
58
|
+
.then((res) => {
|
|
59
|
+
// Resolve each batch element with its own resolver and only its response
|
|
60
|
+
res.forEach((metadata, index) => resolvers[index](metadata));
|
|
61
|
+
})
|
|
62
|
+
.catch((err) => {
|
|
63
|
+
// Reject all batch element with the error
|
|
64
|
+
rejecters.forEach((reject) => reject(err));
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
return {
|
|
70
|
+
// Load the metadata for a given couple contract + tokenId
|
|
71
|
+
load({ contract, tokenId }): Promise<NFTMetadataResponse> {
|
|
72
|
+
return new Promise((resolve, reject) => {
|
|
73
|
+
batch.push({ couple: { contract, tokenId }, resolve, reject });
|
|
74
|
+
timeoutBatchCall();
|
|
75
|
+
});
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
})();
|
|
79
|
+
|
|
80
|
+
export function useNftMetadata(contract: string, tokenId: string): NFTResource {
|
|
81
|
+
const { cache, loadNFTMetadata } = useContext(NftMetadataContext);
|
|
82
|
+
|
|
83
|
+
const key = getNftKey(contract, tokenId);
|
|
84
|
+
|
|
85
|
+
const cachedData = cache[key];
|
|
86
|
+
|
|
87
|
+
useEffect(() => {
|
|
88
|
+
if (!cachedData || isOutdated(cachedData)) {
|
|
89
|
+
loadNFTMetadata(contract, tokenId);
|
|
90
|
+
}
|
|
91
|
+
}, [contract, tokenId, cachedData, loadNFTMetadata]);
|
|
92
|
+
|
|
93
|
+
if (cachedData) {
|
|
94
|
+
return cachedData;
|
|
95
|
+
} else {
|
|
96
|
+
return {
|
|
97
|
+
status: "queued",
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function useNftAPI(): NFTMetadataContextAPI {
|
|
103
|
+
const { clearCache, loadNFTMetadata } = useContext(NftMetadataContext);
|
|
104
|
+
|
|
105
|
+
return {
|
|
106
|
+
clearCache,
|
|
107
|
+
loadNFTMetadata,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
type NFTMetadataProviderProps = {
|
|
112
|
+
children: React.ReactNode;
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
export function NftMetadataProvider({
|
|
116
|
+
children,
|
|
117
|
+
}: NFTMetadataProviderProps): React.ReactElement {
|
|
118
|
+
const [state, setState] = useState<NFTMetadataContextState>({
|
|
119
|
+
cache: {},
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
const api = useMemo(
|
|
123
|
+
() => ({
|
|
124
|
+
loadNFTMetadata: async (contract: string, tokenId: string) => {
|
|
125
|
+
const key = getNftKey(contract, tokenId);
|
|
126
|
+
|
|
127
|
+
setState((oldState) => ({
|
|
128
|
+
...oldState,
|
|
129
|
+
cache: {
|
|
130
|
+
...oldState.cache,
|
|
131
|
+
[key]: {
|
|
132
|
+
status: "loading",
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
}));
|
|
136
|
+
|
|
137
|
+
try {
|
|
138
|
+
const { status, result } = await metadataCallBatcher.load({
|
|
139
|
+
contract,
|
|
140
|
+
tokenId,
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
switch (status) {
|
|
144
|
+
case 500:
|
|
145
|
+
throw new Error("NFT Metadata Provider failed");
|
|
146
|
+
case 404:
|
|
147
|
+
setState((oldState) => ({
|
|
148
|
+
...oldState,
|
|
149
|
+
cache: {
|
|
150
|
+
...oldState.cache,
|
|
151
|
+
[key]: {
|
|
152
|
+
status: "nodata",
|
|
153
|
+
metadata: null,
|
|
154
|
+
updatedAt: Date.now(),
|
|
155
|
+
},
|
|
156
|
+
},
|
|
157
|
+
}));
|
|
158
|
+
break;
|
|
159
|
+
case 200:
|
|
160
|
+
setState((oldState) => ({
|
|
161
|
+
...oldState,
|
|
162
|
+
cache: {
|
|
163
|
+
...oldState.cache,
|
|
164
|
+
[key]: {
|
|
165
|
+
status: "loaded",
|
|
166
|
+
metadata: result || {},
|
|
167
|
+
updatedAt: Date.now(),
|
|
168
|
+
},
|
|
169
|
+
},
|
|
170
|
+
}));
|
|
171
|
+
break;
|
|
172
|
+
default:
|
|
173
|
+
break;
|
|
174
|
+
}
|
|
175
|
+
} catch (error) {
|
|
176
|
+
setState((oldState) => ({
|
|
177
|
+
...oldState,
|
|
178
|
+
cache: {
|
|
179
|
+
...oldState.cache,
|
|
180
|
+
[key]: {
|
|
181
|
+
status: "error",
|
|
182
|
+
error,
|
|
183
|
+
updatedAt: Date.now(),
|
|
184
|
+
},
|
|
185
|
+
},
|
|
186
|
+
}));
|
|
187
|
+
}
|
|
188
|
+
},
|
|
189
|
+
clearCache: () => {
|
|
190
|
+
setState((oldState) => ({
|
|
191
|
+
...oldState,
|
|
192
|
+
cache: {},
|
|
193
|
+
}));
|
|
194
|
+
},
|
|
195
|
+
}),
|
|
196
|
+
[]
|
|
197
|
+
);
|
|
198
|
+
|
|
199
|
+
const value = { ...state, ...api };
|
|
200
|
+
return (
|
|
201
|
+
<NftMetadataContext.Provider value={value}>
|
|
202
|
+
{children}
|
|
203
|
+
</NftMetadataContext.Provider>
|
|
204
|
+
);
|
|
205
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { NFTResource } from "./types";
|
|
2
|
+
|
|
3
|
+
// Handle lifecycle of cached data.
|
|
4
|
+
// Expiration date depend on the resource's status.
|
|
5
|
+
export function isOutdated(resource: NFTResource): boolean {
|
|
6
|
+
const now = Date.now();
|
|
7
|
+
|
|
8
|
+
switch (resource.status) {
|
|
9
|
+
case "loaded": {
|
|
10
|
+
return now - resource.updatedAt > 14 * 24 * 60 * 60 * 1 * 1000; // 14 days
|
|
11
|
+
}
|
|
12
|
+
case "error": {
|
|
13
|
+
return now - resource.updatedAt > 1 * 1000; // 1 second
|
|
14
|
+
}
|
|
15
|
+
case "nodata": {
|
|
16
|
+
return now - resource.updatedAt > 24 * 60 * 60 * 1 * 1000; // 1 day
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { NFTMetadataResponse } from "../../types";
|
|
2
|
+
|
|
3
|
+
export type NFTResourceQueued = {
|
|
4
|
+
status: "queued";
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
export type NFTResourceLoading = {
|
|
8
|
+
status: "loading";
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export type NFTResourceLoaded = {
|
|
12
|
+
status: "loaded";
|
|
13
|
+
metadata: Pick<NFTMetadataResponse, "result">;
|
|
14
|
+
updatedAt: number;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export type NFTResourceError = {
|
|
18
|
+
status: "error";
|
|
19
|
+
error: any;
|
|
20
|
+
updatedAt: number;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export type NFTResourceNoData = {
|
|
24
|
+
status: "nodata";
|
|
25
|
+
updatedAt: number;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export type NFTResource =
|
|
29
|
+
| NFTResourceQueued
|
|
30
|
+
| NFTResourceLoading
|
|
31
|
+
| NFTResourceLoaded
|
|
32
|
+
| NFTResourceError
|
|
33
|
+
| NFTResourceNoData;
|
|
34
|
+
|
|
35
|
+
export type NFTMetadataContextState = {
|
|
36
|
+
cache: Record<string, NFTResource>;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export type NFTMetadataContextAPI = {
|
|
40
|
+
loadNFTMetadata: (contract: string, tokenId: string) => Promise<void>;
|
|
41
|
+
clearCache: () => void;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export type NFTMetadataContextType = NFTMetadataContextState &
|
|
45
|
+
NFTMetadataContextAPI;
|
|
46
|
+
|
|
47
|
+
export type BatchElement = {
|
|
48
|
+
couple: {
|
|
49
|
+
contract: string;
|
|
50
|
+
tokenId: string;
|
|
51
|
+
};
|
|
52
|
+
resolve: (value: NFTMetadataResponse) => void;
|
|
53
|
+
reject: (reason?: Error) => void;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export type Batch = {
|
|
57
|
+
couples: Array<BatchElement["couple"]>;
|
|
58
|
+
resolvers: Array<BatchElement["resolve"]>;
|
|
59
|
+
rejecters: Array<BatchElement["reject"]>;
|
|
60
|
+
};
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import eip55 from "eip55";
|
|
2
|
+
import BigNumber from "bignumber.js";
|
|
3
|
+
import { NFT, Operation } from "../types";
|
|
4
|
+
|
|
5
|
+
type Collection = NFT["collection"];
|
|
6
|
+
|
|
7
|
+
type CollectionMap<C> = Record<string, C>;
|
|
8
|
+
|
|
9
|
+
export type CollectionWithNFT = Collection & {
|
|
10
|
+
nfts: Array<Omit<NFT, "collection">>;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export const nftsFromOperations = (ops: Operation[]): NFT[] => {
|
|
14
|
+
const nftsMap = ops
|
|
15
|
+
// if ops are Operations get the prop nftOperations, else ops are considered nftOperations already
|
|
16
|
+
.flatMap((op) => (op?.nftOperations?.length ? op.nftOperations : op))
|
|
17
|
+
.reduce((acc: Record<string, NFT>, nftOp: Operation) => {
|
|
18
|
+
if (!nftOp?.contract) {
|
|
19
|
+
return acc;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Creating a "token for a contract" unique key
|
|
23
|
+
const contract = eip55.encode(nftOp.contract!);
|
|
24
|
+
const nftKey = contract + nftOp.tokenId!;
|
|
25
|
+
const { tokenId, standard, id } = nftOp;
|
|
26
|
+
|
|
27
|
+
const nft = (acc[nftKey] ?? {
|
|
28
|
+
id,
|
|
29
|
+
tokenId: tokenId!,
|
|
30
|
+
amount: new BigNumber(0),
|
|
31
|
+
collection: {
|
|
32
|
+
contract,
|
|
33
|
+
standard: standard!,
|
|
34
|
+
},
|
|
35
|
+
}) as NFT;
|
|
36
|
+
|
|
37
|
+
if (nftOp.type === "NFT_IN") {
|
|
38
|
+
nft.amount = nft.amount.plus(nftOp.value);
|
|
39
|
+
} else if (nftOp.type === "NFT_OUT") {
|
|
40
|
+
nft.amount = nft.amount.minus(nftOp.value);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
acc[nftKey] = nft;
|
|
44
|
+
|
|
45
|
+
return acc;
|
|
46
|
+
}, {});
|
|
47
|
+
|
|
48
|
+
return Object.values(nftsMap);
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export const nftsByCollections = (
|
|
52
|
+
nfts: NFT[] = [],
|
|
53
|
+
collectionAddress?: string
|
|
54
|
+
): CollectionWithNFT[] => {
|
|
55
|
+
const filteredNfts = collectionAddress
|
|
56
|
+
? nfts.filter((n) => n.collection.contract === collectionAddress)
|
|
57
|
+
: nfts;
|
|
58
|
+
|
|
59
|
+
const collectionMap = filteredNfts.reduce(
|
|
60
|
+
(acc: CollectionMap<CollectionWithNFT>, nft: NFT) => {
|
|
61
|
+
const { collection, ...nftWithoutCollection } = nft;
|
|
62
|
+
|
|
63
|
+
if (!acc[collection.contract]) {
|
|
64
|
+
acc[collection.contract] = { ...collection, nfts: [] };
|
|
65
|
+
}
|
|
66
|
+
acc[collection.contract].nfts.push(nftWithoutCollection);
|
|
67
|
+
|
|
68
|
+
return acc;
|
|
69
|
+
},
|
|
70
|
+
{} as CollectionMap<CollectionWithNFT>
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
return Object.values(collectionMap);
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
export const getNftKey = (contract: string, tokenId: string): string => {
|
|
77
|
+
return `${contract}-${tokenId}`;
|
|
78
|
+
};
|
package/src/nft/index.ts
ADDED
package/src/nft/nftId.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export const encodeNftId = (
|
|
2
|
+
accountId: string,
|
|
3
|
+
contract: string,
|
|
4
|
+
tokenId: string
|
|
5
|
+
): string => {
|
|
6
|
+
return `${accountId}+${contract}+${tokenId.toString()}`;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export const decodeNftId = (id: string): unknown => {
|
|
10
|
+
const [accountId, contract, tokenId] = id.split("+");
|
|
11
|
+
|
|
12
|
+
return {
|
|
13
|
+
accountId,
|
|
14
|
+
contract,
|
|
15
|
+
tokenId,
|
|
16
|
+
};
|
|
17
|
+
};
|