thirdweb 5.89.0 → 5.90.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/adapters/ethers5.js +2 -0
- package/dist/cjs/adapters/ethers5.js.map +1 -1
- package/dist/cjs/chains/constants.js +17 -2
- package/dist/cjs/chains/constants.js.map +1 -1
- package/dist/cjs/contract/actions/resolve-abi.js +9 -11
- package/dist/cjs/contract/actions/resolve-abi.js.map +1 -1
- package/dist/cjs/exports/extensions/erc721.js +4 -2
- package/dist/cjs/exports/extensions/erc721.js.map +1 -1
- package/dist/cjs/exports/wallets/smart.js +2 -1
- package/dist/cjs/exports/wallets/smart.js.map +1 -1
- package/dist/cjs/extensions/erc721/read/getNFT.js +30 -7
- package/dist/cjs/extensions/erc721/read/getNFT.js.map +1 -1
- package/dist/cjs/extensions/erc721/read/getNFTs.js.map +1 -1
- package/dist/cjs/gas/fee-data.js +22 -15
- package/dist/cjs/gas/fee-data.js.map +1 -1
- package/dist/cjs/storage/download.js +16 -0
- package/dist/cjs/storage/download.js.map +1 -1
- package/dist/cjs/storage/mock.js +46 -0
- package/dist/cjs/storage/mock.js.map +1 -0
- package/dist/cjs/storage/upload/web-node.js +6 -1
- package/dist/cjs/storage/upload/web-node.js.map +1 -1
- package/dist/cjs/transaction/actions/estimate-gas-cost.js +1 -1
- package/dist/cjs/transaction/actions/estimate-gas-cost.js.map +1 -1
- package/dist/cjs/transaction/actions/to-serializable-transaction.js +3 -1
- package/dist/cjs/transaction/actions/to-serializable-transaction.js.map +1 -1
- package/dist/cjs/transaction/prepare-transaction.js +8 -0
- package/dist/cjs/transaction/prepare-transaction.js.map +1 -1
- package/dist/cjs/utils/process.js +4 -1
- package/dist/cjs/utils/process.js.map +1 -1
- package/dist/cjs/version.js +1 -1
- package/dist/cjs/wallets/manager/index.js +1 -1
- package/dist/cjs/wallets/manager/index.js.map +1 -1
- package/dist/cjs/wallets/smart/index.js +1 -0
- package/dist/cjs/wallets/smart/index.js.map +1 -1
- package/dist/cjs/wallets/smart/lib/bundler.js +75 -0
- package/dist/cjs/wallets/smart/lib/bundler.js.map +1 -1
- package/dist/cjs/wallets/smart/lib/calls.js +13 -7
- package/dist/cjs/wallets/smart/lib/calls.js.map +1 -1
- package/dist/cjs/wallets/smart/lib/userop.js +42 -17
- package/dist/cjs/wallets/smart/lib/userop.js.map +1 -1
- package/dist/esm/adapters/ethers5.js +2 -0
- package/dist/esm/adapters/ethers5.js.map +1 -1
- package/dist/esm/chains/constants.js +17 -2
- package/dist/esm/chains/constants.js.map +1 -1
- package/dist/esm/contract/actions/resolve-abi.js +9 -11
- package/dist/esm/contract/actions/resolve-abi.js.map +1 -1
- package/dist/esm/exports/extensions/erc721.js +1 -0
- package/dist/esm/exports/extensions/erc721.js.map +1 -1
- package/dist/esm/exports/wallets/smart.js +1 -1
- package/dist/esm/exports/wallets/smart.js.map +1 -1
- package/dist/esm/extensions/erc721/read/getNFT.js +30 -7
- package/dist/esm/extensions/erc721/read/getNFT.js.map +1 -1
- package/dist/esm/extensions/erc721/read/getNFTs.js.map +1 -1
- package/dist/esm/gas/fee-data.js +22 -15
- package/dist/esm/gas/fee-data.js.map +1 -1
- package/dist/esm/storage/download.js +16 -0
- package/dist/esm/storage/download.js.map +1 -1
- package/dist/esm/storage/mock.js +42 -0
- package/dist/esm/storage/mock.js.map +1 -0
- package/dist/esm/storage/upload/web-node.js +6 -1
- package/dist/esm/storage/upload/web-node.js.map +1 -1
- package/dist/esm/transaction/actions/estimate-gas-cost.js +1 -1
- package/dist/esm/transaction/actions/estimate-gas-cost.js.map +1 -1
- package/dist/esm/transaction/actions/to-serializable-transaction.js +3 -1
- package/dist/esm/transaction/actions/to-serializable-transaction.js.map +1 -1
- package/dist/esm/transaction/prepare-transaction.js +7 -0
- package/dist/esm/transaction/prepare-transaction.js.map +1 -1
- package/dist/esm/utils/process.js +3 -0
- package/dist/esm/utils/process.js.map +1 -1
- package/dist/esm/version.js +1 -1
- package/dist/esm/wallets/manager/index.js +1 -1
- package/dist/esm/wallets/manager/index.js.map +1 -1
- package/dist/esm/wallets/smart/index.js +1 -1
- package/dist/esm/wallets/smart/index.js.map +1 -1
- package/dist/esm/wallets/smart/lib/bundler.js +74 -0
- package/dist/esm/wallets/smart/lib/bundler.js.map +1 -1
- package/dist/esm/wallets/smart/lib/calls.js +13 -7
- package/dist/esm/wallets/smart/lib/calls.js.map +1 -1
- package/dist/esm/wallets/smart/lib/userop.js +41 -17
- package/dist/esm/wallets/smart/lib/userop.js.map +1 -1
- package/dist/types/adapters/ethers5.d.ts.map +1 -1
- package/dist/types/chains/constants.d.ts +1 -1
- package/dist/types/chains/constants.d.ts.map +1 -1
- package/dist/types/chains/types.d.ts +2 -0
- package/dist/types/chains/types.d.ts.map +1 -1
- package/dist/types/contract/actions/resolve-abi.d.ts.map +1 -1
- package/dist/types/exports/extensions/erc721.d.ts +1 -0
- package/dist/types/exports/extensions/erc721.d.ts.map +1 -1
- package/dist/types/exports/wallets/smart.d.ts +1 -1
- package/dist/types/exports/wallets/smart.d.ts.map +1 -1
- package/dist/types/extensions/erc721/read/getNFT.d.ts +19 -0
- package/dist/types/extensions/erc721/read/getNFT.d.ts.map +1 -1
- package/dist/types/extensions/erc721/read/getNFTs.d.ts +7 -0
- package/dist/types/extensions/erc721/read/getNFTs.d.ts.map +1 -1
- package/dist/types/gas/fee-data.d.ts +6 -5
- package/dist/types/gas/fee-data.d.ts.map +1 -1
- package/dist/types/storage/download.d.ts.map +1 -1
- package/dist/types/storage/mock.d.ts +11 -0
- package/dist/types/storage/mock.d.ts.map +1 -0
- package/dist/types/storage/upload/web-node.d.ts.map +1 -1
- package/dist/types/transaction/actions/to-serializable-transaction.d.ts +2 -0
- package/dist/types/transaction/actions/to-serializable-transaction.d.ts.map +1 -1
- package/dist/types/transaction/prepare-transaction.d.ts +3 -0
- package/dist/types/transaction/prepare-transaction.d.ts.map +1 -1
- package/dist/types/utils/process.d.ts +1 -0
- package/dist/types/utils/process.d.ts.map +1 -1
- package/dist/types/version.d.ts +1 -1
- package/dist/types/wallets/manager/index.d.ts +1 -1
- package/dist/types/wallets/manager/index.d.ts.map +1 -1
- package/dist/types/wallets/smart/index.d.ts +2 -0
- package/dist/types/wallets/smart/index.d.ts.map +1 -1
- package/dist/types/wallets/smart/lib/bundler.d.ts +30 -1
- package/dist/types/wallets/smart/lib/bundler.d.ts.map +1 -1
- package/dist/types/wallets/smart/lib/calls.d.ts.map +1 -1
- package/dist/types/wallets/smart/lib/userop.d.ts +10 -0
- package/dist/types/wallets/smart/lib/userop.d.ts.map +1 -1
- package/package.json +33 -33
- package/src/adapters/ethers5.ts +5 -1
- package/src/adapters/viem-legacy.test.ts +1 -1
- package/src/chains/constants.ts +17 -2
- package/src/chains/types.ts +3 -0
- package/src/contract/actions/resolve-abi.test.ts +3 -3
- package/src/contract/actions/resolve-abi.ts +27 -25
- package/src/exports/extensions/erc721.ts +1 -0
- package/src/exports/wallets/smart.ts +1 -0
- package/src/extensions/erc1155/customDrop1155.test.ts +3 -14
- package/src/extensions/erc1155/drop1155.test.ts +3 -14
- package/src/extensions/erc1155/token1155.test.ts +16 -109
- package/src/extensions/erc20/read/getCurrencyMetadata.test.ts +1 -1
- package/src/extensions/erc721/customDrop721.test.ts +3 -13
- package/src/extensions/erc721/drop721.test.ts +3 -13
- package/src/extensions/erc721/read/getNFT.ts +37 -7
- package/src/extensions/erc721/read/getNFTs.test.ts +1 -1
- package/src/extensions/erc721/read/getNFTs.ts +7 -0
- package/src/extensions/erc721/read/getOwnedTokenIds.test.ts +1 -1
- package/src/extensions/lens/read/resolveAddress.test.ts +1 -1
- package/src/extensions/prebuilts/deploy-vote.test.ts +1 -1
- package/src/extensions/unstoppable-domains/read/resolveAddress.test.ts +1 -1
- package/src/extensions/unstoppable-domains/read/resolveName.test.ts +1 -1
- package/src/gas/fee-data.ts +32 -19
- package/src/pay/convert/cryptoToFiat.test.ts +4 -4
- package/src/pay/convert/fiatToCrypto.test.ts +4 -4
- package/src/react/core/hooks/wallets/useConnect.test.tsx +3 -3
- package/src/react/web/ui/prebuilt/Account/balance.test.tsx +7 -13
- package/src/react/web/ui/prebuilt/Chain/icon.test.tsx +1 -1
- package/src/react/web/ui/prebuilt/NFT/description.test.tsx +1 -1
- package/src/react/web/ui/prebuilt/NFT/media.test.tsx +1 -1
- package/src/react/web/ui/prebuilt/NFT/name.test.tsx +1 -1
- package/src/react/web/ui/prebuilt/NFT/utils.test.ts +1 -1
- package/src/react/web/ui/prebuilt/Token/name.test.tsx +1 -1
- package/src/react/web/ui/prebuilt/Token/symbol.test.tsx +1 -1
- package/src/react/web/ui/prebuilt/Wallet/icon.test.tsx +1 -1
- package/src/react/web/ui/prebuilt/Wallet/name.test.tsx +1 -1
- package/src/react/web/ui/prebuilt/thirdweb/ClaimButton/ClaimButton.test.tsx +1 -1
- package/src/storage/download.test.ts +1 -1
- package/src/storage/download.ts +17 -0
- package/src/storage/mock.ts +52 -0
- package/src/storage/upload/web-node.test.ts +2 -1
- package/src/storage/upload/web-node.ts +8 -1
- package/src/transaction/actions/encode.test.ts +1 -1
- package/src/transaction/actions/estimate-gas-cost.ts +1 -1
- package/src/transaction/actions/to-serializable-transaction.ts +46 -35
- package/src/transaction/prepare-transaction.ts +11 -0
- package/src/utils/nft/fetch-token-metadata.test.ts +1 -1
- package/src/utils/process.ts +4 -0
- package/src/version.ts +1 -1
- package/src/wallets/manager/index.ts +6 -2
- package/src/wallets/smart/index.ts +1 -1
- package/src/wallets/smart/lib/bundler.ts +95 -0
- package/src/wallets/smart/lib/calls.ts +18 -9
- package/src/wallets/smart/lib/userop.ts +53 -18
- package/src/wallets/smart/smart-wallet-integration-v07.test.ts +23 -16
- package/src/wallets/smart/smart-wallet-integration.test.ts +21 -1
- package/src/wallets/smart/smart-wallet-modular.test.ts +1 -1
@@ -1,10 +1,9 @@
|
|
1
1
|
import { type Abi, formatAbi, parseAbi } from "abitype";
|
2
2
|
import { download } from "../../storage/download.js";
|
3
3
|
import { getClientFetch } from "../../utils/fetch.js";
|
4
|
+
import { withCache } from "../../utils/promise/withCache.js";
|
4
5
|
import { type ThirdwebContract, getContract } from "../contract.js";
|
5
6
|
|
6
|
-
const ABI_RESOLUTION_CACHE = new WeakMap<ThirdwebContract<Abi>, Promise<Abi>>();
|
7
|
-
|
8
7
|
/**
|
9
8
|
* Resolves the ABI (Application Binary Interface) for a given contract.
|
10
9
|
* If the ABI is already cached, it returns the cached value.
|
@@ -32,31 +31,34 @@ export function resolveContractAbi<abi extends Abi>(
|
|
32
31
|
contract: ThirdwebContract<abi>,
|
33
32
|
contractApiBaseUrl = "https://contract.thirdweb.com/abi",
|
34
33
|
): Promise<abi> {
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
if (contract.abi) {
|
42
|
-
return contract.abi as abi;
|
43
|
-
}
|
34
|
+
return withCache(
|
35
|
+
async () => {
|
36
|
+
// if the contract already HAS a user defined we always use that!
|
37
|
+
if (contract.abi) {
|
38
|
+
return contract.abi as abi;
|
39
|
+
}
|
44
40
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
41
|
+
// for local chains, we need to resolve the composite abi from bytecode
|
42
|
+
if (contract.chain.id === 31337 || contract.chain.id === 1337) {
|
43
|
+
return (await resolveCompositeAbi(contract as ThirdwebContract)) as abi;
|
44
|
+
}
|
49
45
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
46
|
+
// try to get it from the api
|
47
|
+
try {
|
48
|
+
return (await resolveAbiFromContractApi(
|
49
|
+
contract,
|
50
|
+
contractApiBaseUrl,
|
51
|
+
)) as abi;
|
52
|
+
} catch {
|
53
|
+
// if that fails, try to resolve it from the bytecode
|
54
|
+
return (await resolveCompositeAbi(contract as ThirdwebContract)) as abi;
|
55
|
+
}
|
56
|
+
},
|
57
|
+
{
|
58
|
+
cacheKey: `${contract.chain.id}-${contract.address}`,
|
59
|
+
cacheTime: 1000 * 60 * 60 * 1, // 1 hour
|
60
|
+
},
|
61
|
+
);
|
60
62
|
}
|
61
63
|
|
62
64
|
/**
|
@@ -16,6 +16,7 @@ export {
|
|
16
16
|
nextTokenIdToMint,
|
17
17
|
isNextTokenIdToMintSupported,
|
18
18
|
} from "../../extensions/erc721/__generated__/IERC721Enumerable/read/nextTokenIdToMint.js";
|
19
|
+
export { isTokenByIndexSupported } from "../../extensions/erc721/__generated__/IERC721Enumerable/read/tokenByIndex.js";
|
19
20
|
export {
|
20
21
|
ownerOf,
|
21
22
|
type OwnerOfParams,
|
@@ -69,20 +69,9 @@ describe.runIf(process.env.TW_SECRET_KEY)(
|
|
69
69
|
});
|
70
70
|
|
71
71
|
await expect(nextTokenIdToMint({ contract })).resolves.toBe(6n);
|
72
|
-
await
|
73
|
-
|
74
|
-
)
|
75
|
-
{
|
76
|
-
"id": 0n,
|
77
|
-
"metadata": {
|
78
|
-
"name": "Test NFT",
|
79
|
-
},
|
80
|
-
"owner": null,
|
81
|
-
"supply": 0n,
|
82
|
-
"tokenURI": "ipfs://QmTo68Dm1ntSp2BHLmE9gesS6ELuXosRz5mAgFCK6tfsRk/0",
|
83
|
-
"type": "ERC1155",
|
84
|
-
}
|
85
|
-
`);
|
72
|
+
expect((await getNFT({ contract, tokenId: 0n })).metadata.name).toBe(
|
73
|
+
"Test NFT",
|
74
|
+
);
|
86
75
|
});
|
87
76
|
|
88
77
|
it("should allow to claim tokens", async () => {
|
@@ -89,20 +89,9 @@ describe.runIf(process.env.TW_SECRET_KEY)(
|
|
89
89
|
});
|
90
90
|
|
91
91
|
await expect(nextTokenIdToMint({ contract })).resolves.toBe(6n);
|
92
|
-
await
|
93
|
-
|
94
|
-
)
|
95
|
-
{
|
96
|
-
"id": 0n,
|
97
|
-
"metadata": {
|
98
|
-
"name": "Test NFT",
|
99
|
-
},
|
100
|
-
"owner": null,
|
101
|
-
"supply": 0n,
|
102
|
-
"tokenURI": "ipfs://QmTo68Dm1ntSp2BHLmE9gesS6ELuXosRz5mAgFCK6tfsRk/0",
|
103
|
-
"type": "ERC1155",
|
104
|
-
}
|
105
|
-
`);
|
92
|
+
expect((await getNFT({ contract, tokenId: 0n })).metadata.name).toBe(
|
93
|
+
"Test NFT",
|
94
|
+
);
|
106
95
|
});
|
107
96
|
|
108
97
|
it("should update metadata", async () => {
|
@@ -81,20 +81,9 @@ describe.runIf(process.env.TW_SECRET_KEY)("TokenERC1155", () => {
|
|
81
81
|
// now 1 token minted
|
82
82
|
await expect(nextTokenIdToMint({ contract })).resolves.toBe(1n);
|
83
83
|
// tokenId 0 is minted
|
84
|
-
await
|
85
|
-
|
86
|
-
)
|
87
|
-
{
|
88
|
-
"id": 0n,
|
89
|
-
"metadata": {
|
90
|
-
"name": "Test NFT",
|
91
|
-
},
|
92
|
-
"owner": null,
|
93
|
-
"supply": 10n,
|
94
|
-
"tokenURI": "ipfs://QmUut8sypH8NaPUeksnirut7MgggMeQa9RvJ37sV513sw3/0",
|
95
|
-
"type": "ERC1155",
|
96
|
-
}
|
97
|
-
`);
|
84
|
+
expect((await getNFT({ contract, tokenId: 0n })).metadata.name).toBe(
|
85
|
+
"Test NFT",
|
86
|
+
);
|
98
87
|
// account should have a balance of 10
|
99
88
|
await expect(
|
100
89
|
balanceOf({ contract, owner: TEST_ACCOUNT_A.address, tokenId: 0n }),
|
@@ -119,20 +108,8 @@ describe.runIf(process.env.TW_SECRET_KEY)("TokenERC1155", () => {
|
|
119
108
|
await expect(nextTokenIdToMint({ contract })).resolves.toBe(1n);
|
120
109
|
// tokenId 0 is minted
|
121
110
|
// supply should be 15
|
122
|
-
|
123
|
-
|
124
|
-
).resolves.toMatchInlineSnapshot(`
|
125
|
-
{
|
126
|
-
"id": 0n,
|
127
|
-
"metadata": {
|
128
|
-
"name": "Test NFT",
|
129
|
-
},
|
130
|
-
"owner": null,
|
131
|
-
"supply": 15n,
|
132
|
-
"tokenURI": "ipfs://QmUut8sypH8NaPUeksnirut7MgggMeQa9RvJ37sV513sw3/0",
|
133
|
-
"type": "ERC1155",
|
134
|
-
}
|
135
|
-
`);
|
111
|
+
// @ts-expect-error - supply is there
|
112
|
+
expect((await getNFT({ contract, tokenId: 0n })).supply).toBe(15n);
|
136
113
|
// account should have a balance of 15
|
137
114
|
await expect(
|
138
115
|
balanceOf({ contract, owner: TEST_ACCOUNT_A.address, tokenId: 0n }),
|
@@ -155,30 +132,13 @@ describe.runIf(process.env.TW_SECRET_KEY)("TokenERC1155", () => {
|
|
155
132
|
|
156
133
|
// now 2 tokens minted
|
157
134
|
await expect(nextTokenIdToMint({ contract })).resolves.toBe(2n);
|
158
|
-
await expect(getNFTs({ contract })).resolves.
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
"owner": null,
|
166
|
-
"supply": 15n,
|
167
|
-
"tokenURI": "ipfs://QmUut8sypH8NaPUeksnirut7MgggMeQa9RvJ37sV513sw3/0",
|
168
|
-
"type": "ERC1155",
|
169
|
-
},
|
170
|
-
{
|
171
|
-
"id": 1n,
|
172
|
-
"metadata": {
|
173
|
-
"name": "Test NFT 2",
|
174
|
-
},
|
175
|
-
"owner": null,
|
176
|
-
"supply": 5n,
|
177
|
-
"tokenURI": "ipfs://QmV6gsfzdiMRtpnh8ay3CgutStVbes7qoF4DKpYE64h8hT/0",
|
178
|
-
"type": "ERC1155",
|
179
|
-
},
|
180
|
-
]
|
181
|
-
`);
|
135
|
+
await expect(getNFTs({ contract })).resolves.length(2);
|
136
|
+
expect((await getNFT({ contract, tokenId: 0n })).metadata.name).toBe(
|
137
|
+
"Test NFT",
|
138
|
+
);
|
139
|
+
expect((await getNFT({ contract, tokenId: 1n })).metadata.name).toBe(
|
140
|
+
"Test NFT 2",
|
141
|
+
);
|
182
142
|
});
|
183
143
|
|
184
144
|
it("isGetNFTsSupported should work with our Edition contracts", async () => {
|
@@ -241,63 +201,10 @@ describe.runIf(process.env.TW_SECRET_KEY)("TokenERC1155", () => {
|
|
241
201
|
});
|
242
202
|
|
243
203
|
const nfts = await getNFTs({ contract });
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
owner: null,
|
249
|
-
id: 0n,
|
250
|
-
tokenURI: "ipfs://QmTSyt6YgoFtH8yhWgevC22BxjyrkCKuzdk86nqQJCwrV9/0",
|
251
|
-
type: "ERC1155",
|
252
|
-
supply: 15n,
|
253
|
-
},
|
254
|
-
{
|
255
|
-
metadata: { name: "Test NFT 2" },
|
256
|
-
owner: null,
|
257
|
-
id: 1n,
|
258
|
-
tokenURI: "ipfs://QmV6gsfzdiMRtpnh8ay3CgutStVbes7qoF4DKpYE64h8hT/0",
|
259
|
-
type: "ERC1155",
|
260
|
-
supply: 5n,
|
261
|
-
},
|
262
|
-
{
|
263
|
-
metadata: {
|
264
|
-
name: "tw-contract-name",
|
265
|
-
symbol: "tw-contract-symbol",
|
266
|
-
description: "tw-contract-description",
|
267
|
-
},
|
268
|
-
owner: null,
|
269
|
-
id: 2n,
|
270
|
-
// Minted using URI from the test above
|
271
|
-
tokenURI:
|
272
|
-
"ipfs://bafybeiewg2e3vehsb5pb34xwk25eyyfwlvajzmgz3rtdrvn3upsxnsbhzi/contractUri.json",
|
273
|
-
type: "ERC1155",
|
274
|
-
supply: 1n,
|
275
|
-
},
|
276
|
-
{
|
277
|
-
metadata: { name: "batch token 0" },
|
278
|
-
owner: null,
|
279
|
-
id: 3n,
|
280
|
-
tokenURI: "ipfs://QmPSQhC2J6Wig4pH1Lt5th19FM58J5oukhfLfpc9L1i39Q/0",
|
281
|
-
type: "ERC1155",
|
282
|
-
supply: 1n,
|
283
|
-
},
|
284
|
-
{
|
285
|
-
metadata: { name: "batch token 1" },
|
286
|
-
owner: null,
|
287
|
-
id: 4n,
|
288
|
-
tokenURI: "ipfs://QmWRQwLBAeq6Wr65Yj7A4aeYqMD1C7Hs1moz15ncS6EhGu/0",
|
289
|
-
type: "ERC1155",
|
290
|
-
supply: 2n,
|
291
|
-
},
|
292
|
-
{
|
293
|
-
metadata: { name: "batch token 2" },
|
294
|
-
owner: null,
|
295
|
-
id: 5n,
|
296
|
-
tokenURI: "ipfs://QmTg1wxKGvdZR4NrdkYZGcfB5YYaBz75DH83td5RwprMRP/0",
|
297
|
-
type: "ERC1155",
|
298
|
-
supply: 3n,
|
299
|
-
},
|
300
|
-
]);
|
204
|
+
const names = nfts.map((nft) => nft.metadata.name);
|
205
|
+
expect(names).toContain("batch token 0");
|
206
|
+
expect(names).toContain("batch token 1");
|
207
|
+
expect(names).toContain("batch token 2");
|
301
208
|
});
|
302
209
|
|
303
210
|
it("getOwnedTokenIds should work", async () => {
|
@@ -4,7 +4,7 @@ import { getCurrencyMetadata } from "./getCurrencyMetadata.js";
|
|
4
4
|
|
5
5
|
describe("getCurrencyMetadata", () => {
|
6
6
|
it("should throw if not a valid ERC20 contract", async () => {
|
7
|
-
await expect(
|
7
|
+
await expect(
|
8
8
|
getCurrencyMetadata({ contract: DOODLES_CONTRACT }),
|
9
9
|
).rejects.toThrowError("Invalid currency token");
|
10
10
|
});
|
@@ -65,19 +65,9 @@ describe.runIf(process.env.TW_SECRET_KEY)(
|
|
65
65
|
});
|
66
66
|
|
67
67
|
await expect(nextTokenIdToMint({ contract })).resolves.toBe(6n);
|
68
|
-
await
|
69
|
-
|
70
|
-
)
|
71
|
-
{
|
72
|
-
"id": 0n,
|
73
|
-
"metadata": {
|
74
|
-
"name": "Test NFT",
|
75
|
-
},
|
76
|
-
"owner": null,
|
77
|
-
"tokenURI": "ipfs://QmTo68Dm1ntSp2BHLmE9gesS6ELuXosRz5mAgFCK6tfsRk/0",
|
78
|
-
"type": "ERC721",
|
79
|
-
}
|
80
|
-
`);
|
68
|
+
expect((await getNFT({ contract, tokenId: 0n })).metadata.name).toBe(
|
69
|
+
"Test NFT",
|
70
|
+
);
|
81
71
|
});
|
82
72
|
|
83
73
|
it("should allow to claim tokens", async () => {
|
@@ -96,19 +96,9 @@ describe.runIf(process.env.TW_SECRET_KEY)(
|
|
96
96
|
});
|
97
97
|
|
98
98
|
await expect(nextTokenIdToMint({ contract })).resolves.toBe(10n);
|
99
|
-
await
|
100
|
-
|
101
|
-
)
|
102
|
-
{
|
103
|
-
"id": 0n,
|
104
|
-
"metadata": {
|
105
|
-
"name": "Test NFT",
|
106
|
-
},
|
107
|
-
"owner": null,
|
108
|
-
"tokenURI": "ipfs://QmY1Rr4C7cYVPAaXykMMxg3AVbatDZ6Rd7u3gzt79CiDSB/0",
|
109
|
-
"type": "ERC721",
|
110
|
-
}
|
111
|
-
`);
|
99
|
+
expect((await getNFT({ contract, tokenId: 0n })).metadata.name).toBe(
|
100
|
+
"Test NFT",
|
101
|
+
);
|
112
102
|
});
|
113
103
|
|
114
104
|
it("should allow to claim tokens", async () => {
|
@@ -6,6 +6,7 @@ import {
|
|
6
6
|
type TokenURIParams,
|
7
7
|
tokenURI,
|
8
8
|
} from "../__generated__/IERC721A/read/tokenURI.js";
|
9
|
+
import { tokenByIndex } from "../__generated__/IERC721Enumerable/read/tokenByIndex.js";
|
9
10
|
|
10
11
|
export { isTokenURISupported as isGetNFTSupported } from "../__generated__/IERC721A/read/tokenURI.js";
|
11
12
|
|
@@ -19,6 +20,13 @@ export type GetNFTParams = Prettify<
|
|
19
20
|
* Whether to include the owner of the NFT.
|
20
21
|
*/
|
21
22
|
includeOwner?: boolean;
|
23
|
+
/**
|
24
|
+
* Whether to check and fetch tokenID by index, in case of non-sequential IDs.
|
25
|
+
*
|
26
|
+
* It should be set to true if it's an ERC721Enumerable contract, and has `tokenByIndex` function.
|
27
|
+
* In this case, the provided tokenId will be considered as token-index and actual tokenId will be fetched from the contract.
|
28
|
+
*/
|
29
|
+
tokenByIndex?: boolean;
|
22
30
|
}
|
23
31
|
>;
|
24
32
|
|
@@ -35,15 +43,37 @@ export type GetNFTParams = Prettify<
|
|
35
43
|
* tokenId: 1n,
|
36
44
|
* });
|
37
45
|
* ```
|
46
|
+
*
|
47
|
+
* * @example
|
48
|
+
* ```ts
|
49
|
+
* import { getNFT } from "thirdweb/extensions/erc721";
|
50
|
+
*
|
51
|
+
*
|
52
|
+
* const nft = await getNFT({
|
53
|
+
* contract,
|
54
|
+
* tokenId: 1n,
|
55
|
+
* tokenByIndex: true // use this flag if the contract supports `tokenByIndex` and the above tokenId should be treated as an index.
|
56
|
+
* });
|
57
|
+
* ```
|
38
58
|
*/
|
39
59
|
export async function getNFT(
|
40
60
|
options: BaseTransactionOptions<GetNFTParams>,
|
41
61
|
): Promise<NFT> {
|
62
|
+
let tokenId = options.tokenId;
|
63
|
+
if (options.tokenByIndex) {
|
64
|
+
try {
|
65
|
+
tokenId = await tokenByIndex({
|
66
|
+
contract: options.contract,
|
67
|
+
index: options.tokenId,
|
68
|
+
});
|
69
|
+
} catch {}
|
70
|
+
}
|
71
|
+
|
42
72
|
const [uri, owner] = await Promise.all([
|
43
|
-
tokenURI(options).catch(() => null),
|
73
|
+
tokenURI({ contract: options.contract, tokenId }).catch(() => null),
|
44
74
|
options.includeOwner
|
45
75
|
? import("../__generated__/IERC721A/read/ownerOf.js")
|
46
|
-
.then((m) => m.ownerOf(options))
|
76
|
+
.then((m) => m.ownerOf({ contract: options.contract, tokenId }))
|
47
77
|
.catch(() => null)
|
48
78
|
: null,
|
49
79
|
]);
|
@@ -51,12 +81,12 @@ export async function getNFT(
|
|
51
81
|
if (!uri?.trim()) {
|
52
82
|
return parseNFT(
|
53
83
|
{
|
54
|
-
id:
|
84
|
+
id: tokenId,
|
55
85
|
type: "ERC721",
|
56
86
|
uri: "",
|
57
87
|
},
|
58
88
|
{
|
59
|
-
tokenId
|
89
|
+
tokenId,
|
60
90
|
tokenUri: "",
|
61
91
|
type: "ERC721",
|
62
92
|
owner,
|
@@ -67,15 +97,15 @@ export async function getNFT(
|
|
67
97
|
return parseNFT(
|
68
98
|
await fetchTokenMetadata({
|
69
99
|
client: options.contract.client,
|
70
|
-
tokenId
|
100
|
+
tokenId,
|
71
101
|
tokenUri: uri,
|
72
102
|
}).catch(() => ({
|
73
|
-
id:
|
103
|
+
id: tokenId,
|
74
104
|
type: "ERC721",
|
75
105
|
uri,
|
76
106
|
})),
|
77
107
|
{
|
78
|
-
tokenId:
|
108
|
+
tokenId: tokenId,
|
79
109
|
tokenUri: uri,
|
80
110
|
type: "ERC721",
|
81
111
|
owner,
|
@@ -189,7 +189,7 @@ describe.runIf(process.env.TW_SECRET_KEY)("erc721.getNFTs", () => {
|
|
189
189
|
});
|
190
190
|
|
191
191
|
it("should throw error if totalSupply and nextTokenIdToMint are not supported", async () => {
|
192
|
-
await expect(
|
192
|
+
await expect(
|
193
193
|
getNFTs({ contract: UNISWAPV3_FACTORY_CONTRACT }),
|
194
194
|
).rejects.toThrowError(
|
195
195
|
"Contract requires either `nextTokenIdToMint` or `totalSupply` function available to determine the next token ID to mint",
|
@@ -34,6 +34,13 @@ export type GetNFTsParams = {
|
|
34
34
|
* @default false
|
35
35
|
*/
|
36
36
|
includeOwners?: boolean;
|
37
|
+
/**
|
38
|
+
* Whether to check and fetch tokenID by index, in case of non-sequential IDs.
|
39
|
+
*
|
40
|
+
* It should be set to true if it's an ERC721Enumerable contract, and has `tokenByIndex` function.
|
41
|
+
* In this case, the provided tokenId will be considered as token-index and actual tokenId will be fetched from the contract.
|
42
|
+
*/
|
43
|
+
tokenByIndex?: boolean;
|
37
44
|
};
|
38
45
|
|
39
46
|
/**
|
@@ -22,7 +22,7 @@ describe.runIf(process.env.TW_SECRET_KEY)("erc721.getOwnedTokenIds", () => {
|
|
22
22
|
it("should throw if tokenOfOwnerByIndex or tokensOfOwner not supported", async () => {
|
23
23
|
// We know current Lens contract on Polygon doesn't have this.
|
24
24
|
const contract = UNISWAPV3_FACTORY_CONTRACT;
|
25
|
-
await expect(
|
25
|
+
await expect(
|
26
26
|
getOwnedTokenIds({ contract, owner: TEST_ACCOUNT_B.address }),
|
27
27
|
).rejects.toThrowError(
|
28
28
|
`The contract at ${contract.address} on chain ${contract.chain.id} does not support the tokenOfOwnerByIndex or tokensOfOwner interface`,
|
@@ -14,7 +14,7 @@ describe.runIf(process.env.TW_SECRET_KEY)("resolve lens address", () => {
|
|
14
14
|
});
|
15
15
|
|
16
16
|
it("should throw if passed an invalid lens handle", async () => {
|
17
|
-
await expect(
|
17
|
+
await expect(
|
18
18
|
resolveAddress({ client: TEST_CLIENT, name: "vitalik.eth" }),
|
19
19
|
).rejects.toThrowError(
|
20
20
|
"Could not fetch the wallet address for lens handle: vitalik.eth",
|
@@ -9,7 +9,7 @@ const account = TEST_ACCOUNT_B;
|
|
9
9
|
|
10
10
|
describe.runIf(process.env.TW_SECRET_KEY)("deploy-voteERC20 contract", () => {
|
11
11
|
it("should throw if passed an non-integer-like value to minVoteQuorumRequiredPercent", async () => {
|
12
|
-
await expect(
|
12
|
+
await expect(
|
13
13
|
deployVoteContract({
|
14
14
|
account,
|
15
15
|
client: TEST_CLIENT,
|
@@ -18,7 +18,7 @@ describe.runIf(process.env.TW_SECRET_KEY)(
|
|
18
18
|
});
|
19
19
|
|
20
20
|
it("should throw error on addresses that dont own any UD", async () => {
|
21
|
-
await expect(
|
21
|
+
await expect(
|
22
22
|
resolveName({ client: TEST_CLIENT, address: TEST_ACCOUNT_D.address }),
|
23
23
|
).rejects.toThrowError(
|
24
24
|
`Failed to retrieve domain for address: ${TEST_ACCOUNT_D.address}. Make sure you have set the Reverse Resolution address for your domain at https://unstoppabledomains.com/manage?page=reverseResolution&domain=your-domain`,
|
package/src/gas/fee-data.ts
CHANGED
@@ -52,11 +52,13 @@ export async function getGasOverridesForTransaction(
|
|
52
52
|
transaction: PreparedTransaction,
|
53
53
|
): Promise<FeeDataParams> {
|
54
54
|
// first check for explicit values
|
55
|
-
const [maxFeePerGas, maxPriorityFeePerGas, gasPrice] =
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
55
|
+
const [maxFeePerGas, maxPriorityFeePerGas, gasPrice, type] =
|
56
|
+
await Promise.all([
|
57
|
+
resolvePromisedValue(transaction.maxFeePerGas),
|
58
|
+
resolvePromisedValue(transaction.maxPriorityFeePerGas),
|
59
|
+
resolvePromisedValue(transaction.gasPrice),
|
60
|
+
resolvePromisedValue(transaction.type),
|
61
|
+
]);
|
60
62
|
|
61
63
|
// Exit early if the user explicitly provided enough options
|
62
64
|
if (maxFeePerGas !== undefined && maxPriorityFeePerGas !== undefined) {
|
@@ -65,6 +67,7 @@ export async function getGasOverridesForTransaction(
|
|
65
67
|
maxPriorityFeePerGas,
|
66
68
|
};
|
67
69
|
}
|
70
|
+
|
68
71
|
if (typeof gasPrice === "bigint") {
|
69
72
|
return { gasPrice };
|
70
73
|
}
|
@@ -73,6 +76,7 @@ export async function getGasOverridesForTransaction(
|
|
73
76
|
const defaultGasOverrides = await getDefaultGasOverrides(
|
74
77
|
transaction.client,
|
75
78
|
transaction.chain,
|
79
|
+
type === "legacy" ? "legacy" : "eip1559", // 7702, 2930, and eip1559 all qualify as "eip1559" fee type
|
76
80
|
);
|
77
81
|
|
78
82
|
if (transaction.chain.experimental?.increaseZeroByteCount) {
|
@@ -103,6 +107,8 @@ export async function getGasOverridesForTransaction(
|
|
103
107
|
};
|
104
108
|
}
|
105
109
|
|
110
|
+
export type FeeType = "legacy" | "eip1559";
|
111
|
+
|
106
112
|
/**
|
107
113
|
* Retrieves the default gas overrides for a given client and chain ID.
|
108
114
|
* If the fee data contains both maxFeePerGas and maxPriorityFeePerGas, it returns an object with those values.
|
@@ -115,20 +121,27 @@ export async function getGasOverridesForTransaction(
|
|
115
121
|
export async function getDefaultGasOverrides(
|
116
122
|
client: ThirdwebClient,
|
117
123
|
chain: Chain,
|
124
|
+
feeType?: FeeType,
|
118
125
|
) {
|
119
|
-
//
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
126
|
+
// give priority to the transaction fee type over the chain fee type
|
127
|
+
const resolvedFeeType = feeType ?? chain.feeType;
|
128
|
+
// if chain is configured to force legacy transactions or is in the legacy chain list
|
129
|
+
if (
|
130
|
+
resolvedFeeType === "legacy" ||
|
131
|
+
FORCE_GAS_PRICE_CHAIN_IDS.includes(chain.id)
|
132
|
+
) {
|
133
|
+
return {
|
134
|
+
gasPrice: await getGasPrice({ client, chain, percentMultiplier: 10 }),
|
135
|
+
};
|
136
|
+
}
|
137
|
+
const feeData = await getDynamicFeeData(client, chain);
|
138
|
+
if (feeData.maxFeePerGas !== null && feeData.maxPriorityFeePerGas !== null) {
|
139
|
+
return {
|
140
|
+
maxFeePerGas: feeData.maxFeePerGas,
|
141
|
+
maxPriorityFeePerGas: feeData.maxPriorityFeePerGas,
|
142
|
+
};
|
131
143
|
}
|
144
|
+
// TODO: resolvedFeeType here could be "EIP1559", but we could not get EIP1559 fee data. should we throw?
|
132
145
|
return {
|
133
146
|
gasPrice: await getGasPrice({ client, chain, percentMultiplier: 10 }),
|
134
147
|
};
|
@@ -156,7 +169,7 @@ async function getDynamicFeeData(
|
|
156
169
|
eth_maxPriorityFeePerGas(rpcRequest).catch(() => null),
|
157
170
|
]);
|
158
171
|
|
159
|
-
const baseBlockFee = block?.baseFeePerGas
|
172
|
+
const baseBlockFee = block?.baseFeePerGas;
|
160
173
|
|
161
174
|
const chainId = chain.id;
|
162
175
|
// flag chain testnet & flag chain
|
@@ -174,7 +187,7 @@ async function getDynamicFeeData(
|
|
174
187
|
maxPriorityFeePerGas_ = maxPriorityFeePerGas;
|
175
188
|
}
|
176
189
|
|
177
|
-
if (maxPriorityFeePerGas_ == null) {
|
190
|
+
if (maxPriorityFeePerGas_ == null || baseBlockFee == null) {
|
178
191
|
// chain does not support eip-1559, return null for both
|
179
192
|
return { maxFeePerGas: null, maxPriorityFeePerGas: null };
|
180
193
|
}
|
@@ -52,7 +52,7 @@ describe.runIf(process.env.TW_SECRET_KEY)("Pay: crypto-to-fiat", () => {
|
|
52
52
|
});
|
53
53
|
|
54
54
|
it("should throw error for testnet chain (because testnets are not supported", async () => {
|
55
|
-
await expect(
|
55
|
+
await expect(
|
56
56
|
convertCryptoToFiat({
|
57
57
|
chain: sepolia,
|
58
58
|
fromTokenAddress: NATIVE_TOKEN_ADDRESS,
|
@@ -66,7 +66,7 @@ describe.runIf(process.env.TW_SECRET_KEY)("Pay: crypto-to-fiat", () => {
|
|
66
66
|
});
|
67
67
|
|
68
68
|
it("should throw error if fromTokenAddress is set to an invalid EVM address", async () => {
|
69
|
-
await expect(
|
69
|
+
await expect(
|
70
70
|
convertCryptoToFiat({
|
71
71
|
chain: ethereum,
|
72
72
|
fromTokenAddress: "haha",
|
@@ -80,7 +80,7 @@ describe.runIf(process.env.TW_SECRET_KEY)("Pay: crypto-to-fiat", () => {
|
|
80
80
|
});
|
81
81
|
|
82
82
|
it("should throw error if fromTokenAddress is set to a wallet address", async () => {
|
83
|
-
await expect(
|
83
|
+
await expect(
|
84
84
|
convertCryptoToFiat({
|
85
85
|
chain: base,
|
86
86
|
fromTokenAddress: TEST_ACCOUNT_A.address,
|
@@ -99,7 +99,7 @@ describe.runIf(process.env.TW_SECRET_KEY)("Pay: crypto-to-fiat", () => {
|
|
99
99
|
status: 400,
|
100
100
|
statusText: "Bad Request",
|
101
101
|
});
|
102
|
-
await expect(
|
102
|
+
await expect(
|
103
103
|
convertCryptoToFiat({
|
104
104
|
chain: base,
|
105
105
|
fromTokenAddress: NATIVE_TOKEN_ADDRESS,
|