Compare commits

...
Author SHA1 Message Date
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
7129aef6e1 Version Packages (#370)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-11-11 08:51:19 -08:00
Joaquim VergesandGitHub f50b74819d [SDK] make isConnected sync, remove useless fn (#381) 2022-11-11 06:14:24 +00:00
Joaquim VergesandGitHub 3eba69afd7 [SDK] expose signer's chain id via sdk.wallet (#380) 2022-11-11 00:42:05 +00:00
Jarrod WattsandGitHub 98c2253e8f [SDK] Docs - Add await before sdk.getContract snippets (#379) 2022-11-10 16:08:31 -08:00
32d91ef709 [REACT] Fix Border Radius for Safari (#378)
Co-authored-by: Jarrod Watts <[email protected]>
2022-11-10 09:53:17 -08:00
Adam MajmudarandGitHub b79dc18f7a Add event filtering on topics (#377) 2022-11-09 11:51:13 -05:00
Adam MajmudarandGitHub 4af785c37c Add TokensClaimed event to tiered-drop (#376) 2022-11-08 20:34:45 +00:00
Adam MajmudarandGitHub 6f818f393e Updated tiered-drop contract (#373) 2022-11-03 17:03:00 -07:00
Adam MajmudarandGitHub 97ed4779a9 Update nft drop claimTo documentation (#372) 2022-11-03 22:42:42 +00:00
Nacho IacovinoandGitHub 2d3fca5237 [React] Add useClaimerProofs hook (#369) 2022-11-03 01:17:15 +01:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
7016019f2a Version Packages (#366)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-11-02 15:16:25 -07:00
Joaquim VergesandGitHub 4e7b293c54 [SDK] Fix handling of old allowlist proof format (#368) 2022-11-02 14:11:13 -07:00
Jonas DanielsandGitHub c9c0e4ff39 [SDK] - make EVM side react native compatible (#365) 2022-11-02 05:38:56 +00:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
06c24c153a Version Packages (#361)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-11-01 16:13:47 -07:00
aa6d9ed75d [SDK] Support new Drop claim conditions mechanics (#333)
Co-authored-by: adam-maj <[email protected]>
Co-authored-by: Adam Majmudar <[email protected]>
2022-11-01 23:07:10 +00:00
Joaquim VergesandGitHub 42a606588d [SDK] Fix fetching NFTs with numbers as ids (#362) 2022-11-01 22:01:01 +00:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
2820761003 Version Packages (#360)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-11-01 13:49:34 -07:00
Adam MajmudarandGitHub 5200d5d7e7 Add support for TieredDrop in extensions (#329) 2022-11-01 13:48:48 -07:00
Joaquim VergesandGitHub e731c1a7c3 [SDK] Allow fetching claimerProofs from any claim condition (#359) 2022-10-31 23:26:27 +00:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
2e8d3fd6e7 Version Packages (#358)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-10-28 18:14:35 -07:00
Nacho IacovinoandGitHub cc4613b5fd [React] Add Pack and Multiwrap as NFTContract types (#356) 2022-10-29 01:28:40 +02:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
78566babaa Version Packages (#354)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-10-28 10:29:57 -07:00
Joaquim VergesandGitHub 65fc7ba6ff [SDK] Expose claimCondtions.getClaimerProofs(address) (#357) 2022-10-28 10:25:36 -07:00
Nacho IacovinoandGitHub 1c24c3c3c4 [Multi] Add useUpdateCreator hook, fix CreatorInput schema (#353) 2022-10-26 18:19:32 +00:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
408c5a81fa Version Packages (#347)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-10-26 08:38:25 -07:00
Joaquim VergesandGitHub 352291791e [SDK, React] expose marketplace.auction.executeSale (#352) 2022-10-26 00:19:01 +00:00
Jonas DanielsandGitHub aabbb149e8 [React] - fix wallet connection during hot reload (#351) 2022-10-25 22:48:39 +00:00
Jonas DanielsandGitHub d59450f814 do not release e2e tests (#350) 2022-10-25 20:29:22 +00:00
7d9a4c6abc Update name of useRoyalty, and update (#348)
Co-authored-by: Jonas Daniels <[email protected]>
2022-10-25 19:56:09 +00:00
Jonas DanielsandGitHub 99806e595d [VARIOUS] - exclude sizes of test files etc (#349) 2022-10-25 18:39:30 +00:00
Nacho IacovinoandGitHub 21c21c1adf resolveReleasesFromAddress -> resolveContractUriFromAddress (#344) 2022-10-24 23:43:43 +00:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
4b10719047 Version Packages (#345)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-10-24 16:18:44 -07:00
Joaquim VergesandGitHub 989cf7347c [React, SDK] Small fixes for useOffers and makeOffers (#346) 2022-10-24 23:09:32 +00:00
Joaquim VergesandGitHub 72227b2e16 [SDK, React] Expose new useOffers and useMakeOffer hooks (#343) 2022-10-24 15:28:34 -07:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
ed6c54f20d Version Packages (#336)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-10-21 13:26:24 -07:00
Joaquim VergesandGitHub 26116a6f63 [SDK] Update to new univeral RPC endpoint (#342) 2022-10-21 13:21:11 -07:00
Jonas DanielsandGitHub f11a2719ff [workflow] github hosted runners (#340) 2022-10-20 22:02:58 -07:00
Jonas DanielsandGitHub ef27aad0aa [Various, React] - add initial e2e testing setup (#327) 2022-10-21 04:19:04 +00:00
Nacho IacovinoandGitHub b03a902145 [React] Docs update (#339) 2022-10-20 20:17:01 -07:00
Adam MajmudarandGitHub 340605b507 Clean native token address in changeset (#337) 2022-10-20 23:57:55 +00:00
Adam MajmudarandGitHub 15f8006e1f Export smart contract (#335) 2022-10-20 23:46:02 +00:00
Adam MajmudarandGitHub 811c5a1166 Update code snippets for signature minting (#334) 2022-10-20 23:29:07 +00:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
c41a80d86d Version Packages (#331)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-10-20 15:35:12 -07:00
Joaquim VergesandGitHub ad851f7b4d [SDK] Fix for finding addresses in legacy merkle tree format (#332) 2022-10-20 21:35:52 +00:00
Joaquim VergesandGitHub 6218c88584 [SDK] Fix for deploy byte parameters (#330) 2022-10-20 04:47:55 +00:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
c9b564feaf Version Packages (#323)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-10-19 21:19:06 -07:00
Adam MajmudarandGitHub 87fd6ab14e Update storage download on fail (#324) 2022-10-19 12:44:36 -07:00
Joaquim VergesandGitHub df74340fa5 [SDK] Import cbor-x fully for es builds (#328) 2022-10-19 17:46:54 +00:00
Joaquim VergesandGitHub a57b4f01f3 [SDK] Implement sharded merkle trees for lightweight allowlist checks (#321) 2022-10-19 16:54:09 +00:00
Jonas DanielsandGitHub 126752d7d0 [Various] - clean up extraneous dependencies (#326) 2022-10-18 23:56:04 -07:00
89ff921a7d Fix vite (#315)
Co-authored-by: Jonas Daniels <[email protected]>
Co-authored-by: Joaquim Verges <[email protected]>
2022-10-18 15:23:57 -07:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
9124b3180c Version Packages (#308)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-10-18 15:02:14 -07:00
Adam MajmudarandGitHub f41a443c57 Add update creators and update royalty (#320) 2022-10-18 21:50:47 +00:00
Joaquim VergesandGitHub b7cd744cd0 [SDK] Sign multiple solana claims at once (#322) 2022-10-18 21:35:03 +00:00
bb1b4e6ac3 [React] Add useMinimumNextBid hook (#318)
Co-authored-by: Jonas Daniels <[email protected]>
2022-10-18 11:57:26 -07:00
2f8ec89c4c [SDK, React] Expose total claimed/unclaimed supply for erc721 contracts (#319)
Co-authored-by: Nacho Iacovino <[email protected]>
2022-10-17 18:17:54 -07:00
Joaquim VergesandGitHub 6ae52664e5 [React] Fix passing wrong RPC url to UserWallet when using an external signer (#316) 2022-10-17 22:26:15 +00:00
Adam MajmudarandGitHub 4f867e15ed Fix supply in nft.get() (#317) 2022-10-17 19:17:43 +00:00
cc8f983615 fix typo (#314)
Co-authored-by: Joaquim Verges <[email protected]>
2022-10-16 22:27:40 +00:00
Joaquim VergesandGitHub f45ef62128 [SDK] return fallback metadata when tokenId is not found on nft contract (#313) 2022-10-16 15:15:26 -07:00
9727502eb3 Add useMintNFTSupply hook (#310)
Co-authored-by: Jonas Daniels <[email protected]>
2022-10-14 13:32:43 -07:00
Adam MajmudarandGitHub b6eec6146c [Solana] Add quantity to mint additional supply (#312) 2022-10-14 19:23:07 +00:00
88daa3c5cb [SDK] Check for undefined ABI (#309)
Co-authored-by: Joaquim Verges <[email protected]>
2022-10-14 03:37:40 +00:00
Adam MajmudarandGitHub 53c6507669 Add deploy event listener (#306) 2022-10-13 21:28:24 +00:00
YashandGitHub 782b58d3f6 [SDK] Fix multicall txns in a gasless setup (#307) 2022-10-13 11:57:02 -07:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
e17e41f4d1 Version Packages (#304)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-10-12 15:22:33 -07:00
Joaquim VergesandGitHub 712b11a43e [SDK] Fix not getting real owner of Solana NFTs (#303) 2022-10-12 22:12:53 +00:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
306debb029 Version Packages (#302)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-10-12 10:57:32 -07:00
Joaquim VergesandGitHub 86f0ceff46 [React] Remove useAddress hook for now (#301) 2022-10-12 17:54:14 +00:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
7f0d1ec947 Version Packages (#300)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-10-12 10:23:15 -07:00
Joaquim VergesandGitHub b1218cbd7d [React] Recreate SDK on wallet changes to propagate to useAddress and other hooks (#299) 2022-10-12 10:12:21 -07:00
Joaquim Verges 97ef35ce0f [SDK] docstrings for claim conditions 2022-10-12 08:48:58 -07:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
1f46f270cd Version Packages (#267)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-10-12 00:36:55 -07:00
Jonas DanielsandGitHub e8c25ed0f2 [React] - fix getBalance (#298) 2022-10-12 05:28:15 +00:00
Jonas DanielsandGitHub 78d6d3c159 [SDK] - fix solana getAddress (#297) 2022-10-12 04:49:39 +00:00
Joaquim Verges c72d92956a update generate snippets scripts 2022-10-11 21:28:39 -07:00
Jonas DanielsandGitHub cf88795376 [SDK, React] - useBalance hook (#296) 2022-10-12 04:15:06 +00:00
Jonas DanielsandGitHub 47287fb189 [Various] - fix lint (#295) 2022-10-11 21:02:38 -07:00
Joaquim Verges 3763326c25 [React] Fix docstrings 2022-10-11 20:32:10 -07:00
Joaquim Verges 894eb27dfa [SDK] Fix docstrings 2022-10-11 20:21:11 -07:00
Joaquim VergesandGitHub 8b3b97c8f7 [SDK] Fix getAllClaimed for Solana NFT drops (#293) 2022-10-12 00:31:19 +00:00
Jonas DanielsandGitHub 89d87a5eb3 [SDK] - fix batch upload for lazy mint (#294) 2022-10-12 00:18:15 +00:00
Jonas DanielsandGitHub 492a818079 [SOL] - expose useAddress() (#292) 2022-10-11 21:10:43 +00:00
Jonas DanielsandGitHub 86f608390a [React] - expose .program on useProgram() (#291) 2022-10-11 21:00:56 +00:00
Jonas DanielsandGitHub 8096c78b46 [SDK, React] - claim changes (#290) 2022-10-11 20:48:51 +00:00
Nacho IacovinoandGitHub 521a49c6ec [Solana] Standarize use claim (#289) 2022-10-11 10:50:35 -07:00
Joaquim VergesandGitHub 25a4773bba [SDK] Add pagination to getAll() for solana nft collections (#286) 2022-10-11 05:16:20 +00:00
18381fac73 Fix errorneous ERC20 Allowance check (#280)
Co-authored-by: Joaquim Verges <[email protected]>
2022-10-10 21:56:16 +00:00
Joaquim VergesandGitHub 89b5c6fe07 [SDK] Make solana drop creation and register transactions sequential (#285) 2022-10-10 14:43:09 -07:00
Joaquim VergesandGitHub 4dccccc6fa [SDK] Solana docs update (#284) 2022-10-10 12:44:55 -07:00
Jarrod WattsandGitHub de15e4212f [REACT] - Update Broken Links to Portal in README (#281) 2022-10-10 00:54:37 +00:00
Jarrod WattsandGitHub 1f41adbd2c [REACT] - Fix useActiveClaimCondition invariant failing for token ID 0 (#282) 2022-10-10 11:13:00 +11:00
Furqan RydhanandGitHub 1918495f2e Command Updates (detect -> build) + helper text (#279) 2022-10-09 16:21:07 -07:00
24514a6e9f Fr/command updates (#276)
Co-authored-by: Joaquim Verges <[email protected]>
2022-10-08 03:29:20 +00:00
Adam MajmudarandGitHub ad06c5b289 Add auth support to react and auth packages (#274) 2022-10-07 21:20:36 +00:00
Adam MajmudarandGitHub 9de3f8c8e9 Add support for recursive folder upload (#277) 2022-10-07 19:09:40 +00:00
Joaquim VergesandGitHub b1fa17158d [SDK] Expose maxClaimable and disallow claiming drops by default (#273) 2022-10-07 02:16:52 +00:00
Nacho IacovinoandGitHub 2bdf1984cb [Solana] Add claim conditions hooks (#272) 2022-10-07 02:36:20 +02:00
Adam MajmudarandGitHub df048af605 Add support for auth to Solana (#271) 2022-10-06 17:05:18 -07:00
Joaquim VergesandGitHub 41507d32a7 [SDK] Solana Program Registry Integration (#270) 2022-10-06 16:21:17 -07:00
Jonas DanielsandGitHub b2cadf164c [SDK, React] - remove deprecated chains (#269) 2022-10-06 00:20:53 -07:00
Adam MajmudarandGitHub 57b9c671aa [CLI] Update storage upload CLI (#268) 2022-10-05 15:29:16 -07:00
Adam MajmudarandGitHub 3b7d10ed93 [React] Add hook to create a storage upload component (#266) 2022-10-05 15:08:43 -07:00
Jonas DanielsandGitHub d6bb61b7fa [SDK] - align getProgram & make chainId getter available on EVM contracts (#263) 2022-10-05 20:06:34 +00:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
8e591383fc Version Packages (#265)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-10-05 12:53:02 -07:00
Adam MajmudarandGitHub 6f95a520e6 [CLI] Add npx thirdweb upload command (#259) 2022-10-05 18:42:03 +00:00
Nacho IacovinoandGitHub f669d3ef4a [React] Use same data structure for useLazyMint (#264) 2022-10-05 11:25:42 -07:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
83575058db Version Packages (#235)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-10-04 21:02:13 -07:00
Jonas DanielsandGitHub 028cb78125 [SDK] - fix HexColor schema (#262) 2022-10-05 01:31:10 +00:00
Jonas DanielsandGitHub c8261b74b5 [SDK, React] - align getContract and useContract across sdk and react (#261) 2022-10-04 18:09:36 -07:00
Jonas DanielsandGitHub a36c788415 Create size.yml 2022-10-04 12:47:09 -07:00
Nacho IacovinoandGitHub ac232b5aff Accept TokenContract instead of erc20 instance (#258) 2022-10-04 18:39:13 +00:00
Jonas DanielsandGitHub ba6f45027a [SDK] - fix permissions enumerable export (#257) 2022-10-04 17:51:10 +00:00
Jonas DanielsandGitHub d677e9d634 [React] - enable permissions enumerable feature (#256) 2022-10-04 17:21:22 +00:00
Jonas DanielsandGitHub 9b92697fb7 [React] - useContract can accept an abi now (#255) 2022-10-04 04:40:16 +00:00
Joaquim VergesandGitHub c19e471f65 [React] Update react docs (#254) 2022-10-03 21:13:07 -07:00
Jonas DanielsandGitHub b6fc298d0c [React] - allow passing in contractType in useContract() (#253) 2022-10-03 20:18:07 -07:00
Jonas DanielsandGitHub ea41231b3e [React] - fix usage of Buffer (#251) 2022-10-04 01:26:37 +00:00
Joaquim VergesandGitHub 2ea3f00c41 [SDK] Expose additional claim condition information (#252) 2022-10-04 00:26:46 +00:00
20745f8cd6 [SDK] Add check if abi exists (#250)
Co-authored-by: Jonas Daniels <[email protected]>
2022-10-03 22:36:32 +02:00
Furqan RydhanandGitHub caf979537b AppURI detection enabled [SDK] (#247) 2022-10-03 19:12:09 +00:00
Joaquim VergesandGitHub e4b10bc5bd [SDK] Polish + Fixes for Solana NFT Drops (#249) 2022-10-03 12:03:16 -07:00
ef0bac7f44 Renamed initial commit message during thirdweb cli createApp (#246)
Co-authored-by: simon.lim <[email protected]>
Co-authored-by: Jonas Daniels <[email protected]>
2022-10-02 16:43:30 +00:00
Jonas DanielsandGitHub 23de9eac4f Update README.md 2022-10-02 09:38:13 -07:00
Jonas DanielsandGitHub 1972f3ec0d [SDK, React] - useBurnNFT for solana (#245) 2022-10-01 01:46:07 +00:00
Adam MajmudarandGitHub ffd0fc42ae [React] Add code snippets for Solana (#244) 2022-09-30 15:28:21 -07:00
Jonas Daniels 93531a5d47 fix build outputs 2022-09-30 15:16:16 -07:00
Joaquim Verges b16aaebc4c update solana docs 2022-09-30 10:30:32 -07:00
Jonas DanielsandGitHub 1136d959ba fix persister logic (#243) 2022-09-30 04:38:40 +00:00
Jonas DanielsandGitHub db23f0588d [Storage] - send different header in tests (#242) 2022-09-29 20:14:53 -07:00
Jonas DanielsandGitHub e662408e19 [SDK] - unify program metadata return type again (#240) 2022-09-30 01:41:08 +00:00
Adam MajmudarandGitHub afe4ba6471 Update solana docs (#239) 2022-09-30 00:27:45 +00:00
Joaquim VergesandGitHub 6647f707bd [SDK, React] Unify NFT types for EVM and Solana (#238) 2022-09-30 00:12:37 +00:00
Jonas DanielsandGitHub cac373b010 [React, SDK] - solana improvements (#236) 2022-09-29 06:51:16 +00:00
Joaquim VergesandGitHub 9412d41683 [SDK] add burn function, better RPC, rename inputs (#234) 2022-09-28 22:16:09 -07:00
344 changed files with 22984 additions and 7338 deletions
+2 -2
View File
@@ -2,12 +2,12 @@
"$schema": "https://unpkg.com/@changesets/[email protected]/schema.json",
"changelog": ["@changesets/changelog-github", { "repo": "thirdweb-dev/js" }],
"commit": false,
"fixed": [],
"fixed": [["@thirdweb-dev/sdk", "@thirdweb-dev/react"]],
"linked": [],
"access": "public",
"baseBranch": "main",
"updateInternalDependencies": "patch",
"ignore": [],
"ignore": ["e2e-cra-5", "e2e-next-12", "e2e-vite-3"],
"___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH": {
"updateInternalDependents": "always",
"onlyUpdatePeerDependentsWhenOutOfRange": true
+2 -2
View File
@@ -9,10 +9,10 @@ runs:
with:
node-version: 16
registry-url: "https://registry.npmjs.org"
# cache: "yarn"
cache: "yarn"
- name: Install dependencies
shell: bash
run: yarn install --frozen-lockfile
run: yarn install
env:
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: "1"
+3 -6
View File
@@ -14,8 +14,7 @@ jobs:
build:
name: Build and Test
# timeout-minutes: 15
# try out buildjet to speed up build and test
runs-on: buildjet-32vcpu-ubuntu-2204
runs-on: ubuntu-latest-32
# To use Remote Caching, uncomment the next lines and follow the steps below.
env:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
@@ -30,8 +29,6 @@ jobs:
- name: Install
uses: ./.github/composite-actions/install
- name: Build
run: yarn build
- name: Test
# this already builds anything that changed...
- name: Build & Test
run: yarn test
+1 -8
View File
@@ -13,8 +13,7 @@ concurrency:
jobs:
integration-tests:
timeout-minutes: 60
# try out buildjet to speed up build and test
runs-on: buildjet-32vcpu-ubuntu-2204
runs-on: ubuntu-latest-32
steps:
- uses: actions/checkout@v3
@@ -28,9 +27,3 @@ jobs:
run: yarn e2e
env:
NODE_OPTIONS: "--max_old_space_size=4096"
- uses: actions/upload-artifact@v2
if: always()
with:
name: playwright-report
path: packages/react/playwright-report/
retention-days: 30
+1 -2
View File
@@ -40,8 +40,7 @@ jobs:
comment_body: ${{ github.event.comment.body }}
deploy:
# try out buildjet to speed up build and test
runs-on: buildjet-32vcpu-ubuntu-2204
runs-on: ubuntu-latest-32
needs: deploy-check
if: needs.deploy-check.outputs.triggered == 'true'
steps:
+1 -2
View File
@@ -14,8 +14,7 @@ concurrency:
jobs:
release:
name: Release
# try out buildjet to speed up build and test
runs-on: buildjet-32vcpu-ubuntu-2204
runs-on: ubuntu-latest-32
steps:
- name: Checkout branch
uses: actions/checkout@v3
+19
View File
@@ -0,0 +1,19 @@
name: Compressed Size
on: [pull_request]
jobs:
build:
runs-on: ubuntu-latest-32
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 1
- uses: preactjs/compressed-size-action@v2
with:
repo-token: "${{ secrets.GITHUB_TOKEN }}"
strip-hash: "\\-(\\w{8})(?:\\.esm|\\.cjs)"
minimum-change-threshold: 100
# exclude source maps, node modules and any test related files
exclude: "{**/*.map,**/node_modules/**,**/test/**}"
+6 -1
View File
@@ -12,4 +12,9 @@ server/dist
public/dist
.turbo
./packages/contracts-js/abis
.idea/
.idea/
.yalc/
yalc.lock
build/
playwright-report/
.vscode/
-1
View File
@@ -1,4 +1,3 @@
lib
node_modules
dist
contracts-js
+1 -1
View File
@@ -27,7 +27,7 @@
| [/auth](./packages/auth) | Best in class wallet authentication for Node backends | <a href="https://www.npmjs.com/package/@thirdweb-dev/auth"><img src="https://img.shields.io/npm/v/@thirdweb-dev/auth?color=red&label=npm&logo=npm" alt="npm version"/></a> |
| [/storage](./packages/storage) | Best in class decentralized storage SDK for Browser and Node | <a href="https://www.npmjs.com/package/@thirdweb-dev/storage"><img src="https://img.shields.io/npm/v/@thirdweb-dev/storage?color=red&label=npm&logo=npm" alt="npm version"/></a> |
| [/cli](./packages/cli) | Publish and deploy smart contracts without dealing with private keys | <a href="https://www.npmjs.com/package/thirdweb"><img src="https://img.shields.io/npm/v/thirdweb?color=red&label=npm&logo=npm" alt="npm version"/></a> |
| [/solana](./packages/solana) | Solana SDK for Browser, Node and React Native | <a href="https://www.npmjs.com/package/@thirdweb-dev/solana"><img src="https://img.shields.io/npm/v/@thirdweb-dev/solana?color=red&label=npm&logo=npm" alt="npm version"/></a> |
## Documentation
+6 -10
View File
@@ -8,14 +8,15 @@
"private": true,
"workspaces": {
"packages": [
"packages/*"
"packages/*",
"test/**/*"
]
},
"scripts": {
"test": "turbo run test && turbo run test:evm && turbo run test:solana",
"test": "turbo run test test:evm test:solana",
"e2e": "turbo run e2e",
"build": "turbo run build",
"build:release": "turbo run build --force",
"build": "turbo run build --filter=./packages/*",
"build:release": "turbo run build --force --filter=./packages/*",
"dev": "turbo run dev --no-cache --parallel --continue",
"fix:repo": "manypkg fix",
"lint": "turbo run lint",
@@ -27,12 +28,7 @@
"release": "node ./scripts/pre-release.mjs && changeset publish",
"version-packages:nightly": "changeset version --snapshot nightly",
"release:nightly": "node ./scripts/pre-release.mjs && changeset publish --tag nightly",
"push": "turbo run push"
},
"preconstruct": {
"packages": [
"packages/*"
]
"push": "turbo run push --filter=./packages/*"
},
"packageManager": "[email protected]",
"dependencies": {
+44
View File
@@ -1,5 +1,49 @@
# @thirdweb-dev/auth
## 2.0.29
## 2.0.28
## 2.0.27
## 2.0.26
## 2.0.25
## 2.0.24
## 2.0.23
## 2.0.22
## 2.0.21
### Patch Changes
- [#327](https://github.com/thirdweb-dev/js/pull/327) [`ef27aad`](https://github.com/thirdweb-dev/js/commit/ef27aad0aafc4577e85f44dc77dfbe880bd239b5) Thanks [@jnsdls](https://github.com/jnsdls)! - enable e2e testing
## 2.0.20
## 2.0.19
## 2.0.18
## 2.0.17
## 2.0.16
## 2.0.15
## 2.0.14
### Patch Changes
- [#274](https://github.com/thirdweb-dev/js/pull/274) [`ad06c5b`](https://github.com/thirdweb-dev/js/commit/ad06c5b28902422f9b416d65255c64c937a5e046) Thanks [@adam-maj](https://github.com/adam-maj)! - Add solana auth support and plugins for react and auth package
## 2.0.13
## 2.0.12
## 2.0.11
## 2.0.10
+4
View File
@@ -0,0 +1,4 @@
{
"main": "dist/thirdweb-dev-auth-next-evm.cjs.js",
"module": "dist/thirdweb-dev-auth-next-evm.esm.js"
}
+4
View File
@@ -0,0 +1,4 @@
{
"main": "dist/thirdweb-dev-auth-next-solana.cjs.js",
"module": "dist/thirdweb-dev-auth-next-solana.esm.js"
}
+15 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/auth",
"version": "2.0.11",
"version": "2.0.29",
"exports": {
"./next": {
"module": "./next/dist/thirdweb-dev-auth-next.esm.js",
@@ -10,10 +10,18 @@
"module": "./express/dist/thirdweb-dev-auth-express.esm.js",
"default": "./express/dist/thirdweb-dev-auth-express.cjs.js"
},
"./next/evm": {
"module": "./next/evm/dist/thirdweb-dev-auth-next-evm.esm.js",
"default": "./next/evm/dist/thirdweb-dev-auth-next-evm.cjs.js"
},
"./next-auth": {
"module": "./next-auth/dist/thirdweb-dev-auth-next-auth.esm.js",
"default": "./next-auth/dist/thirdweb-dev-auth-next-auth.cjs.js"
},
"./next/solana": {
"module": "./next/solana/dist/thirdweb-dev-auth-next-solana.esm.js",
"default": "./next/solana/dist/thirdweb-dev-auth-next-solana.cjs.js"
},
"./package.json": "./package.json"
},
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/auth",
@@ -35,6 +43,8 @@
"preconstruct": {
"entrypoints": [
"next/index.ts",
"next/evm/index.ts",
"next/solana/index.ts",
"express/index.ts",
"next-auth/index.ts"
],
@@ -44,6 +54,7 @@
"exports": true
},
"devDependencies": {
"@babel/core": "^7.19.3",
"@babel/preset-env": "^7.18.10",
"@babel/preset-typescript": "^7.18.6",
"@microsoft/api-documenter": "^7.19.4",
@@ -62,11 +73,13 @@
"eslint-plugin-inclusive-language": "^2.2.0",
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-tsdoc": "^0.2.16",
"ethers": "^5.7.0",
"ethers": "^5.7.1",
"express": "^4.18.1",
"next": "^12.2.0",
"next-auth": "^4.10.3",
"prettier": "^2.7.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"typescript": "^4.7.4"
},
"peerDependencies": {
+68
View File
@@ -0,0 +1,68 @@
import loginHandler from "./routes/login";
import logoutHandler from "./routes/logout";
import userHandler from "./routes/user";
import {
ThirdwebAuthConfig,
ThirdwebAuthRoute,
ThirdwebAuthUser,
} from "./types";
import { ThirdwebSDK } from "@thirdweb-dev/sdk";
import cookieParser from "cookie-parser";
import { Express, NextFunction, Request, Response } from "express";
export * from "./types";
export function getUser(req: Request): ThirdwebAuthUser | null {
return req.user;
}
export function ThirdwebAuth(app: Express, cfg: ThirdwebAuthConfig) {
const ctx = {
...cfg,
sdk: ThirdwebSDK.fromPrivateKey(cfg.privateKey, "mainnet"),
};
const authUrl = cfg.authUrl?.replace(/\/$/, "") || "/auth";
app.use(cookieParser());
app.use(async (req: Request, _: Response, next: NextFunction) => {
const { sdk, domain } = ctx;
let user = null;
const token = req.cookies.thirdweb_auth_token;
if (token) {
try {
const address = await sdk.auth.authenticate(domain, token);
if (ctx.callbacks?.user) {
user = await ctx.callbacks.user(address);
}
user = { ...user, address };
} catch {
// No-op
}
}
req.user = user as ThirdwebAuthUser | null;
next();
});
app.get(`${authUrl}/:route`, (req: Request, res: Response) => {
const action = req.params.route as ThirdwebAuthRoute;
switch (action) {
case "login":
return loginHandler(req, res, ctx);
case "user":
return userHandler(req, res);
case "logout":
return logoutHandler(req, res);
default:
return res.status(400).json({
message: "Invalid route for authentication.",
});
}
});
}
+1 -68
View File
@@ -1,68 +1 @@
import loginHandler from "./routes/login";
import logoutHandler from "./routes/logout";
import userHandler from "./routes/user";
import {
ThirdwebAuthConfig,
ThirdwebAuthRoute,
ThirdwebAuthUser,
} from "./types";
import { ThirdwebSDK } from "@thirdweb-dev/sdk";
import cookieParser from "cookie-parser";
import { Express, NextFunction, Request, Response } from "express";
export * from "./types";
export function getUser(req: Request): ThirdwebAuthUser | null {
return req.user;
}
export function ThirdwebAuth(app: Express, cfg: ThirdwebAuthConfig) {
const ctx = {
...cfg,
sdk: ThirdwebSDK.fromPrivateKey(cfg.privateKey, "mainnet"),
};
const authUrl = cfg.authUrl?.replace(/\/$/, "") || "/auth";
app.use(cookieParser());
app.use(async (req: Request, _: Response, next: NextFunction) => {
const { sdk, domain } = ctx;
let user = null;
const token = req.cookies.thirdweb_auth_token;
if (token) {
try {
const address = await sdk.auth.authenticate(domain, token);
if (ctx.callbacks?.user) {
user = await ctx.callbacks.user(address);
}
user = { ...user, address };
} catch {
// No-op
}
}
req.user = user as ThirdwebAuthUser | null;
next();
});
app.get(`${authUrl}/:route`, (req: Request, res: Response) => {
const action = req.params.route as ThirdwebAuthRoute;
switch (action) {
case "login":
return loginHandler(req, res, ctx);
case "user":
return userHandler(req, res);
case "logout":
return logoutHandler(req, res);
default:
return res.status(400).json({
message: "Invalid route for authentication.",
});
}
});
}
export * from "./evm";
+145
View File
@@ -0,0 +1,145 @@
import { ThirdwebNextAuthConfig } from "./types";
import { ThirdwebSDK } from "@thirdweb-dev/sdk";
import { serialize } from "cookie";
import {
GetServerSidePropsContext,
NextApiRequest,
NextApiResponse,
} from "next";
import NextAuth, {
NextAuthOptions,
Session,
unstable_getServerSession,
} from "next-auth";
import CredentialsProvider from "next-auth/providers/credentials";
export function ThirdwebNextAuth(cfg: ThirdwebNextAuthConfig) {
const sdk = ThirdwebSDK.fromPrivateKey(cfg.privateKey, "mainnet");
function ThirdwebProvider(res: GetServerSidePropsContext["res"]) {
return CredentialsProvider({
name: "ThirdwebAuth",
credentials: {
payload: {
label: "Payload",
type: "text",
placeholder: "",
},
},
async authorize({ payload }: any) {
try {
const parsed = JSON.parse(payload);
const token = await sdk.auth.generateAuthToken(cfg.domain, parsed);
const address = await sdk.auth.authenticate(cfg.domain, token);
// Securely set httpOnly cookie on request to prevent XSS on frontend
// And set path to / to enable thirdweb_auth_token usage on all endpoints
res.setHeader(
"Set-Cookie",
serialize("thirdweb_auth_token", token, {
path: "/",
httpOnly: true,
secure: true,
sameSite: "strict",
}),
);
return { address };
} catch (err) {
return null;
}
},
});
}
function nextOptions(
req: GetServerSidePropsContext["req"],
res: GetServerSidePropsContext["res"],
): NextAuthOptions {
async function session({
session: _session,
}: {
session: Session;
}): Promise<Session> {
const token = req.cookies.thirdweb_auth_token || "";
try {
const address = await sdk.auth.authenticate(cfg.domain, token);
_session.user = { ..._session.user, address } as Session["user"];
return _session;
} catch {
return _session;
}
}
function signOut() {
res.setHeader(
"Set-Cookie",
serialize("thirdweb_auth_token", "", {
path: "/",
expires: new Date(Date.now() + 5 * 1000),
}),
);
}
const providers: NextAuthOptions["providers"] = [
...cfg.nextOptions.providers,
ThirdwebProvider(res),
];
const configSession = cfg.nextOptions.callbacks?.session;
const callbacks: NextAuthOptions["callbacks"] = {
...cfg.nextOptions.callbacks,
session: configSession
? async (params) => {
params.session = await session(params);
return configSession(params);
}
: session,
};
const configSignOut = cfg.nextOptions.events?.signOut;
const events: NextAuthOptions["events"] = {
...cfg.nextOptions.events,
signOut: configSignOut
? async (params) => {
signOut();
return configSignOut(params);
}
: signOut,
};
return {
...cfg.nextOptions,
providers,
callbacks,
events,
};
}
async function getUser(
...args:
| [NextApiRequest, NextApiResponse]
| [GetServerSidePropsContext["req"], GetServerSidePropsContext["res"]]
) {
return unstable_getServerSession(
args[0],
args[1],
nextOptions(args[0], args[1]),
);
}
function NextAuthHandler(...args: [] | [NextApiRequest, NextApiResponse]) {
if (args.length === 0) {
return (req: NextApiRequest, res: NextApiResponse) => {
return NextAuth(req, res, nextOptions(req, res));
};
}
return NextAuth(args[0], args[1], nextOptions(args[0], args[1]));
}
return {
NextAuthHandler,
getUser,
};
}
+1 -145
View File
@@ -1,145 +1 @@
import { ThirdwebNextAuthConfig } from "./types";
import { ThirdwebSDK } from "@thirdweb-dev/sdk";
import { serialize } from "cookie";
import {
GetServerSidePropsContext,
NextApiRequest,
NextApiResponse,
} from "next";
import NextAuth, {
NextAuthOptions,
Session,
unstable_getServerSession,
} from "next-auth";
import CredentialsProvider from "next-auth/providers/credentials";
export function ThirdwebNextAuth(cfg: ThirdwebNextAuthConfig) {
const sdk = ThirdwebSDK.fromPrivateKey(cfg.privateKey, "mainnet");
function ThirdwebProvider(res: GetServerSidePropsContext["res"]) {
return CredentialsProvider({
name: "ThirdwebAuth",
credentials: {
payload: {
label: "Payload",
type: "text",
placeholder: "",
},
},
async authorize({ payload }: any) {
try {
const parsed = JSON.parse(payload);
const token = await sdk.auth.generateAuthToken(cfg.domain, parsed);
const address = await sdk.auth.authenticate(cfg.domain, token);
// Securely set httpOnly cookie on request to prevent XSS on frontend
// And set path to / to enable thirdweb_auth_token usage on all endpoints
res.setHeader(
"Set-Cookie",
serialize("thirdweb_auth_token", token, {
path: "/",
httpOnly: true,
secure: true,
sameSite: "strict",
}),
);
return { address };
} catch (err) {
return null;
}
},
});
}
function nextOptions(
req: GetServerSidePropsContext["req"],
res: GetServerSidePropsContext["res"],
): NextAuthOptions {
async function session({
session: _session,
}: {
session: Session;
}): Promise<Session> {
const token = req.cookies.thirdweb_auth_token || "";
try {
const address = await sdk.auth.authenticate(cfg.domain, token);
_session.user = { ..._session.user, address } as Session["user"];
return _session;
} catch {
return _session;
}
}
function signOut() {
res.setHeader(
"Set-Cookie",
serialize("thirdweb_auth_token", "", {
path: "/",
expires: new Date(Date.now() + 5 * 1000),
}),
);
}
const providers: NextAuthOptions["providers"] = [
...cfg.nextOptions.providers,
ThirdwebProvider(res),
];
const configSession = cfg.nextOptions.callbacks?.session;
const callbacks: NextAuthOptions["callbacks"] = {
...cfg.nextOptions.callbacks,
session: configSession
? async (params) => {
params.session = await session(params);
return configSession(params);
}
: session,
};
const configSignOut = cfg.nextOptions.events?.signOut;
const events: NextAuthOptions["events"] = {
...cfg.nextOptions.events,
signOut: configSignOut
? async (params) => {
signOut();
return configSignOut(params);
}
: signOut,
};
return {
...cfg.nextOptions,
providers,
callbacks,
events,
};
}
async function getUser(
...args:
| [NextApiRequest, NextApiResponse]
| [GetServerSidePropsContext["req"], GetServerSidePropsContext["res"]]
) {
return unstable_getServerSession(
args[0],
args[1],
nextOptions(args[0], args[1]),
);
}
function NextAuthHandler(...args: [] | [NextApiRequest, NextApiResponse]) {
if (args.length === 0) {
return (req: NextApiRequest, res: NextApiResponse) => {
return NextAuth(req, res, nextOptions(req, res));
};
}
return NextAuth(args[0], args[1], nextOptions(args[0], args[1]));
}
return {
NextAuthHandler,
getUser,
};
}
export * from "./evm";
+83
View File
@@ -0,0 +1,83 @@
import loginHandler from "./routes/login";
import logoutHandler from "./routes/logout";
import userHandler from "./routes/user";
import {
ThirdwebAuthConfig,
ThirdwebAuthContext,
ThirdwebAuthRoute,
ThirdwebAuthUser,
} from "./types";
import { ThirdwebSDK } from "@thirdweb-dev/sdk";
import { NextRequest } from "next/server";
import {
GetServerSidePropsContext,
NextApiRequest,
NextApiResponse,
} from "next/types";
export * from "./types";
async function ThirdwebAuthRouter(
req: NextApiRequest,
res: NextApiResponse,
ctx: ThirdwebAuthContext,
) {
// Catch-all route must be named with [...thirdweb]
const { thirdweb } = req.query;
const action = thirdweb?.[0] as ThirdwebAuthRoute;
switch (action) {
case "login":
return await loginHandler(req, res, ctx);
case "user":
return await userHandler(req, res, ctx);
case "logout":
return await logoutHandler(req, res);
default:
return res.status(400).json({
message: "Invalid route for authentication.",
});
}
}
export function ThirdwebAuth(cfg: ThirdwebAuthConfig) {
const ctx = {
...cfg,
sdk: ThirdwebSDK.fromPrivateKey(cfg.privateKey, "mainnet"),
};
function ThirdwebAuthHandler(
...args: [] | [NextApiRequest, NextApiResponse]
) {
if (args.length === 0) {
return async (req: NextApiRequest, res: NextApiResponse) =>
await ThirdwebAuthRouter(req, res, ctx);
}
return ThirdwebAuthRouter(args[0], args[1], ctx);
}
async function getUser(
req: GetServerSidePropsContext["req"] | NextRequest | NextApiRequest,
) {
const { sdk, domain } = ctx;
let user: ThirdwebAuthUser | null = null;
const token =
typeof req.cookies.get === "function"
? (req.cookies as any).get("thirdweb_auth_token")
: (req.cookies as any).thirdweb_auth_token;
if (token) {
try {
const address = await sdk.auth.authenticate(domain, token);
user = { address };
} catch {
// No-op
}
}
return user;
}
return { ThirdwebAuthHandler, getUser };
}
+1 -83
View File
@@ -1,83 +1 @@
import loginHandler from "./routes/login";
import logoutHandler from "./routes/logout";
import userHandler from "./routes/user";
import {
ThirdwebAuthConfig,
ThirdwebAuthContext,
ThirdwebAuthRoute,
ThirdwebAuthUser,
} from "./types";
import { ThirdwebSDK } from "@thirdweb-dev/sdk";
import { NextRequest } from "next/server";
import {
GetServerSidePropsContext,
NextApiRequest,
NextApiResponse,
} from "next/types";
export * from "./types";
async function ThirdwebAuthRouter(
req: NextApiRequest,
res: NextApiResponse,
ctx: ThirdwebAuthContext,
) {
// Catch-all route must be named with [...thirdweb]
const { thirdweb } = req.query;
const action = thirdweb?.[0] as ThirdwebAuthRoute;
switch (action) {
case "login":
return await loginHandler(req, res, ctx);
case "user":
return await userHandler(req, res, ctx);
case "logout":
return await logoutHandler(req, res);
default:
return res.status(400).json({
message: "Invalid route for authentication.",
});
}
}
export function ThirdwebAuth(cfg: ThirdwebAuthConfig) {
const ctx = {
...cfg,
sdk: ThirdwebSDK.fromPrivateKey(cfg.privateKey, "mainnet"),
};
function ThirdwebAuthHandler(
...args: [] | [NextApiRequest, NextApiResponse]
) {
if (args.length === 0) {
return async (req: NextApiRequest, res: NextApiResponse) =>
await ThirdwebAuthRouter(req, res, ctx);
}
return ThirdwebAuthRouter(args[0], args[1], ctx);
}
async function getUser(
req: GetServerSidePropsContext["req"] | NextRequest | NextApiRequest,
) {
const { sdk, domain } = ctx;
let user: ThirdwebAuthUser | null = null;
const token =
typeof req.cookies.get === "function"
? (req.cookies as any).get("thirdweb_auth_token")
: (req.cookies as any).thirdweb_auth_token;
if (token) {
try {
const address = await sdk.auth.authenticate(domain, token);
user = { address };
} catch {
// No-op
}
}
return user;
}
return { ThirdwebAuthHandler, getUser };
}
export * from "./evm";
+83
View File
@@ -0,0 +1,83 @@
import loginHandler from "./routes/login";
import logoutHandler from "./routes/logout";
import userHandler from "./routes/user";
import {
ThirdwebAuthConfig,
ThirdwebAuthContext,
ThirdwebAuthRoute,
ThirdwebAuthUser,
} from "./types";
import { ThirdwebSDK } from "@thirdweb-dev/sdk/solana";
import { NextRequest } from "next/server";
import {
GetServerSidePropsContext,
NextApiRequest,
NextApiResponse,
} from "next/types";
export * from "./types";
async function ThirdwebAuthRouter(
req: NextApiRequest,
res: NextApiResponse,
ctx: ThirdwebAuthContext,
) {
// Catch-all route must be named with [...thirdweb]
const { thirdweb } = req.query;
const action = thirdweb?.[0] as ThirdwebAuthRoute;
switch (action) {
case "login":
return await loginHandler(req, res, ctx);
case "user":
return await userHandler(req, res, ctx);
case "logout":
return await logoutHandler(req, res);
default:
return res.status(400).json({
message: "Invalid route for authentication.",
});
}
}
export function ThirdwebAuth(cfg: ThirdwebAuthConfig) {
const ctx = {
...cfg,
sdk: ThirdwebSDK.fromPrivateKey("mainnet-beta", cfg.privateKey),
};
function ThirdwebAuthHandler(
...args: [] | [NextApiRequest, NextApiResponse]
) {
if (args.length === 0) {
return async (req: NextApiRequest, res: NextApiResponse) =>
await ThirdwebAuthRouter(req, res, ctx);
}
return ThirdwebAuthRouter(args[0], args[1], ctx);
}
async function getUser(
req: GetServerSidePropsContext["req"] | NextRequest | NextApiRequest,
) {
const { sdk, domain } = ctx;
let user: ThirdwebAuthUser | null = null;
const token =
typeof req.cookies.get === "function"
? (req.cookies as any).get("thirdweb_auth_token")
: (req.cookies as any).thirdweb_auth_token;
if (token) {
try {
const address = await sdk.auth.authenticate(domain, token);
user = { address };
} catch {
// No-op
}
}
return user;
}
return { ThirdwebAuthHandler, getUser };
}
@@ -0,0 +1,60 @@
import { ThirdwebAuthContext } from "../types";
import { LoginPayload } from "@thirdweb-dev/sdk";
import { serialize } from "cookie";
import { NextApiRequest, NextApiResponse } from "next";
function redirectWithError(
req: NextApiRequest,
res: NextApiResponse,
error: string,
) {
const encodedError = encodeURIComponent(error);
const url = new URL(req.headers.referer as string);
url.searchParams.set("error", encodedError);
return res.redirect(url.toString());
}
export default async function handler(
req: NextApiRequest,
res: NextApiResponse,
ctx: ThirdwebAuthContext,
) {
if (req.method !== "GET") {
return redirectWithError(req, res, "INVALID_METHOD");
}
const { sdk, domain } = ctx;
// Get signed login payload from the frontend
const payload = JSON.parse(atob(req.query.payload as string)) as LoginPayload;
if (!payload) {
redirectWithError(req, res, "MISSING_LOGIN_PAYLOAD");
}
let token;
try {
// Generate an access token with the SDK using the signed payload
token = await sdk.auth.generateAuthToken(domain, payload);
} catch {
return redirectWithError(req, res, "INVALID_LOGIN_PAYLOAD");
}
// Securely set httpOnly cookie on request to prevent XSS on frontend
// And set path to / to enable thirdweb_auth_token usage on all endpoints
res.setHeader(
"Set-Cookie",
serialize("thirdweb_auth_token", token, {
path: "/",
httpOnly: true,
secure: true,
sameSite: "strict",
}),
);
if (ctx.callbacks?.login) {
const address = sdk.auth.verify(domain, payload);
await ctx.callbacks.login(address);
}
return res.status(301).redirect(req.query.redirect as string);
}
@@ -0,0 +1,24 @@
import { serialize } from "cookie";
import { NextApiRequest, NextApiResponse } from "next";
export default async function handler(
req: NextApiRequest,
res: NextApiResponse,
) {
if (req.method !== "GET") {
return res.status(400).json({
error: "Invalid method. Only GET supported.",
});
}
// Set the access token to 'none' and expire in 5 seconds
res.setHeader(
"Set-Cookie",
serialize("thirdweb_auth_token", "", {
path: "/",
expires: new Date(Date.now() + 5 * 1000),
}),
);
return res.status(301).redirect(req.headers.referer as string);
}
@@ -0,0 +1,34 @@
import { ThirdwebAuthContext, ThirdwebAuthUser } from "../types";
import { NextApiRequest, NextApiResponse } from "next";
export default async function handler(
req: NextApiRequest,
res: NextApiResponse,
ctx: ThirdwebAuthContext,
) {
if (req.method !== "GET") {
return res.status(400).json({
error: "Invalid method. Only GET supported.",
});
}
const { sdk, domain } = ctx;
let user = null;
const token = req.cookies.thirdweb_auth_token;
if (token) {
try {
const address = await sdk.auth.authenticate(domain, token);
if (ctx.callbacks?.user) {
user = await ctx.callbacks.user(address);
}
user = { ...user, address };
} catch {
// No-op
}
}
return res.status(200).json(user as ThirdwebAuthUser | null);
}
@@ -0,0 +1,34 @@
import { ThirdwebSDK } from "@thirdweb-dev/sdk/solana";
export type ThirdwebAuthRoute = "login" | "logout" | "user";
export type ThirdwebAuthConfig = {
privateKey: string;
domain: string;
callbacks?: {
login?: (address: string) => Promise<void> | void;
user?: (
address: string,
) =>
| Promise<Omit<ThirdwebAuthUser, "address">>
| Omit<ThirdwebAuthUser, "address">;
};
};
export type ThirdwebAuthContext = {
sdk: ThirdwebSDK;
domain: string;
callbacks?: {
login?: (address: string) => Promise<void> | void;
user?: (
address: string,
) =>
| Promise<Omit<ThirdwebAuthUser, "address">>
| Omit<ThirdwebAuthUser, "address">;
};
};
export type ThirdwebAuthUser = {
address: string;
[key: string]: any;
};
+144
View File
@@ -1,5 +1,149 @@
# thirdweb
## 0.8.15
### Patch Changes
- Updated dependencies [[`3eba69a`](https://github.com/thirdweb-dev/js/commit/3eba69afd78b7aa472a9c00c9d4220bb69ad1d3e), [`4af785c`](https://github.com/thirdweb-dev/js/commit/4af785c37c831ecd7087c1c9eb9095922cac0855), [`6f818f3`](https://github.com/thirdweb-dev/js/commit/6f818f393eb9023b53a3358d62d60ec23a9246bd), [`b79dc18`](https://github.com/thirdweb-dev/js/commit/b79dc18f7ab5155bbf4af02dc1a953546160bad0)]:
- @thirdweb-dev/sdk@3.5.2
## 0.8.14
### Patch Changes
- Updated dependencies [[`4e7b293`](https://github.com/thirdweb-dev/js/commit/4e7b293c543244de79c150ca8c37d941221fc9a3), [`c9c0e4f`](https://github.com/thirdweb-dev/js/commit/c9c0e4ff39b054af746f3aec75fcbd0a3ef7239d)]:
- @thirdweb-dev/sdk@3.5.1
## 0.8.13
### Patch Changes
- Updated dependencies [[`5200d5d`](https://github.com/thirdweb-dev/js/commit/5200d5d7e730da58bd4d3e7c222e3c661265f913), [`42a6065`](https://github.com/thirdweb-dev/js/commit/42a606588dfed03c67c57357dd0ee1ad19fad6ea)]:
- @thirdweb-dev/sdk@3.5.0
## 0.8.12
### Patch Changes
- Updated dependencies [[`e731c1a`](https://github.com/thirdweb-dev/js/commit/e731c1a7c341e6f2b9cc42c88d5c1b036dcf4a7c)]:
- @thirdweb-dev/sdk@3.4.5
## 0.8.11
### Patch Changes
- Updated dependencies []:
- @thirdweb-dev/sdk@3.4.4
## 0.8.10
### Patch Changes
- Updated dependencies [[`1c24c3c`](https://github.com/thirdweb-dev/js/commit/1c24c3c3c48476a824f817e09e7bf0fbe67c1db5), [`65fc7ba`](https://github.com/thirdweb-dev/js/commit/65fc7ba6fff093a971b53cb0979c39768e090ba1)]:
- @thirdweb-dev/sdk@3.4.3
## 0.8.9
### Patch Changes
- Updated dependencies [[`21c21c1`](https://github.com/thirdweb-dev/js/commit/21c21c1adfd09b60ad563bfd4c308597140de33c), [`3522917`](https://github.com/thirdweb-dev/js/commit/352291791ee900e1500f84f095290497934d2f60)]:
- @thirdweb-dev/sdk@3.4.2
## 0.8.8
### Patch Changes
- Updated dependencies [[`72227b2`](https://github.com/thirdweb-dev/js/commit/72227b2e166a3a68bbb41cf2b389322f5b7547a2)]:
- @thirdweb-dev/sdk@3.4.1
## 0.8.7
### Patch Changes
- [#327](https://github.com/thirdweb-dev/js/pull/327) [`ef27aad`](https://github.com/thirdweb-dev/js/commit/ef27aad0aafc4577e85f44dc77dfbe880bd239b5) Thanks [@jnsdls](https://github.com/jnsdls)! - enable e2e testing
- Updated dependencies [[`340605b`](https://github.com/thirdweb-dev/js/commit/340605b507f384fbd2999b9c16542af3c53e84a9), [`26116a6`](https://github.com/thirdweb-dev/js/commit/26116a6f637ee845f7bd17f20ffe17caf184658e), [`15f8006`](https://github.com/thirdweb-dev/js/commit/15f8006e1fb22333b7ee239b45e7b1b12d6dccc8), [`ef27aad`](https://github.com/thirdweb-dev/js/commit/ef27aad0aafc4577e85f44dc77dfbe880bd239b5)]:
- @thirdweb-dev/sdk@3.4.0
## 0.8.6
### Patch Changes
- Updated dependencies [[`ad851f7`](https://github.com/thirdweb-dev/js/commit/ad851f7b4d6abb5ab9364ef3dec229f913e18ce5), [`6218c88`](https://github.com/thirdweb-dev/js/commit/6218c885842e6b4e44ec38ec92a9211e56c49bf4)]:
- @thirdweb-dev/sdk@3.3.1
## 0.8.5
### Patch Changes
- [#326](https://github.com/thirdweb-dev/js/pull/326) [`126752d`](https://github.com/thirdweb-dev/js/commit/126752d7d02c9a808a63fb9e67a9df6658b5682b) Thanks [@jnsdls](https://github.com/jnsdls)! - remove tons of unused dependencies, use SDK function for extracting ipfs has from bytecode
- Updated dependencies [[`126752d`](https://github.com/thirdweb-dev/js/commit/126752d7d02c9a808a63fb9e67a9df6658b5682b), [`df74340`](https://github.com/thirdweb-dev/js/commit/df74340fa51323304c32419761d6f18628b060fa), [`89ff921`](https://github.com/thirdweb-dev/js/commit/89ff921a7d9f0e42b4e6707c8b56d0aab95c1aa0), [`a57b4f0`](https://github.com/thirdweb-dev/js/commit/a57b4f01f3a49590b897101f2730582e4124a554)]:
- @thirdweb-dev/sdk@3.3.0
## 0.8.4
### Patch Changes
- Updated dependencies [[`4f867e1`](https://github.com/thirdweb-dev/js/commit/4f867e15edbe5757afbf54cab207458711a8986c), [`b6eec61`](https://github.com/thirdweb-dev/js/commit/b6eec6146c23b66a3681bbb48b173b9019254214), [`9727502`](https://github.com/thirdweb-dev/js/commit/9727502eb30ac139382b7c7c8e8fc0967cbfbcf1), [`782b58d`](https://github.com/thirdweb-dev/js/commit/782b58d3f6a3b49ff9b3d837551db5161f96171a), [`f41a443`](https://github.com/thirdweb-dev/js/commit/f41a443c5785240197607b5bf93ff18a1c8e8307), [`f45ef62`](https://github.com/thirdweb-dev/js/commit/f45ef62128cfefe06275a38440c90d45e9f3cef7), [`2f8ec89`](https://github.com/thirdweb-dev/js/commit/2f8ec89c4c83ae577092c840c198e6fcfb114e69), [`b7cd744`](https://github.com/thirdweb-dev/js/commit/b7cd744cd0482a6c3fe5ae0c858f20cd380d4114), [`53c6507`](https://github.com/thirdweb-dev/js/commit/53c65076696a43bd9a7e58a7493d4e65c172900f)]:
- @thirdweb-dev/sdk@3.2.6
## 0.8.3
### Patch Changes
- Updated dependencies [[`712b11a`](https://github.com/thirdweb-dev/js/commit/712b11a43e389ae10b7f09648779a69c05b2c913)]:
- @thirdweb-dev/sdk@3.2.5
## 0.8.2
### Patch Changes
- Updated dependencies []:
- @thirdweb-dev/sdk@3.2.4
## 0.8.1
### Patch Changes
- Updated dependencies []:
- @thirdweb-dev/sdk@3.2.3
## 0.8.0
### Minor Changes
- [#276](https://github.com/thirdweb-dev/js/pull/276) [`24514a6`](https://github.com/thirdweb-dev/js/commit/24514a6e9fc1316622c1494e5be225e53f88d26e) Thanks [@furqanrydhan](https://github.com/furqanrydhan)! - build is now default compile command, reorganized the help list
- [#279](https://github.com/thirdweb-dev/js/pull/279) [`1918495`](https://github.com/thirdweb-dev/js/commit/1918495f2ee7f50d94083d9577dab0bf1c828c94) Thanks [@furqanrydhan](https://github.com/furqanrydhan)! - Updating command list order, helper text and changed detect -> build
### Patch Changes
- [#277](https://github.com/thirdweb-dev/js/pull/277) [`9de3f8c`](https://github.com/thirdweb-dev/js/commit/9de3f8c8e954733bfc3eb6e8e0e786ff5b296f0c) Thanks [@adam-maj](https://github.com/adam-maj)! - Add recursive folder upload support
- [#268](https://github.com/thirdweb-dev/js/pull/268) [`57b9c67`](https://github.com/thirdweb-dev/js/commit/57b9c671aaf7fce0076dd332c171ba6944752e7a) Thanks [@adam-maj](https://github.com/adam-maj)! - Update storage upload cLI
- Updated dependencies [[`521a49c`](https://github.com/thirdweb-dev/js/commit/521a49c6ec6a73068adcfbc1d94d2f3f17afae86), [`8b3b97c`](https://github.com/thirdweb-dev/js/commit/8b3b97c8f7b3710ce62ed86cfeb4f6de3c23d559), [`25a4773`](https://github.com/thirdweb-dev/js/commit/25a4773bba406f4e270ac86e68c5266d24c08330), [`b2cadf1`](https://github.com/thirdweb-dev/js/commit/b2cadf164cfe9fb27081df7530356baf70ec2b3a), [`d6bb61b`](https://github.com/thirdweb-dev/js/commit/d6bb61b7fac759fff7d6293edd46f693f5a7889c), [`78d6d3c`](https://github.com/thirdweb-dev/js/commit/78d6d3c15968a449924b3f24354e151a90e4b20d), [`89d87a5`](https://github.com/thirdweb-dev/js/commit/89d87a5eb31e78e68e097718e9ae6e4c4d101868), [`41507d3`](https://github.com/thirdweb-dev/js/commit/41507d32a78c556f50c88de81c4d6109b1783c78), [`8096c78`](https://github.com/thirdweb-dev/js/commit/8096c78b466a7f5f9c2aac6e2c56a06f9d3a25d6), [`b1fa171`](https://github.com/thirdweb-dev/js/commit/b1fa17158dcac00265bc8f4d64f1f9708482cdb5), [`18381fa`](https://github.com/thirdweb-dev/js/commit/18381fac73741826934212ac6441842ed42a64a0), [`df048af`](https://github.com/thirdweb-dev/js/commit/df048af605f449279b01d7352b604e7856ac223c), [`d6bb61b`](https://github.com/thirdweb-dev/js/commit/d6bb61b7fac759fff7d6293edd46f693f5a7889c), [`cf88795`](https://github.com/thirdweb-dev/js/commit/cf88795376d3110f9d3aa839928d22276904b15a), [`89b5c6f`](https://github.com/thirdweb-dev/js/commit/89b5c6fe079e4df2cf12cb557aa9784f88c5d159)]:
- @thirdweb-dev/sdk@3.2.2
## 0.7.24
### Patch Changes
- [#259](https://github.com/thirdweb-dev/js/pull/259) [`6f95a52`](https://github.com/thirdweb-dev/js/commit/6f95a520e6cc69e08f734705e8adc71431b091cc) Thanks [@adam-maj](https://github.com/adam-maj)! - Add npx thirdweb upload command
- Updated dependencies []:
- @thirdweb-dev/sdk@3.2.1
## 0.7.23
### Patch Changes
- [#246](https://github.com/thirdweb-dev/js/pull/246) [`ef0bac7`](https://github.com/thirdweb-dev/js/commit/ef0bac7f44b93939bfc9e2706b39edaa22992eec) Thanks [@simonlim94](https://github.com/simonlim94)! - Renamed initial commit message during thirdweb cli createApp
- Updated dependencies [[`ba6f450`](https://github.com/thirdweb-dev/js/commit/ba6f45027a6ec11aebb4a1918ab2ba4f88894ede), [`c8261b7`](https://github.com/thirdweb-dev/js/commit/c8261b74b5828ac66ea3a6d7636aa57e40ea1a14), [`20745f8`](https://github.com/thirdweb-dev/js/commit/20745f8cd603235eb5039e61a39c0959c5860bca), [`2ea3f00`](https://github.com/thirdweb-dev/js/commit/2ea3f00c41b5e70d9bb634cd86790890520fc4d6), [`9412d41`](https://github.com/thirdweb-dev/js/commit/9412d416832c87ab2c858cb120c6c5de03459c50), [`d677e9d`](https://github.com/thirdweb-dev/js/commit/d677e9d6341252013762882d25408ea8f93791e5), [`e662408`](https://github.com/thirdweb-dev/js/commit/e662408e1936983485936609552f80a56cbed082), [`e4b10bc`](https://github.com/thirdweb-dev/js/commit/e4b10bc5bd12178cba8856b60255732123cb7ae5), [`caf9795`](https://github.com/thirdweb-dev/js/commit/caf979537b5acb1610f182c94afa5d95c208ec3f), [`028cb78`](https://github.com/thirdweb-dev/js/commit/028cb781252b0050943b14cfdf38132c18753a4b), [`1972f3e`](https://github.com/thirdweb-dev/js/commit/1972f3ec0d511fbc17642b2a30852a177092a09e), [`6647f70`](https://github.com/thirdweb-dev/js/commit/6647f707bdd050725264a8a74220c11912f68e57), [`cac373b`](https://github.com/thirdweb-dev/js/commit/cac373b010ce3be3615a36671b66815a27785061)]:
- @thirdweb-dev/sdk@3.2.0
## 0.7.22
### Patch Changes
+1 -8
View File
@@ -1,7 +1,7 @@
{
"name": "thirdweb",
"main": "dist/cli/index.js",
"version": "0.7.22",
"version": "0.8.15",
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/cli",
"author": "thirdweb eng <[email protected]>",
"license": "Apache-2.0",
@@ -31,24 +31,17 @@
"dependencies": {
"@thirdweb-dev/sdk": "*",
"async-retry": "^1.3.3",
"cbor": "^8.1.0",
"commander": "^9.1.0",
"cpy": "^9.0.1",
"enquirer": "^2.3.6",
"ethers": "^5.7.0",
"form-data": "^4.0.0",
"got": "11.8.5",
"inquirer": "^8.2.3",
"ipfs-unixfs": "^6.0.7",
"ipld-dag-pb": "^0.22.3",
"multihashes": "^4.0.3",
"open": "^8.4.0",
"prompts": "^2.4.2",
"rimraf": "^3.0.2",
"solc": "0.8.13",
"tar": "^6.1.11",
"tslog": "^3.3.3",
"update-check": "^1.5.4",
"update-notifier": "^6.0.2",
"validate-npm-package-name": "^4.0.0",
"yaml": "^2.1.1"
+35 -20
View File
@@ -1,11 +1,12 @@
#!/usr/bin/env node
import { installGithubAction } from "../common/ci-installer";
import { detectExtensions } from "../common/feature-detector";
import { processProject } from "../common/processor";
import { cliVersion, pkg } from "../constants/urls";
import { info, logger } from "../core/helpers/logger";
import { twCreate } from "../create/command";
import generateDashboardUrl from "../helpers/generate-dashboard-url";
import { upload } from "../storage/command";
import { ThirdwebStorage } from "@thirdweb-dev/storage";
import chalk from "chalk";
import { Command } from "commander";
import open from "open";
@@ -39,12 +40,12 @@ $$$$$$\\ $$$$$$$\\ $$\\ $$$$$$\\ $$$$$$$ |$$\\ $$\\ $$\\ $$$$$$\\ $$$$
.version(cliVersion, "-v, --version");
program
.command("create")
.command("create [projectPath]")
.description(
"Create a thirdweb app from any of our official templates. Checkout some examples you can use here: https://github.com/thirdweb-example/",
"Create a web3 app from any of our official templates: https://github.com/thirdweb-example/",
)
.option("--app", `Create a thirdweb app.`)
.option("--contract", `Create a thirdweb contracts project.`)
.option("--app", `Create a web3 app.`)
.option("--contract", `Create a web3 contract project`)
.option("--ts, --typescript", `Initialize as a TypeScript project.`)
.option("--js, --javascript", `Initialize as a JavaScript project.`)
.option("--forge", `Initialize as a Forge project.`)
@@ -65,15 +66,23 @@ $$$$$$\\ $$$$$$$\\ $$\\ $$$$$$\\ $$$$$$$ |$$\\ $$\\ $$\\ $$$$$$\\ $$$$
"-t, --template [name]",
`A template to start your project from. You can use an template repository name from the official thirdweb-example org.`,
)
.action(async (path, options) => {
await twCreate(path, options);
});
program
.command("build")
.description("Compile contract and detect thirdweb contract extensions")
.option("-p, --path <project-path>", "path to project", ".")
.option("-d, --debug", "show debug logs")
.option("-a, --all", "run detection on all contracts")
.action(async (options) => {
await twCreate(options);
await detectExtensions(options);
});
program
.command("deploy")
.description(
"Deploy contracts through your thirdweb dashboard, without dealing with private keys.",
)
.description("Deploy your (or team) contracts securely to blockchains")
.option("-p, --path <project-path>", "path to project", ".")
.option("--dry-run", "dry run (skip actually publishing)")
.option("-d, --debug", "show debug logs")
@@ -116,7 +125,7 @@ $$$$$$\\ $$$$$$$\\ $$\\ $$$$$$\\ $$$$$$$ |$$\\ $$\\ $$\\ $$$$$$\\ $$$$
program
.command("release")
.description(
"Release contracts, making them available for others to deploy and unlocking SDKs, Dashboards and Analytics.",
"Release your protocol so other devs can deploy them and unlock SDKs, Dashboards and Analytics",
)
.option("-p, --path <project-path>", "path to project", ".")
.option("--dry-run", "dry run (skip actually publishing)")
@@ -129,10 +138,25 @@ $$$$$$\\ $$$$$$$\\ $$\\ $$$$$$\\ $$$$$$$ |$$\\ $$\\ $$\\ $$$$$$\\ $$$$
open(url.toString());
});
program
.command("upload")
.description("Upload any file or directory to decentralized storage (IPFS)")
.argument("[upload]", "path to file or directory to upload")
.action(async (path) => {
const storage = new ThirdwebStorage();
const uri = await upload(storage, path);
info(`Files stored at the following IPFS URI:`);
logger.info(chalk.blueBright(uri.toString()));
const url = storage.resolveScheme(uri);
info(`Open this link to view your upload:`);
logger.info(chalk.blueBright(url.toString()));
});
program
.command("detect")
.description(
"Compile contracts and detect implemented thirdweb contract extensions",
"(deprecated) Compile contracts and detect implemented thirdweb contract extensions",
)
.option("-p, --path <project-path>", "path to project", ".")
.option("-d, --debug", "show debug logs")
@@ -141,15 +165,6 @@ $$$$$$\\ $$$$$$$\\ $$\\ $$$$$$\\ $$$$$$$ |$$\\ $$\\ $$\\ $$$$$$\\ $$$$
await detectExtensions(options);
});
program
.command("install-ci")
.description(
"(alpha) Set up continuous integration for your contracts. This adds a github action to deploy the project on pull requests and pushes to branches. Publishes on push the the main branch.",
)
.action(async (options) => {
await installGithubAction(options);
});
await program.parseAsync();
};
@@ -1,7 +1,7 @@
import { extractIPFSHashFromBytecode } from "../helpers/ipfs";
import { logger } from "../helpers/logger";
import { CompileOptions, IBuilder } from "../interfaces/Builder";
import { ContractPayload } from "../interfaces/ContractPayload";
import { extractIPFSHashFromBytecode } from "@thirdweb-dev/sdk/evm";
export abstract class BaseBuilder implements IBuilder {
abstract compile(
-66
View File
@@ -1,66 +0,0 @@
import { decodeFirstSync } from "cbor";
import { UnixFS } from "ipfs-unixfs";
import { DAGNode } from "ipld-dag-pb";
import { toB58String } from "multihashes";
/**
* Derives IPFS hash of string
* @param {String} str
* @return {String} IPFS hash (ex: "Qm")
*/
export async function getIPFSHash(str: string) {
const file = new UnixFS({
type: "file",
data: Buffer.from(str),
});
const node = new DAGNode(file.marshal());
const metadataLink = await node.toDAGLink();
return toB58String(metadataLink.Hash.multihash);
}
/**
* Extract the IPFS hash from the given bytecode
* @param bytecode
* @returns
*/
export function extractIPFSHashFromBytecode(
bytecode: string,
): string | undefined {
const numericBytecode = hexToBytes(bytecode);
const cborLength: number =
numericBytecode[numericBytecode.length - 2] * 0x100 +
numericBytecode[numericBytecode.length - 1];
const bytecodeBuffer = Buffer.from(
numericBytecode.slice(numericBytecode.length - 2 - cborLength, -2),
);
const cborData = decodeFirstSync(bytecodeBuffer);
if (cborData["ipfs"]) {
const uri = toB58String(cborData["ipfs"]);
return uri;
}
return undefined;
}
function hexToBytes(hex: string | number) {
hex = hex.toString(16);
if (!hex.startsWith("0x")) {
hex = `0x${hex}`;
}
if (!isHexStrict(hex)) {
throw new Error('Given value "' + hex + '" is not a valid hex string.');
}
hex = hex.replace(/^0x/i, "");
for (var bytes = [], c = 0; c < hex.length; c += 2) {
bytes.push(parseInt(hex.slice(c, c + 2), 16));
}
return bytes;
}
function isHexStrict(hex: string | number) {
return (
(typeof hex === "string" || typeof hex === "number") &&
/^(-)?0x[0-9a-f]*$/i.test(hex.toString())
);
}
+3 -3
View File
@@ -17,9 +17,9 @@ let language: string = "";
let baseContract: string = "";
/* let createType: string = "app"; */
export async function twCreate(options: any) {
if (typeof projectPath === "string") {
projectPath = projectPath.trim();
export async function twCreate(pPath: string = "", options: any) {
if (typeof pPath === "string") {
projectPath = pPath;
}
if (options.app) {
+1 -1
View File
@@ -33,7 +33,7 @@ export function tryGitInit(root: string): boolean {
execSync("git checkout -b main", { stdio: "ignore" });
execSync("git add -A", { stdio: "ignore" });
execSync('git commit -m "Initial commit from Create thirdweb app"', {
execSync('git commit -m "Initial commit from npx thirdweb create"', {
stdio: "ignore",
});
return true;
+94
View File
@@ -0,0 +1,94 @@
import { logger } from "../core/helpers/logger";
import { spinner } from "../core/helpers/logger";
import { BufferOrStringWithName, ThirdwebStorage } from "@thirdweb-dev/storage";
import chalk from "chalk";
import fs from "fs";
import path from "path";
function recurseFiles(
uploadPath: string,
basePath: string,
): BufferOrStringWithName[] {
const files = [];
const fileType = fs.lstatSync(uploadPath);
if (fileType.isFile()) {
const fileName = path.relative(basePath, uploadPath);
const fileData = fs.readFileSync(uploadPath);
files.push({ name: fileName, data: fileData });
} else if (fileType.isDirectory()) {
const paths = fs.readdirSync(uploadPath);
paths.forEach((subPath) => {
const fullSubPath = path.join(uploadPath, subPath);
const subFiles = recurseFiles(fullSubPath, basePath);
subFiles.forEach((file) => files.push(file));
});
}
return files;
}
export async function upload(
storage: ThirdwebStorage,
uploadPath: string,
): Promise<string> {
if (!uploadPath) {
logger.error(
`Please pass a path to a file or directory to upload with the following format:\n ${chalk.blueBright(
`npx thirdweb@latest upload path/to/file.jpg`,
)}\n`,
);
process.exit(1);
}
const pathExists = fs.existsSync(uploadPath);
if (!pathExists) {
logger.error(
`Invalid path ${chalk.blueBright(
uploadPath,
)} provided. Please provide a valid path to a file or directory to upload."`,
);
process.exit(1);
}
let uri = "";
const fileType = fs.lstatSync(uploadPath);
if (fileType.isDirectory()) {
const files = recurseFiles(uploadPath, uploadPath);
if (files.length === 0) {
logger.error(
`No files detected in specified directory ${chalk.blueBright(
uploadPath,
)} to upload.`,
);
process.exit(1);
}
const spin = spinner(
"Uploading directory to IPFS. This may take a while depending on file sizes.",
);
const uris = await storage.uploadBatch(files);
spin.succeed("Successfully uploaded directory to IPFS");
uri = uris[0].substring(0, uris[0].lastIndexOf("/"));
} else if (fileType.isFile()) {
const fileName = path.parse(uploadPath).base;
const fileData = fs.readFileSync(uploadPath);
const file = { name: fileName, data: fileData };
const spin = spinner(
"Uploading file to IPFS. This may take a while depending on file sizes.",
);
uri = await storage.upload(file, { uploadWithoutDirectory: true });
spin.succeed("Succesfully uploaded file to IPFS.");
} else {
logger.error(
`Path ${chalk.blueBright(
uploadPath,
)} does not point to a valid file or directory. Please provide a valid path to a file or directory to upload`,
);
process.exit(1);
}
return uri;
}
+14
View File
@@ -1,5 +1,19 @@
# @thirdweb-dev/contracts-js
## 1.2.1
### Patch Changes
- [#376](https://github.com/thirdweb-dev/js/pull/376) [`4af785c`](https://github.com/thirdweb-dev/js/commit/4af785c37c831ecd7087c1c9eb9095922cac0855) Thanks [@adam-maj](https://github.com/adam-maj)! - Update tiered-drop with tokens claimed
- [#373](https://github.com/thirdweb-dev/js/pull/373) [`6f818f3`](https://github.com/thirdweb-dev/js/commit/6f818f393eb9023b53a3358d62d60ec23a9246bd) Thanks [@adam-maj](https://github.com/adam-maj)! - Update tiered-drop contract version
## 1.2.0
### Minor Changes
- [#333](https://github.com/thirdweb-dev/js/pull/333) [`aa6d9ed`](https://github.com/thirdweb-dev/js/commit/aa6d9ed75d126d7a0cca9fb8fc389ff94e9d1e14) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Update to contracts package v3.2.0
## 1.1.9
### Patch Changes
@@ -0,0 +1,4 @@
{
"main": "dist/thirdweb-dev-contracts-js-factories-TWMultichainRegistry__factory.cjs.js",
"module": "dist/thirdweb-dev-contracts-js-factories-TWMultichainRegistry__factory.esm.js"
}
+3 -2
View File
@@ -1,17 +1,18 @@
{
"name": "@thirdweb-dev/contracts-js",
"version": "1.1.9",
"version": "1.2.1",
"main": "dist/thirdweb-dev-contracts-js.cjs.js",
"module": "dist/thirdweb-dev-contracts-js.esm.js",
"license": "Apache-2.0",
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/contracts-js",
"dependencies": {
"@thirdweb-dev/contracts": "3.1.8-0"
"@thirdweb-dev/contracts": "^3.2.2"
},
"devDependencies": {
"@babel/preset-env": "^7.18.10",
"@babel/preset-typescript": "^7.18.6",
"@preconstruct/cli": "^2.2.1",
"@typechain/ethers-v5": "10.0.0",
"ethers": "^5.7.0",
"typechain": "^8.1.0",
"typescript": "^4.7.4"
+2 -1
View File
@@ -11,7 +11,8 @@ module.exports = {
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-non-null-assertion": "error",
"@typescript-eslint/no-parameter-properties": "error",
"@typescript-eslint/no-unused-vars": "warn",
// making this an error going forward
"@typescript-eslint/no-unused-vars": "error",
// import rules
"import/first": "error",
+5
View File
@@ -5,3 +5,8 @@ node_modules/
/test-results/
/playwright-report/
/playwright/.cache/
etc/
temp/
temp-evm/
temp-solana/
docs/
+172
View File
@@ -1,5 +1,177 @@
# @thirdweb-dev/react
## 3.5.2
### Patch Changes
- [#369](https://github.com/thirdweb-dev/js/pull/369) [`2d3fca5`](https://github.com/thirdweb-dev/js/commit/2d3fca52373046c88392139922e9c9da79f9a2d4) Thanks [@nachoiacovino](https://github.com/nachoiacovino)! - Add useClaimerProofs hook
- [#378](https://github.com/thirdweb-dev/js/pull/378) [`32d91ef`](https://github.com/thirdweb-dev/js/commit/32d91ef709c8fb92a903e52c65b27538a80ea4eb) Thanks [@mykcryptodev](https://github.com/mykcryptodev)! - round borders for buttons and menus on safari
- [#377](https://github.com/thirdweb-dev/js/pull/377) [`b79dc18`](https://github.com/thirdweb-dev/js/commit/b79dc18f7ab5155bbf4af02dc1a953546160bad0) Thanks [@adam-maj](https://github.com/adam-maj)! - Add event filters
## 3.5.1
## 3.5.0
## 3.4.5
## 3.4.4
### Patch Changes
- [#356](https://github.com/thirdweb-dev/js/pull/356) [`cc4613b`](https://github.com/thirdweb-dev/js/commit/cc4613b5fd69840f4f9cfd0ac1c4e6743e62fe52) Thanks [@nachoiacovino](https://github.com/nachoiacovino)! - Add Pack and Multiwrap as NFTContract types
## 3.4.3
### Patch Changes
- [#353](https://github.com/thirdweb-dev/js/pull/353) [`1c24c3c`](https://github.com/thirdweb-dev/js/commit/1c24c3c3c48476a824f817e09e7bf0fbe67c1db5) Thanks [@nachoiacovino](https://github.com/nachoiacovino)! - Add useUpdateCreators hook, fix CreatorInput schema
## 3.4.2
### Patch Changes
- [#348](https://github.com/thirdweb-dev/js/pull/348) [`7d9a4c6`](https://github.com/thirdweb-dev/js/commit/7d9a4c6abcad1b4f43e431ce7b0b38db9016ea9a) Thanks [@nachoiacovino](https://github.com/nachoiacovino)! - [SOL] - add `useUpdateRoyaltySettings()` hook
[SOL] - change `useRoyalty()` hook -> `useRoyaltySettings()` for consistency
- [#352](https://github.com/thirdweb-dev/js/pull/352) [`3522917`](https://github.com/thirdweb-dev/js/commit/352291791ee900e1500f84f095290497934d2f60) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - expose useExecuteAuctionSale and useAcceptDirectListingOffer
- [#351](https://github.com/thirdweb-dev/js/pull/351) [`aabbb14`](https://github.com/thirdweb-dev/js/commit/aabbb149e81d6824015a4797b2b6c929ca359545) Thanks [@jnsdls](https://github.com/jnsdls)! - [EVM] - fix wallet connection during dev (hot reload)
## 3.4.1
### Patch Changes
- [#343](https://github.com/thirdweb-dev/js/pull/343) [`72227b2`](https://github.com/thirdweb-dev/js/commit/72227b2e166a3a68bbb41cf2b389322f5b7547a2) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Expose new useOffers and useMakeOffer hooks
## 3.4.0
### Minor Changes
- [#327](https://github.com/thirdweb-dev/js/pull/327) [`ef27aad`](https://github.com/thirdweb-dev/js/commit/ef27aad0aafc4577e85f44dc77dfbe880bd239b5) Thanks [@jnsdls](https://github.com/jnsdls)! - Gnosis Safe and Magic Link connectors are no longer included in the default export. They are now available as named exports instead.
## Gnosis Safe
### Connector
```diff
- import { GnosisSafeConnector } from "@thirdweb-dev/react";
+ import { GnosisSafeConnector } from "@thirdweb-dev/react/evm/connectors/gnosis-safe";
```
### Hook
```diff
- import { useGnosis } from "@thirdweb-dev/react";
+ import { useGnosis } from "@thirdweb-dev/react/evm/connectors/gnosis-safe";
```
## Magic Link
### Connector
```diff
- import { MagicLink } from "@thirdweb-dev/react";
+ import { MagicConnector } from "@thirdweb-dev/react/evm/connectors/magic";
```
### Hook
```diff
- import { useMagic } from "@thirdweb-dev/react";
+ import { useMagic } from "@thirdweb-dev/react/evm/connectors/magic";
```
### Patch Changes
- [#339](https://github.com/thirdweb-dev/js/pull/339) [`b03a902`](https://github.com/thirdweb-dev/js/commit/b03a9021451b79f802f682f66e5ae8e9355d7e6f) Thanks [@nachoiacovino](https://github.com/nachoiacovino)! - Fix docs for some functions
## 3.3.1
## 3.3.0
## 3.2.6
### Patch Changes
- [#316](https://github.com/thirdweb-dev/js/pull/316) [`6ae5266`](https://github.com/thirdweb-dev/js/commit/6ae52664e5865564cd4fc2e00e3120675b9e0ca3) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Fix SIWE when using an external signer
- [#310](https://github.com/thirdweb-dev/js/pull/310) [`9727502`](https://github.com/thirdweb-dev/js/commit/9727502eb30ac139382b7c7c8e8fc0967cbfbcf1) Thanks [@nachoiacovino](https://github.com/nachoiacovino)! - [SOL] - add useMintNFTSupply hook in react and accomodate inputs for it in SDK
- [#319](https://github.com/thirdweb-dev/js/pull/319) [`2f8ec89`](https://github.com/thirdweb-dev/js/commit/2f8ec89c4c83ae577092c840c198e6fcfb114e69) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Expose total claimed and unclaimed supply for erc721 drop contracts
- [#318](https://github.com/thirdweb-dev/js/pull/318) [`bb1b4e6`](https://github.com/thirdweb-dev/js/commit/bb1b4e6ac39d98cc68b8e31e659cd68be3a5b967) Thanks [@nachoiacovino](https://github.com/nachoiacovino)! - Add useMinimumNextBid hook and change invariant for other marketplace hooks
## 3.2.5
## 3.2.4
### Patch Changes
- [#301](https://github.com/thirdweb-dev/js/pull/301) [`86f0cef`](https://github.com/thirdweb-dev/js/commit/86f0ceff46f72df8ebd81f843e3c66a245f23992) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Remove useAddress hook for now
## 3.2.3
### Patch Changes
- [#299](https://github.com/thirdweb-dev/js/pull/299) [`b1218cb`](https://github.com/thirdweb-dev/js/commit/b1218cbd7d97ca7949d94a7e4ab93fef4ffbacd5) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Recreate SDK on wallet changes for hook propagation
## 3.2.2
### Patch Changes
- [#289](https://github.com/thirdweb-dev/js/pull/289) [`521a49c`](https://github.com/thirdweb-dev/js/commit/521a49c6ec6a73068adcfbc1d94d2f3f17afae86) Thanks [@nachoiacovino](https://github.com/nachoiacovino)! - Standarize useClaimNFT to evm
- [#274](https://github.com/thirdweb-dev/js/pull/274) [`ad06c5b`](https://github.com/thirdweb-dev/js/commit/ad06c5b28902422f9b416d65255c64c937a5e046) Thanks [@adam-maj](https://github.com/adam-maj)! - Add solana auth support and plugins for react and auth package
- [#269](https://github.com/thirdweb-dev/js/pull/269) [`b2cadf1`](https://github.com/thirdweb-dev/js/commit/b2cadf164cfe9fb27081df7530356baf70ec2b3a) Thanks [@jnsdls](https://github.com/jnsdls)! - [EVM] - remove deprecated chains from support
- [#272](https://github.com/thirdweb-dev/js/pull/272) [`2bdf198`](https://github.com/thirdweb-dev/js/commit/2bdf1984cb97121c447cffd27aa6a5f4f92679b3) Thanks [@nachoiacovino](https://github.com/nachoiacovino)! - Add claim conditions hook for Solana
- [#291](https://github.com/thirdweb-dev/js/pull/291) [`86f6083`](https://github.com/thirdweb-dev/js/commit/86f608390a7848cda82c0f7e8913ca60f7d3901f) Thanks [@jnsdls](https://github.com/jnsdls)! - [SOL] - expose `.program` on `useProgram()` return type to mirror `.contract` on EVM
- [#292](https://github.com/thirdweb-dev/js/pull/292) [`492a818`](https://github.com/thirdweb-dev/js/commit/492a818079aff67c7f88bc43666c87c3714cc957) Thanks [@jnsdls](https://github.com/jnsdls)! - [SOL] expose `useAddress()` hook that returns the connected wallet address in base58
- [#290](https://github.com/thirdweb-dev/js/pull/290) [`8096c78`](https://github.com/thirdweb-dev/js/commit/8096c78b466a7f5f9c2aac6e2c56a06f9d3a25d6) Thanks [@jnsdls](https://github.com/jnsdls)! - [SOL] - `useClaimNFT()` allow claiming to connected wallet without passing address explicitly
[EVM] - `useClaimNFT()` allow claiming to connected wallet without passing address explicitly
- [#298](https://github.com/thirdweb-dev/js/pull/298) [`e8c25ed`](https://github.com/thirdweb-dev/js/commit/e8c25ed0f28d566203d70bf59a8908e59216334a) Thanks [@jnsdls](https://github.com/jnsdls)! - [SOL] - fix useBalance
- [#263](https://github.com/thirdweb-dev/js/pull/263) [`d6bb61b`](https://github.com/thirdweb-dev/js/commit/d6bb61b7fac759fff7d6293edd46f693f5a7889c) Thanks [@jnsdls](https://github.com/jnsdls)! - [EVM] - add chainId getter on contracts
- [#266](https://github.com/thirdweb-dev/js/pull/266) [`3b7d10e`](https://github.com/thirdweb-dev/js/commit/3b7d10ed93d8e3b698e9f905c93ba79863b35325) Thanks [@adam-maj](https://github.com/adam-maj)! - Add storage hooks to react
- [#282](https://github.com/thirdweb-dev/js/pull/282) [`1f41adb`](https://github.com/thirdweb-dev/js/commit/1f41adbd2ccfbc367757620e0842ef32bc783a08) Thanks [@jarrodwatts](https://github.com/jarrodwatts)! - Fix useActiveClaimCondition invariant failing for token ID 0
- [#296](https://github.com/thirdweb-dev/js/pull/296) [`cf88795`](https://github.com/thirdweb-dev/js/commit/cf88795376d3110f9d3aa839928d22276904b15a) Thanks [@jnsdls](https://github.com/jnsdls)! - [SOL] - add `useBalance` hook
## 3.2.1
### Patch Changes
- [#264](https://github.com/thirdweb-dev/js/pull/264) [`f669d3e`](https://github.com/thirdweb-dev/js/commit/f669d3ef4a84368c23f0359aec304f66a4063042) Thanks [@nachoiacovino](https://github.com/nachoiacovino)! - Standarize data between evm/solana for useLazyMint hook
## 3.2.0
### Patch Changes
- [#261](https://github.com/thirdweb-dev/js/pull/261) [`c8261b7`](https://github.com/thirdweb-dev/js/commit/c8261b74b5828ac66ea3a6d7636aa57e40ea1a14) Thanks [@jnsdls](https://github.com/jnsdls)! - [EVM] - align behavior of `useContract()` and `getContract()` across react & sdk to both allow passing optional second params of contract types or ABIs
- [#236](https://github.com/thirdweb-dev/js/pull/236) [`cac373b`](https://github.com/thirdweb-dev/js/commit/cac373b010ce3be3615a36671b66815a27785061) Thanks [@jnsdls](https://github.com/jnsdls)! - [SOL] - do not require a connection, instead handle a network directly
- [#251](https://github.com/thirdweb-dev/js/pull/251) [`ea41231`](https://github.com/thirdweb-dev/js/commit/ea41231b3ec4a2aef6a203db195d0e450c45ef56) Thanks [@jnsdls](https://github.com/jnsdls)! - fix missing Buffer implementation for WalletConnect and CoinbaseWallet connectors
- [#253](https://github.com/thirdweb-dev/js/pull/253) [`b6fc298`](https://github.com/thirdweb-dev/js/commit/b6fc298d0cf63bc7129104f7779cc9d84e405093) Thanks [@jnsdls](https://github.com/jnsdls)! - [EVM] - allow passing contractType as second param to `useContract()`
- [#243](https://github.com/thirdweb-dev/js/pull/243) [`1136d95`](https://github.com/thirdweb-dev/js/commit/1136d959baf936c166e5f7c051d5173d68d9eb9b) Thanks [@jnsdls](https://github.com/jnsdls)! - fix persister logic
- [#258](https://github.com/thirdweb-dev/js/pull/258) [`ac232b5`](https://github.com/thirdweb-dev/js/commit/ac232b5affe31780ef1c10ec76edb21596850e7e) Thanks [@nachoiacovino](https://github.com/nachoiacovino)! - Accept SmartContract instead of erc20 instance
- [#245](https://github.com/thirdweb-dev/js/pull/245) [`1972f3e`](https://github.com/thirdweb-dev/js/commit/1972f3ec0d511fbc17642b2a30852a177092a09e) Thanks [@jnsdls](https://github.com/jnsdls)! - [SOL] - expose `useBurnNFT` hook
- [#255](https://github.com/thirdweb-dev/js/pull/255) [`9b92697`](https://github.com/thirdweb-dev/js/commit/9b92697fb77d3072e3e53b451f9b348595ae410e) Thanks [@jnsdls](https://github.com/jnsdls)! - [EVM] - `useContract()` and `<Web3Button />` can now accept an optional ABI directly and will return a `SmartContract` based on it
## 3.1.2
### Patch Changes
+2 -2
View File
@@ -26,11 +26,11 @@ The `useContractCall` hook to call a smart contract function has been renamed to
### Prebuilt Contract Hooks
Hooks to connect to prebuilt contracts such as `useNFTDrop` and `useMarketplace` are now deprecated in favour of using the generic `useContract` hook:
Hooks to connect to prebuilt contracts such as `useNFTDrop` and `useMarketplace` are now deprecated in favour of using the generic `useContract` hook followed by the contract type:
```diff
- const contract = useNFTDrop("0x...")
+ const { contract } = useContract("0x...")
+ const { contract } = useContract("0x...", "nft-drop")
```
You can currently still continue to use the prebuilt hooks, however they provide a warning in the console informing you of their deprecation. We will likely remove these hooks in a future version of the SDK.
+6 -6
View File
@@ -71,7 +71,7 @@ Below are examples of where to set this up in your application:
### Connect to a User's Wallet
Now the provider is set up, we can use all of the hooks and UI components available in the SDK, such as the [ConnectWallet](https://portal.thirdweb.com/sdk/ui-components/connectwalletbutton) component.
Now the provider is set up, we can use all of the hooks and UI components available in the SDK, such as the [ConnectWallet](https://portal.thirdweb.com/ui-components/connectwalletbutton) component.
Once the user has connected their wallet, all the calls we make to interact with contracts using the SDK will be on behalf of the user.
@@ -143,7 +143,7 @@ Each [extension](https://portal.thirdweb.com/extensions) you implement in your s
These hooks make it easy to interact with your smart contracts by implementing the complex logic for you under the hood.
For example, if your smart contract implements [ERC721Supply](https://portal.thirdweb.com/extensions/erc721supply#unlocked-features), you unlock the ability to [view all NFTs](https://portal.thirdweb.com/sdk/interacting-with-contracts/erc721supply#get-all-minted-nfts) on that contract using the SDK; which fetches all of your NFT metadata and the current owner of each NFT in parallel. In the React SDK, that is available using `useNFTs`:
For example, if your smart contract implements [ERC721Supply](https://portal.thirdweb.com/contractkit/interfaces/erc721supply#unlocked-features), you unlock the ability to [view all NFTs](https://portal.thirdweb.com/sdk/interacting-with-contracts/erc721supply#get-all-minted-nfts) on that contract using the SDK; which fetches all of your NFT metadata and the current owner of each NFT in parallel. In the React SDK, that is available using `useNFTs`:
```jsx
import { useContract, useNFTs } from "@thirdweb-dev/react";
@@ -154,7 +154,7 @@ export default function Home() {
}
```
If we want to mint an NFT and our contract implements [ERC721Mintable](https://portal.thirdweb.com/extensions/erc721mintable#unlocked-features), we can use the [`useMintNFT`](https://portal.thirdweb.com/sdk/interacting-with-contracts/erc721mintable) hook to mint an NFT from the connected wallet; handling all of the logic of uploading and pinning the metadata to IPFS for us behind the scenes.
If we want to mint an NFT and our contract implements [ERC721Mintable](https://portal.thirdweb.com/contractkit/interfaces/erc721mintable#unlocked-features), we can use the [`useMintNFT`](https://portal.thirdweb.com/sdk/interacting-with-contracts/erc721mintable) hook to mint an NFT from the connected wallet; handling all of the logic of uploading and pinning the metadata to IPFS for us behind the scenes.
```jsx
import { useContract, useNFTs, useMintNFT } from "@thirdweb-dev/react";
@@ -172,7 +172,7 @@ export default function Home() {
The SDK provides many UI components to help you build your application.
For example, we can render each of the NFTs using the [`NFT Media Renderer`](https://portal.thirdweb.com/sdk/ui-components/nft-renderer)
For example, we can render each of the NFTs using the [`NFT Media Renderer`](https://portal.thirdweb.com/ui-components/nft-renderer)
component, making use of the loading state from `useNFTs`:
```jsx title="pages/index.jsx"
@@ -191,7 +191,7 @@ export default function Home() {
<div>
{nfts.map((nft) => (
<ThirdwebNftMedia
key={nft.tokenId}
key={nft.metadata.id}
metadata={nft.metadata}
height={200}
/>
@@ -203,7 +203,7 @@ export default function Home() {
}
```
The [`Web3Button`](https://portal.thirdweb.com/sdk/ui-components/web3button) component ensures the user has connected their wallet and is currently configured to the same network as your smart contract before calling the function. It also has access to the `contract` directly, allowing you to perform any action on your smart contract when the button is clicked.
The [`Web3Button`](https://portal.thirdweb.com/ui-components/web3button) component ensures the user has connected their wallet and is currently configured to the same network as your smart contract before calling the function. It also has access to the `contract` directly, allowing you to perform any action on your smart contract when the button is clicked.
For example, we can mint an NFT like so:
@@ -0,0 +1,342 @@
/**
* Config file for API Extractor. For more info, please visit: https://api-extractor.com
*/
{
"$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
/**
* Optionally specifies another JSON config file that this file extends from. This provides a way for
* standard settings to be shared across multiple projects.
*
* If the path starts with "./" or "../", the path is resolved relative to the folder of the file that contains
* the "extends" field. Otherwise, the first path segment is interpreted as an NPM package name, and will be
* resolved using NodeJS require().
*
* SUPPORTED TOKENS: none
* DEFAULT VALUE: ""
*/
// "extends": "./shared/api-extractor-base.json"
// "extends": "my-package/include/api-extractor-base.json"
/**
* Determines the "<projectFolder>" token that can be used with other config file settings. The project folder
* typically contains the tsconfig.json and package.json config files, but the path is user-defined.
*
* The path is resolved relative to the folder of the config file that contains the setting.
*
* The default value for "projectFolder" is the token "<lookup>", which means the folder is determined by traversing
* parent folders, starting from the folder containing api-extractor.json, and stopping at the first folder
* that contains a tsconfig.json file. If a tsconfig.json file cannot be found in this way, then an error
* will be reported.
*
* SUPPORTED TOKENS: <lookup>
* DEFAULT VALUE: "<lookup>"
*/
// "projectFolder": "..",
/**
* (REQUIRED) Specifies the .d.ts file to be used as the starting point for analysis. API Extractor
* analyzes the symbols exported by this module.
*
* The file extension must be ".d.ts" and not ".ts".
*
* The path is resolved relative to the folder of the config file that contains the setting; to change this,
* prepend a folder token such as "<projectFolder>".
*
* SUPPORTED TOKENS: <projectFolder>, <packageName>, <unscopedPackageName>
*/
"mainEntryPointFilePath": "<projectFolder>/solana/dist/thirdweb-dev-react-solana.cjs.d.ts",
/**
* A list of NPM package names whose exports should be treated as part of this package.
*
* For example, suppose that Webpack is used to generate a distributed bundle for the project "library1",
* and another NPM package "library2" is embedded in this bundle. Some types from library2 may become part
* of the exported API for library1, but by default API Extractor would generate a .d.ts rollup that explicitly
* imports library2. To avoid this, we can specify:
*
* "bundledPackages": [ "library2" ],
*
* This would direct API Extractor to embed those types directly in the .d.ts rollup, as if they had been
* local files for library1.
*/
"bundledPackages": [
"wagmi",
"wagmi-core"
],
/**
* Determines how the TypeScript compiler engine will be invoked by API Extractor.
*/
"compiler": {
/**
* Specifies the path to the tsconfig.json file to be used by API Extractor when analyzing the project.
*
* The path is resolved relative to the folder of the config file that contains the setting; to change this,
* prepend a folder token such as "<projectFolder>".
*
* Note: This setting will be ignored if "overrideTsconfig" is used.
*
* SUPPORTED TOKENS: <projectFolder>, <packageName>, <unscopedPackageName>
* DEFAULT VALUE: "<projectFolder>/tsconfig.json"
*/
// "tsconfigFilePath": "<projectFolder>/tsconfig.json",
/**
* Provides a compiler configuration that will be used instead of reading the tsconfig.json file from disk.
* The object must conform to the TypeScript tsconfig schema:
*
* http://json.schemastore.org/tsconfig
*
* If omitted, then the tsconfig.json file will be read from the "projectFolder".
*
* DEFAULT VALUE: no overrideTsconfig section
*/
"overrideTsconfig": {
"skipLibCheck": true
}
/**
* This option causes the compiler to be invoked with the --skipLibCheck option. This option is not recommended
* and may cause API Extractor to produce incomplete or incorrect declarations, but it may be required when
* dependencies contain declarations that are incompatible with the TypeScript engine that API Extractor uses
* for its analysis. Where possible, the underlying issue should be fixed rather than relying on skipLibCheck.
*
* DEFAULT VALUE: false
*/
// "skipLibCheck": true,
},
/**
* Configures how the API report file (*.api.md) will be generated.
*/
"apiReport": {
/**
* (REQUIRED) Whether to generate an API report.
*/
"enabled": true
/**
* The filename for the API report files. It will be combined with "reportFolder" or "reportTempFolder" to produce
* a full file path.
*
* The file extension should be ".api.md", and the string should not contain a path separator such as "\" or "/".
*
* SUPPORTED TOKENS: <packageName>, <unscopedPackageName>
* DEFAULT VALUE: "<unscopedPackageName>.api.md"
*/
// "reportFileName": "<unscopedPackageName>.api.md",
/**
* Specifies the folder where the API report file is written. The file name portion is determined by
* the "reportFileName" setting.
*
* The API report file is normally tracked by Git. Changes to it can be used to trigger a branch policy,
* e.g. for an API review.
*
* The path is resolved relative to the folder of the config file that contains the setting; to change this,
* prepend a folder token such as "<projectFolder>".
*
* SUPPORTED TOKENS: <projectFolder>, <packageName>, <unscopedPackageName>
* DEFAULT VALUE: "<projectFolder>/etc/"
*/
// "reportFolder": "<projectFolder>/etc/",
/**
* Specifies the folder where the temporary report file is written. The file name portion is determined by
* the "reportFileName" setting.
*
* After the temporary file is written to disk, it is compared with the file in the "reportFolder".
* If they are different, a production build will fail.
*
* The path is resolved relative to the folder of the config file that contains the setting; to change this,
* prepend a folder token such as "<projectFolder>".
*
* SUPPORTED TOKENS: <projectFolder>, <packageName>, <unscopedPackageName>
* DEFAULT VALUE: "<projectFolder>/temp/"
*/
// "reportTempFolder": "<projectFolder>/temp/"
},
/**
* Configures how the doc model file (*.api.json) will be generated.
*/
"docModel": {
/**
* (REQUIRED) Whether to generate a doc model file.
*/
"enabled": true
/**
* The output path for the doc model file. The file extension should be ".api.json".
*
* The path is resolved relative to the folder of the config file that contains the setting; to change this,
* prepend a folder token such as "<projectFolder>".
*
* SUPPORTED TOKENS: <projectFolder>, <packageName>, <unscopedPackageName>
* DEFAULT VALUE: "<projectFolder>/temp/<unscopedPackageName>.api.json"
*/
// "apiJsonFilePath": "<projectFolder>/temp/<unscopedPackageName>.api.json"
},
/**
* Configures how the .d.ts rollup file will be generated.
*/
"dtsRollup": {
/**
* (REQUIRED) Whether to generate the .d.ts rollup file.
*/
"enabled": false,
/**
* Specifies the output path for a .d.ts rollup file to be generated without any trimming.
* This file will include all declarations that are exported by the main entry point.
*
* If the path is an empty string, then this file will not be written.
*
* The path is resolved relative to the folder of the config file that contains the setting; to change this,
* prepend a folder token such as "<projectFolder>".
*
* SUPPORTED TOKENS: <projectFolder>, <packageName>, <unscopedPackageName>
* DEFAULT VALUE: "<projectFolder>/dist/<unscopedPackageName>.d.ts"
*/
"untrimmedFilePath": "<projectFolder>/lib/<unscopedPackageName>.d.ts"
/**
* Specifies the output path for a .d.ts rollup file to be generated with trimming for a "beta" release.
* This file will include only declarations that are marked as "@public" or "@beta".
*
* The path is resolved relative to the folder of the config file that contains the setting; to change this,
* prepend a folder token such as "<projectFolder>".
*
* SUPPORTED TOKENS: <projectFolder>, <packageName>, <unscopedPackageName>
* DEFAULT VALUE: ""
*/
// "betaTrimmedFilePath": "<projectFolder>/dist/<unscopedPackageName>-beta.d.ts",
/**
* Specifies the output path for a .d.ts rollup file to be generated with trimming for a "public" release.
* This file will include only declarations that are marked as "@public".
*
* If the path is an empty string, then this file will not be written.
*
* The path is resolved relative to the folder of the config file that contains the setting; to change this,
* prepend a folder token such as "<projectFolder>".
*
* SUPPORTED TOKENS: <projectFolder>, <packageName>, <unscopedPackageName>
* DEFAULT VALUE: ""
*/
// "publicTrimmedFilePath": "<projectFolder>/dist/<unscopedPackageName>-public.d.ts",
/**
* When a declaration is trimmed, by default it will be replaced by a code comment such as
* "Excluded from this release type: exampleMember". Set "omitTrimmingComments" to true to remove the
* declaration completely.
*
* DEFAULT VALUE: false
*/
// "omitTrimmingComments": true
},
/**
* Configures how the tsdoc-metadata.json file will be generated.
*/
"tsdocMetadata": {
/**
* Whether to generate the tsdoc-metadata.json file.
*
* DEFAULT VALUE: true
*/
// "enabled": true,
/**
* Specifies where the TSDoc metadata file should be written.
*
* The path is resolved relative to the folder of the config file that contains the setting; to change this,
* prepend a folder token such as "<projectFolder>".
*
* The default value is "<lookup>", which causes the path to be automatically inferred from the "tsdocMetadata",
* "typings" or "main" fields of the project's package.json. If none of these fields are set, the lookup
* falls back to "tsdoc-metadata.json" in the package folder.
*
* SUPPORTED TOKENS: <projectFolder>, <packageName>, <unscopedPackageName>
* DEFAULT VALUE: "<lookup>"
*/
// "tsdocMetadataFilePath": "<projectFolder>/dist/tsdoc-metadata.json"
},
/**
* Specifies what type of newlines API Extractor should use when writing output files. By default, the output files
* will be written with Windows-style newlines. To use POSIX-style newlines, specify "lf" instead.
* To use the OS's default newline kind, specify "os".
*
* DEFAULT VALUE: "crlf"
*/
// "newlineKind": "crlf",
/**
* Configures how API Extractor reports error and warning messages produced during analysis.
*
* There are three sources of messages: compiler messages, API Extractor messages, and TSDoc messages.
*/
"messages": {
/**
* Configures handling of diagnostic messages reported by the TypeScript compiler engine while analyzing
* the input .d.ts files.
*
* TypeScript message identifiers start with "TS" followed by an integer. For example: "TS2551"
*
* DEFAULT VALUE: A single "default" entry with logLevel=warning.
*/
"compilerMessageReporting": {
/**
* Configures the default routing for messages that don't match an explicit rule in this table.
*/
"default": {
/**
* Specifies whether the message should be written to the the tool's output log. Note that
* the "addToApiReportFile" property may supersede this option.
*
* Possible values: "error", "warning", "none"
*
* Errors cause the build to fail and return a nonzero exit code. Warnings cause a production build fail
* and return a nonzero exit code. For a non-production build (e.g. when "api-extractor run" includes
* the "--local" option), the warning is displayed but the build will not fail.
*
* DEFAULT VALUE: "warning"
*/
"logLevel": "warning"
/**
* When addToApiReportFile is true: If API Extractor is configured to write an API report file (.api.md),
* then the message will be written inside that file; otherwise, the message is instead logged according to
* the "logLevel" option.
*
* DEFAULT VALUE: false
*/
// "addToApiReportFile": false
}
// "TS2551": {
// "logLevel": "warning",
// "addToApiReportFile": true
// },
//
// . . .
},
/**
* Configures handling of messages reported by API Extractor during its analysis.
*
* API Extractor message identifiers start with "ae-". For example: "ae-extra-release-tag"
*
* DEFAULT VALUE: See api-extractor-defaults.json for the complete table of extractorMessageReporting mappings
*/
"extractorMessageReporting": {
"default": {
"logLevel": "warning"
// "addToApiReportFile": false
}
// "ae-extra-release-tag": {
// "logLevel": "warning",
// "addToApiReportFile": true
// },
//
// . . .
},
/**
* Configures handling of messages reported by the TSDoc parser when analyzing code comments.
*
* TSDoc message identifiers start with "tsdoc-". For example: "tsdoc-link-tag-unescaped-text"
*
* DEFAULT VALUE: A single "default" entry with logLevel=warning.
*/
"tsdocMessageReporting": {
"default": {
"logLevel": "warning"
// "addToApiReportFile": false
}
// "tsdoc-link-tag-unescaped-text": {
// "logLevel": "warning",
// "addToApiReportFile": true
// },
//
// . . .
}
}
}
@@ -0,0 +1,4 @@
{
"main": "dist/thirdweb-dev-react-evm-connectors-gnosis-safe.cjs.js",
"module": "dist/thirdweb-dev-react-evm-connectors-gnosis-safe.esm.js"
}
@@ -0,0 +1,4 @@
{
"main": "dist/thirdweb-dev-react-evm-connectors-magic.cjs.js",
"module": "dist/thirdweb-dev-react-evm-connectors-magic.esm.js"
}
+26 -22
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/react",
"version": "3.1.2",
"version": "3.5.2",
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/react",
"author": "thirdweb eng <[email protected]>",
"license": "Apache-2.0",
@@ -20,6 +20,14 @@
"module": "./solana/dist/thirdweb-dev-react-solana.esm.js",
"default": "./solana/dist/thirdweb-dev-react-solana.cjs.js"
},
"./evm/connectors/magic": {
"module": "./evm/connectors/magic/dist/thirdweb-dev-react-evm-connectors-magic.esm.js",
"default": "./evm/connectors/magic/dist/thirdweb-dev-react-evm-connectors-magic.cjs.js"
},
"./evm/connectors/gnosis-safe": {
"module": "./evm/connectors/gnosis-safe/dist/thirdweb-dev-react-evm-connectors-gnosis-safe.esm.js",
"default": "./evm/connectors/gnosis-safe/dist/thirdweb-dev-react-evm-connectors-gnosis-safe.cjs.js"
},
"./package.json": "./package.json"
},
"files": [
@@ -31,7 +39,8 @@
"entrypoints": [
"index.ts",
"solana/index.ts",
"evm/index.ts"
"evm/index.ts",
"evm/connectors/**.ts"
],
"___experimentalFlags_WILL_CHANGE_IN_PATCH": {
"exports": true
@@ -44,12 +53,11 @@
"lint": "eslint src/",
"fix": "eslint src/ --fix",
"clean": "rm -rf dist/",
"generate-docs": "api-extractor run --local && yarn api-documenter markdown -i ./temp -o ./docs && yarn generate-snippets",
"generate:md-docs": "yarn api-documenter markdown -i ./temp -o ./docs",
"generate-snippets": "node ./scripts/generate-snippets.mjs && node ./scripts/generate-feature-snippets.mjs",
"generate-docs": "yarn generate-docs:evm && yarn generate-docs:solana && yarn generate-snippets",
"generate-docs:evm": "api-extractor run --local --config ./config/api-extractor-evm.json && api-documenter markdown -i ./temp -o ./docs/evm && rm -rf ./temp-evm && mv ./temp ./temp-evm",
"generate-docs:solana": "api-extractor run --local --config ./config/api-extractor-solana.json && api-documenter markdown -i ./temp -o ./docs/solana && rm -rf ./temp-solana && mv ./temp ./temp-solana",
"generate-snippets": "node ./scripts/generate-snippets-evm.mjs && node ./scripts/generate-snippets-solana.mjs && node ./scripts/generate-feature-snippets-evm.mjs",
"build": "tsc && preconstruct build",
"e2e": "yarn test-ct",
"test-ct": "playwright test -c playwright-ct.config.ts",
"push": "yalc push"
},
"sideEffects:": false,
@@ -58,18 +66,15 @@
"@babel/preset-env": "^7.18.10",
"@babel/preset-react": "^7.18.6",
"@babel/preset-typescript": "^7.18.6",
"@babel/runtime": "^7.19.0",
"@microsoft/api-documenter": "^7.19.4",
"@microsoft/api-extractor": "^7.29.2",
"@microsoft/tsdoc": "^0.14.1",
"@playwright/experimental-ct-react": "1.25.1",
"@playwright/test": "1.25.1",
"@preconstruct/cli": "^2.2.1",
"@solana/wallet-adapter-base": "^0.9.17",
"@solana/wallet-adapter-react": "^0.15.19",
"@solana/wallet-adapter-wallets": "^0.19.0",
"@solana/web3.js": "^1.63.0",
"@solana/web3.js": "^1.62.0",
"@thirdweb-dev/sdk": "*",
"@thirdweb-dev/solana": "*",
"@types/color": "^3.0.3",
"@types/mime": "^3.0.1",
"@types/react": "^18.0.5",
@@ -82,7 +87,7 @@
"eslint-plugin-inclusive-language": "^2.2.0",
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-tsdoc": "^0.2.16",
"ethers": "^5.7.0",
"ethers": "^5.7.1",
"prettier": "^2.7.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
@@ -91,13 +96,15 @@
"dependencies": {
"@emotion/react": "^11.10.0",
"@emotion/styled": "^11.10.0",
"@gnosis.pm/safe-core-sdk": "2.1.0",
"@gnosis.pm/safe-ethers-adapters": "0.1.0-alpha.9",
"@gnosis.pm/safe-ethers-lib": "1.1.0",
"@gnosis.pm/safe-core-sdk": "^3.1.1",
"@gnosis.pm/safe-ethers-adapters": "0.1.0-alpha.11",
"@gnosis.pm/safe-ethers-lib": "^1.6.1",
"@react-icons/all-files": "^4.1.0",
"@solana/wallet-adapter-phantom": "^0.9.17",
"@tanstack/react-query": "^4.0.10",
"@zag-js/menu": "0.1.14",
"@zag-js/react": "0.1.16",
"@zag-js/menu": "^0.1.16",
"@zag-js/react": "^0.1.18",
"buffer": "^6.0.3",
"color": "^4.2.3",
"copy-to-clipboard": "^3.3.2",
"detect-browser": "^5.3.0",
@@ -111,7 +118,7 @@
"@solana/wallet-adapter-base": "^0.9.17",
"@solana/wallet-adapter-react": "^0.15.19",
"@solana/wallet-adapter-wallets": "^0.19.0",
"@solana/web3.js": "^1.63.0",
"@solana/web3.js": "^1.62.0",
"@thirdweb-dev/sdk": "*",
"ethers": ">=5.5.1",
"react": ">=18.0.0",
@@ -133,9 +140,6 @@
},
"@solana/web3.js": {
"optional": true
},
"@thirdweb-dev/solana": {
"optional": true
}
}
}
-57
View File
@@ -1,57 +0,0 @@
import type { PlaywrightTestConfig } from "@playwright/experimental-ct-react";
import { devices } from "@playwright/experimental-ct-react";
/**
* See https://playwright.dev/docs/test-configuration.
*/
const config: PlaywrightTestConfig = {
testDir: "./",
/* The base directory, relative to the config file, for snapshot files created with toMatchSnapshot and toHaveScreenshot. */
snapshotDir: "./__snapshots__",
/* Maximum time one test can run for. */
timeout: 10 * 1000,
/* Run tests in files in parallel */
fullyParallel: true,
/* Fail the build on CI if you accidentally left test.only in the source code. */
forbidOnly: !!process.env.CI,
/* Retry on CI only */
retries: process.env.CI ? 2 : 0,
/* Opt out of parallel tests on CI. */
// workers: process.env.CI ? 1 : undefined,
// we can use parallel tests because we have more cores now
workers: undefined,
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
reporter: "html",
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
use: {
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
trace: "on-first-retry",
/* Port to use for Playwright component endpoint. */
ctPort: 3100,
},
/* Configure projects for major browsers */
projects: [
{
name: "chromium",
use: {
...devices["Desktop Chrome"],
},
},
{
name: "firefox",
use: {
...devices["Desktop Firefox"],
},
},
{
name: "webkit",
use: {
...devices["Desktop Safari"],
},
},
],
};
export default config;
-4
View File
@@ -1,4 +0,0 @@
// Import styles, initialize component theme here.
// import '../src/common.css';
export {};
@@ -44,7 +44,7 @@ config.setSupportForTag(tag, true);
const tsdocParser = new TSDocParser(config);
const json = JSON.parse(
fs.readFileSync(`${process.cwd()}/temp/react.api.json`, "utf8"),
fs.readFileSync(`${process.cwd()}/temp-evm/react.api.json`, "utf8"),
);
function languageNameToKey(languageName) {
@@ -160,6 +160,6 @@ classes.forEach((m) => {
});
fs.writeFileSync(
`${process.cwd()}/docs/feature_snippets.json`,
`${process.cwd()}/docs/evm/feature_snippets.json`,
JSON.stringify(features, null, 2),
);
@@ -27,41 +27,6 @@ export class Formatter {
}
}
const tsdocParser = new TSDocParser();
const json = JSON.parse(
fs.readFileSync(`${process.cwd()}/temp/react.api.json`, "utf8"),
);
function languageNameToKey(languageName) {
switch (languageName) {
case "js":
case "jsx":
return "javascript";
case "ts":
case "tsx":
return "typescript";
default:
return languageName;
}
}
const CONTRACT_HOOKS = [
"useSignatureDrop",
"useNFTDrop",
"useEditionDrop",
"useNFTCollection",
"useEdition",
"useTokenDrop",
"useToken",
"useMarketplace",
"useSplit",
"useVote",
"usePack",
"useMultiwrap",
"useContract",
];
const NFT_HOOKS = [
"useNFT",
"useNFTs",
@@ -141,7 +106,7 @@ const CONTRACT_SETTINGS_HOOKS = [
"useRevokeRole",
];
const CONTRACT_SUBHOOKS = {
const HOOKS_CONFIG = {
useSignatureDrop: [...NFT_HOOKS, ...DROP_HOOKS, ...CLAIM_CONDITIONS_HOOKS],
useNFTDrop: [...NFT_HOOKS, ...DROP_HOOKS, ...CLAIM_CONDITIONS_HOOKS],
useEditionDrop: [...NFT_HOOKS, ...DROP_HOOKS, ...CLAIM_CONDITIONS_HOOKS],
@@ -162,8 +127,27 @@ const CONTRACT_SUBHOOKS = {
],
};
const tsdocParser = new TSDocParser();
const json = JSON.parse(
fs.readFileSync(`${process.cwd()}/temp-evm/react.api.json`, "utf8"),
);
function languageNameToKey(languageName) {
switch (languageName) {
case "js":
case "jsx":
return "javascript";
case "ts":
case "tsx":
return "typescript";
default:
return languageName;
}
}
const hooks = json.members[0].members.filter(
(m) => m.kind === "Function" && CONTRACT_HOOKS.includes(m.name),
(m) => m.kind === "Function" && Object.keys(HOOKS_CONFIG).includes(m.name),
);
function parseExampleTag(docComment) {
@@ -192,7 +176,7 @@ function parseExampleTag(docComment) {
return examples;
}
const baseDocUrl = "https://docs.thirdweb.com/typescript/react.";
const baseDocUrl = "https://portal.thirdweb.com/react/react.";
const extractReferenceLink = (m, kind, contractName) => {
if (kind === "Property") {
@@ -212,7 +196,7 @@ const moduleMap = hooks.reduce((acc, m) => {
const examples = parseExampleTag(docComment);
const contractSubhooks = json.members[0].members.filter(
(f) => f.kind === "Function" && CONTRACT_SUBHOOKS[m.name].includes(f.name),
(f) => f.kind === "Function" && HOOKS_CONFIG[m.name].includes(f.name),
);
const contractHooks = contractSubhooks.map((subhookContent) => {
@@ -223,7 +207,7 @@ const moduleMap = hooks.reduce((acc, m) => {
return {
name: subhookContent.name,
example: subhookExamples?.javascript || "",
reference: `https://portal.thirdweb.com/react/react.${subhookContent.name.toLowerCase()}`,
reference: `${baseDocUrl}${subhookContent.name.toLowerCase()}`,
};
});
@@ -241,6 +225,6 @@ const moduleMap = hooks.reduce((acc, m) => {
}, {});
fs.writeFileSync(
`${process.cwd()}/docs/snippets.json`,
`${process.cwd()}/docs/evm/snippets.json`,
JSON.stringify(moduleMap, null, 2),
);
@@ -0,0 +1,135 @@
import { Formatter } from "./generate-snippets-evm.mjs";
import { TSDocParser } from "@microsoft/tsdoc";
import fs from "fs";
const NFT_HOOKS = ["useMintNFT", "useNFT", "useNFTs", "useTransferNFT"];
const DROP_HOOKS = [
"useClaimedSupply",
"useClaimNFT",
"useLazyMintNFT",
"useUnclaimedSupply",
];
const TOKEN_HOOKS = [
"useMintToken",
"useTokenBalance",
"useTokenSupply",
"useTransferToken",
];
const HOOKS_CONFIG = {
useNFTCollection: [...NFT_HOOKS],
useNFTDrop: [...NFT_HOOKS, ...DROP_HOOKS],
useToken: [...TOKEN_HOOKS],
useProgram: [],
};
const tsdocParser = new TSDocParser();
const json = JSON.parse(
fs.readFileSync(`${process.cwd()}/temp-solana/react.api.json`, "utf8"),
);
function languageNameToKey(languageName) {
switch (languageName) {
case "js":
case "jsx":
return "javascript";
case "ts":
case "tsx":
return "typescript";
default:
return languageName;
}
}
function parseExampleTag(docComment) {
const exampleBlocks = docComment._customBlocks.filter(
(b) => b._blockTag._tagName === "@example",
);
const examplesString = Formatter.renderDocNodes(exampleBlocks);
const regex = /```([a-zA-Z]*)\n([\S\s]*?)\n```/g;
let matches;
const examples = {};
while ((matches = regex.exec(examplesString)) !== null) {
// This is necessary to avoid infinite loops with zero-width matches
if (matches.index === regex.lastIndex) {
regex.lastIndex++;
}
const re = /\n( *)\/\/ For example((.|\n)*)\.\.\.\n/g;
const example = matches[2].replace(re, "");
examples[languageNameToKey(matches[1])] = example;
}
return examples;
}
const baseDocUrl = "https://portal.thirdweb.com/react/solana/react.";
const extractReferenceLink = (m, kind, contractName) => {
if (kind === "Property") {
return m.excerptTokens
.filter((e) => e.kind === "Reference")
.map((e) => `${baseDocUrl}${e.text.toLowerCase()}`)[0];
}
if (kind === "Method") {
return `${baseDocUrl}${contractName}.${m.name}`;
}
return `${baseDocUrl}${m.name}`;
};
const moduleMap = Object.keys(HOOKS_CONFIG).reduce((acc, hookName) => {
let summary = "";
let examples = {};
let reference = "";
const doc = json.members[0].members.find(
(m) =>
m.kind === "Function" && Object.keys(HOOKS_CONFIG).includes(hookName),
);
if (doc) {
const parserContext = tsdocParser.parseString(doc);
const docComment = parserContext.docComment;
summary = Formatter.renderDocNode(docComment.summarySection);
examples = parseExampleTag(docComment);
reference = extractReferenceLink(doc);
}
const contractSubhooks = json.members[0].members.filter(
(f) => f.kind === "Function" && HOOKS_CONFIG[hookName].includes(f.name),
);
const subhooks = contractSubhooks.map((subhookContent) => {
const subhookContext = tsdocParser.parseString(subhookContent.docComment);
const subhookComment = subhookContext.docComment;
const subhookExamples = parseExampleTag(subhookComment);
return {
name: subhookContent.name,
example: subhookExamples?.javascript || "",
reference: `${baseDocUrl}${subhookContent.name.toLowerCase()}`,
};
});
acc[hookName] = {
name: hookName,
summary,
examples,
subhooks,
reference,
};
return acc;
}, {});
fs.writeFileSync(
`${process.cwd()}/docs/solana/snippets.json`,
JSON.stringify(moduleMap, null, 2),
);
@@ -1,6 +1,6 @@
import { RequiredParam } from "../types/shared";
import { QueryKey } from "@tanstack/react-query";
import type { ChainId } from "@thirdweb-dev/sdk";
import { RequiredParam } from "./required-param";
import type { QueryKey } from "@tanstack/react-query";
import type { ValidContractInstance } from "@thirdweb-dev/sdk";
import type {
Network,
NFTCollection,
@@ -13,7 +13,7 @@ const TW_QUERY_KEY_PREFIX = "__tw__";
// marker to make sure the query will not get stored in local storage by a query persister
const NEVER_PERSIST_QUERY_POSTFIX = { persist: false } as const;
function ensureTWPrefix<TKey extends QueryKey>(key: TKey) {
export function ensureTWPrefix<TKey extends QueryKey>(key: TKey) {
if (key[0] === TW_QUERY_KEY_PREFIX) {
return key as unknown as Readonly<[typeof TW_QUERY_KEY_PREFIX, ...TKey]>;
}
@@ -27,7 +27,7 @@ export function neverPersist<TKey extends QueryKey>(key: TKey) {
export function shouldNeverPersistQuery<TKey extends QueryKey>(
key: TKey,
): boolean {
return key[key.length] === NEVER_PERSIST_QUERY_POSTFIX;
return key[key.length - 1] === NEVER_PERSIST_QUERY_POSTFIX;
}
// EVM
@@ -37,11 +37,23 @@ export function createEVMQueryKey<TKey extends QueryKey>(key: TKey) {
}
export function createEVMQueryKeyWithChain<TKey extends QueryKey>(
key: TKey,
chainId: RequiredParam<ChainId>,
chainId: RequiredParam<number>,
) {
return ensureTWPrefix([chainId, ...key] as const);
}
export function createEVMContractQueryKey<TKey extends QueryKey>(
contract: RequiredParam<ValidContractInstance>,
key: TKey = [] as unknown as TKey,
) {
const chainId = contract?.chainId;
const address = contract?.getAddress();
return createEVMQueryKeyWithChain(
["contract", address, ...key] as const,
chainId,
);
}
// SOL
export function createSOLQueryKey<TKey extends QueryKey>(key: TKey) {
@@ -0,0 +1,15 @@
import invariant from "tiny-invariant";
/**
* Makes a parameter required to be passed, but still allowes it to be null or undefined.
*
* @beta
*/
export type RequiredParam<T> = T | null | undefined;
export function requiredParamInvariant<TValue>(
condition: RequiredParam<TValue>,
message?: string,
): asserts condition {
invariant(condition !== null || condition !== undefined, message);
}
-6
View File
@@ -1,6 +0,0 @@
/**
* Makes a parameter required to be passed, but still allowes it to be null or undefined.
*
* @beta
*/
export type RequiredParam<T> = T | null | undefined;
@@ -1,15 +0,0 @@
import { ConnectWallet } from ".";
import { ThirdwebProvider } from "../../providers/full";
import { test, expect } from "@playwright/experimental-ct-react";
test.use({ viewport: { width: 500, height: 500 } });
test("should render the connect wallet button", async ({ mount }) => {
const component = await mount(
<ThirdwebProvider desiredChainId={1}>
<ConnectWallet />
</ThirdwebProvider>,
);
await expect(component).toContainText("Connect Wallet");
});
@@ -76,10 +76,8 @@ const chainIdToCurrencyMap: Record<
> = {
[ChainId.Mainnet]: "ethereum",
[ChainId.Goerli]: "ethereum",
[ChainId.Rinkeby]: "ethereum",
[ChainId.Arbitrum]: "arbitrum",
[ChainId.ArbitrumRinkeby]: "arbitrum",
[ChainId.ArbitrumGoerli]: "arbitrum",
[ChainId.Avalanche]: "avalanche",
@@ -89,7 +87,6 @@ const chainIdToCurrencyMap: Record<
[ChainId.FantomTestnet]: "fantom",
[ChainId.Optimism]: "optimism",
[ChainId.OptimismKovan]: "optimism",
[ChainId.OptimismGoerli]: "optimism",
[ChainId.Polygon]: "polygon",
@@ -147,7 +144,7 @@ export const ConnectWallet: React.FC<ConnectWalletProps> = ({
},
connect,
] = useConnect();
const disconnect = useDisconnect({ reconnectAfterGnosis: false });
const disconnect = useDisconnect({ reconnectPrevious: false });
const supportedConnectors = connectors.filter((c) =>
SUPPORTED_CONNECTORS.includes(c.id as typeof SUPPORTED_CONNECTORS[number]),
@@ -13,12 +13,14 @@ export interface ThirdwebNftMediaProps extends SharedMediaProps {
}
/**
*
* This component can be used to render NFTs from the thirdweb SDK.
* Props: {@link ThirdwebNftMediaProps}
*
* @example
* ```jsx
* import { ThirdwebNftMedia, useNFTCollection, useNFT } from "@thirdweb-dev/react";
* import { ThirdwebNftMedia, useContract, useNFT } from "@thirdweb-dev/react";
* export default function NFTCollectionRender() {
* const contract = useNFTCollection(<your-contract-address>);
* const { contract } = useContract(<your-contract-address>);
* const { data: nft, isLoading } = useNFT(contract, 0);
*
* return (
@@ -32,11 +34,6 @@ export interface ThirdwebNftMediaProps extends SharedMediaProps {
* );
* }
* ```
*
* Use this to get the primary sales recipient of your {@link SmartContract}
* @param contract - an instance of a {@link SmartContract}
* @returns the wallet address of the primary sales recipient
* @beta
*/
export const ThirdwebNftMedia = React.forwardRef<
HTMLMediaElement,
@@ -1,16 +0,0 @@
import { Web3Button } from ".";
import { ThirdwebProvider } from "../../providers/full";
import { test, expect } from "@playwright/experimental-ct-react";
test.use({ viewport: { width: 500, height: 500 } });
test("should render the connect wallet button", async ({ mount }) => {
const component = await mount(
<ThirdwebProvider desiredChainId={1}>
<Web3Button contractAddress="0x123" action={() => undefined}>
Do the thing
</Web3Button>
</ThirdwebProvider>,
);
await expect(component).toContainText("Connect Wallet");
});
@@ -13,7 +13,7 @@ import { ThemeProvider, ThemeProviderProps } from "../shared/ThemeProvider";
import { FiWifi } from "@react-icons/all-files/fi/FiWifi";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import type { SmartContract } from "@thirdweb-dev/sdk/dist/declarations/src/evm/contracts/smart-contract";
import type { CallOverrides } from "ethers";
import type { CallOverrides, ContractInterface } from "ethers";
import { PropsWithChildren, useMemo } from "react";
import invariant from "tiny-invariant";
@@ -23,6 +23,7 @@ interface Web3ButtonProps<TActionFn extends ActionFn>
extends ThemeProviderProps {
className?: string;
contractAddress: `0x${string}` | `${string}.eth` | string;
contractAbi?: ContractInterface;
overrides?: CallOverrides;
// called with the result
@@ -64,6 +65,7 @@ export const Web3Button = <TAction extends ActionFn>({
onError,
onSubmit,
isDisabled,
contractAbi,
children,
action,
className,
@@ -85,7 +87,7 @@ export const Web3Button = <TAction extends ActionFn>({
return null;
}, [sdkChainId, walletChainId]);
const { contract } = useContract(contractAddress);
const { contract } = useContract(contractAddress, contractAbi || "custom");
const mutation = useMutation(
async () => {
@@ -37,8 +37,19 @@ const BaseButton = styled.button<BaseButtonProps>`
cursor: not-allowed;
}
&:focus {
outline: 2px solid ${(props) => (props.theme as TwUiTheme).colors.accent};
outline-offset: 1px;
position: relative;
border-radius: 0.5em;
outline: 0;
}
&:focus:after {
content: "";
position: absolute;
top: -5px;
right: -5px;
bottom: -5px;
left: -5px;
border: 2px solid ${(props) => (props.theme as TwUiTheme).colors.accent};
border-radius: 11px;
}
`;
@@ -8,10 +8,23 @@ export const Menu = styled.ul<MenuProps>`
margin: 0;
background: ${(props) => (props.theme as TwUiTheme).colors.background};
color: ${(props) => (props.theme as TwUiTheme).colors.text};
outline-color: ${(props) => (props.theme as TwUiTheme).colors.accent};
outline-style: solid;
outline-offset: 0px;
list-style: none;
border-radius: 0.5em;
overflow: hidden;
position: relative;
pointer-events: auto;
&:focus {
outline: 0;
}
&:after {
content: "";
position: absolute;
border: 1px solid ${(props) => (props.theme as TwUiTheme).colors.accent};
border-radius: 0.5em;
pointer-events: none;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
`;
@@ -1,14 +1,23 @@
import { ChainId } from "@thirdweb-dev/sdk";
import { useConnect } from "../hooks/wagmi-required/useConnect";
import { Signer, ethers, utils } from "ethers";
import invariant from "tiny-invariant";
import { Chain, Connector, ConnectorData, normalizeChainId } from "wagmi";
import {
Chain,
Connector,
ConnectorData,
normalizeChainId,
useContext as useWagmiContext,
} from "wagmi";
const CHAIN_ID_TO_GNOSIS_SERVER_URL = {
[ChainId.Mainnet]: "https://safe-transaction.mainnet.gnosis.io",
[ChainId.Avalanche]: "https://safe-transaction.avalanche.gnosis.io",
[ChainId.Polygon]: "https://safe-transaction.polygon.gnosis.io",
[ChainId.Goerli]: "https://safe-transaction.goerli.gnosis.io",
[ChainId.Rinkeby]: "https://safe-transaction.rinkeby.gnosis.io",
// mainnet
1: "https://safe-transaction.mainnet.gnosis.io",
// avalanche
43114: "https://safe-transaction.avalanche.gnosis.io",
// polygon
137: "https://safe-transaction.polygon.gnosis.io",
// goerli
5: "https://safe-transaction.goerli.gnosis.io",
};
export interface GnosisConnectorArguments {
@@ -164,3 +173,67 @@ export class GnosisSafeConnector extends Connector {
this.config = config;
}
}
/**
* Hook for connecting to a Gnosis Safe. This enables multisig wallets to connect to your application and sing transactions.
*
* ```javascript
* import { useGnosis } from "@thirdweb-dev/react"
* ```
*
*
* @example
* ```javascript
* import { useGnosis } from "@thirdweb-dev/react"
*
* const App = () => {
* const connectWithGnosis = useGnosis()
*
* return (
* <button onClick={() => connectWithGnosis({ safeAddress: "0x...", safeChainId: 1 })}>
* Connect Gnosis Safe
* </button>
* )
* }
* ```
*
* @public
*/
export function useGnosis() {
const wagmiContext = useWagmiContext();
invariant(
wagmiContext,
`useGnosis() can only be used inside <ThirdwebProvider />. If you are using <ThirdwebSDKProvider /> you will have to use your own wallet-connection logic.`,
);
const [connectors, connect] = useConnect();
if (connectors.loading) {
return () => Promise.reject("Gnosis connector not ready to be used, yet");
}
const connector = connectors.data.connectors.find((c) => c.id === "gnosis");
invariant(
connector,
"Gnosis connector not found, please make sure it is provided to your <ThirdwebProvider />",
);
return async (config: GnosisConnectorArguments) => {
const previousConnector = connectors.data.connector;
const previousConnectorChain = await previousConnector?.getChainId();
invariant(
!!previousConnector,
"Cannot connect to Gnosis Safe without first being connected to a personal wallet.",
);
invariant(
previousConnectorChain === config.safeChainId,
"Gnosis safe chain id must match personal wallet chain id.",
);
invariant(
utils.isAddress(config.safeAddress),
"Gnosis safe address must be a valid address.",
);
(connector as GnosisSafeConnector).setConfiguration(
previousConnector,
config,
);
return connect(connector);
};
}
+93 -1
View File
@@ -1,3 +1,4 @@
import { useConnect } from "../hooks/wagmi-required/useConnect";
import { providers, utils } from "ethers";
import type {
LoginWithMagicLinkConfiguration,
@@ -5,7 +6,12 @@ import type {
MagicSDKAdditionalConfiguration,
} from "magic-sdk";
import invariant from "tiny-invariant";
import { Chain, Connector, normalizeChainId } from "wagmi";
import {
Chain,
Connector,
normalizeChainId,
useContext as useWagmiContext,
} from "wagmi";
export interface MagicConnectorArguments
extends MagicSDKAdditionalConfiguration {
@@ -186,3 +192,89 @@ export class MagicConnector extends Connector {
}
}
}
/**
* Hook for connecting to an email wallet using magic link.
* This enables users without their own wallets to connect to your application and sign transactions securely using their email.
*
* ```javascript
* import { useMagic } from "@thirdweb-dev/react"
* ```
*
*
* @example
* Before using this hook, you first need to set up the magic configuration in your `ThirdwebProvider`, including your magic API key.
*
* ```javascript
* // Add the magic configuration object to your wallet connectors
* const connectors = [
* "metamask",
* "walletConnect",
* "walletLink",
* {
* name: "magic",
* options: {
* apiKey: "your-magic-api-key",
* }
* }
* ]
*
* // Add the above to the walletConnectors prop of your <ThirdwebProvider />
* const Provider = ({ children }) => (
* return (
* <ThirdwebProvider
* walletConnectors={connectors}
* // Specify remaining parameters
* ...
* >
* {children}
* </ThirdwebProvider>
* )
* }
* ```
*
* In order to use the hook to connect users with magic link, you just need to provide the users email to the connect function.
*
* You can setup the hook with the following configuration:
* ```javascript
* import { useMagic } from "@thirdweb-dev/react"
* import { useState } from "react"
*
* const LoginWithMagicLink = () => {
* const connectWithMagic = useMagic()
* const [email, setEmail] = useState()
*
* return (
* <div>
* <input value={email} onChange={(e) => setEmail(e.target.value)} />
* <button onClick={() => connectWithMagic({ email })}>Login</button>
* </div>
* )
* }
* ```
*
* @public
*/
export function useMagic() {
const wagmiContext = useWagmiContext();
invariant(
wagmiContext,
`useMagic() can only be used inside <ThirdwebProvider />. If you are using <ThirdwebSDKProvider /> you will have to use your own wallet-connection logic.`,
);
const [connectors, connect] = useConnect();
if (connectors.loading) {
return () => Promise.reject("Magic connector not ready to be used, yet");
}
const connector = connectors.data.connectors.find((c) => c.id === "magic");
invariant(
connector,
"Magic connector not found, please make sure it is provided to your <ThirdwebProvider />",
);
return (configuration: LoginWithMagicLinkConfiguration) => {
(connector as MagicConnector).setConfiguration(configuration);
return connect(connector);
};
}
+3 -42
View File
@@ -16,20 +16,7 @@ const chain: Record<string, Chain> = {
},
],
},
rinkeby: {
id: ChainId.Rinkeby,
name: "Rinkeby",
nativeCurrency: NATIVE_TOKENS[ChainId.Rinkeby],
rpcUrls: ["https://rinkeby.infura.io/v3/9aa3d95b3bc440fa88ea12eaa4456161"],
blockExplorers: [
{
name: "Etherscan",
url: "https://rinkeby.etherscan.io",
},
],
testnet: true,
deprecated: true,
},
goerli: {
id: ChainId.Goerli,
name: "Goerli",
@@ -147,20 +134,7 @@ const chain: Record<string, Chain> = {
],
testnet: false,
},
optimismKovan: {
id: ChainId.OptimismKovan,
name: "Optimism Kovan",
nativeCurrency: NATIVE_TOKENS[ChainId.OptimismKovan],
rpcUrls: ["https://kovan.optimism.io"],
blockExplorers: [
{
name: "Etherscan",
url: "https://kovan-optimistic.etherscan.io/",
},
],
testnet: true,
deprecated: true,
},
optimismGoerli: {
id: ChainId.OptimismGoerli,
name: "Optimism Goerli Testnet",
@@ -187,20 +161,7 @@ const chain: Record<string, Chain> = {
],
testnet: false,
},
arbitrumRinkeby: {
id: ChainId.ArbitrumRinkeby,
name: "Arbitrum Rinkeby",
nativeCurrency: NATIVE_TOKENS[ChainId.ArbitrumRinkeby],
rpcUrls: ["https://rinkeby.arbitrum.io/rpc"],
blockExplorers: [
{
name: "Arbiscan",
url: "https://testnet.arbiscan.io/",
},
],
testnet: true,
deprecated: true,
},
arbitrumGoerli: {
id: ChainId.ArbitrumGoerli,
name: "Arbitrum Goerli",
@@ -3,7 +3,7 @@ import {
SupportedChainId,
defaultSupportedChains,
} from "../constants/chain";
import { ChainId } from "@thirdweb-dev/sdk";
import { DEFAULT_RPC_URLS } from "@thirdweb-dev/sdk";
import React, { PropsWithChildren, createContext, useContext } from "react";
interface ThirdwebConfigContext {
@@ -11,18 +11,8 @@ interface ThirdwebConfigContext {
supportedChains: Chain[];
}
export const defaultChainRpc: Record<SupportedChainId | number, string> = {
[ChainId.Mainnet]: "mainnet",
[ChainId.Rinkeby]: "rinkeby",
[ChainId.Goerli]: "goerli",
[ChainId.Polygon]: "polygon",
[ChainId.Mumbai]: "mumbai",
[ChainId.Fantom]: "fantom",
[ChainId.Avalanche]: "avalanche",
};
const ThirdwebConfigContext = createContext<ThirdwebConfigContext>({
rpcUrlMap: defaultChainRpc,
rpcUrlMap: DEFAULT_RPC_URLS,
supportedChains: defaultSupportedChains,
});
@@ -33,7 +33,17 @@ export const ThirdwebConnectedWalletProvider: React.FC<
const { rpcUrlMap } = useThirdwebConfigContext();
const [contextValue, setContextValue] =
useState<ThirdwebConnectedWalletContext>(INITIAL_CONTEXT_VALUE);
useState<ThirdwebConnectedWalletContext>({
...INITIAL_CONTEXT_VALUE,
signer: signer ? signer : undefined,
});
useEffect(() => {
setContextValue((val) => ({
...val,
signer: signer ? signer : undefined,
}));
}, [signer]);
useEffect(() => {
let s = signer;
@@ -1,4 +1,7 @@
import { RequiredParam } from "../../../core/types/shared";
import {
RequiredParam,
requiredParamInvariant,
} from "../../../core/query-utils/required-param";
import { useSDKChainId } from "../../providers/base";
import { getErcs, DropContract, WalletAddress } from "../../types";
import {
@@ -7,7 +10,11 @@ import {
} from "../../utils/cache-keys";
import { useQueryWithNetwork } from "../query-utils/useQueryWithNetwork";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { ClaimCondition, ClaimConditionInput } from "@thirdweb-dev/sdk";
import {
ClaimCondition,
ClaimConditionFetchOptions,
ClaimConditionInput,
} from "@thirdweb-dev/sdk";
import { BigNumberish } from "ethers";
import invariant from "tiny-invariant";
@@ -38,7 +45,7 @@ export type SetClaimConditionsParams = {
/** **********************/
/**
* Use this to get the active claim conditon for ERC20, ERC721 or ERC1155 based contracts. They need to extend the `claimCondition` extension for this hook to work.
* Use this to get the active claim condition for ERC20, ERC721 or ERC1155 based contracts. They need to extend the `claimCondition` extension for this hook to work.
*
* @example
* ```javascript
@@ -53,7 +60,7 @@ export type SetClaimConditionsParams = {
* const { data: activeClaimCondition, isLoading, error } = useActiveClaimCondition(<YourERC1155ContractInstance>, <tokenId>);
* ```
*
* @param contract - an instance of a contract that extends the ERC721 or ERC1155 spec and implements the `claimConditions` extension.
* @param contract - an instance of a contract that extends the ERC721, ERC1155 or ERC20 spec and implements the `claimConditions` extension.
* @param tokenId - the id of the token to fetch the claim conditions for (if the contract is an ERC1155 contract)
* @returns a response object with the currently active claim condition
* @twfeature ERC721ClaimableWithConditions | ERC1155ClaimableWithConditions | ERC20ClaimableWithConditions
@@ -62,22 +69,30 @@ export type SetClaimConditionsParams = {
export function useActiveClaimCondition(
contract: RequiredParam<DropContract>,
tokenId?: BigNumberish,
options?: ClaimConditionFetchOptions,
) {
const contractAddress = contract?.getAddress();
const { erc1155, erc721, erc20 } = getErcs(contract);
return useQueryWithNetwork(
cacheKeys.extensions.claimConditions.getActive(contractAddress, tokenId),
cacheKeys.extensions.claimConditions.getActive(
contractAddress,
tokenId,
options,
),
() => {
if (erc1155) {
invariant(tokenId, "tokenId is required for ERC1155 claim conditions");
return erc1155.claimConditions.getActive(tokenId);
requiredParamInvariant(
tokenId,
"tokenId is required for ERC1155 claim conditions",
);
return erc1155.claimConditions.getActive(tokenId, options);
}
if (erc721) {
return erc721.claimConditions.getActive();
return erc721.claimConditions.getActive(options);
}
if (erc20) {
return erc20.claimConditions.getActive();
return erc20.claimConditions.getActive(options);
}
throw new Error("Contract must be ERC721, ERC1155 or ERC20");
},
@@ -91,7 +106,70 @@ export function useActiveClaimCondition(
}
/**
* Use this to get all claim conditons for ERC20, ERC721 or ERC1155 based contracts. They need to extend the `claimCondition` extension for this hook to work.
* Use this to get the claimer proofs for an adddress for ERC20, ERC721 or ERC1155 based contracts. They need to extend the `claimCondition` extension for this hook to work.
*
* @example
* ```javascript
* const { data: claimerProofs, isLoading, error } = useClaimerProofs(<YourERC20ContractInstance>);
* ```
* @example
* ```javascript
* const { data: claimerProofs, isLoading, error } = useClaimerProofs(<YourERC721ContractInstance>);
* ```
* @example
* ```javascript
* const { data: claimerProofs, isLoading, error } = useClaimerProofs(<YourERC1155ContractInstance>, <tokenId>);
* ```
*
* @param contract - an instance of a contract that extends the ERC721, ERC1155 or ERC20 spec and implements the `claimConditions` extension.
* @param claimerAddress - the address of the claimer to fetch the claimer proofs for
* @param tokenId - the id of the token to fetch the claimer proofs for (if the contract is an ERC1155 contract)
* @param claimConditionId - optional the claim condition id to get the proofs for
* @returns a response object with the snapshot for the provided address
* @twfeature ERC721ClaimableWithConditions | ERC1155ClaimableWithConditions | ERC20ClaimableWithConditions
* @beta
*/
export function useClaimerProofs(
contract: RequiredParam<DropContract>,
claimerAddress: string,
tokenId?: BigNumberish,
claimConditionId?: BigNumberish,
) {
const contractAddress = contract?.getAddress();
const { erc1155, erc721, erc20 } = getErcs(contract);
return useQueryWithNetwork(
cacheKeys.extensions.claimConditions.getClaimerProofs(
contractAddress,
tokenId,
),
() => {
if (erc1155) {
requiredParamInvariant(
tokenId,
"tokenId is required for ERC1155 claim conditions",
);
return erc1155.claimConditions.getClaimerProofs(tokenId, claimerAddress, claimConditionId);
}
if (erc721) {
return erc721.claimConditions.getClaimerProofs(claimerAddress, claimConditionId);
}
if (erc20) {
return erc20.claimConditions.getClaimerProofs(claimerAddress, claimConditionId);
}
throw new Error("Contract must be ERC721, ERC1155 or ERC20");
},
{
// Checks that happen here:
// 1. if the contract is based on ERC1155 contract => tokenId cannot be `undefined`
// 2. if the contract is NOT based on ERC1155 => we have to have either an ERC721 or ERC20 contract
enabled: erc1155 ? tokenId !== undefined : !!erc721 || !!erc20,
},
);
}
/**
* Use this to get all claim conditions for ERC20, ERC721 or ERC1155 based contracts. They need to extend the `claimCondition` extension for this hook to work.
*
* @example
* ```javascript
@@ -106,7 +184,7 @@ export function useActiveClaimCondition(
* const { data: claimConditions, isLoading, error } = useClaimConditions(<YourERC1155ContractInstance>, <tokenId>);
* ```
*
* @param contract - an instance of a contract that extends the ERC721 or ERC1155 spec and implements the `claimConditions` extension.
* @param contract - an instance of a contract that extends the ERC721, ERC1155 or ERC20 spec and implements the `claimConditions` extension.
* @param tokenId - the id of the token to fetch the claim conditions for (if the contract is an ERC1155 contract)
* @returns a response object with the list of claim conditions
* @twfeature ERC721ClaimableWithConditions | ERC1155ClaimableWithConditions | ERC20ClaimableWithConditions
@@ -115,22 +193,30 @@ export function useActiveClaimCondition(
export function useClaimConditions(
contract: RequiredParam<DropContract>,
tokenId?: BigNumberish,
options?: ClaimConditionFetchOptions,
) {
const contractAddress = contract?.getAddress();
const { erc1155, erc721, erc20 } = getErcs(contract);
return useQueryWithNetwork(
cacheKeys.extensions.claimConditions.getAll(contractAddress, tokenId),
cacheKeys.extensions.claimConditions.getAll(
contractAddress,
tokenId,
options,
),
() => {
if (erc1155) {
invariant(tokenId, "tokenId is required for ERC1155 claim conditions");
return erc1155.claimConditions.getAll(tokenId);
requiredParamInvariant(
tokenId,
"tokenId is required for ERC1155 claim conditions",
);
return erc1155.claimConditions.getAll(tokenId, options);
}
if (erc721) {
return erc721.claimConditions.getAll();
return erc721.claimConditions.getAll(options);
}
if (erc20) {
return erc20.claimConditions.getAll();
return erc20.claimConditions.getAll(options);
}
throw new Error("Contract must be ERC721, ERC1155 or ERC20");
},
@@ -181,7 +267,7 @@ export function useClaimIneligibilityReasons(
),
() => {
if (erc1155) {
invariant(
requiredParamInvariant(
tokenId,
"tokenId is required for ERC1155 claim ineligibility reasons",
);
@@ -267,11 +353,14 @@ export function useSetClaimConditions(
return useMutation(
async (data: SetClaimConditionsParams) => {
invariant(contract, "No Contract instance provided");
requiredParamInvariant(contract, "No Contract instance provided");
const { phases, reset = false } = data;
invariant(phases, 'No "phases" provided');
if (erc1155) {
invariant(tokenId, "tokenId is required for ERC1155 claim conditions");
requiredParamInvariant(
tokenId,
"tokenId is required for ERC1155 claim conditions",
);
return erc1155.claimConditions.set(tokenId, phases, reset);
}
if (erc721) {
@@ -338,35 +427,42 @@ export function useResetClaimConditions(
return useMutation(
async () => {
const cleanConditions = (conditions: ClaimCondition[]) => {
const cleanConditions = async (conditions: ClaimCondition[]) => {
return conditions.map((c) => ({
...c,
price: c.currencyMetadata.displayValue,
maxQuantity: c.maxQuantity.toString(),
quantityLimitPerTransaction: c.quantityLimitPerTransaction.toString(),
}));
};
if (erc1155) {
invariant(tokenId, "tokenId is required for ERC1155 claim conditions");
const claimConditions = await erc1155.claimConditions.getAll(tokenId);
requiredParamInvariant(
tokenId,
"tokenId is required for ERC1155 claim conditions",
);
const claimConditions = await erc1155.claimConditions.getAll(tokenId, {
withAllowList: true,
});
return erc1155.claimConditions.set(
tokenId,
cleanConditions(claimConditions || []),
await cleanConditions(claimConditions || []),
true,
);
}
if (erc721) {
const claimConditions = await erc721.claimConditions.getAll();
const claimConditions = await erc721.claimConditions.getAll({
withAllowList: true,
});
return await erc721.claimConditions.set(
cleanConditions(claimConditions || []),
await cleanConditions(claimConditions || []),
true,
);
}
if (erc20) {
const claimConditions = await erc20.claimConditions.getAll();
const claimConditions = await erc20.claimConditions.getAll({
withAllowList: true,
});
return await erc20.claimConditions.set(
cleanConditions(claimConditions || []),
await cleanConditions(claimConditions || []),
true,
);
}
@@ -1,4 +1,7 @@
import { RequiredParam } from "../../../core/types/shared";
import {
RequiredParam,
requiredParamInvariant,
} from "../../../core/query-utils/required-param";
import { useSDKChainId } from "../../providers/base";
import { WalletAddress } from "../../types";
import {
@@ -35,7 +38,7 @@ export function usePrimarySaleRecipient(
return useQueryWithNetwork(
cacheKeys.extensions.sales.getRecipient(contractAddress),
() => {
invariant(contract, "No contract provided");
requiredParamInvariant(contract, "No contract provided");
invariant(
"sales" in contract && contract.sales,
"Contract does not support primarySale",
@@ -86,7 +89,7 @@ export function useUpdatePrimarySaleRecipient(
const activeChainId = useSDKChainId();
return useMutation(
(newRecipient: WalletAddress) => {
invariant(contract, "No contract provided");
requiredParamInvariant(contract, "No contract provided");
invariant(
"sales" in contract && contract.sales,
"Contract does not support primarySale",
@@ -128,7 +131,7 @@ export function useRoyaltySettings(
return useQueryWithNetwork(
cacheKeys.extensions.royalties.getDefaultRoyaltyInfo(contractAddress),
() => {
invariant(contract, "No contract provided");
requiredParamInvariant(contract, "No contract provided");
invariant(
"royalties" in contract && contract.royalties,
"Contract does not support royalties",
@@ -182,7 +185,7 @@ export function useUpdateRoyaltySettings(
seller_fee_basis_points?: number;
fee_recipient?: WalletAddress;
}) => {
invariant(contract, "No contract provided");
requiredParamInvariant(contract, "No contract provided");
invariant(
"royalties" in contract && contract.royalties,
"Contract does not support royalties",
@@ -224,7 +227,7 @@ export function usePlatformFees(
return useQueryWithNetwork(
cacheKeys.extensions.platformFees.get(contractAddress),
() => {
invariant(contract, "No contract provided");
requiredParamInvariant(contract, "No contract provided");
invariant(
"platformFees" in contract && contract.platformFees,
"Contract does not support platformFees",
@@ -277,7 +280,7 @@ export function useUpdatePlatformFees(
platform_fee_basis_points?: number;
fee_recipient?: WalletAddress;
}) => {
invariant(contract, "No contract provided");
requiredParamInvariant(contract, "No contract provided");
invariant(
"platformFees" in contract && contract.platformFees,
"Contract does not support platformFees",
@@ -317,7 +320,7 @@ export function useMetadata(contract: RequiredParam<ValidContractInstance>) {
return useQueryWithNetwork(
cacheKeys.extensions.metadata.get(contractAddress),
() => {
invariant(contract, "No contract provided");
requiredParamInvariant(contract, "No contract provided");
invariant(
"metadata" in contract && contract.metadata,
"Contract does not support metadata",
@@ -366,7 +369,7 @@ export function useUpdateMetadata(
const activeChainId = useSDKChainId();
return useMutation(
(updatePayload: CustomContractMetadata) => {
invariant(contract, "No contract provided");
requiredParamInvariant(contract, "No contract provided");
invariant(
"metadata" in contract && contract.metadata,
"Contract does not support metadata",
+94 -38
View File
@@ -1,5 +1,8 @@
import { neverPersist } from "../../../core/query-utils/query-key";
import { RequiredParam } from "../../../core/types/shared";
import {
RequiredParam,
requiredParamInvariant,
} from "../../../core/query-utils/required-param";
import { useSDK, useSDKChainId } from "../../providers/base";
import { ContractAddress } from "../../types";
import {
@@ -18,13 +21,16 @@ import {
import {
CommonContractSchemaInput,
ContractEvent,
EventQueryFilter,
ContractForPrebuiltContractType,
ContractType,
EventQueryOptions,
PrebuiltContractType,
SUPPORTED_CHAIN_ID,
ThirdwebSDK,
ValidContractInstance,
} from "@thirdweb-dev/sdk";
import type { SmartContract } from "@thirdweb-dev/sdk/dist/declarations/src/evm/contracts/smart-contract";
import { CallOverrides } from "ethers";
import { CallOverrides, ContractInterface } from "ethers";
import { useEffect, useMemo } from "react";
import invariant from "tiny-invariant";
@@ -147,9 +153,55 @@ export type UseContractResult<
* @returns a response object that includes the contract once it is resolved
* @public
*/
export function useContract<
TContract extends ValidContractInstance = SmartContract,
>(contractAddress: RequiredParam<ContractAddress>) {
export function useContract(
contractAddress: RequiredParam<ContractAddress>,
): UseContractResult<SmartContract>;
/**
* Use this resolve a contract address to a smart contract instance.
*
* @example
* ```javascript
* const { contract, isLoading, error } = useContract("{{contract_address}}", "nft-drop");
* ```
*
* @param contractAddress - the address of the deployed contract
* @param _contractType - the type of the contract
* @returns a response object that includes the contract once it is resolved
* @public
*/
export function useContract<TContractType extends ContractType>(
contractAddress: RequiredParam<ContractAddress>,
_contractType: TContractType,
): UseContractResult<
TContractType extends PrebuiltContractType
? ContractForPrebuiltContractType<TContractType>
: SmartContract
>;
/**
* Use this resolve a contract address to a smart contract instance.
*
* @example
* ```javascript
* const { contract, isLoading, error } = useContract("{{contract_address}}", ABI);
* ```
*
* @param contractAddress - the address of the deployed contract
* @param _abi - the ABI of the contract to use
* @returns a response object that includes the contract once it is resolved
* @public
*/
export function useContract(
contractAddress: RequiredParam<ContractAddress>,
_abi: ContractInterface,
): UseContractResult<SmartContract>;
export function useContract(
contractAddress: RequiredParam<ContractAddress>,
contractTypeOrABI?: ContractType | ContractInterface,
) {
const sdk = useSDK();
const queryClient = useQueryClient();
const activeChainId = useSDKChainId();
@@ -167,32 +219,36 @@ export function useContract<
{ wallet, walletChainId, sdkTimestamp },
]),
async () => {
invariant(contractAddress, "contract address is required");
requiredParamInvariant(contractAddress, "contract address is required");
invariant(sdk, "SDK not initialized");
invariant(activeChainId, "active chain id is required");
// first fetch the contract type (we fetch this explicitly via the queryClient so **it** gets cached!)
const cType = await queryClient.fetchQuery(
contractType.cacheKey(contractAddress, activeChainId),
() => contractType.fetchQuery(contractAddress, sdk),
{ cacheTime: Infinity, staleTime: Infinity },
);
// if we can't get the contract type, we need to exit
invariant(cType, "could not get contract type");
// if the contract type is NOT "custom", we can use the built-in contract method from the SDK
if (cType !== "custom") {
return await sdk.getBuiltInContract(contractAddress, cType);
}
// if the contract type is "custom", we need to fetch the compiler metadata
const compMetadata = await queryClient.fetchQuery(
compilerMetadata.cacheKey(contractAddress, activeChainId),
() => compilerMetadata.fetchQuery(contractAddress, sdk),
{ cacheTime: Infinity, staleTime: Infinity },
);
// if we can't get the compiler metadata, we need to exit
invariant(compMetadata, "could not get compiler metadata");
// if we have the compiler metadata, we can use the custom contract
return sdk.getContractFromAbi(contractAddress, compMetadata.abi);
// if we don't have a contractType or ABI then we will have to resolve it regardless
// we also handle it being "custom" just in case...
if (!contractTypeOrABI || contractTypeOrABI === "custom") {
// we just resolve here (sdk does this internally anyway)
const resolvedContractType = await queryClient.fetchQuery(
contractType.cacheKey(contractAddress, activeChainId),
() => contractType.fetchQuery(contractAddress, sdk),
{ cacheTime: Infinity, staleTime: Infinity },
);
let abi: ContractInterface | undefined;
if (resolvedContractType === "custom") {
abi = (
await queryClient.fetchQuery(
compilerMetadata.cacheKey(contractAddress, activeChainId),
() => compilerMetadata.fetchQuery(contractAddress, sdk),
{ cacheTime: Infinity, staleTime: Infinity },
)
)?.abi;
}
invariant(resolvedContractType, "failed to resolve contract type");
// just let the sdk handle the rest
// if we have resolved an ABI for a custom contract, use that otherwise use contract type
return sdk.getContract(contractAddress, abi || resolvedContractType);
}
// every other case can just be handled by the sdk directly
return sdk.getContract(contractAddress, contractTypeOrABI);
},
{
// keep the previous value around while we fetch the new one
@@ -211,7 +267,7 @@ export function useContract<
...contractQuery,
data: contractQuery.data,
contract: contractQuery.data,
} as UseContractResult<TContract>;
} as UseContractResult<ValidContractInstance>;
}
/**
@@ -237,7 +293,7 @@ export function useContractMetadata<TContract extends ValidContractInstance>(
>(
cacheKeys.contract.metadata(contract?.getAddress()),
async () => {
invariant(contract, "contract is required");
requiredParamInvariant(contract, "contract is required");
return await contract.metadata.get();
},
{
@@ -258,7 +314,7 @@ export function useContractMetadataUpdate(
return useMutation(
async (metadata: CommonContractSchemaInput) => {
invariant(contract, "contract must be defined");
requiredParamInvariant(contract, "contract must be defined");
return contract.metadata.update(metadata);
},
@@ -290,7 +346,7 @@ export function useContractMetadataUpdate(
export function useContractEvents(
contract: RequiredParam<ValidContractInstance>,
eventName?: string,
options: { queryFilter?: EventQueryFilter; subscribe?: boolean } = {
options: { queryFilter?: EventQueryOptions; subscribe?: boolean } = {
subscribe: true,
},
) {
@@ -353,7 +409,7 @@ export function useContractEvents(
return useQuery(
cacheKey,
() => {
invariant(contract, "contract must be defined");
requiredParamInvariant(contract, "contract must be defined");
if (eventName) {
return contract.events.getEvents(eventName, options.queryFilter);
}
@@ -394,8 +450,8 @@ export function useContractRead(
return useQueryWithNetwork(
cacheKeys.contract.call(contractAddress, functionName, args),
() => {
invariant(contract, "contract must be defined");
invariant(functionName, "function name must be provided");
requiredParamInvariant(contract, "contract must be defined");
requiredParamInvariant(functionName, "function name must be provided");
return contract.call(functionName, ...args);
},
{
@@ -432,8 +488,8 @@ export function useContractWrite(
return useMutation(
async (callParams?: unknown[] | [...unknown[], CallOverrides]) => {
invariant(contract, "contract must be defined");
invariant(functionName, "function name must be provided");
requiredParamInvariant(contract, "contract must be defined");
requiredParamInvariant(functionName, "function name must be provided");
if (!callParams?.length) {
return contract.call(functionName);
}
+38 -24
View File
@@ -1,4 +1,7 @@
import { RequiredParam } from "../../../core/types/shared";
import {
RequiredParam,
requiredParamInvariant,
} from "../../../core/query-utils/required-param";
import { useSDKChainId } from "../../providers/base";
import {
ClaimNFTParams,
@@ -51,7 +54,7 @@ export function useUnclaimedNFTs(
return useQueryWithNetwork(
cacheKeys.contract.nft.drop.getAllUnclaimed(contractAddress, queryParams),
() => {
invariant(contract, "No Contract instance provided");
requiredParamInvariant(contract, "No Contract instance provided");
// TODO make this work for custom contracts (needs ABI change)
invariant(
contract.getAllUnclaimed,
@@ -94,18 +97,18 @@ export function useClaimedNFTs(
*/
export function useUnclaimedNFTSupply(contract: RequiredParam<DropContract>) {
const contractAddress = contract?.getAddress();
const { erc721 } = getErcs(contract);
return useQueryWithNetwork(
cacheKeys.contract.nft.drop.totalUnclaimedSupply(contractAddress),
() => {
invariant(contract, "No Contract instance provided");
// TODO make this work for custom contracts (needs ABI change)
invariant(
"totalUnclaimedSupply" in contract && contract.totalUnclaimedSupply,
"Contract instance does not support fetching unclaimed supply",
);
return contract.totalUnclaimedSupply();
requiredParamInvariant(contract, "No Contract instance provided");
if (erc721) {
invariant(erc721, "No ERC721 Contract instance provided");
return erc721.totalUnclaimedSupply();
}
invariant(false, "Contract is not an instance of ERC721")
},
{ enabled: !!contract },
{ enabled: !!erc721 },
);
}
@@ -118,18 +121,17 @@ export function useUnclaimedNFTSupply(contract: RequiredParam<DropContract>) {
*/
export function useClaimedNFTSupply(contract: RequiredParam<DropContract>) {
const contractAddress = contract?.getAddress();
const { erc721 } = getErcs(contract);
return useQueryWithNetwork(
cacheKeys.contract.nft.drop.totalClaimedSupply(contractAddress),
() => {
invariant(contract, "No Contract instance provided");
// TODO make this work for custom contracts (needs ABI change)
invariant(
"totalClaimedSupply" in contract && contract.totalClaimedSupply,
"Contract instance does not support fetching unclaimed supply",
);
return contract.totalClaimedSupply();
requiredParamInvariant(contract, "No Contract instance provided");
if (erc721) {
return erc721.totalClaimedSupply();
}
invariant(false, "Contract is not an instance of ERC721")
},
{ enabled: !!contract },
{ enabled: !!erc721 },
);
}
@@ -206,11 +208,17 @@ export function useClaimNFT<TContract extends DropContract>(
return useMutation(
async (data: ClaimNFTParams) => {
invariant(data.to, 'No "to" address provided');
invariant(contract, "contract is undefined");
requiredParamInvariant(contract, "contract is undefined");
if (erc1155) {
invariant(!!data.tokenId, "tokenId not provided");
requiredParamInvariant(data.tokenId, "tokenId not provided");
if (!data.to) {
return (await erc1155.claim(
data.tokenId,
data.quantity,
data.options,
)) as ClaimNFTReturnType;
}
return (await erc1155.claimTo(
data.to,
data.tokenId,
@@ -219,6 +227,12 @@ export function useClaimNFT<TContract extends DropContract>(
)) as ClaimNFTReturnType;
}
if (erc721) {
if (!data.to) {
return (await erc721.claim(
data.quantity,
data.options,
)) as ClaimNFTReturnType;
}
return (await erc721.claimTo(
data.to,
data.quantity,
@@ -258,7 +272,7 @@ export function useLazyMint<TContract extends DropContract>(
return useMutation(
async (data: { metadatas: NFTMetadataInput[] }) => {
invariant(contract, "contract is undefined");
requiredParamInvariant(contract, "contract is undefined");
let options;
if (onProgress) {
options = {
@@ -304,7 +318,7 @@ export function useDelayedRevealLazyMint<TContract extends RevealableContract>(
return useMutation(
async (data: DelayedRevealLazyMintInput) => {
invariant(contract, "contract is undefined");
requiredParamInvariant(contract, "contract is undefined");
let options;
if (onProgress) {
options = {
@@ -358,7 +372,7 @@ export function useRevealLazyMint<TContract extends RevealableContract>(
return useMutation(
async (data: RevealLazyMintInput) => {
invariant(contract, "contract is undefined");
requiredParamInvariant(contract, "contract is undefined");
const { erc721, erc1155 } = getErcs(contract);
if (erc721) {
return await erc721.revealer.reveal(data.batchId, data.password);
+266 -20
View File
@@ -1,12 +1,22 @@
import { RequiredParam } from "../../../core/types/shared";
import {
RequiredParam,
requiredParamInvariant,
} from "../../../core/query-utils/required-param";
import { useSDKChainId } from "../../providers/base";
import { BuyNowParams, MakeBidParams } from "../../types";
import {
AcceptDirectOffer,
BuyNowParams,
ExecuteAuctionSale,
MakeBidParams,
MakeOfferParams,
} from "../../types";
import {
cacheKeys,
invalidateContractAndBalances,
} from "../../utils/cache-keys";
import { useQueryWithNetwork } from "../query-utils/useQueryWithNetwork";
import { useAddress } from "../wallet";
import { useContractEvents } from "./contracts";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import type {
AuctionListing,
@@ -17,7 +27,7 @@ import type {
} from "@thirdweb-dev/sdk";
import { ListingType } from "@thirdweb-dev/sdk";
import { Marketplace } from "@thirdweb-dev/sdk/dist/declarations/src/evm/contracts/prebuilt-implementations/marketplace";
import { BigNumber, BigNumberish } from "ethers";
import { BigNumberish } from "ethers";
import invariant from "tiny-invariant";
/** **********************/
@@ -45,8 +55,9 @@ export function useListing(
return useQueryWithNetwork(
cacheKeys.contract.marketplace.getListing(contractAddress, listingId),
() => {
invariant(contract, "No Contract instance provided");
return contract.getListing(BigNumber.from(listingId || 0));
requiredParamInvariant(contract, "No Contract instance provided");
requiredParamInvariant(listingId, "No listing id provided");
return contract.getListing(listingId);
},
{
enabled: !!contract,
@@ -76,7 +87,7 @@ export function useListings(
return useQueryWithNetwork(
cacheKeys.contract.marketplace.getAllListings(contractAddress, filter),
() => {
invariant(contract, "No Contract instance provided");
requiredParamInvariant(contract, "No Contract instance provided");
return contract.getAllListings(filter);
},
{
@@ -103,7 +114,7 @@ export function useListingsCount(contract: RequiredParam<Marketplace>) {
return useQueryWithNetwork(
cacheKeys.contract.marketplace.getTotalCount(contractAddress),
() => {
invariant(contract, "No Contract instance provided");
requiredParamInvariant(contract, "No Contract instance provided");
return contract.getTotalCount();
},
{
@@ -133,7 +144,7 @@ export function useActiveListings(
return useQueryWithNetwork(
cacheKeys.contract.marketplace.getActiveListings(contractAddress, filter),
() => {
invariant(contract, "No Contract instance provided");
requiredParamInvariant(contract, "No Contract instance provided");
return contract.getActiveListings(filter);
},
@@ -168,8 +179,9 @@ export function useWinningBid(
listingId,
),
() => {
invariant(contract, "No Contract instance provided");
return contract.auction.getWinningBid(BigNumber.from(listingId || 0));
requiredParamInvariant(contract, "No Contract instance provided");
requiredParamInvariant(listingId, "No listing id provided");
return contract.auction.getWinningBid(listingId);
},
{
enabled: !!contract && listingId !== undefined,
@@ -201,12 +213,11 @@ export function useAuctionWinner(
listingId,
),
async () => {
invariant(contract, "No Contract instance provided");
requiredParamInvariant(contract, "No Contract instance provided");
requiredParamInvariant(listingId, "No listing id provided");
let winner: string | undefined;
try {
winner = await contract.auction.getWinner(
BigNumber.from(listingId || 0),
);
winner = await contract.auction.getWinner(listingId);
} catch (err) {
if (!(err as Error)?.message?.includes("Could not find auction")) {
throw err;
@@ -238,7 +249,7 @@ export function useBidBuffer(contract: RequiredParam<Marketplace>) {
return useQueryWithNetwork(
cacheKeys.contract.marketplace.getBidBufferBps(contractAddress),
() => {
invariant(contract, "No Contract instance provided");
requiredParamInvariant(contract, "No Contract instance provided");
return contract.getBidBufferBps();
},
{
@@ -247,6 +258,40 @@ export function useBidBuffer(contract: RequiredParam<Marketplace>) {
);
}
/**
* Use this to get the minimum next bid for the auction listing from your marketplace contract.
*
* @example
* ```javascript
* const { data: minimumNextBid, isLoading, error } = useMinimumNextBid(<YourMarketplaceContractInstance>, <listingId>);
* ```
*
* @param contract - an instance of a marketplace contract
* @param listingId - the listing id to check
* @returns a response object that includes the minimum next bid for the auction listing
* @beta
*/
export function useMinimumNextBid(
contract: RequiredParam<Marketplace>,
listingId: RequiredParam<BigNumberish>,
) {
const contractAddress = contract?.getAddress();
return useQueryWithNetwork(
cacheKeys.contract.marketplace.auction.getWinner(
contractAddress,
listingId,
),
async () => {
requiredParamInvariant(contract, "No Contract instance provided");
requiredParamInvariant(listingId, "No listing id provided");
return await contract.auction.getMinimumNextBid(listingId);
},
{
enabled: !!contract && listingId !== undefined,
},
);
}
/** **********************/
/** WRITE HOOKS **/
/** **********************/
@@ -290,8 +335,9 @@ export function useCreateDirectListing(contract: RequiredParam<Marketplace>) {
return useMutation(
async (data: NewDirectListing) => {
invariant(walletAddress, "no wallet connected, cannot create listing");
requiredParamInvariant(contract, "No Contract instance provided");
invariant(
contract?.direct?.createListing,
contract.direct.createListing,
"contract does not support direct.createListing",
);
return await contract.direct.createListing(data);
@@ -346,8 +392,9 @@ export function useCreateAuctionListing(contract: RequiredParam<Marketplace>) {
return useMutation(
async (data: NewAuctionListing) => {
invariant(walletAddress, "no wallet connected, cannot create listing");
requiredParamInvariant(contract, "No Contract instance provided");
invariant(
contract?.direct?.createListing,
contract.direct.createListing,
"contract does not support auction.createListing",
);
return await contract.auction.createListing(data);
@@ -456,8 +503,9 @@ export function useMakeBid(contract: RequiredParam<Marketplace>) {
return useMutation(
async (data: MakeBidParams) => {
invariant(walletAddress, "no wallet connected, cannot make bid");
requiredParamInvariant(contract, "No Contract instance provided");
invariant(
contract?.auction?.makeBid,
contract.auction.makeBid,
"contract does not support auction.makeBid",
);
return await contract.auction.makeBid(data.listingId, data.bid);
@@ -473,6 +521,203 @@ export function useMakeBid(contract: RequiredParam<Marketplace>) {
);
}
/**
* Use this to make an offer on direct or auction listing from your marketplace contract.
*
* @example
* ```jsx
* const Component = () => {
* const {
* mutate: makeOffer,
* isLoading,
* error,
* } = useMakeOffer(">>YourMarketplaceContractInstance<<");
*
* if (error) {
* console.error("failed to make a bid", error);
* }
*
* return (
* <button
* disabled={isLoading}
* onClick={() => makeOffer({ listingId: 1, pricePerToken: 0.5, quantity: 1 })}
* >
* Bid!
* </button>
* );
* };
* ```
*
* @param contract - an instance of a Marketplace contract
* @returns a mutation object that can be used to make a bid on an auction listing
* @beta
*/
export function useMakeOffer(contract: RequiredParam<Marketplace>) {
const activeChainId = useSDKChainId();
const contractAddress = contract?.getAddress();
const queryClient = useQueryClient();
const walletAddress = useAddress();
return useMutation(
async (data: MakeOfferParams) => {
invariant(walletAddress, "no wallet connected, cannot make bid");
requiredParamInvariant(contract, "No Contract instance provided");
return await contract.makeOffer(
data.listingId,
data.pricePerToken,
data.quantity,
);
},
{
onSettled: () =>
invalidateContractAndBalances(
queryClient,
contractAddress,
activeChainId,
),
},
);
}
/**
* Accept an offer on a direct listing from an offeror, will accept the latest offer by the given offeror.
*
* @example
* ```jsx
* const Component = () => {
* const {
* mutate: acceptOffer,
* isLoading,
* error,
* } = useAcceptDirectListingOffer(">>YourMarketplaceContractInstance<<");
*
* if (error) {
* console.error("failed to accept offer", error);
* }
*
* return (
* <button
* disabled={isLoading}
* onClick={() => acceptOffer({ listingId: 1, addressOfOfferor: "0x..." })}
* >
* Accept offer
* </button>
* );
* };
* ```
*
* @param contract - an instance of a Marketplace contract
* @returns a mutation object that can be used to accept an offer on a direct listing
* @beta
*/
export function useAcceptDirectListingOffer(
contract: RequiredParam<Marketplace>,
) {
const activeChainId = useSDKChainId();
const contractAddress = contract?.getAddress();
const queryClient = useQueryClient();
const walletAddress = useAddress();
return useMutation(
async (data: AcceptDirectOffer) => {
invariant(walletAddress, "no wallet connected, cannot make bid");
requiredParamInvariant(contract?.direct, "No Direct instance provided");
return await contract.direct.acceptOffer(
data.listingId,
data.addressOfOfferor,
);
},
{
onSettled: () =>
invalidateContractAndBalances(
queryClient,
contractAddress,
activeChainId,
),
},
);
}
/**
* Execute an auction sale. Can only be executed once the auction has ended and the auction has a winning bid.
*
* @example
* ```jsx
* const Component = () => {
* const {
* mutate: executeAuctionSale,
* isLoading,
* error,
* } = useExecuteAuctionSale(">>YourMarketplaceContractInstance<<");
*
* if (error) {
* console.error("failed to execute sale", error);
* }
*
* return (
* <button
* disabled={isLoading}
* onClick={() => executeAuctionSale({ listingId: 1 })}
* >
* Execute sale
* </button>
* );
* };
* ```
*
* @param contract - an instance of a Marketplace contract
* @returns a mutation object that can be used to accept an offer on a direct listing
* @beta
*/
export function useExecuteAuctionSale(contract: RequiredParam<Marketplace>) {
const activeChainId = useSDKChainId();
const contractAddress = contract?.getAddress();
const queryClient = useQueryClient();
const walletAddress = useAddress();
return useMutation(
async (data: ExecuteAuctionSale) => {
invariant(walletAddress, "no wallet connected, cannot make bid");
requiredParamInvariant(
contract?.auction,
"No Auction marketplace instance provided",
);
return await contract.auction.executeSale(data.listingId);
},
{
onSettled: () =>
invalidateContractAndBalances(
queryClient,
contractAddress,
activeChainId,
),
},
);
}
/**
* Get all the offers for a listing
*
* @remarks Fetch all the offers for a specified direct or auction listing.
* @example
* ```javascript
* const { data: offers, isLoading, error } = useOffers(<YourMarketplaceContractInstance>, <listingId>);
* ```
*
* @param contract - an instance of a Marketplace contract
* @param listingId - the id of the listing to fetch offers for
* @beta
*/
export function useOffers(
contract: RequiredParam<Marketplace>,
listingId: RequiredParam<BigNumberish>,
) {
const result = useContractEvents(contract, "NewOffer");
return {
...result,
data: result.data
?.filter((ev) => ev.data.listingId.eq(listingId))
?.map((ev) => ev.data),
};
}
/**
* Use this to buy out an auction listing from your marketplace contract.
*
@@ -512,9 +757,10 @@ export function useBuyNow(contract: RequiredParam<Marketplace>) {
return useMutation(
async (data: BuyNowParams) => {
invariant(walletAddress, "no wallet connected, cannot make bid");
requiredParamInvariant(contract, "No Contract instance provided");
if (data.type === ListingType.Direct) {
invariant(
contract?.direct.buyoutListing,
contract.direct.buyoutListing,
"contract does not support direct.buyoutListing",
);
@@ -525,7 +771,7 @@ export function useBuyNow(contract: RequiredParam<Marketplace>) {
);
}
invariant(
contract?.auction?.buyoutListing,
contract.auction.buyoutListing,
"contract does not support auction.buyoutListing",
);
return await contract.auction.buyoutListing(data.id);
+31 -82
View File
@@ -1,4 +1,7 @@
import { RequiredParam } from "../../../core/types/shared";
import {
requiredParamInvariant,
RequiredParam,
} from "../../../core/query-utils/required-param";
import { useSDKChainId } from "../../providers/base";
import {
AirdropNFTParams,
@@ -6,10 +9,8 @@ import {
MintNFTParams,
MintNFTReturnType,
MintNFTSupplyParams,
NFT,
TransferNFTParams,
WalletAddress,
Erc721OrErc1155,
NFTContract,
getErcs,
} from "../../types";
@@ -19,45 +20,10 @@ import {
} from "../../utils/cache-keys";
import { useQueryWithNetwork } from "../query-utils/useQueryWithNetwork";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { Erc721, Erc1155, QueryAllParams } from "@thirdweb-dev/sdk";
import { Erc1155, QueryAllParams, NFT } from "@thirdweb-dev/sdk";
import { BigNumber, BigNumberish } from "ethers";
import invariant from "tiny-invariant";
/**
* @internal
*/
export function convertResponseToNFTType(
contract: Erc721OrErc1155,
metadata: Awaited<ReturnType<typeof contract["get"]>>,
): NFT<typeof contract> {
if (contract instanceof Erc721) {
return {
type: "ERC721",
supply: 1,
owner: "",
...metadata,
} as NFT<Erc721>;
}
if (contract instanceof Erc1155) {
return {
type: "ERC1155",
supply: 0,
owner: "",
...metadata,
} as NFT<Erc1155>;
}
invariant(false, "Unknown NFT type");
}
/**
* @internal
*/
export function convertResponseToNFTTypeArray(
contract: Erc721OrErc1155,
metadata: Awaited<ReturnType<typeof contract["get"]>>[],
): NFT<typeof contract>[] {
return metadata.map((m) => convertResponseToNFTType(contract, m));
}
/** **********************/
/** READ HOOKS **/
/** **********************/
@@ -84,24 +50,18 @@ export function useNFT<TContract extends NFTContract>(
const contractAddress = contract?.getAddress();
const { erc721, erc1155 } = getErcs(contract);
return useQueryWithNetwork<NFT<Erc721OrErc1155>>(
return useQueryWithNetwork<NFT>(
cacheKeys.contract.nft.get(contractAddress, tokenId),
async () => {
invariant(contract, "No Contract instance provided");
requiredParamInvariant(contract, "No Contract instance provided");
if (erc1155) {
invariant(erc1155.get, "Contract instance does not support get");
return convertResponseToNFTType(
erc1155,
await erc1155.get(BigNumber.from(tokenId || 0)),
);
return await erc1155.get(BigNumber.from(tokenId || 0));
}
if (erc721) {
invariant(erc721.get, "Contract instance does not support get");
return convertResponseToNFTType(
erc721,
await erc721.get(BigNumber.from(tokenId || 0)),
);
return await erc721.get(BigNumber.from(tokenId || 0));
}
invariant(false, "Unknown NFT type");
},
@@ -133,24 +93,18 @@ export function useNFTs<TContract extends NFTContract>(
const contractAddress = contract?.getAddress();
const { erc721, erc1155 } = getErcs(contract);
return useQueryWithNetwork<NFT<Erc721OrErc1155>[]>(
return useQueryWithNetwork<NFT[]>(
cacheKeys.contract.nft.query.all(contractAddress, queryParams),
async () => {
invariant(contract, "No Contract instance provided");
requiredParamInvariant(contract, "No Contract instance provided");
if (erc721) {
invariant(erc721.getAll, "Contract instance does not support getAll");
return convertResponseToNFTTypeArray(
erc721,
await erc721.getAll(queryParams),
);
return await erc721.getAll(queryParams);
}
if (erc1155) {
invariant(erc1155.getAll, "Contract instance does not support getAll");
return convertResponseToNFTTypeArray(
erc1155,
await erc1155.getAll(queryParams),
);
return await erc1155.getAll(queryParams);
}
invariant(false, "Unknown NFT type");
},
@@ -183,7 +137,7 @@ export function useTotalCount<TContract extends NFTContract>(
return useQueryWithNetwork<BigNumber>(
cacheKeys.contract.nft.query.totalCount(contractAddress),
async () => {
invariant(contract, "No Contract instance provided");
requiredParamInvariant(contract, "No Contract instance provided");
if (erc1155) {
invariant(
@@ -231,14 +185,14 @@ export function useTotalCirculatingSupply(
return useQueryWithNetwork<BigNumber>(
cacheKeys.contract.nft.query.totalCirculatingSupply(contractAddress),
async () => {
invariant(contract, "No Contract instance provided");
requiredParamInvariant(contract, "No Contract instance provided");
if (erc1155) {
invariant(
erc1155.totalCirculatingSupply,
"Contract instance does not support totalCirculatingSupply",
);
invariant(tokenId, "No tokenId provided");
requiredParamInvariant(tokenId, "No tokenId provided");
return await erc1155.totalCirculatingSupply(tokenId);
}
if (erc721) {
@@ -278,22 +232,16 @@ export function useOwnedNFTs<TContract extends NFTContract>(
const contractAddress = contract?.getAddress();
const { erc721, erc1155 } = getErcs(contract);
return useQueryWithNetwork<NFT<Erc721OrErc1155>[]>(
return useQueryWithNetwork<NFT[]>(
cacheKeys.contract.nft.query.owned.all(contractAddress, ownerWalletAddress),
async () => {
invariant(contract, "No Contract instance provided");
requiredParamInvariant(contract, "No Contract instance provided");
invariant(ownerWalletAddress, "No wallet address provided");
if (erc721) {
return convertResponseToNFTTypeArray(
erc721,
await erc721.getOwned(ownerWalletAddress),
);
return await erc721.getOwned(ownerWalletAddress);
}
if (erc1155) {
return convertResponseToNFTTypeArray(
erc1155,
await erc1155.getOwned(ownerWalletAddress),
);
return await erc1155.getOwned(ownerWalletAddress);
}
invariant(false, "Unknown NFT type");
},
@@ -332,11 +280,11 @@ export function useNFTBalance(
tokenId,
),
() => {
invariant(contract, "No Contract instance provided");
requiredParamInvariant(contract, "No Contract instance provided");
invariant(ownerWalletAddress, "No owner wallet address provided");
if (erc1155) {
invariant(tokenId, "No tokenId provided");
requiredParamInvariant(tokenId, "No tokenId provided");
invariant(
erc1155.balanceOf,
"Contract instance does not support balanceOf",
@@ -430,7 +378,7 @@ export function useMintNFT<TContract extends NFTContract>(
return useMutation(
async (data: MintNFTParams) => {
invariant(data.to, 'No "to" address provided');
invariant(contract, "contract is undefined");
requiredParamInvariant(contract, "contract is undefined");
if (erc1155) {
invariant("supply" in data, "supply not provided");
const { to, metadata, supply } = data;
@@ -499,9 +447,9 @@ export function useMintNFTSupply(contract: Erc1155) {
return useMutation(
async (data: MintNFTSupplyParams) => {
invariant(data.to, 'No "to" address provided');
invariant(contract, "contract is undefined");
requiredParamInvariant(contract, "contract is undefined");
invariant("tokenId" in data, "tokenId not provided");
requiredParamInvariant(data.tokenId, "tokenId not provided");
invariant("additionalSupply" in data, "additionalSupply not provided");
const { to, tokenId, additionalSupply } = data;
return await contract.mintAdditionalSupplyTo(
@@ -567,7 +515,7 @@ export function useTransferNFT<TContract extends NFTContract>(
invariant("to" in data, "to not provided");
if (erc1155) {
invariant(erc1155.transfer, "contract does not support transfer");
invariant("tokenId" in data, "tokenId not provided");
requiredParamInvariant(data.tokenId, "tokenId not provided");
invariant("amount" in data, "amount not provided");
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
return erc1155.transfer(data.to, data.tokenId, data.amount!);
@@ -594,7 +542,7 @@ export function useTransferNFT<TContract extends NFTContract>(
* @example
* ```jsx
* const Component = () => {
* const editionDrop = useEditionDrop(<ContractAddress>);
* const editionDrop = useContract(<ContractAddress>, "edition-drop");
* const {
* mutate: airdropNFT,
* isLoading,
@@ -657,7 +605,8 @@ export function useAirdropNFT(contract: Erc1155) {
return useMutation(
({ tokenId, addresses }: AirdropNFTParams) => {
invariant(contract?.airdrop, "contract does not support airdrop");
requiredParamInvariant(contract, "contract is undefined");
invariant(contract.airdrop, "contract does not support airdrop");
return contract.airdrop(tokenId, addresses);
},
@@ -743,8 +692,8 @@ export function useBurnNFT<TContract extends NFTContract>(
return useMutation(
async (data: BurnNFTParams) => {
invariant(data.tokenId, "No tokenId provided");
invariant(contract, "contract is undefined");
requiredParamInvariant(data.tokenId, "No tokenId provided");
requiredParamInvariant(contract, "contract is undefined");
if (erc1155) {
invariant("amount" in data, "amount not provided");
const { tokenId, amount } = data;
+9 -6
View File
@@ -1,4 +1,7 @@
import { RequiredParam } from "../../../core/types/shared";
import {
requiredParamInvariant,
RequiredParam,
} from "../../../core/query-utils/required-param";
import { useSDKChainId } from "../../providers/base";
import { WalletAddress } from "../../types";
import {
@@ -61,7 +64,7 @@ export function useAllRoleMembers<TContract extends ContractWithRoles>(
return useQueryWithNetwork<Awaited<GetAllReturnType<TContract>>>(
cacheKeys.extensions.roles.getAll(contractAddress),
() => {
invariant(contract, "No contract provided");
requiredParamInvariant(contract, "No contract provided");
invariant(contract.roles, "Contract does not support roles");
// have to cast to any because of role bs, type is defined in the useQueryWithNetwork definition above
return contract.roles.getAll() as any;
@@ -94,7 +97,7 @@ export function useRoleMembers<TContract extends ContractWithRoles>(
return useQueryWithNetwork(
cacheKeys.extensions.roles.get(contractAddress, role),
() => {
invariant(contract, "No contract provided");
requiredParamInvariant(contract, "No contract provided");
invariant(contract.roles, "Contract does not support roles");
return contract.roles.get(role);
},
@@ -192,7 +195,7 @@ export function useSetAllRoleMembers<TContract extends ContractWithRoles>(
async (rolesWithAddresses: {
[role in RolesForContract<TContract>]: string[];
}) => {
invariant(contract, "No contract provided");
requiredParamInvariant(contract, "No contract provided");
invariant(contract.roles, "Contract does not support roles");
await contract.roles.setAll(rolesWithAddresses);
},
@@ -253,7 +256,7 @@ export function useGrantRole<TContract extends ContractWithRoles>(
role: RolesForContract<TContract>;
address: WalletAddress;
}) => {
invariant(contract, "No contract provided");
requiredParamInvariant(contract, "No contract provided");
invariant(contract.roles, "Contract does not support roles");
await contract.roles.grant(params.role as any, params.address);
},
@@ -311,7 +314,7 @@ export function useRevokeRole<TContract extends ContractWithRoles>(
role: RolesForContract<TContract>;
address: WalletAddress;
}) => {
invariant(contract, "No contract provided");
requiredParamInvariant(contract, "No contract provided");
invariant(contract.roles, "Contract does not support roles");
await contract.roles.revoke(params.role as any, params.address);
},
+98 -55
View File
@@ -1,8 +1,13 @@
import { RequiredParam } from "../../../core/types/shared";
import {
requiredParamInvariant,
RequiredParam,
} from "../../../core/query-utils/required-param";
import { useSDKChainId } from "../../providers/base";
import {
ClaimTokenParams,
getErc20,
TokenBurnParams,
TokenContract,
TokenParams,
WalletAddress,
} from "../../types";
@@ -12,7 +17,6 @@ import {
} from "../../utils/cache-keys";
import { useQueryWithNetwork } from "../query-utils/useQueryWithNetwork";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import type { Erc20 } from "@thirdweb-dev/sdk";
import invariant from "tiny-invariant";
/** **********************/
@@ -24,21 +28,26 @@ import invariant from "tiny-invariant";
*
* @example
* ```javascript
* const { data: totalSupply, isLoading, error } = useTokenSupply(<YourTokenContractInstance>);
* const { contract } = useContract(<ContractAddress>);
* const { data: totalSupply, isLoading, error } = useTokenSupply(contract);
* ```
*
* @param contract - an instance of a ERC20 contract.
* @param contract - an instance of a {@link TokenContract}
* @returns a response object that incudes the total minted supply
* @twfeature ERC20
* @beta
*/
export function useTokenSupply(contract: RequiredParam<Erc20>) {
export function useTokenSupply(contract: RequiredParam<TokenContract>) {
const contractAddress = contract?.getAddress();
return useQueryWithNetwork(
cacheKeys.contract.token.totalSupply(contractAddress),
() => {
invariant(contract, "No Contract instance provided");
return contract.totalSupply();
requiredParamInvariant(contract, "No Contract instance provided");
const erc20 = getErc20(contract);
if (erc20) {
return erc20.totalSupply();
}
invariant(false, "Smart contract is not a valid erc20 contract");
},
{
enabled: !!contract || !!contractAddress,
@@ -51,26 +60,32 @@ export function useTokenSupply(contract: RequiredParam<Erc20>) {
*
* @example
* ```javascript
* const { data: balance, isLoading, error } = useTokenBalance(<YourTokenContractInstance>);
* const { contract } = useContract(<ContractAddress>);
* const { data: balance, isLoading, error } = useTokenBalance(contract);
* ```
*
* @param contract - an instance of a ERC20 contract.
* @param contract - an instance of a {@link TokenContract}
* @returns a response object that includes the balance of the address
* @twfeature ERC20
* @beta
*/
export function useTokenBalance(
contract: RequiredParam<Erc20>,
contract: RequiredParam<TokenContract>,
walletAddress: RequiredParam<WalletAddress>,
) {
const contractAddress = contract?.getAddress();
const erc20 = getErc20(contract);
return useQueryWithNetwork(
cacheKeys.contract.token.balanceOf(contractAddress, walletAddress),
async () => {
invariant(contract, "No Contract instance provided");
requiredParamInvariant(contract, "No Contract instance provided");
invariant(walletAddress, "No address provided");
return await contract.balanceOf(walletAddress);
if (erc20) {
return await erc20.balanceOf(walletAddress);
}
invariant(false, "Smart contract is not a valid erc20 contract");
},
{
enabled: !!walletAddress && !!contract,
},
@@ -82,21 +97,27 @@ export function useTokenBalance(
*
* @example
* ```javascript
* const { data: decimals, isLoading, error } = useTokenDecimals(<YourTokenContractInstance>);
* const { contract } = useContract(<ContractAddress>);
* const { data: decimals, isLoading, error } = useTokenDecimals(contract);
* ```
*
* @param contract - an instance of an ERC20 contract.
* @param contract - an instance of a {@link TokenContract}
* @returns a response object that includes the decimals of the ERC20 token
* @twfeature ERC20
* @beta
*/
export function useTokenDecimals(contract: RequiredParam<Erc20>) {
export function useTokenDecimals(contract: RequiredParam<TokenContract>) {
const contractAddress = contract?.getAddress();
const erc20 = getErc20(contract);
return useQueryWithNetwork(
cacheKeys.contract.token.decimals(contractAddress),
async () => {
invariant(contract, "No Contract instance provided");
return (await contract.get()).decimals;
requiredParamInvariant(contract, "No Contract instance provided");
if (erc20) {
return (await erc20.get()).decimals;
}
invariant(false, "Smart contract is not a valid erc20 contract");
},
{
enabled: !!contract,
@@ -114,11 +135,12 @@ export function useTokenDecimals(contract: RequiredParam<Erc20>) {
* @example
* ```jsx
* const Component = () => {
* const { contract } = useContract(<ContractAddress>);
* const {
* mutate: mintTokens,
* isLoading,
* error,
* } = useMintToken(">>YourERC20ContractInstance<<");
* } = useMintToken(contract);
*
* if (error) {
* console.error("failed to mint tokens", error);
@@ -135,21 +157,25 @@ export function useTokenDecimals(contract: RequiredParam<Erc20>) {
* };
* ```
*
* @param contract - an instance of a contract that extends the ERC20 spec (token, token drop, custom contract that follows the ERC20 spec)
* @param contract - an instance of a {@link TokenContract}
* @returns a mutation object that can be used to mint new tokens to the connected wallet
* @twfeature ERC20Mintable
* @beta
*/
export function useMintToken(contract: RequiredParam<Erc20>) {
export function useMintToken(contract: RequiredParam<TokenContract>) {
const activeChainId = useSDKChainId();
const contractAddress = contract?.getAddress();
const queryClient = useQueryClient();
const erc20 = getErc20(contract);
return useMutation(
(data: TokenParams) => {
const { to, amount } = data;
invariant(contract, "contract is undefined");
return contract.mintTo(to, amount);
requiredParamInvariant(contract, "contract is undefined");
if (erc20) {
return erc20.mintTo(to, amount);
}
invariant(false, "Smart contract is not a valid erc20 contract");
},
{
onSettled: () =>
@@ -168,11 +194,12 @@ export function useMintToken(contract: RequiredParam<Erc20>) {
* @example
* ```jsx
* const Component = () => {
* const { contract } = useContract(<ContractAddress>);
* const {
* mutate: claimTokens,
* isLoading,
* error,
* } = useClaimToken(TokenDropContract);
* } = useClaimToken(contract);
*
* if (error) {
* console.error("failed to claim tokens", error);
@@ -189,27 +216,27 @@ export function useMintToken(contract: RequiredParam<Erc20>) {
* };
* ```
*
* @param contract - an instance of a {@link Erc20}
* @param contract - an instance of a {@link TokenContract}
* @returns a mutation object that can be used to tokens to the wallet specificed in the params
* @twfeature ERC20ClaimableWithConditions
* @beta
*/
export function useClaimToken<TContract extends Erc20>(
contract: RequiredParam<TContract>,
) {
export function useClaimToken(contract: RequiredParam<TokenContract>) {
const activeChainId = useSDKChainId();
const contractAddress = contract?.getAddress();
const queryClient = useQueryClient();
const erc20 = getErc20(contract);
return useMutation(
async (data: ClaimTokenParams) => {
invariant(data.to, 'No "to" address provided');
invariant(contract?.claimTo, "contract does not support claimTo");
return await contract.claimTo(
data.to,
data.amount,
data.checkERC20Allowance,
);
if (erc20) {
invariant(erc20?.claimTo, "contract does not support claimTo");
return await erc20.claimTo(data.to, data.amount, {
checkERC20Allowance: data.checkERC20Allowance,
});
}
invariant(false, "Smart contract is not a valid erc20 contract");
},
{
onSettled: () =>
@@ -228,11 +255,12 @@ export function useClaimToken<TContract extends Erc20>(
* @example
* ```jsx
* const Component = () => {
* const { contract } = useContract(<ContractAddress>);
* const {
* mutate: transferTokens,
* isLoading,
* error,
* } = useTransferToken(">>YourERC20ContractInstance<<");
* } = useTransferToken(contract);
*
* if (error) {
* console.error("failed to transfer tokens", error);
@@ -249,21 +277,25 @@ export function useClaimToken<TContract extends Erc20>(
* };
* ```
*
* @param contract - an instance of a contract that extends the ERC20 spec (token, token drop, custom contract that follows the ERC20 spec)
* @param contract - an instance of a {@link TokenContract}
* @returns a mutation object that can be used to transfer tokens
* @twfeature ERC20
* @beta
*/
export function useTransferToken(contract: RequiredParam<Erc20>) {
export function useTransferToken(contract: RequiredParam<TokenContract>) {
const activeChainId = useSDKChainId();
const contractAddress = contract?.getAddress();
const queryClient = useQueryClient();
const erc20 = getErc20(contract);
return useMutation(
(data: TokenParams) => {
const { to, amount } = data;
invariant(contract?.transfer, "contract does not support transfer");
return contract.transfer(to, amount);
if (erc20) {
invariant(erc20?.transfer, "contract does not support transfer");
return erc20.transfer(to, amount);
}
invariant(false, "Smart contract is not a valid erc20 contract");
},
{
onSettled: () =>
@@ -282,11 +314,12 @@ export function useTransferToken(contract: RequiredParam<Erc20>) {
* @example
* ```jsx
* const Component = () => {
* const { contract } = useContract(<ContractAddress>);
* const {
* mutate: transferBatchTokens,
* isLoading,
* error,
* } = useTransferToken(">>YourERC20ContractInstance<<");
* } = useTransferToken(contract);
*
* if (error) {
* console.error("failed to transfer batch tokens", error);
@@ -303,28 +336,32 @@ export function useTransferToken(contract: RequiredParam<Erc20>) {
* };
* ```
*
* @param contract - an instance of a contract that extends the ERC20 spec (token, token drop, custom contract that follows the ERC20 spec)
* @param contract - an instance of a {@link TokenContract}
* @returns a mutation object that can be used to transfer batch tokens
* @twfeature ERC20
* @beta
*/
export function useTransferBatchToken(contract: RequiredParam<Erc20>) {
export function useTransferBatchToken(contract: RequiredParam<TokenContract>) {
const activeChainId = useSDKChainId();
const contractAddress = contract?.getAddress();
const queryClient = useQueryClient();
const erc20 = getErc20(contract);
return useMutation(
(data: TokenParams[]) => {
invariant(
contract?.transferBatch,
"contract does not support transferBatch",
);
const convertedData = data.map((token) => ({
toAddress: token.to,
amount: token.amount,
}));
if (erc20) {
invariant(
erc20.transferBatch,
"contract does not support transferBatch",
);
const convertedData = data.map((token) => ({
toAddress: token.to,
amount: token.amount,
}));
return contract.transferBatch(convertedData);
return erc20.transferBatch(convertedData);
}
invariant(false, "Smart contract is not a valid erc20 contract");
},
{
onSettled: () =>
@@ -343,11 +380,12 @@ export function useTransferBatchToken(contract: RequiredParam<Erc20>) {
* @example
* ```jsx
* const Component = () => {
* const { contract } = useContract(<ContractAddress>);
* const {
* mutate: burnTokens,
* isLoading,
* error,
* } = useBurnToken(">>YourERC20ContractInstance<<");
* } = useBurnToken(contract);
*
* if (error) {
* console.error("failed to burn tokens", error);
@@ -364,21 +402,26 @@ export function useTransferBatchToken(contract: RequiredParam<Erc20>) {
* };
* ```
*
* @param contract - an instance of a contract that extends the ERC20 spec (token, token drop, custom contract that follows the ERC20 spec)
* @param contract - an instance of a {@link TokenContract}
* @returns a mutation object that can be used to burn tokens from the connected wallet
* @twfeature ERC20Burnable
* @beta
*/
export function useBurnToken(contract: RequiredParam<Erc20>) {
export function useBurnToken(contract: RequiredParam<TokenContract>) {
const activeChainId = useSDKChainId();
const contractAddress = contract?.getAddress();
const queryClient = useQueryClient();
const erc20 = getErc20(contract);
return useMutation(
(data: TokenBurnParams) => {
const { amount } = data;
invariant(contract, "contract is undefined");
return contract.burn(amount);
requiredParamInvariant(contract, "contract is undefined");
if (erc20) {
invariant(erc20.burn, "contract does not support burn");
return erc20.burn(amount);
}
invariant(false, "Smart contract is not a valid erc20 contract");
},
{
onSettled: () =>
@@ -1,72 +0,0 @@
import {
GnosisConnectorArguments,
GnosisSafeConnector,
} from "../../connectors/gnosis-safe";
import { useConnect } from "../wagmi-required/useConnect";
import { utils } from "ethers";
import invariant from "tiny-invariant";
import { useContext as useWagmiContext } from "wagmi";
/**
* Hook for connecting to a Gnosis Safe. This enables multisig wallets to connect to your application and sing transactions.
*
* ```javascript
* import { useGnosis } from "@thirdweb-dev/react"
* ```
*
*
* @example
* ```javascript
* import { useGnosis } from "@thirdweb-dev/react"
*
* const App = () => {
* const connectWithGnosis = useGnosis()
*
* return (
* <button onClick={() => connectWithGnosis({ safeAddress: "0x...", safeChainId: 1 })}>
* Connect Gnosis Safe
* </button>
* )
* }
* ```
*
* @public
*/
export function useGnosis() {
const wagmiContext = useWagmiContext();
invariant(
wagmiContext,
`useGnosis() can only be used inside <ThirdwebProvider />. If you are using <ThirdwebSDKProvider /> you will have to use your own wallet-connection logic.`,
);
const [connectors, connect] = useConnect();
if (connectors.loading) {
return () => Promise.reject("Gnosis connector not ready to be used, yet");
}
const connector = connectors.data.connectors.find((c) => c.id === "gnosis");
invariant(
connector,
"Gnosis connector not found, please make sure it is provided to your <ThirdwebProvider />",
);
return async (config: GnosisConnectorArguments) => {
const previousConnector = connectors.data.connector;
const previousConnectorChain = await previousConnector?.getChainId();
invariant(
!!previousConnector,
"Cannot connect to Gnosis Safe without first being connected to a personal wallet.",
);
invariant(
previousConnectorChain === config.safeChainId,
"Gnosis safe chain id must match personal wallet chain id.",
);
invariant(
utils.isAddress(config.safeAddress),
"Gnosis safe address must be a valid address.",
);
(connector as GnosisSafeConnector).setConfiguration(
previousConnector,
config,
);
return connect(connector);
};
}
@@ -1,91 +0,0 @@
import { MagicConnector } from "../../connectors/magic";
import { useConnect } from "../wagmi-required/useConnect";
import { LoginWithMagicLinkConfiguration } from "magic-sdk";
import invariant from "tiny-invariant";
import { useContext as useWagmiContext } from "wagmi";
/**
* Hook for connecting to an email wallet using magic link.
* This enables users without their own wallets to connect to your application and sign transactions securely using their email.
*
* ```javascript
* import { useMagic } from "@thirdweb-dev/react"
* ```
*
*
* @example
* Before using this hook, you first need to set up the magic configuration in your `ThirdwebProvider`, including your magic API key.
*
* ```javascript
* // Add the magic configuration object to your wallet connectors
* const connectors = [
* "metamask",
* "walletConnect",
* "walletLink",
* {
* name: "magic",
* options: {
* apiKey: "your-magic-api-key",
* }
* }
* ]
*
* // Add the above to the walletConnectors prop of your <ThirdwebProvider />
* const Provider = ({ children }) => (
* return (
* <ThirdwebProvider
* walletConnectors={connectors}
* // Specify remaining parameters
* ...
* >
* {children}
* </ThirdwebProvider>
* )
* }
* ```
*
* In order to use the hook to connect users with magic link, you just need to provide the users email to the connect function.
*
* You can setup the hook with the following configuration:
* ```javascript
* import { useMagic } from "@thirdweb-dev/react"
* import { useState } from "react"
*
* const LoginWithMagicLink = () => {
* const connectWithMagic = useMagic()
* const [email, setEmail] = useState()
*
* return (
* <div>
* <input value={email} onChange={(e) => setEmail(e.target.value)} />
* <button onClick={() => connectWithMagic({ email })}>Login</button>
* </div>
* )
* }
* ```
*
* @public
*/
export function useMagic() {
const wagmiContext = useWagmiContext();
invariant(
wagmiContext,
`useMagic() can only be used inside <ThirdwebProvider />. If you are using <ThirdwebSDKProvider /> you will have to use your own wallet-connection logic.`,
);
const [connectors, connect] = useConnect();
if (connectors.loading) {
return () => Promise.reject("Magic connector not ready to be used, yet");
}
const connector = connectors.data.connectors.find((c) => c.id === "magic");
invariant(
connector,
"Magic connector not found, please make sure it is provided to your <ThirdwebProvider />",
);
return (configuration: LoginWithMagicLinkConfiguration) => {
(connector as MagicConnector).setConfiguration(configuration);
return connect(connector);
};
}
@@ -1,7 +1,10 @@
import { useConnect } from "../wagmi-required/useConnect";
import { Buffer } from "buffer";
import invariant from "tiny-invariant";
import { useContext as useWagmiContext } from "wagmi";
globalThis.Buffer = Buffer;
/**
* Hook for connecting to a mobile wallet with Wallet Connect
*
@@ -1,7 +1,10 @@
import { useConnect } from "../wagmi-required/useConnect";
import { Buffer } from "buffer";
import invariant from "tiny-invariant";
import { useContext as useWagmiContext } from "wagmi";
globalThis.Buffer = Buffer;
/**
* Hook for connecting to a Coinbase wallet.
*
@@ -1,6 +1,6 @@
import { RequiredParam } from "../../../core/query-utils/required-param";
import { showDeprecationWarning } from "../../utils/deprecation-warning";
import { useContract } from "../async/contracts";
import { Edition } from "@thirdweb-dev/sdk/dist/declarations/src/evm/contracts/prebuilt-implementations/edition";
/**
* Hook for getting an instance of an `Edition` contract. This contract is used to interface with ERC1155 compliant NFTs.
@@ -8,16 +8,16 @@ import { Edition } from "@thirdweb-dev/sdk/dist/declarations/src/evm/contracts/p
*
* @example
* ```javascript
* import { useEdition } from '@thirdweb-dev/react'
* import { useContract } from '@thirdweb-dev/react'
*
* export default function Component() {
* const edition = useEdition("<YOUR-CONTRACT-ADDRESS>")
* const { contract } = useContract("<YOUR-CONTRACT-ADDRESS>", "edition")
*
* // Now you can use the edition contract in the rest of the component
*
* // For example, this function will return all the NFTs on this contract
* async function getNFTs() {
* const nfts = await edition.getAll()
* const nfts = await contract.getAll()
* return nfts
* }
*
@@ -25,9 +25,17 @@ import { Edition } from "@thirdweb-dev/sdk/dist/declarations/src/evm/contracts/p
* }
* ```
* @public
* @deprecated use `useContract()` instead
* @deprecated
* This hook is deprecated and will be removed in a future major version. You should use {@link useContract} instead.
* ```diff
* - const edition = useEdition("0x1234...");
* + const edition = useContract("0x1234...", "edition").contract;
* ```
*/
export function useEdition(contractAddress?: string) {
showDeprecationWarning("useEdition()", "useContract<Edition>()");
return useContract<Edition>(contractAddress).contract;
export function useEdition(contractAddress: RequiredParam<string>) {
showDeprecationWarning(
`useEdition("${contractAddress || "0x..."}")`,
`useContract("${contractAddress || "0x..."}", "edition")`,
);
return useContract(contractAddress, "edition").contract;
}
@@ -1,6 +1,6 @@
import { RequiredParam } from "../../../core/query-utils/required-param";
import { showDeprecationWarning } from "../../utils/deprecation-warning";
import { useContract } from "../async/contracts";
import { EditionDrop } from "@thirdweb-dev/sdk/dist/declarations/src/evm/contracts/prebuilt-implementations/edition-drop";
/**
* Hook for getting an instance of an `EditionDrop` contract. This conract is used to interface with ERC1155 compliant NFTs that can be lazily minted.
@@ -8,25 +8,33 @@ import { EditionDrop } from "@thirdweb-dev/sdk/dist/declarations/src/evm/contrac
*
* @example
* ```javascript
* import { useEditionDrop } from '@thirdweb-dev/react'
* import { useContract } from '@thirdweb-dev/react'
*
* export default function Component() {
* const editionDrop = useEditionDrop("<YOUR-CONTRACT-ADDRESS>")
* const { contract } = useContract("<YOUR-CONTRACT-ADDRESS>", "edition-drop")
*
* // Now you can use the edition drop contract in the rest of the component
*
* // For example, this function will let the connected wallet claim a new NFT
* async function claim(tokenId, quantity) {
* await editionDrop.claim(tokenId, quantity)
* await contract.claim(tokenId, quantity)
* }
*
* ...
* }
* ```
* @public
* @deprecated use `useContract()` instead
* @deprecated
* This hook is deprecated and will be removed in a future major version. You should use {@link useContract} instead.
* ```diff
* - const editionDrop = useEditionDrop("0x1234...");
* + const editionDrop = useContract("0x1234...", "edition-drop").contract;
* ```
*/
export function useEditionDrop(contractAddress?: string) {
showDeprecationWarning("useEditionDrop()", "useContract<EditionDrop>()");
return useContract<EditionDrop>(contractAddress).contract;
export function useEditionDrop(contractAddress: RequiredParam<string>) {
showDeprecationWarning(
`useEditionDrop("${contractAddress || "0x..."}")`,
`useContract("${contractAddress || "0x..."}", "edition-drop")`,
);
return useContract(contractAddress, "edition-drop").contract;
}
@@ -1,6 +1,6 @@
import { RequiredParam } from "../../../core/query-utils/required-param";
import { showDeprecationWarning } from "../../utils/deprecation-warning";
import { useContract } from "../async/contracts";
import { Marketplace } from "@thirdweb-dev/sdk/dist/declarations/src/evm/contracts/prebuilt-implementations/marketplace";
/**
* Hook for getting an instance of a `Marketplace` contract. This contract is used to support marketplace for purchase and sale of on-chain assets.
@@ -8,16 +8,16 @@ import { Marketplace } from "@thirdweb-dev/sdk/dist/declarations/src/evm/contrac
*
* @example
* ```javascript
* import { useMarketplace } from '@thirdweb-dev/react'
* import { useContract } from '@thirdweb-dev/react'
*
* export default function Component() {
* const marketplace = useMarketplace("<YOUR-CONTRACT-ADDRESS>")
* const { contract } = useContract("<YOUR-CONTRACT-ADDRESS>", "marketplace")
*
* // Now you can use the marketplace contract in the rest of the component
*
* // For example, this function will return all the listings on the marketplace
* async function getListings() {
* const listings = await marketplace.getAll()
* const listings = await contract.getAll()
* return listings
* }
*
@@ -25,9 +25,17 @@ import { Marketplace } from "@thirdweb-dev/sdk/dist/declarations/src/evm/contrac
* }
* ```
* @public
* @deprecated use `useContract()` instead
* @deprecated
* This hook is deprecated and will be removed in a future major version. You should use {@link useContract} instead.
* ```diff
* - const marketplace = useMarketplace("0x1234...");
* + const marketplace = useContract("0x1234...", "marketplace").contract;
* ```
*/
export function useMarketplace(contractAddress?: string) {
showDeprecationWarning("useMarketplace()", "useContract<Marketplace>()");
return useContract<Marketplace>(contractAddress).contract;
export function useMarketplace(contractAddress: RequiredParam<string>) {
showDeprecationWarning(
`useMarketplace("${contractAddress || "0x..."}")`,
`useContract("${contractAddress || "0x..."}", "marketplace")`,
);
return useContract(contractAddress, "marketplace").contract;
}
@@ -1,6 +1,6 @@
import { RequiredParam } from "../../../core/query-utils/required-param";
import { showDeprecationWarning } from "../../utils/deprecation-warning";
import { useContract } from "../async/contracts";
import { Multiwrap } from "@thirdweb-dev/sdk/dist/declarations/src/evm/contracts/prebuilt-implementations/multiwrap";
/**
* Hook for getting an instance of an `Multiwrap` contract. This contract is an ERC721 in which you can wrap ERC721, ERC1155 and ERC20 tokens.
@@ -8,25 +8,33 @@ import { Multiwrap } from "@thirdweb-dev/sdk/dist/declarations/src/evm/contracts
*
* @example
* ```javascript
* import { useMultiwrap } from '@thirdweb-dev/react'
* import { useContract } from '@thirdweb-dev/react'
*
* export default function Component() {
* const multiwrap = useMultiwrap("<YOUR-CONTRACT-ADDRESS>")
* const { contract } = useContract("<YOUR-CONTRACT-ADDRESS>", "multiwrap")
*
* // Now you can use the multiwrap contract in the rest of the component
*
* // For example, this function will let the connected wallet wrap tokens
* async function wrap(tokensToWrap, wrappedNFTMetadata) {
* await multiwrap.wrap(tokensToWrap, wrappedNFTMetadata)
* await contract.wrap(tokensToWrap, wrappedNFTMetadata)
* }
*
* ...
* }
* ```
* @public
* @deprecated use `useContract()` instead
* @deprecated
* This hook is deprecated and will be removed in a future major version. You should use {@link useContract} instead.
* ```diff
* - const multiwrap = useMultiwrap("0x1234...");
* + const multiwrap = useContract("0x1234...", "multiwrap").contract;
* ```
*/
export function useMultiwrap(contractAddress?: string) {
showDeprecationWarning("useMultiwrap()", "useContract<Multiwrap>()");
return useContract<Multiwrap>(contractAddress).contract;
export function useMultiwrap(contractAddress: RequiredParam<string>) {
showDeprecationWarning(
`useMultiwrap("${contractAddress || "0x..."}")`,
`useContract("${contractAddress || "0x..."}", "multiwrap")`,
);
return useContract(contractAddress, "multiwrap").contract;
}
@@ -1,6 +1,6 @@
import { RequiredParam } from "../../../core/query-utils/required-param";
import { showDeprecationWarning } from "../../utils/deprecation-warning";
import { useContract } from "../async/contracts";
import { NFTCollection } from "@thirdweb-dev/sdk/dist/declarations/src/evm/contracts/prebuilt-implementations/nft-collection";
/**
* Hook for getting an instance of an `NFTCollection` contract. This contract is meant to interface with ERC721 compliant NFTs.
@@ -8,16 +8,16 @@ import { NFTCollection } from "@thirdweb-dev/sdk/dist/declarations/src/evm/contr
*
* @example
* ```javascript
* import { useNFTCollection } from '@thirdweb-dev/react'
* import { useContract } from '@thirdweb-dev/react'
*
* export default function Component() {
* const nftCollection = useNFTCollection("<YOUR-CONTRACT-ADDRESS>")
* const { contract, isLoading, error } = useContract("<YOUR-CONTRACT-ADDRESS>", "nft-collection")
*
* // Now you can use the nftCollection contract in the rest of the component
*
* // For example, this function will return all the NFTs on this contract
* async function getNFTs() {
* const nfts = await nftCollection.getAll()
* const nfts = await contract.getAll()
* return nfts
* }
*
@@ -25,9 +25,17 @@ import { NFTCollection } from "@thirdweb-dev/sdk/dist/declarations/src/evm/contr
* }
* ```
* @public
* @deprecated use `useContract()` instead
* @deprecated
* This hook is deprecated and will be removed in a future major version. You should use {@link useContract} instead.
* ```diff
* - const nftCollection = useNFTCollection("0x1234...");
* + const nftCollection = useContract("0x1234...", "nft-collection").contract;
* ```
*/
export function useNFTCollection(contractAddress?: string) {
showDeprecationWarning("useNFTCollection()", "useContract<NFTCollection>()");
return useContract<NFTCollection>(contractAddress).contract;
export function useNFTCollection(contractAddress: RequiredParam<string>) {
showDeprecationWarning(
`useNFTCollection("${contractAddress || "0x..."}")`,
`useContract("${contractAddress || "0x..."}", "nft-collection")`,
);
return useContract(contractAddress, "nft-collection").contract;
}
@@ -1,6 +1,6 @@
import { RequiredParam } from "../../../core/query-utils/required-param";
import { showDeprecationWarning } from "../../utils/deprecation-warning";
import { useContract } from "../async/contracts";
import { NFTDrop } from "@thirdweb-dev/sdk/dist/declarations/src/evm/contracts/prebuilt-implementations/nft-drop";
/**
* Hook for getting an instance of an `NFTDrop` contract. This contract is meant to interface with ERC721 compliant NFTs that can be lazily minted.
@@ -8,25 +8,33 @@ import { NFTDrop } from "@thirdweb-dev/sdk/dist/declarations/src/evm/contracts/p
*
* @example
* ```javascript
* import { useNFTDrop } from '@thirdweb-dev/react'
* import { useContract } from '@thirdweb-dev/react'
*
* export default function Component() {
* const nftDrop = useNFTDrop("<YOUR-CONTRACT-ADDRESS>")
* const { contract } = useContract("<YOUR-CONTRACT-ADDRESS>", "nft-drop")
*
* // Now you can use the nft drop contract in the rest of the component
*
* // For example, this function will let the connected wallet claim a new NFT
* async function claim(quantity) {
* await nftDrop.claim(quantity)
* await contract.claim(quantity)
* }
*
* ...
* }
* ```
* @public
* @deprecated use `useContract()` instead
* @deprecated
* This hook is deprecated and will be removed in a future major version. You should use {@link useContract} instead.
* ```diff
* - const nftDrop = useNFTDrop("0x1234...");
* + const nftDrop = useContract("0x1234...", "nft-drop").contract;
* ```
*/
export function useNFTDrop(contractAddress?: string) {
showDeprecationWarning("useNFTDrop()", "useContract<NFTDrop>()");
return useContract<NFTDrop>(contractAddress).contract;
export function useNFTDrop(contractAddress: RequiredParam<string>) {
showDeprecationWarning(
`useNFTDrop("${contractAddress || "0x..."}")`,
`useContract("${contractAddress || "0x..."}", "nft-drop")`,
);
return useContract(contractAddress, "nft-drop").contract;
}
@@ -1,6 +1,6 @@
import { RequiredParam } from "../../../core/query-utils/required-param";
import { showDeprecationWarning } from "../../utils/deprecation-warning";
import { useContract } from "../async/contracts";
import { Pack } from "@thirdweb-dev/sdk/dist/declarations/src/evm/contracts/prebuilt-implementations/pack";
/**
* Hook for getting an instance of a `Pack` contract. This contract supports the creation of on-chain luck-based lootboxes.
@@ -8,16 +8,16 @@ import { Pack } from "@thirdweb-dev/sdk/dist/declarations/src/evm/contracts/preb
*
* @example
* ```javascript
* import { usePack } from '@thirdweb-dev/react'
* import { useContract } from '@thirdweb-dev/react'
*
* export default function Component() {
* const pack = usePack("<YOUR-CONTRACT-ADDRESS>")
* const { contract } = usePack("<YOUR-CONTRACT-ADDRESS>", "pack")
*
* // Now you can use the pack contract in the rest of the component
*
* // For example, this function will get all the packs on this contract
* async function getPacks() {
* const packs = await pack.getAll()
* const packs = await contract.getAll()
* return packs
* }
*
@@ -25,9 +25,17 @@ import { Pack } from "@thirdweb-dev/sdk/dist/declarations/src/evm/contracts/preb
* }
* ```
* @public
* @deprecated use `useContract()` instead
* @deprecated
* This hook is deprecated and will be removed in a future major version. You should use {@link useContract} instead.
* ```diff
* - const pack = usePack("0x1234...");
* + const pack = useContract("0x1234...", "pack").contract;
* ```
*/
export function usePack(contractAddress?: string) {
showDeprecationWarning("usePack()", "useContract<Pack>()");
return useContract<Pack>(contractAddress).contract;
export function usePack(contractAddress: RequiredParam<string>) {
showDeprecationWarning(
`usePack("${contractAddress || "0x..."}")`,
`useContract("${contractAddress || "0x..."}", "pack")`,
);
return useContract(contractAddress, "pack").contract;
}
@@ -1,6 +1,6 @@
import { RequiredParam } from "../../../core/query-utils/required-param";
import { showDeprecationWarning } from "../../utils/deprecation-warning";
import { useContract } from "../async/contracts";
import { SignatureDrop } from "@thirdweb-dev/sdk/dist/declarations/src/evm/contracts/prebuilt-implementations/signature-drop";
/**
* Hook for getting an instance of an `SignatureDrop` contract. This contract is meant to interface with ERC721 compliant NFTs that can be lazily minted.
@@ -8,25 +8,33 @@ import { SignatureDrop } from "@thirdweb-dev/sdk/dist/declarations/src/evm/contr
*
* @example
* ```javascript
* import { useSignatureDrop } from '@thirdweb-dev/react'
* import { useContract } from '@thirdweb-dev/react'
*
* export default function Component() {
* const signatureDrop = useSignatureDrop("<YOUR-CONTRACT-ADDRESS>")
* const { contract } = useContract("<YOUR-CONTRACT-ADDRESS>", "signature-drop")
*
* // Now you can use the Signature drop contract in the rest of the component
*
* // For example, this function will let the connected wallet claim a new NFT
* async function claim(quantity) {
* await signatureDrop.claim(quantity)
* await contract.claim(quantity)
* }
*
* ...
* }
* ```
* @public
* @deprecated use `useContract()` instead
* @deprecated
* This hook is deprecated and will be removed in a future major version. You should use {@link useContract} instead.
* ```diff
* - const signatureDrop = useSignatureDrop("0x1234...");
* + const signatureDrop = useContract("0x1234...", "signature-drop").contract;
* ```
*/
export function useSignatureDrop(contractAddress?: string) {
showDeprecationWarning("useSignatureDrop()", "useContract<SignatureDrop>()");
return useContract<SignatureDrop>(contractAddress).contract;
export function useSignatureDrop(contractAddress?: RequiredParam<string>) {
showDeprecationWarning(
`useSignatureDrop("${contractAddress || "0x..."}")`,
`useContract("${contractAddress || "0x..."}", "signature-drop")`,
);
return useContract(contractAddress, "signature-drop").contract;
}
@@ -1,6 +1,6 @@
import { RequiredParam } from "../../../core/query-utils/required-param";
import { showDeprecationWarning } from "../../utils/deprecation-warning";
import { useContract } from "../async/contracts";
import { Split } from "@thirdweb-dev/sdk/dist/declarations/src/evm/contracts/prebuilt-implementations/split";
/**
* Hook for getting an instance of a `Split` contract. This contract supports fund distribution to multiple parties.
@@ -8,16 +8,16 @@ import { Split } from "@thirdweb-dev/sdk/dist/declarations/src/evm/contracts/pre
*
* @example
* ```javascript
* import { useSplit } from '@thirdweb-dev/react'
* import { useContract } from '@thirdweb-dev/react'
*
* export default function Component() {
* const split = useSplit("<YOUR-CONTRACT-ADDRESS>")
* const { contract } = useContract("<YOUR-CONTRACT-ADDRESS>", "split")
*
* // Now you can use the split contract in the rest of the component
*
* // For example, this function will retrun all the receipients of the split
* async function getRecipients() {
* const recipients = await split.getAllRecipients()
* const recipients = await contract.getAllRecipients()
* return recipients
* }
*
@@ -25,9 +25,17 @@ import { Split } from "@thirdweb-dev/sdk/dist/declarations/src/evm/contracts/pre
* }
* ```
* @public
* @deprecated use `useContract()` instead
* @deprecated
* This hook is deprecated and will be removed in a future major version. You should use {@link useContract} instead.
* ```diff
* - const split = useSplit("0x1234...");
* + const split = useContract("0x1234...", "split").contract;
* ```
*/
export function useSplit(contractAddress?: string) {
showDeprecationWarning("useSplit()", "useContract<Split>()");
return useContract<Split>(contractAddress).contract;
export function useSplit(contractAddress: RequiredParam<string>) {
showDeprecationWarning(
`useSplit("${contractAddress || "0x..."}")`,
`useContract("${contractAddress || "0x..."}", "split")`,
);
return useContract(contractAddress, "split").contract;
}
@@ -1,6 +1,6 @@
import { RequiredParam } from "../../../core/query-utils/required-param";
import { showDeprecationWarning } from "../../utils/deprecation-warning";
import { useContract } from "../async/contracts";
import { Token } from "@thirdweb-dev/sdk/dist/declarations/src/evm/contracts/prebuilt-implementations/token";
/**
* Hook for getting an instance of a `Token` contract. This contract supports ERC20 compliant tokens.
@@ -8,16 +8,16 @@ import { Token } from "@thirdweb-dev/sdk/dist/declarations/src/evm/contracts/pre
*
* @example
* ```javascript
* import { useToken } from '@thirdweb-dev/react'
* import { useContract } from '@thirdweb-dev/react'
*
* export default function Component() {
* const token = useToken("<YOUR-CONTRACT-ADDRESS>")
* const { contract } = useContract("<YOUR-CONTRACT-ADDRESS>", "token")
*
* // Now you can use the token contract in the rest of the component
*
* // For example, this function will get the connected wallets token balance
* async function balance() {
* const balance = await token.balance()
* const balance = await contract.balance()
* return balance
* }
*
@@ -25,9 +25,17 @@ import { Token } from "@thirdweb-dev/sdk/dist/declarations/src/evm/contracts/pre
* }
* ```
* @public
* @deprecated use `useContract()` instead
* @deprecated
* This hook is deprecated and will be removed in a future major version. You should use {@link useContract} instead.
* ```diff
* - const token = useToken("0x1234...");
* + const token = useContract("0x1234...", "token").contract;
* ```
*/
export function useToken(contractAddress?: string) {
showDeprecationWarning("useToken()", "useContract<Token>()");
return useContract<Token>(contractAddress).contract;
export function useToken(contractAddress: RequiredParam<string>) {
showDeprecationWarning(
`useToken("${contractAddress || "0x..."}")`,
`useContract("${contractAddress || "0x..."}", "token")`,
);
return useContract(contractAddress, "token").contract;
}
@@ -1,6 +1,6 @@
import { RequiredParam } from "../../../core/query-utils/required-param";
import { showDeprecationWarning } from "../../utils/deprecation-warning";
import { useContract } from "../async/contracts";
import { TokenDrop } from "@thirdweb-dev/sdk/dist/declarations/src/evm/contracts/prebuilt-implementations/token-drop";
/**
* Hook for getting an instance of a `Token Drop` contract.
@@ -8,16 +8,16 @@ import { TokenDrop } from "@thirdweb-dev/sdk/dist/declarations/src/evm/contracts
*
* @example
* ```javascript
* import { useTokenDrop } from '@thirdweb-dev/react'
* import { useContract } from '@thirdweb-dev/react'
*
* export default function Component() {
* const tokenDrop = useTokenDrop("<YOUR-CONTRACT-ADDRESS>")
* const { contract } = useContract("<YOUR-CONTRACT-ADDRESS>", "token-drop")
*
* // Now you can use the token drop contract in the rest of the component
*
* // For example, this function will get the connected wallets token balance
* async function balance() {
* const balance = await tokenDrop.balance()
* const balance = await contract.balance()
* return balance
* }
*
@@ -25,9 +25,17 @@ import { TokenDrop } from "@thirdweb-dev/sdk/dist/declarations/src/evm/contracts
* }
* ```
* @public
* @deprecated use `useContract()` instead
* @deprecated
* This hook is deprecated and will be removed in a future major version. You should use {@link useContract} instead.
* ```diff
* - const token = useTokenDrop("0x1234...");
* + const token = useContract("0x1234...", "token-drop").contract;
* ```
*/
export function useTokenDrop(contractAddress?: string) {
showDeprecationWarning("useTokenDrop()", "useContract<TokenDrop>()");
return useContract<TokenDrop>(contractAddress).contract;
export function useTokenDrop(contractAddress: RequiredParam<string>) {
showDeprecationWarning(
`useTokenDrop("${contractAddress || "0x..."}")`,
`useContract("${contractAddress || "0x..."}", "token-drop")`,
);
return useContract(contractAddress, "token-drop").contract;
}

Some files were not shown because too many files have changed in this diff Show More