thirdweb 5.95.0-nightly-64bd603301b3067b6a69a29f66a28dee7ffa38f5-20250416000349 → 5.95.0-nightly-4cf15a2475fce1c5b55d19f7cf51ab080e80e33f-20250418000341
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/dist/cjs/chains/utils.js +2 -4
- package/dist/cjs/chains/utils.js.map +1 -1
- package/dist/cjs/event/actions/get-events.js +4 -10
- package/dist/cjs/event/actions/get-events.js.map +1 -1
- package/dist/cjs/extensions/erc1155/read/getNFT.js +40 -0
- package/dist/cjs/extensions/erc1155/read/getNFT.js.map +1 -1
- package/dist/cjs/extensions/erc1155/read/getNFTs.js +26 -0
- package/dist/cjs/extensions/erc1155/read/getNFTs.js.map +1 -1
- package/dist/cjs/extensions/erc1155/read/getOwnedNFTs.js +46 -0
- package/dist/cjs/extensions/erc1155/read/getOwnedNFTs.js.map +1 -1
- package/dist/cjs/extensions/erc20/read/getBalance.js +2 -0
- package/dist/cjs/extensions/erc20/read/getBalance.js.map +1 -1
- package/dist/cjs/extensions/erc721/read/getNFT.js +42 -0
- package/dist/cjs/extensions/erc721/read/getNFT.js.map +1 -1
- package/dist/cjs/extensions/erc721/read/getNFTs.js +44 -17
- package/dist/cjs/extensions/erc721/read/getNFTs.js.map +1 -1
- package/dist/cjs/extensions/erc721/read/getOwnedNFTs.js +46 -0
- package/dist/cjs/extensions/erc721/read/getOwnedNFTs.js.map +1 -1
- package/dist/cjs/extensions/marketplace/direct-listings/utils.js +7 -4
- package/dist/cjs/extensions/marketplace/direct-listings/utils.js.map +1 -1
- package/dist/cjs/extensions/marketplace/english-auctions/utils.js +9 -4
- package/dist/cjs/extensions/marketplace/english-auctions/utils.js.map +1 -1
- package/dist/cjs/extensions/marketplace/offers/utils.js +7 -4
- package/dist/cjs/extensions/marketplace/offers/utils.js.map +1 -1
- package/dist/cjs/insight/common.js +18 -0
- package/dist/cjs/insight/common.js.map +1 -0
- package/dist/cjs/insight/get-events.js +12 -9
- package/dist/cjs/insight/get-events.js.map +1 -1
- package/dist/cjs/insight/get-nfts.js +213 -16
- package/dist/cjs/insight/get-nfts.js.map +1 -1
- package/dist/cjs/insight/get-tokens.js +34 -14
- package/dist/cjs/insight/get-tokens.js.map +1 -1
- package/dist/cjs/insight/get-transactions.js +17 -12
- package/dist/cjs/insight/get-transactions.js.map +1 -1
- package/dist/cjs/insight/index.js +3 -1
- package/dist/cjs/insight/index.js.map +1 -1
- package/dist/cjs/react/web/ui/ConnectWallet/screens/ViewNFTs.js +6 -43
- package/dist/cjs/react/web/ui/ConnectWallet/screens/ViewNFTs.js.map +1 -1
- package/dist/cjs/react/web/ui/ConnectWallet/screens/ViewTokens.js +3 -9
- package/dist/cjs/react/web/ui/ConnectWallet/screens/ViewTokens.js.map +1 -1
- package/dist/cjs/react/web/ui/components/TokenIcon.js +3 -1
- package/dist/cjs/react/web/ui/components/TokenIcon.js.map +1 -1
- package/dist/cjs/react/web/ui/prebuilt/NFT/utils.js +7 -2
- package/dist/cjs/react/web/ui/prebuilt/NFT/utils.js.map +1 -1
- package/dist/cjs/rpc/fetch-rpc.js +2 -22
- package/dist/cjs/rpc/fetch-rpc.js.map +1 -1
- package/dist/cjs/rpc/rpc.js +1 -1
- package/dist/cjs/rpc/rpc.js.map +1 -1
- package/dist/cjs/social/profiles.js +3 -8
- package/dist/cjs/social/profiles.js.map +1 -1
- package/dist/cjs/storage/download.js +2 -2
- package/dist/cjs/storage/download.js.map +1 -1
- package/dist/cjs/storage/unpin.js +2 -2
- package/dist/cjs/storage/unpin.js.map +1 -1
- package/dist/cjs/storage/upload/web-node.js +0 -1
- package/dist/cjs/storage/upload/web-node.js.map +1 -1
- package/dist/cjs/utils/nft/parseNft.js +4 -0
- package/dist/cjs/utils/nft/parseNft.js.map +1 -1
- package/dist/cjs/utils/signatures/resolve-signature.js +0 -2
- package/dist/cjs/utils/signatures/resolve-signature.js.map +1 -1
- package/dist/cjs/version.js +1 -1
- package/dist/cjs/wallets/in-app/core/users/getUser.js +2 -1
- package/dist/cjs/wallets/in-app/core/users/getUser.js.map +1 -1
- package/dist/cjs/wallets/smart/lib/calls.js +2 -2
- package/dist/cjs/wallets/smart/lib/calls.js.map +1 -1
- package/dist/cjs/wallets/utils/getWalletBalance.js +3 -0
- package/dist/cjs/wallets/utils/getWalletBalance.js.map +1 -1
- package/dist/esm/chains/utils.js +2 -4
- package/dist/esm/chains/utils.js.map +1 -1
- package/dist/esm/event/actions/get-events.js +1 -7
- package/dist/esm/event/actions/get-events.js.map +1 -1
- package/dist/esm/extensions/erc1155/read/getNFT.js +40 -0
- package/dist/esm/extensions/erc1155/read/getNFT.js.map +1 -1
- package/dist/esm/extensions/erc1155/read/getNFTs.js +26 -0
- package/dist/esm/extensions/erc1155/read/getNFTs.js.map +1 -1
- package/dist/esm/extensions/erc1155/read/getOwnedNFTs.js +46 -0
- package/dist/esm/extensions/erc1155/read/getOwnedNFTs.js.map +1 -1
- package/dist/esm/extensions/erc20/read/getBalance.js +2 -0
- package/dist/esm/extensions/erc20/read/getBalance.js.map +1 -1
- package/dist/esm/extensions/erc721/read/getNFT.js +42 -0
- package/dist/esm/extensions/erc721/read/getNFT.js.map +1 -1
- package/dist/esm/extensions/erc721/read/getNFTs.js +44 -17
- package/dist/esm/extensions/erc721/read/getNFTs.js.map +1 -1
- package/dist/esm/extensions/erc721/read/getOwnedNFTs.js +46 -0
- package/dist/esm/extensions/erc721/read/getOwnedNFTs.js.map +1 -1
- package/dist/esm/extensions/marketplace/direct-listings/utils.js +7 -4
- package/dist/esm/extensions/marketplace/direct-listings/utils.js.map +1 -1
- package/dist/esm/extensions/marketplace/english-auctions/utils.js +9 -4
- package/dist/esm/extensions/marketplace/english-auctions/utils.js.map +1 -1
- package/dist/esm/extensions/marketplace/offers/utils.js +7 -4
- package/dist/esm/extensions/marketplace/offers/utils.js.map +1 -1
- package/dist/esm/insight/common.js +15 -0
- package/dist/esm/insight/common.js.map +1 -0
- package/dist/esm/insight/get-events.js +8 -5
- package/dist/esm/insight/get-events.js.map +1 -1
- package/dist/esm/insight/get-nfts.js +208 -13
- package/dist/esm/insight/get-nfts.js.map +1 -1
- package/dist/esm/insight/get-tokens.js +30 -10
- package/dist/esm/insight/get-tokens.js.map +1 -1
- package/dist/esm/insight/get-transactions.js +13 -8
- package/dist/esm/insight/get-transactions.js.map +1 -1
- package/dist/esm/insight/index.js +1 -1
- package/dist/esm/insight/index.js.map +1 -1
- package/dist/esm/react/web/ui/ConnectWallet/screens/ViewNFTs.js +6 -43
- package/dist/esm/react/web/ui/ConnectWallet/screens/ViewNFTs.js.map +1 -1
- package/dist/esm/react/web/ui/ConnectWallet/screens/ViewTokens.js +3 -9
- package/dist/esm/react/web/ui/ConnectWallet/screens/ViewTokens.js.map +1 -1
- package/dist/esm/react/web/ui/components/TokenIcon.js +3 -1
- package/dist/esm/react/web/ui/components/TokenIcon.js.map +1 -1
- package/dist/esm/react/web/ui/prebuilt/NFT/utils.js +7 -2
- package/dist/esm/react/web/ui/prebuilt/NFT/utils.js.map +1 -1
- package/dist/esm/rpc/fetch-rpc.js +2 -22
- package/dist/esm/rpc/fetch-rpc.js.map +1 -1
- package/dist/esm/rpc/rpc.js +1 -1
- package/dist/esm/rpc/rpc.js.map +1 -1
- package/dist/esm/social/profiles.js +3 -8
- package/dist/esm/social/profiles.js.map +1 -1
- package/dist/esm/storage/download.js +2 -2
- package/dist/esm/storage/download.js.map +1 -1
- package/dist/esm/storage/unpin.js +2 -2
- package/dist/esm/storage/unpin.js.map +1 -1
- package/dist/esm/storage/upload/web-node.js +0 -1
- package/dist/esm/storage/upload/web-node.js.map +1 -1
- package/dist/esm/utils/nft/parseNft.js +4 -0
- package/dist/esm/utils/nft/parseNft.js.map +1 -1
- package/dist/esm/utils/signatures/resolve-signature.js +0 -2
- package/dist/esm/utils/signatures/resolve-signature.js.map +1 -1
- package/dist/esm/version.js +1 -1
- package/dist/esm/wallets/in-app/core/users/getUser.js +2 -1
- package/dist/esm/wallets/in-app/core/users/getUser.js.map +1 -1
- package/dist/esm/wallets/smart/lib/calls.js +2 -2
- package/dist/esm/wallets/smart/lib/calls.js.map +1 -1
- package/dist/esm/wallets/utils/getWalletBalance.js +3 -0
- package/dist/esm/wallets/utils/getWalletBalance.js.map +1 -1
- package/dist/types/chains/utils.d.ts.map +1 -1
- package/dist/types/event/actions/get-events.d.ts.map +1 -1
- package/dist/types/extensions/erc1155/read/getNFT.d.ts +5 -0
- package/dist/types/extensions/erc1155/read/getNFT.d.ts.map +1 -1
- package/dist/types/extensions/erc1155/read/getNFTs.d.ts +5 -0
- package/dist/types/extensions/erc1155/read/getNFTs.d.ts.map +1 -1
- package/dist/types/extensions/erc1155/read/getOwnedNFTs.d.ts +7 -1
- package/dist/types/extensions/erc1155/read/getOwnedNFTs.d.ts.map +1 -1
- package/dist/types/extensions/erc20/read/getBalance.d.ts +2 -0
- package/dist/types/extensions/erc20/read/getBalance.d.ts.map +1 -1
- package/dist/types/extensions/erc721/read/getNFT.d.ts +5 -0
- package/dist/types/extensions/erc721/read/getNFT.d.ts.map +1 -1
- package/dist/types/extensions/erc721/read/getNFTs.d.ts +5 -0
- package/dist/types/extensions/erc721/read/getNFTs.d.ts.map +1 -1
- package/dist/types/extensions/erc721/read/getOwnedNFTs.d.ts +3 -1
- package/dist/types/extensions/erc721/read/getOwnedNFTs.d.ts.map +1 -1
- package/dist/types/extensions/marketplace/direct-listings/utils.d.ts.map +1 -1
- package/dist/types/extensions/marketplace/english-auctions/utils.d.ts.map +1 -1
- package/dist/types/extensions/marketplace/offers/utils.d.ts.map +1 -1
- package/dist/types/insight/common.d.ts +3 -0
- package/dist/types/insight/common.d.ts.map +1 -0
- package/dist/types/insight/get-events.d.ts +2 -2
- package/dist/types/insight/get-events.d.ts.map +1 -1
- package/dist/types/insight/get-nfts.d.ts +52 -4
- package/dist/types/insight/get-nfts.d.ts.map +1 -1
- package/dist/types/insight/get-tokens.d.ts +3 -3
- package/dist/types/insight/get-tokens.d.ts.map +1 -1
- package/dist/types/insight/get-transactions.d.ts +1 -1
- package/dist/types/insight/get-transactions.d.ts.map +1 -1
- package/dist/types/insight/index.d.ts +2 -2
- package/dist/types/insight/index.d.ts.map +1 -1
- package/dist/types/react/core/utils/wallet.d.ts +1 -1
- package/dist/types/react/web/ui/ConnectWallet/screens/Buy/main/useBuyTxStates.d.ts +1 -1
- package/dist/types/react/web/ui/ConnectWallet/screens/ViewNFTs.d.ts.map +1 -1
- package/dist/types/react/web/ui/ConnectWallet/screens/ViewTokens.d.ts.map +1 -1
- package/dist/types/react/web/ui/components/TokenIcon.d.ts.map +1 -1
- package/dist/types/react/web/ui/prebuilt/NFT/utils.d.ts.map +1 -1
- package/dist/types/rpc/fetch-rpc.d.ts.map +1 -1
- package/dist/types/rpc/rpc.d.ts.map +1 -1
- package/dist/types/social/profiles.d.ts.map +1 -1
- package/dist/types/storage/download.d.ts.map +1 -1
- package/dist/types/storage/unpin.d.ts.map +1 -1
- package/dist/types/storage/upload/web-node.d.ts.map +1 -1
- package/dist/types/utils/nft/parseNft.d.ts +8 -0
- package/dist/types/utils/nft/parseNft.d.ts.map +1 -1
- package/dist/types/utils/signatures/resolve-signature.d.ts.map +1 -1
- package/dist/types/version.d.ts +1 -1
- package/dist/types/wallets/in-app/core/users/getUser.d.ts.map +1 -1
- package/dist/types/wallets/utils/getWalletBalance.d.ts +2 -7
- package/dist/types/wallets/utils/getWalletBalance.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/chains/utils.ts +6 -4
- package/src/event/actions/get-events.ts +1 -13
- package/src/extensions/erc1155/read/getNFT.test.ts +13 -0
- package/src/extensions/erc1155/read/getNFT.ts +53 -1
- package/src/extensions/erc1155/read/getNFTs.ts +38 -0
- package/src/extensions/erc1155/read/getOwnedNFTs.ts +65 -2
- package/src/extensions/erc20/drop20.test.ts +48 -132
- package/src/extensions/erc20/read/getBalance.test.ts +5 -9
- package/src/extensions/erc20/read/getBalance.ts +4 -0
- package/src/extensions/erc721/read/getNFT.test.ts +106 -0
- package/src/extensions/erc721/read/getNFT.ts +55 -1
- package/src/extensions/erc721/read/getNFTs.test.ts +212 -6
- package/src/extensions/erc721/read/getNFTs.ts +59 -20
- package/src/extensions/erc721/read/getOwnedNFTs.test.ts +27 -2
- package/src/extensions/erc721/read/getOwnedNFTs.ts +61 -1
- package/src/extensions/erc721/token721.test.ts +2 -0
- package/src/extensions/marketplace/direct-listings/direct-listings.test.ts +2 -18
- package/src/extensions/marketplace/direct-listings/utils.ts +7 -4
- package/src/extensions/marketplace/english-auctions/utils.ts +9 -4
- package/src/extensions/marketplace/offers/utils.ts +7 -4
- package/src/insight/common.ts +24 -0
- package/src/insight/get-events.ts +23 -9
- package/src/insight/get-nfts.ts +315 -20
- package/src/insight/get-tokens.ts +51 -23
- package/src/insight/get-transactions.ts +26 -16
- package/src/insight/index.ts +6 -2
- package/src/react/web/ui/ConnectWallet/screens/ViewNFTs.tsx +43 -68
- package/src/react/web/ui/ConnectWallet/screens/ViewTokens.tsx +4 -14
- package/src/react/web/ui/components/TokenIcon.tsx +12 -3
- package/src/react/web/ui/prebuilt/NFT/utils.test.ts +2 -64
- package/src/react/web/ui/prebuilt/NFT/utils.ts +7 -2
- package/src/rpc/fetch-rpc.ts +2 -20
- package/src/rpc/rpc.ts +5 -1
- package/src/social/profiles.ts +5 -9
- package/src/storage/download.ts +4 -2
- package/src/storage/unpin.ts +4 -2
- package/src/storage/upload/web-node.ts +0 -1
- package/src/utils/nft/parseNft.test.ts +8 -0
- package/src/utils/nft/parseNft.ts +12 -0
- package/src/utils/signatures/resolve-signature.ts +0 -2
- package/src/version.ts +1 -1
- package/src/wallets/in-app/core/users/getUser.test.ts +4 -1
- package/src/wallets/in-app/core/users/getUser.ts +4 -1
- package/src/wallets/smart/lib/calls.ts +2 -2
- package/src/wallets/utils/getWalletBalance.ts +5 -7
@@ -1,4 +1,5 @@
|
|
1
1
|
import { maxUint256 } from "ox/Solidity";
|
2
|
+
import { getContractNFTs } from "../../../insight/get-nfts.js";
|
2
3
|
import type { BaseTransactionOptions } from "../../../transaction/types.js";
|
3
4
|
import { min } from "../../../utils/bigint.js";
|
4
5
|
import type { NFT } from "../../../utils/nft/parseNft.js";
|
@@ -23,6 +24,11 @@ export type GetNFTsParams = {
|
|
23
24
|
* The number of NFTs to retrieve.
|
24
25
|
*/
|
25
26
|
count?: number;
|
27
|
+
/**
|
28
|
+
* Whether to use the insight API to fetch the NFTs.
|
29
|
+
* @default true
|
30
|
+
*/
|
31
|
+
useIndexer?: boolean;
|
26
32
|
};
|
27
33
|
|
28
34
|
/**
|
@@ -42,6 +48,38 @@ export type GetNFTsParams = {
|
|
42
48
|
*/
|
43
49
|
export async function getNFTs(
|
44
50
|
options: BaseTransactionOptions<GetNFTsParams>,
|
51
|
+
): Promise<NFT[]> {
|
52
|
+
const { useIndexer = true } = options;
|
53
|
+
if (useIndexer) {
|
54
|
+
try {
|
55
|
+
return await getNFTsFromInsight(options);
|
56
|
+
} catch {
|
57
|
+
return await getNFTsFromRPC(options);
|
58
|
+
}
|
59
|
+
}
|
60
|
+
return await getNFTsFromRPC(options);
|
61
|
+
}
|
62
|
+
|
63
|
+
async function getNFTsFromInsight(
|
64
|
+
options: BaseTransactionOptions<GetNFTsParams>,
|
65
|
+
): Promise<NFT[]> {
|
66
|
+
const { contract, start, count = Number(DEFAULT_QUERY_ALL_COUNT) } = options;
|
67
|
+
|
68
|
+
const result = await getContractNFTs({
|
69
|
+
client: contract.client,
|
70
|
+
chains: [contract.chain],
|
71
|
+
contractAddress: contract.address,
|
72
|
+
queryOptions: {
|
73
|
+
limit: count,
|
74
|
+
page: start ? Math.floor(start / count) : undefined,
|
75
|
+
},
|
76
|
+
});
|
77
|
+
|
78
|
+
return result;
|
79
|
+
}
|
80
|
+
|
81
|
+
async function getNFTsFromRPC(
|
82
|
+
options: BaseTransactionOptions<GetNFTsParams>,
|
45
83
|
): Promise<NFT[]> {
|
46
84
|
const start = BigInt(options.start || 0);
|
47
85
|
const count = BigInt(options.count || DEFAULT_QUERY_ALL_COUNT);
|
@@ -1,3 +1,4 @@
|
|
1
|
+
import { getOwnedNFTs as getInsightNFTs } from "../../../insight/get-nfts.js";
|
1
2
|
import type { BaseTransactionOptions } from "../../../transaction/types.js";
|
2
3
|
import type { NFT } from "../../../utils/nft/parseNft.js";
|
3
4
|
import { getNFT } from "./getNFT.js";
|
@@ -5,12 +6,17 @@ import {
|
|
5
6
|
type GetOwnedTokenIdsParams,
|
6
7
|
getOwnedTokenIds,
|
7
8
|
} from "./getOwnedTokenIds.js";
|
8
|
-
|
9
9
|
/**
|
10
10
|
* Parameters for retrieving NFTs.
|
11
11
|
* @extension ERC1155
|
12
12
|
*/
|
13
|
-
export type GetOwnedNFTsParams = GetOwnedTokenIdsParams
|
13
|
+
export type GetOwnedNFTsParams = GetOwnedTokenIdsParams & {
|
14
|
+
/**
|
15
|
+
* Whether to use the insight API to fetch the NFTs.
|
16
|
+
* @default true
|
17
|
+
*/
|
18
|
+
useIndexer?: boolean;
|
19
|
+
};
|
14
20
|
|
15
21
|
/**
|
16
22
|
* Retrieves the owned ERC1155 NFTs for a given wallet address.
|
@@ -30,6 +36,63 @@ export type GetOwnedNFTsParams = GetOwnedTokenIdsParams;
|
|
30
36
|
*/
|
31
37
|
export async function getOwnedNFTs(
|
32
38
|
options: BaseTransactionOptions<GetOwnedNFTsParams>,
|
39
|
+
): Promise<(NFT & { quantityOwned: bigint })[]> {
|
40
|
+
const { useIndexer = true } = options;
|
41
|
+
if (useIndexer) {
|
42
|
+
try {
|
43
|
+
return await getOwnedNFTsFromInsight(options);
|
44
|
+
} catch {
|
45
|
+
return await getOwnedNFTsFromRPC(options);
|
46
|
+
}
|
47
|
+
}
|
48
|
+
return await getOwnedNFTsFromRPC(options);
|
49
|
+
}
|
50
|
+
|
51
|
+
async function getOwnedNFTsFromInsight(
|
52
|
+
options: BaseTransactionOptions<GetOwnedNFTsParams>,
|
53
|
+
): Promise<(NFT & { quantityOwned: bigint })[]> {
|
54
|
+
const limit = 50;
|
55
|
+
const nfts: (NFT & { quantityOwned: bigint })[] = [];
|
56
|
+
let page = 0;
|
57
|
+
let hasMore = true;
|
58
|
+
|
59
|
+
// TODO (insight): add support for contract address filters
|
60
|
+
while (hasMore) {
|
61
|
+
const pageResults = await getInsightNFTs({
|
62
|
+
client: options.contract.client,
|
63
|
+
chains: [options.contract.chain],
|
64
|
+
ownerAddress: options.address,
|
65
|
+
queryOptions: {
|
66
|
+
limit,
|
67
|
+
page,
|
68
|
+
},
|
69
|
+
});
|
70
|
+
|
71
|
+
nfts.push(...pageResults);
|
72
|
+
|
73
|
+
// If we got fewer results than the limit, we've reached the end
|
74
|
+
if (pageResults.length < limit) {
|
75
|
+
hasMore = false;
|
76
|
+
} else {
|
77
|
+
page++;
|
78
|
+
}
|
79
|
+
}
|
80
|
+
|
81
|
+
const results = nfts;
|
82
|
+
|
83
|
+
return results
|
84
|
+
.filter(
|
85
|
+
(n) =>
|
86
|
+
n.tokenAddress.toLowerCase() === options.contract.address.toLowerCase(),
|
87
|
+
)
|
88
|
+
.map((result) => ({
|
89
|
+
...result,
|
90
|
+
owner: options.address,
|
91
|
+
}));
|
92
|
+
}
|
93
|
+
|
94
|
+
async function getOwnedNFTsFromRPC(
|
95
|
+
options: BaseTransactionOptions<GetOwnedNFTsParams>,
|
33
96
|
): Promise<(NFT & { quantityOwned: bigint })[]> {
|
34
97
|
const ownedBalances = await getOwnedTokenIds(options);
|
35
98
|
|
@@ -59,17 +59,10 @@ describe.runIf(process.env.TW_SECRET_KEY)(
|
|
59
59
|
}, 60_000);
|
60
60
|
|
61
61
|
it("should allow to claim tokens", async () => {
|
62
|
-
|
63
|
-
getBalance({ contract, address: TEST_ACCOUNT_A.address })
|
64
|
-
|
65
|
-
|
66
|
-
"decimals": 18,
|
67
|
-
"displayValue": "0",
|
68
|
-
"name": "Test DropERC20",
|
69
|
-
"symbol": "",
|
70
|
-
"value": 0n,
|
71
|
-
}
|
72
|
-
`);
|
62
|
+
expect(
|
63
|
+
(await getBalance({ contract, address: TEST_ACCOUNT_A.address }))
|
64
|
+
.displayValue,
|
65
|
+
).toBe("0");
|
73
66
|
await sendAndConfirmTransaction({
|
74
67
|
transaction: setClaimConditions({
|
75
68
|
contract,
|
@@ -98,31 +91,17 @@ describe.runIf(process.env.TW_SECRET_KEY)(
|
|
98
91
|
transaction: claimTx,
|
99
92
|
account: TEST_ACCOUNT_A,
|
100
93
|
});
|
101
|
-
|
102
|
-
getBalance({ contract, address: TEST_ACCOUNT_A.address })
|
103
|
-
|
104
|
-
|
105
|
-
"decimals": 18,
|
106
|
-
"displayValue": "1",
|
107
|
-
"name": "Test DropERC20",
|
108
|
-
"symbol": "",
|
109
|
-
"value": 1000000000000000000n,
|
110
|
-
}
|
111
|
-
`);
|
94
|
+
expect(
|
95
|
+
(await getBalance({ contract, address: TEST_ACCOUNT_A.address }))
|
96
|
+
.displayValue,
|
97
|
+
).toBe("1");
|
112
98
|
});
|
113
99
|
|
114
100
|
it("should allow to claim tokens with value", async () => {
|
115
|
-
|
116
|
-
getBalance({ contract, address: TEST_ACCOUNT_C.address })
|
117
|
-
|
118
|
-
|
119
|
-
"decimals": 18,
|
120
|
-
"displayValue": "0",
|
121
|
-
"name": "Test DropERC20",
|
122
|
-
"symbol": "",
|
123
|
-
"value": 0n,
|
124
|
-
}
|
125
|
-
`);
|
101
|
+
expect(
|
102
|
+
(await getBalance({ contract, address: TEST_ACCOUNT_C.address }))
|
103
|
+
.displayValue,
|
104
|
+
).toBe("0");
|
126
105
|
// set cc with price
|
127
106
|
await sendAndConfirmTransaction({
|
128
107
|
transaction: setClaimConditions({
|
@@ -150,17 +129,10 @@ describe.runIf(process.env.TW_SECRET_KEY)(
|
|
150
129
|
transaction: claimTx,
|
151
130
|
account: TEST_ACCOUNT_C,
|
152
131
|
});
|
153
|
-
|
154
|
-
getBalance({ contract, address: TEST_ACCOUNT_C.address })
|
155
|
-
|
156
|
-
|
157
|
-
"decimals": 18,
|
158
|
-
"displayValue": "2",
|
159
|
-
"name": "Test DropERC20",
|
160
|
-
"symbol": "",
|
161
|
-
"value": 2000000000000000000n,
|
162
|
-
}
|
163
|
-
`);
|
132
|
+
expect(
|
133
|
+
(await getBalance({ contract, address: TEST_ACCOUNT_C.address }))
|
134
|
+
.displayValue,
|
135
|
+
).toBe("2");
|
164
136
|
});
|
165
137
|
|
166
138
|
describe("Allowlists", () => {
|
@@ -181,17 +153,10 @@ describe.runIf(process.env.TW_SECRET_KEY)(
|
|
181
153
|
account: TEST_ACCOUNT_A,
|
182
154
|
});
|
183
155
|
|
184
|
-
|
185
|
-
getBalance({ contract, address: TEST_ACCOUNT_B.address })
|
186
|
-
|
187
|
-
|
188
|
-
"decimals": 18,
|
189
|
-
"displayValue": "0",
|
190
|
-
"name": "Test DropERC20",
|
191
|
-
"symbol": "",
|
192
|
-
"value": 0n,
|
193
|
-
}
|
194
|
-
`);
|
156
|
+
expect(
|
157
|
+
(await getBalance({ contract, address: TEST_ACCOUNT_B.address }))
|
158
|
+
.displayValue,
|
159
|
+
).toBe("0");
|
195
160
|
|
196
161
|
expect(
|
197
162
|
await canClaim({
|
@@ -228,17 +193,10 @@ describe.runIf(process.env.TW_SECRET_KEY)(
|
|
228
193
|
}),
|
229
194
|
});
|
230
195
|
|
231
|
-
|
232
|
-
getBalance({ contract, address: TEST_ACCOUNT_B.address })
|
233
|
-
|
234
|
-
|
235
|
-
"decimals": 18,
|
236
|
-
"displayValue": "1",
|
237
|
-
"name": "Test DropERC20",
|
238
|
-
"symbol": "",
|
239
|
-
"value": 1000000000000000000n,
|
240
|
-
}
|
241
|
-
`);
|
196
|
+
expect(
|
197
|
+
(await getBalance({ contract, address: TEST_ACCOUNT_B.address }))
|
198
|
+
.displayValue,
|
199
|
+
).toBe("1");
|
242
200
|
|
243
201
|
await expect(
|
244
202
|
sendAndConfirmTransaction({
|
@@ -274,17 +232,10 @@ describe.runIf(process.env.TW_SECRET_KEY)(
|
|
274
232
|
account: TEST_ACCOUNT_A,
|
275
233
|
});
|
276
234
|
|
277
|
-
|
278
|
-
getBalance({ contract, address: TEST_ACCOUNT_A.address })
|
279
|
-
|
280
|
-
|
281
|
-
"decimals": 18,
|
282
|
-
"displayValue": "1",
|
283
|
-
"name": "Test DropERC20",
|
284
|
-
"symbol": "",
|
285
|
-
"value": 1000000000000000000n,
|
286
|
-
}
|
287
|
-
`);
|
235
|
+
expect(
|
236
|
+
(await getBalance({ contract, address: TEST_ACCOUNT_A.address }))
|
237
|
+
.displayValue,
|
238
|
+
).toBe("1");
|
288
239
|
|
289
240
|
// we try to claim an extra `2` tokens
|
290
241
|
// this should faile bcause the max claimable is `3` and we have previously already claimed 2 tokens (one for ourselves, one for the other wallet)
|
@@ -318,17 +269,10 @@ describe.runIf(process.env.TW_SECRET_KEY)(
|
|
318
269
|
}),
|
319
270
|
});
|
320
271
|
|
321
|
-
|
322
|
-
getBalance({ contract, address: TEST_ACCOUNT_A.address })
|
323
|
-
|
324
|
-
|
325
|
-
"decimals": 18,
|
326
|
-
"displayValue": "2",
|
327
|
-
"name": "Test DropERC20",
|
328
|
-
"symbol": "",
|
329
|
-
"value": 2000000000000000000n,
|
330
|
-
}
|
331
|
-
`);
|
272
|
+
expect(
|
273
|
+
(await getBalance({ contract, address: TEST_ACCOUNT_A.address }))
|
274
|
+
.displayValue,
|
275
|
+
).toBe("2");
|
332
276
|
});
|
333
277
|
});
|
334
278
|
|
@@ -353,17 +297,10 @@ describe.runIf(process.env.TW_SECRET_KEY)(
|
|
353
297
|
account: TEST_ACCOUNT_A,
|
354
298
|
});
|
355
299
|
|
356
|
-
|
357
|
-
getBalance({ contract, address: TEST_ACCOUNT_A.address })
|
358
|
-
|
359
|
-
|
360
|
-
"decimals": 18,
|
361
|
-
"displayValue": "2",
|
362
|
-
"name": "Test DropERC20",
|
363
|
-
"symbol": "",
|
364
|
-
"value": 2000000000000000000n,
|
365
|
-
}
|
366
|
-
`);
|
300
|
+
expect(
|
301
|
+
(await getBalance({ contract, address: TEST_ACCOUNT_A.address }))
|
302
|
+
.displayValue,
|
303
|
+
).toBe("2");
|
367
304
|
|
368
305
|
await sendAndConfirmTransaction({
|
369
306
|
account: TEST_ACCOUNT_A,
|
@@ -374,17 +311,10 @@ describe.runIf(process.env.TW_SECRET_KEY)(
|
|
374
311
|
}),
|
375
312
|
});
|
376
313
|
|
377
|
-
|
378
|
-
getBalance({ contract, address: TEST_ACCOUNT_A.address })
|
379
|
-
|
380
|
-
|
381
|
-
"decimals": 18,
|
382
|
-
"displayValue": "3",
|
383
|
-
"name": "Test DropERC20",
|
384
|
-
"symbol": "",
|
385
|
-
"value": 3000000000000000000n,
|
386
|
-
}
|
387
|
-
`);
|
314
|
+
expect(
|
315
|
+
(await getBalance({ contract, address: TEST_ACCOUNT_A.address }))
|
316
|
+
.displayValue,
|
317
|
+
).toBe("3");
|
388
318
|
});
|
389
319
|
|
390
320
|
it("should be able to retrieve multiple phases", async () => {
|
@@ -436,17 +366,10 @@ describe.runIf(process.env.TW_SECRET_KEY)(
|
|
436
366
|
account: TEST_ACCOUNT_D,
|
437
367
|
});
|
438
368
|
// check that the account has claimed one token
|
439
|
-
|
440
|
-
getBalance({ contract, address: TEST_ACCOUNT_D.address })
|
441
|
-
|
442
|
-
|
443
|
-
"decimals": 18,
|
444
|
-
"displayValue": "0.000000000000000001",
|
445
|
-
"name": "Test DropERC20",
|
446
|
-
"symbol": "",
|
447
|
-
"value": 1n,
|
448
|
-
}
|
449
|
-
`);
|
369
|
+
expect(
|
370
|
+
(await getBalance({ contract, address: TEST_ACCOUNT_D.address }))
|
371
|
+
.displayValue,
|
372
|
+
).toBe("0.000000000000000001");
|
450
373
|
|
451
374
|
// attempt to claim another token (this should fail)
|
452
375
|
await expect(
|
@@ -482,17 +405,10 @@ describe.runIf(process.env.TW_SECRET_KEY)(
|
|
482
405
|
account: TEST_ACCOUNT_D,
|
483
406
|
});
|
484
407
|
// check that the account has claimed two tokens
|
485
|
-
|
486
|
-
getBalance({ contract, address: TEST_ACCOUNT_D.address })
|
487
|
-
|
488
|
-
|
489
|
-
"decimals": 18,
|
490
|
-
"displayValue": "0.000000000000000002",
|
491
|
-
"name": "Test DropERC20",
|
492
|
-
"symbol": "",
|
493
|
-
"value": 2n,
|
494
|
-
}
|
495
|
-
`);
|
408
|
+
expect(
|
409
|
+
(await getBalance({ contract, address: TEST_ACCOUNT_D.address }))
|
410
|
+
.displayValue,
|
411
|
+
).toBe("0.000000000000000002");
|
496
412
|
});
|
497
413
|
},
|
498
414
|
);
|
@@ -10,14 +10,10 @@ describe.runIf(process.env.TW_SECRET_KEY)("erc20.getBalance", () => {
|
|
10
10
|
contract: USDT_CONTRACT,
|
11
11
|
address: VITALIK_WALLET,
|
12
12
|
});
|
13
|
-
expect(balance).
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
"symbol": "USDT",
|
19
|
-
"value": 1544900798n,
|
20
|
-
}
|
21
|
-
`);
|
13
|
+
expect(balance.displayValue).toBe("1544.900798");
|
14
|
+
expect(balance.name).toBe("Tether USD");
|
15
|
+
expect(balance.symbol).toBe("USDT");
|
16
|
+
expect(balance.value).toBe(1544900798n);
|
17
|
+
expect(balance.decimals).toBe(6);
|
22
18
|
});
|
23
19
|
});
|
@@ -23,6 +23,8 @@ export type GetBalanceResult = {
|
|
23
23
|
displayValue: string;
|
24
24
|
symbol: string;
|
25
25
|
name: string;
|
26
|
+
tokenAddress: string;
|
27
|
+
chainId: number;
|
26
28
|
};
|
27
29
|
|
28
30
|
/**
|
@@ -48,5 +50,7 @@ export async function getBalance(
|
|
48
50
|
...currencyMetadata,
|
49
51
|
value: balanceWei,
|
50
52
|
displayValue: toTokens(balanceWei, currencyMetadata.decimals),
|
53
|
+
tokenAddress: options.contract.address,
|
54
|
+
chainId: options.contract.chain.id,
|
51
55
|
};
|
52
56
|
}
|
@@ -3,14 +3,116 @@ import { DOODLES_CONTRACT } from "~test/test-contracts.js";
|
|
3
3
|
import { getNFT } from "./getNFT.js";
|
4
4
|
|
5
5
|
describe.runIf(process.env.TW_SECRET_KEY)("erc721.getNFT", () => {
|
6
|
+
it("without owner using indexer", async () => {
|
7
|
+
const nft = await getNFT({
|
8
|
+
contract: DOODLES_CONTRACT,
|
9
|
+
tokenId: 1n,
|
10
|
+
includeOwner: false,
|
11
|
+
});
|
12
|
+
expect(nft.metadata.name).toBe("Doodle #1");
|
13
|
+
expect(nft).toMatchInlineSnapshot(`
|
14
|
+
{
|
15
|
+
"chainId": 1,
|
16
|
+
"id": 1n,
|
17
|
+
"metadata": {
|
18
|
+
"attributes": [
|
19
|
+
{
|
20
|
+
"trait_type": "face",
|
21
|
+
"value": "holographic beard",
|
22
|
+
},
|
23
|
+
{
|
24
|
+
"trait_type": "hair",
|
25
|
+
"value": "white bucket cap",
|
26
|
+
},
|
27
|
+
{
|
28
|
+
"trait_type": "body",
|
29
|
+
"value": "purple sweater with satchel",
|
30
|
+
},
|
31
|
+
{
|
32
|
+
"trait_type": "background",
|
33
|
+
"value": "grey",
|
34
|
+
},
|
35
|
+
{
|
36
|
+
"trait_type": "head",
|
37
|
+
"value": "gradient 2",
|
38
|
+
},
|
39
|
+
],
|
40
|
+
"description": "A community-driven collectibles project featuring art by Burnt Toast. Doodles come in a joyful range of colors, traits and sizes with a collection size of 10,000. Each Doodle allows its owner to vote for experiences and activations paid for by the Doodles Community Treasury. Burnt Toast is the working alias for Scott Martin, a Canadian–based illustrator, designer, animator and muralist.",
|
41
|
+
"image": "ipfs://QmTDxnzcvj2p3xBrKcGv1wxoyhAn2yzCQnZZ9LmFjReuH9",
|
42
|
+
"image_url": "ipfs://QmTDxnzcvj2p3xBrKcGv1wxoyhAn2yzCQnZZ9LmFjReuH9",
|
43
|
+
"name": "Doodle #1",
|
44
|
+
"uri": "ipfs://QmPMc4tcBsMqLRuCQtPmPe84bpSjrC3Ky7t3JWuHXYB4aS/1",
|
45
|
+
},
|
46
|
+
"owner": null,
|
47
|
+
"tokenAddress": "0x8a90cab2b38dba80c64b7734e58ee1db38b8992e",
|
48
|
+
"tokenURI": "ipfs://QmPMc4tcBsMqLRuCQtPmPe84bpSjrC3Ky7t3JWuHXYB4aS/1",
|
49
|
+
"type": "ERC721",
|
50
|
+
}
|
51
|
+
`);
|
52
|
+
});
|
53
|
+
|
54
|
+
it("with owner using indexer", async () => {
|
55
|
+
const nft = await getNFT({
|
56
|
+
contract: { ...DOODLES_CONTRACT },
|
57
|
+
tokenId: 1n,
|
58
|
+
includeOwner: true,
|
59
|
+
});
|
60
|
+
expect(nft.metadata.name).toBe("Doodle #1");
|
61
|
+
expect(nft.owner).toBe("0xbe9936fcfc50666f5425fde4a9decc59cef73b24");
|
62
|
+
expect(nft).toMatchInlineSnapshot(`
|
63
|
+
{
|
64
|
+
"chainId": 1,
|
65
|
+
"id": 1n,
|
66
|
+
"metadata": {
|
67
|
+
"attributes": [
|
68
|
+
{
|
69
|
+
"trait_type": "face",
|
70
|
+
"value": "holographic beard",
|
71
|
+
},
|
72
|
+
{
|
73
|
+
"trait_type": "hair",
|
74
|
+
"value": "white bucket cap",
|
75
|
+
},
|
76
|
+
{
|
77
|
+
"trait_type": "body",
|
78
|
+
"value": "purple sweater with satchel",
|
79
|
+
},
|
80
|
+
{
|
81
|
+
"trait_type": "background",
|
82
|
+
"value": "grey",
|
83
|
+
},
|
84
|
+
{
|
85
|
+
"trait_type": "head",
|
86
|
+
"value": "gradient 2",
|
87
|
+
},
|
88
|
+
],
|
89
|
+
"description": "A community-driven collectibles project featuring art by Burnt Toast. Doodles come in a joyful range of colors, traits and sizes with a collection size of 10,000. Each Doodle allows its owner to vote for experiences and activations paid for by the Doodles Community Treasury. Burnt Toast is the working alias for Scott Martin, a Canadian–based illustrator, designer, animator and muralist.",
|
90
|
+
"image": "ipfs://QmTDxnzcvj2p3xBrKcGv1wxoyhAn2yzCQnZZ9LmFjReuH9",
|
91
|
+
"image_url": "ipfs://QmTDxnzcvj2p3xBrKcGv1wxoyhAn2yzCQnZZ9LmFjReuH9",
|
92
|
+
"name": "Doodle #1",
|
93
|
+
"owner_addresses": [
|
94
|
+
"0xbe9936fcfc50666f5425fde4a9decc59cef73b24",
|
95
|
+
],
|
96
|
+
"uri": "ipfs://QmPMc4tcBsMqLRuCQtPmPe84bpSjrC3Ky7t3JWuHXYB4aS/1",
|
97
|
+
},
|
98
|
+
"owner": "0xbe9936fcfc50666f5425fde4a9decc59cef73b24",
|
99
|
+
"tokenAddress": "0x8a90cab2b38dba80c64b7734e58ee1db38b8992e",
|
100
|
+
"tokenURI": "ipfs://QmPMc4tcBsMqLRuCQtPmPe84bpSjrC3Ky7t3JWuHXYB4aS/1",
|
101
|
+
"type": "ERC721",
|
102
|
+
}
|
103
|
+
`);
|
104
|
+
});
|
105
|
+
|
6
106
|
it("without owner", async () => {
|
7
107
|
const nft = await getNFT({
|
8
108
|
contract: { ...DOODLES_CONTRACT },
|
9
109
|
tokenId: 1n,
|
10
110
|
includeOwner: false,
|
111
|
+
useIndexer: false,
|
11
112
|
});
|
12
113
|
expect(nft).toMatchInlineSnapshot(`
|
13
114
|
{
|
115
|
+
"chainId": 1,
|
14
116
|
"id": 1n,
|
15
117
|
"metadata": {
|
16
118
|
"attributes": [
|
@@ -40,6 +142,7 @@ describe.runIf(process.env.TW_SECRET_KEY)("erc721.getNFT", () => {
|
|
40
142
|
"name": "Doodle #1",
|
41
143
|
},
|
42
144
|
"owner": null,
|
145
|
+
"tokenAddress": "0x8a90cab2b38dba80c64b7734e58ee1db38b8992e",
|
43
146
|
"tokenURI": "ipfs://QmPMc4tcBsMqLRuCQtPmPe84bpSjrC3Ky7t3JWuHXYB4aS/1",
|
44
147
|
"type": "ERC721",
|
45
148
|
}
|
@@ -51,9 +154,11 @@ describe.runIf(process.env.TW_SECRET_KEY)("erc721.getNFT", () => {
|
|
51
154
|
contract: { ...DOODLES_CONTRACT },
|
52
155
|
tokenId: 1n,
|
53
156
|
includeOwner: true,
|
157
|
+
useIndexer: false,
|
54
158
|
});
|
55
159
|
expect(nft).toMatchInlineSnapshot(`
|
56
160
|
{
|
161
|
+
"chainId": 1,
|
57
162
|
"id": 1n,
|
58
163
|
"metadata": {
|
59
164
|
"attributes": [
|
@@ -83,6 +188,7 @@ describe.runIf(process.env.TW_SECRET_KEY)("erc721.getNFT", () => {
|
|
83
188
|
"name": "Doodle #1",
|
84
189
|
},
|
85
190
|
"owner": "0xbE9936FCFC50666f5425FDE4A9decC59cEF73b24",
|
191
|
+
"tokenAddress": "0x8a90cab2b38dba80c64b7734e58ee1db38b8992e",
|
86
192
|
"tokenURI": "ipfs://QmPMc4tcBsMqLRuCQtPmPe84bpSjrC3Ky7t3JWuHXYB4aS/1",
|
87
193
|
"type": "ERC721",
|
88
194
|
}
|
@@ -7,8 +7,8 @@ import {
|
|
7
7
|
tokenURI,
|
8
8
|
} from "../__generated__/IERC721A/read/tokenURI.js";
|
9
9
|
import { tokenByIndex } from "../__generated__/IERC721Enumerable/read/tokenByIndex.js";
|
10
|
-
|
11
10
|
export { isTokenURISupported as isGetNFTSupported } from "../__generated__/IERC721A/read/tokenURI.js";
|
11
|
+
import { getNFT as getNFTInsight } from "../../../insight/index.js";
|
12
12
|
|
13
13
|
/**
|
14
14
|
* Parameters for getting an NFT.
|
@@ -27,6 +27,11 @@ export type GetNFTParams = Prettify<
|
|
27
27
|
* In this case, the provided tokenId will be considered as token-index and actual tokenId will be fetched from the contract.
|
28
28
|
*/
|
29
29
|
tokenByIndex?: boolean;
|
30
|
+
/**
|
31
|
+
* Whether to use the insight API to fetch the NFT.
|
32
|
+
* @default true
|
33
|
+
*/
|
34
|
+
useIndexer?: boolean;
|
30
35
|
}
|
31
36
|
>;
|
32
37
|
|
@@ -58,6 +63,51 @@ export type GetNFTParams = Prettify<
|
|
58
63
|
*/
|
59
64
|
export async function getNFT(
|
60
65
|
options: BaseTransactionOptions<GetNFTParams>,
|
66
|
+
): Promise<NFT> {
|
67
|
+
const { useIndexer = true } = options;
|
68
|
+
if (useIndexer) {
|
69
|
+
try {
|
70
|
+
return await getNFTFromInsight(options);
|
71
|
+
} catch {
|
72
|
+
return await getNFTFromRPC(options);
|
73
|
+
}
|
74
|
+
}
|
75
|
+
return await getNFTFromRPC(options);
|
76
|
+
}
|
77
|
+
|
78
|
+
async function getNFTFromInsight(
|
79
|
+
options: BaseTransactionOptions<GetNFTParams>,
|
80
|
+
): Promise<NFT> {
|
81
|
+
const tokenId = options.tokenId;
|
82
|
+
const nft = await getNFTInsight({
|
83
|
+
client: options.contract.client,
|
84
|
+
chain: options.contract.chain,
|
85
|
+
contractAddress: options.contract.address,
|
86
|
+
tokenId: options.tokenId,
|
87
|
+
includeOwners: options.includeOwner,
|
88
|
+
});
|
89
|
+
if (!nft) {
|
90
|
+
return parseNFT(
|
91
|
+
{
|
92
|
+
id: tokenId,
|
93
|
+
type: "ERC721",
|
94
|
+
uri: "",
|
95
|
+
},
|
96
|
+
{
|
97
|
+
tokenId,
|
98
|
+
tokenUri: "",
|
99
|
+
type: "ERC721",
|
100
|
+
owner: null,
|
101
|
+
tokenAddress: options.contract.address,
|
102
|
+
chainId: options.contract.chain.id,
|
103
|
+
},
|
104
|
+
);
|
105
|
+
}
|
106
|
+
return nft;
|
107
|
+
}
|
108
|
+
|
109
|
+
async function getNFTFromRPC(
|
110
|
+
options: BaseTransactionOptions<GetNFTParams>,
|
61
111
|
): Promise<NFT> {
|
62
112
|
let tokenId = options.tokenId;
|
63
113
|
if (options.tokenByIndex) {
|
@@ -90,6 +140,8 @@ export async function getNFT(
|
|
90
140
|
tokenUri: "",
|
91
141
|
type: "ERC721",
|
92
142
|
owner,
|
143
|
+
tokenAddress: options.contract.address,
|
144
|
+
chainId: options.contract.chain.id,
|
93
145
|
},
|
94
146
|
);
|
95
147
|
}
|
@@ -109,6 +161,8 @@ export async function getNFT(
|
|
109
161
|
tokenUri: uri,
|
110
162
|
type: "ERC721",
|
111
163
|
owner,
|
164
|
+
tokenAddress: options.contract.address,
|
165
|
+
chainId: options.contract.chain.id,
|
112
166
|
},
|
113
167
|
);
|
114
168
|
}
|