Compare commits

...
Author SHA1 Message Date
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
fe7ab1c5d3 Version Packages (#526)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-01-31 15:06:05 -08:00
Jonas DanielsandGitHub bdd9ed2298 [react] - fix zag-js/react issues (#527) 2023-01-31 14:57:55 -08:00
ciaranightingaleandGitHub 0977454d45 update readme (#529) 2023-01-31 14:49:24 -08:00
Joaquim VergesandGitHub 19b8e18b6b [SDK] Fix getClaimTransaction().estimateGasCostInEther() for claims costing native currency (#525) 2023-01-31 09:21:00 -08:00
Adam MajmudarandGitHub 7c0060b7fd Fix useMagic and useGnosis documentation (#523) 2023-01-30 22:34:25 +00:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
0a07d7a248 Version Packages (#521)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-01-28 21:52:12 -08:00
Adam MajmudarandGitHub 8c81ca5c30 Update react-core dependencies (#520) 2023-01-29 05:25:55 +00:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
c911d183e6 Version Packages (#513)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-01-27 14:34:52 -08:00
f15733f34c Update changesets (#519)
Co-authored-by: Joaquim Verges <[email protected]>
2023-01-27 14:30:03 -08:00
Adam MajmudarandGitHub a6c074c3f3 [Auth] complete auth update & restructure (#460) 2023-01-27 17:06:58 -05:00
Jonas DanielsandGitHub fdb9baedee check for exitcode in detect e2e test (#518) 2023-01-27 12:18:47 -08:00
Avneesh AgarwalandGitHub 9e591b32b7 feat: add express, node.js, and react native templates (#517) 2023-01-27 10:23:06 -08:00
Jonas DanielsandGitHub 12ec3be4a3 update changeset file (#516) 2023-01-26 14:50:57 -08:00
Jonas DanielsandGitHub 48893c730e [react, react-core] - rely on react-core in react (#514) 2023-01-26 12:59:58 -08:00
f37e86b7d2 [React] Move Solana to peer dependencies (#493)
Co-authored-by: iketw <[email protected]>
Co-authored-by: Jonas Daniels <[email protected]>
2023-01-26 09:52:21 -08:00
62b7388bb2 [react-core] init (#511)
Co-authored-by: Jonas Daniels <[email protected]>
2023-01-26 06:10:48 +00:00
iketwandGitHub 065c4db41b [ReactSDK] Changes useClaimedNFTs to only fetch claimed NFTs (#512) 2023-01-25 22:02:50 -08:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
431ab23ee2 Bump cookiejar from 2.1.3 to 2.1.4 (#508)
Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-23 14:30:14 -08:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
836484ba98 Version Packages (#504)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-01-23 13:59:28 -08:00
Nacho IacovinoandGitHub 5047686f2a Fix ConnectWallet network selector (#507) 2023-01-23 20:53:44 +00:00
7cab8d1679 [SDK] Plugin pattern: get composite ABI (#475)
Co-authored-by: Joaquim Verges <[email protected]>
2023-01-20 17:52:30 -08:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
0f0125acf2 Version Packages (#477)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-01-18 21:03:46 -08:00
Joaquim VergesandGitHub 894cbefc63 [SDK] Add intermediate event for contract deploy transactions before confirmation (#502) 2023-01-18 20:54:21 -08:00
retocroomanandGitHub 73dc026ff9 [SDK] Fix getting domain separator error (#500) 2023-01-18 15:18:42 -08:00
Joaquim VergesandGitHub af32007b9f [SDK] remove debug logs (#501) 2023-01-18 22:57:07 +00:00
Joaquim VergesandGitHub 5fba324ac1 [SDK] Fix claim conditions detection on legacy contracts (#498) 2023-01-15 20:34:39 -08:00
Joaquim VergesandGitHub bff433e121 [SDK] Fix deploy transaction listener for all deploy types sdk.deployer.addDeployListener() (#494) 2023-01-13 19:32:50 -08:00
Joaquim VergesandGitHub 68c8e3c4d7 [SDK] resolve contracts via multichain registry (#491) 2023-01-11 17:41:08 -08:00
Jonas DanielsandGitHub 4e8ff58208 [CLI] attempt to fix usage of rm on windows (#483) 2023-01-10 15:57:33 +01:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
d0d5a3c23d Bump json5 from 1.0.1 to 1.0.2 (#476)
Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-09 12:56:52 +01:00
9eaa21d09a [SDK] Multichain registry (#311)
Co-authored-by: Yash <[email protected]>
2023-01-05 19:43:28 -08:00
557429b5cf [SDK] Support for PackVRF (#463)
Co-authored-by: Jonas Daniels <[email protected]>
2023-01-05 15:38:59 -08:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
96d2dfd25d Version Packages (#467)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-01-03 14:54:29 +13:00
YashandGitHub 5862c558fb [SDK] Update forwarder address (#469) 2022-12-20 12:42:02 -08:00
e913e0daa9 [SDK] Add SDK options to factory (#465)
Co-authored-by: Joaquim Verges <[email protected]>
2022-12-17 13:05:52 +13:00
Joaquim VergesandGitHub f7a74a3b7c [SDK] Expose sdk.wallet.signTypedData() (#468) 2022-12-16 23:50:36 +00:00
Joaquim VergesandGitHub 73883f56d0 [SDK] workaround for signing typed data with magic.link signers (#466) 2022-12-17 10:44:03 +13:00
Jonas DanielsandGitHub e47085af92 [config] add timeouts for all relevant github actions (#464) 2022-12-16 10:06:58 +01:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
f525e2cc23 Version Packages (#455)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-12-16 15:28:02 +13:00
Nacho IacovinoandGitHub af85389dc0 [CLI] Fix deprecated links (#461) 2022-12-13 16:20:34 -08:00
ccb4ce8d00 [Wallets] - magic wallet connector (#457)
Co-authored-by: Joaquim Verges <[email protected]>
2022-12-13 13:58:49 +13:00
Joaquim VergesandGitHub 4cdd0bd634 [SDK] Fixes for latest wallet package integration (#458) 2022-12-08 22:13:16 +00:00
ea95c5f609 [SDK] add two new utilities toEther and toWei (#421)
Co-authored-by: Nacho Iacovino <[email protected]>
Co-authored-by: Jonas Daniels <[email protected]>
2022-12-08 12:46:41 -08:00
Joaquim VergesandGitHub 699a2b16fb [SOL] Fix claiming drops with custom currencies (#456) 2022-12-08 09:32:47 +00:00
Joaquim VergesandGitHub a8267f912d [SOL] Drop fixes for batch upload (#453) 2022-12-08 21:59:47 +13:00
Jonas DanielsandGitHub c673e39f23 upgrade dependencies across the codebase (#454) 2022-12-08 08:19:16 +00:00
08d04fe4dc [React] add props for button title to change the text (#422)
Co-authored-by: Jonas Daniels <[email protected]>
2022-12-08 08:09:23 +00:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
cbf5508bd5 Version Packages (#449)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-12-07 14:21:51 +13:00
Joaquim VergesandGitHub 3731459d33 [SDK] Less strict prebuilt checks to support OSRF variants (#451) 2022-12-07 13:08:12 +13:00
Adam MajmudarandGitHub 64f24f1bb1 CLI auto-updates on every run (#441) 2022-12-06 02:40:31 +00:00
Joaquim VergesandGitHub cac6c30bca [SDK] Fix setting allowance or approvals gaslessly (#450) 2022-12-06 11:49:04 +13:00
Adam MajmudarandGitHub e7740a59db Detect if forge isn't installed and display error (#447) 2022-12-05 16:54:13 -05:00
Jonas DanielsandGitHub 7a37e564fd [SDK, React] - useContract fail fast (#448) 2022-12-05 21:41:58 +00:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
e173636f4b Version Packages (#446)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-12-04 11:34:00 +13:00
Joaquim VergesandGitHub 8c6cdaa288 [SDK] Fix creating listings with a magic.link wallet (#443) 2022-12-03 21:58:44 +00:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
ad9b3febff Version Packages (#445)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-12-04 07:49:38 +13:00
Adam MajmudarandGitHub 94f49b1294 Move framework check back to template (#444) 2022-12-03 18:40:46 +00:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
517e2a75e3 Version Packages (#442)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-12-02 16:26:33 -08:00
Jonas DanielsandGitHub 72692689e9 initialize pay package (#433) 2022-12-02 22:52:04 +00:00
Jonas DanielsandGitHub efc56fa580 [SDK] - improved batch provider (#440) 2022-12-02 22:39:11 +00:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
564b17e760 Version Packages (#419)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-12-03 11:25:35 +13:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
b4507db963 Bump decode-uri-component from 0.2.0 to 0.2.2 (#439)
Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-12-02 14:10:31 -08:00
Joaquim VergesandGitHub f451da6395 [SDK] Fix SigMint detection for OSRF NFT Collection contracts (#438) 2022-12-03 11:00:21 +13:00
Jonas DanielsandGitHub ee47840767 fix #429 (#436) 2022-12-02 10:47:46 -08:00
Adam MajmudarandGitHub bef97f72f4 [CLI] Make solc default backup compilation (#435) 2022-12-02 17:11:20 +00:00
Adam MajmudarandGitHub 3e87dd3ccb [CLI] display file names when there are duplicate contract names (#432) 2022-12-02 03:21:40 +00:00
Joaquim VergesandGitHub def42511ff [SOL] Remove workaround for getLargestAccount (#434) 2022-12-02 02:42:26 +00:00
Adam MajmudarandGitHub c42545a647 [CLI] fix CLI on paths with spaces for windows (#431) 2022-12-01 19:03:54 -05:00
Adam MajmudarandGitHub 6e20d1f21a [CLI] allow adding contracts to existing projects (#428) 2022-12-01 20:31:06 +00:00
Joaquim VergesandGitHub e684d7e1ca [CLI] Fix metadata upload with special characters (#430) 2022-12-01 20:14:27 +00:00
Adam MajmudarandGitHub f545a67e9f [Auth] include credentials when requesting user (#425) 2022-12-01 18:17:31 +00:00
Jake LooandGitHub 034a257442 [SDK] Support more types of Minimal Proxy (#429) 2022-12-01 20:30:09 +13:00
1bfb91dbf3 [SDK] Improve error parsing for other wallets (#424)
Co-authored-by: Jonas Daniels <[email protected]>
2022-11-30 18:02:21 -08:00
Jonas DanielsandGitHub 5d95cbcdb2 fix turbo (#427) 2022-12-01 00:52:06 +00:00
Jonas DanielsandGitHub 639e535ed5 init unity-js-bridge package (#420) 2022-11-30 22:36:16 +00:00
af3acc6b10 [SDK] Add support for AWS KMS (#416)
Co-authored-by: Jonas Daniels <[email protected]>
2022-11-28 20:34:24 -08:00
Joaquim VergesandGitHub eca47763cd [SDK] increase gas limit for opening packs (#418) 2022-11-29 03:30:15 +00:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
37b5bec897 Version Packages (#417)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-11-28 18:23:23 -08:00
Nacho IacovinoandGitHub 48d91e4431 [CLI] Fix mappings for prebuilts (#415) 2022-11-28 18:13:00 -08:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
a20be881e3 Version Packages (#406)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-11-29 12:09:38 +13:00
Joaquim VergesandGitHub 47f8945325 [SDK] improve error parsing (#414) 2022-11-29 11:45:33 +13:00
Furqan RydhanandGitHub 1b0bf99da7 Improvements to the App Deploy Flow (#413) 2022-11-28 14:23:44 -08:00
f03be398e0 [SDK] Update signTypedData domain to match contract (#412)
Co-authored-by: Joaquim Verges <[email protected]>
2022-11-28 03:00:50 +00:00
27996cb4bc [CLI] - feat: add solana support to cli (#403)
Co-authored-by: Jarrod Watts <[email protected]>
Co-authored-by: Jonas Daniels <[email protected]>
2022-11-24 01:01:00 +00:00
2eaa67acbe [CLI+SDK] app-uri (#283)
Co-authored-by: Furqan Rydhan <[email protected]>
Co-authored-by: Jonas Daniels <[email protected]>
2022-11-23 09:17:29 +00:00
Jonas Daniels aafd7968df bring the registry-url back apparently it was important 2022-11-23 01:05:43 -08:00
Jonas DanielsandGitHub 48797c7cf0 [SDK] - add gasless feature detection (#408) 2022-11-23 00:53:59 -08:00
Jonas DanielsandGitHub 4edcb37100 update prettier & format (#407) 2022-11-23 06:41:39 +00:00
83dcaf7058 Expose sdk.fromWallet and wallet classes (#402)
Co-authored-by: Jonas Daniels <[email protected]>
2022-11-22 18:15:28 -05:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
b4e005be7f Version Packages (#401)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-11-22 14:31:57 -08:00
Nacho IacovinoandGitHub ccb7db4873 [Solana + React] Update hooks for creators/royalties (#405) 2022-11-22 19:22:51 +00:00
Jonas DanielsandGitHub 9151aa7dc8 [SDK] - expose getDefaultTrustedForwarders helper fn (#404) 2022-11-22 03:35:01 +00:00
5a01c537e3 [DO NOT MERGE] Update to Metaplex v0.17 (#305)
Co-authored-by: Jonas Daniels <[email protected]>
2022-11-21 16:14:39 -08:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
f09c153ff7 Version Packages (#391)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-11-21 15:22:56 -08:00
Nacho IacovinoandGitHub 15431621fd [React] Remove console.log (#400) 2022-11-21 20:03:32 +00:00
Jonas DanielsandGitHub 887af24450 [SDK] - fix error handling for nullish error values (#399) 2022-11-21 18:52:02 +00:00
Jonas DanielsandGitHub 15514f190a [SDK] - add default value option to constructorParams schema (#398) 2022-11-21 18:28:18 +00:00
Adam MajmudarandGitHub 72589ec234 Call user callback in getUser (#397) 2022-11-21 17:46:18 +00:00
Jonas DanielsandGitHub f8bceec10c fix solana claim condition native token (#395) 2022-11-19 05:36:42 +00:00
Adam MajmudarandGitHub 7c0744a006 Add support for total supply and pagination on get all (#388) 2022-11-18 02:21:32 +00:00
Nacho IacovinoandGitHub 846022e2cf [React] Fix unlimited price (#392) 2022-11-17 22:25:25 +00:00
Joaquim VergesandGitHub 96bd92e724 [SDK] Expose getAllDetectedFeatures(ABI) helper function (#390) 2022-11-17 14:07:37 -08:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
c2b6d977ed Version Packages (#383)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-11-16 14:02:50 -08:00
7cb8e59be4 [SDK] Add new Drop contract implementation addresses (#364)
Co-authored-by: Jonas Daniels <[email protected]>
Co-authored-by: Adam Majmudar <[email protected]>
2022-11-16 21:52:01 +00:00
Adam MajmudarandGitHub 04a47e8971 Add retroactive creator and royalty update option (#385) 2022-11-16 21:37:45 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
8a6a97a3e9 Bump loader-utils from 2.0.3 to 2.0.4 (#384)
Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-16 19:51:40 +00:00
Jonas DanielsandGitHub 612bcdd16f [REACT] - update gnosis connector (#386) 2022-11-16 19:34:27 +00:00
Jonas DanielsandGitHub 0ed77d2c9e [SDK] - add constructorParams to schema for better release -> deploy (#382) 2022-11-15 20:47:54 +00:00
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
github-actions[bot]andGitHub 2506eeb25d Version Packages (#227) 2022-09-28 19:37:48 -07:00
ba7dcbbad8 [SDK] - Add getMinimumNextBid function to marketplace (#182)
Co-authored-by: Jonas Daniels <[email protected]>
Co-authored-by: Jarrod Watts <[email protected]>
2022-09-28 19:20:17 -07:00
Adam MajmudarandGitHub cd1abe6ba7 [Solana] Add code example snippets (#233) 2022-09-29 02:05:35 +00:00
e1bcc67ceb [CLI] - Set package.json name to the name the user entered for the project (#103)
Co-authored-by: Jarrod Watts <[email protected]>
Co-authored-by: Nacho Iacovino <[email protected]>
Co-authored-by: Jonas Daniels <[email protected]>
2022-09-29 01:14:46 +00:00
d26b76872a [SDK] Dynamically import anchor library (#232)
Co-authored-by: Jonas Daniels <[email protected]>
2022-09-28 18:01:57 -07:00
9711b51067 [SOL] Add README.md (#231)
Co-authored-by: Jonas Daniels <[email protected]>
2022-09-28 22:54:57 +00:00
Jonas Daniels e2037310a6 stale is not a workflow... 2022-09-28 14:32:30 -07:00
Jonas Daniels 683d542334 handle stale issues 2022-09-28 14:32:03 -07:00
9c8a3fb6d4 [SDK, React] Add Solana claimTo, pass quantity to claim, and expose useClaim hook (#229)
Co-authored-by: Jonas Daniels <[email protected]>
2022-09-28 14:10:18 -07:00
Jonas Daniels 41accc2e21 never cache contracts-js build 2022-09-28 13:51:36 -07:00
Jonas DanielsandGitHub f2a62110c4 try out buildjet (#226) 2022-09-28 13:31:55 -07:00
Adam MajmudarandGitHub db9f811bf8 [SDK] Fix docs setup for multiple entrypoints (#230) 2022-09-28 19:44:52 +00:00
Joaquim VergesandGitHub fc5efe9521 [SDK] export TransactionTask for docs (#228) 2022-09-28 11:25:41 -07:00
82397636f4 [SDK] Get all offers for a listing (#191)
Co-authored-by: Jarrod Watts <[email protected]>
Co-authored-by: Joaquim Verges <[email protected]>
2022-09-28 18:15:18 +00:00
27a30e3ffb [SDK] - Add ability to view and update owner of a contract (#194)
Co-authored-by: Jarrod Watts <[email protected]>
Co-authored-by: Joaquim Verges <[email protected]>
2022-09-28 17:25:06 +00:00
github-actions[bot]andGitHub 2f1155239d Version Packages (#223) 2022-09-27 22:20:38 -07:00
Jonas DanielsandGitHub c295a47144 [SDK, React] - fix esm exports (#221) 2022-09-28 04:41:53 +00:00
Joaquim VergesandGitHub 0dade96d0d [SDK] Check that proxy/factory deploy flag properly is set (#224) 2022-09-28 04:17:41 +00:00
Joaquim VergesandGitHub 666ff5f694 [SOL] expose ThirdwebSDK.fromPrivateKey convenience static method (#222) 2022-09-28 02:50:38 +00:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
8bc41cdc60 Version Packages (#214)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-09-27 18:08:47 -07:00
Joaquim VergesandGitHub 37a707f98c [React] lock zag version (#220) 2022-09-27 17:58:44 -07:00
Joaquim VergesandGitHub 3c8f6205e9 [SDK] expose proxy deploy for released contracts (#217) 2022-09-27 17:10:48 -07:00
Jonas DanielsandGitHub 1eaedc262f [React] - allow null and undefined for RequiredParam (#218) 2022-09-27 22:10:05 +00:00
e187d21e12 [SDK] Merge solana package into main SDK (#213)
Co-authored-by: Jonas Daniels <[email protected]>
2022-09-27 13:36:29 -07:00
YashandGitHub b030a86608 [SDK] Update addresses (#216) 2022-09-27 11:10:11 -07:00
acbabc8999 [SDK] - Relayer gas override (#206)
Co-authored-by: Jarrod Watts <[email protected]>
2022-09-27 19:05:29 +05:30
Jonas DanielsandGitHub 196195e2f9 [React] - fix small discrepancies (#215) 2022-09-27 10:27:32 +00:00
Jonas DanielsandGitHub 7c86babb86 [React] - subdivide react sdk (#208) 2022-09-27 09:35:45 +00:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
36f2b70d57 Version Packages (#207)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-09-27 02:13:05 -07:00
Jarrod WattsandGitHub 849146bf5c [SDK] Docs: Update getContract snippet to use await (#212) 2022-09-27 03:41:12 +00:00
Jarrod WattsandGitHub 4cac79c45d [REACT] Docs: Remove broken link from migration guide (#210) 2022-09-27 02:17:13 +00:00
Jonas DanielsandGitHub 1bda83b114 Jd/fix react effect warning (#209) 2022-09-26 22:40:48 +00:00
Joaquim VergesandGitHub ce05bfd861 [SDK] Remove usages of Buffer to decode bytecodes (#205) 2022-09-26 13:29:24 -07:00
Jarrod WattsandGitHub d982a98b0c Jw/fix signature mint snipptes (#203) 2022-09-26 06:51:50 +00:00
Jarrod WattsandGitHub b983087101 Jw/fix signature mint snipptes (#202) 2022-09-26 05:23:08 +00:00
Jarrod WattsandGitHub b25789fa46 Jw/migration guide (#200) 2022-09-26 14:42:26 +10:00
github-actions[bot]andGitHub e39b66f309 Version Packages (#198) 2022-09-23 19:34:55 -07:00
5f5ab015e1 [SDK, SOL] Fixes for getOwned and solana balance (#199)
Co-authored-by: Jonas Daniels <[email protected]>
2022-09-24 02:11:58 +00:00
Jonas DanielsandGitHub f4f05bd9a4 [React] - make web3 button network switch obvious (#197) 2022-09-24 00:16:56 +00:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
2f5b66bef9 Version Packages (#193)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-09-23 15:27:49 -07:00
63258bc9c3 [React] Expose simple provider and useSDK hook for solana (#179)
Co-authored-by: Jonas Daniels <[email protected]>
2022-09-23 19:12:24 +00:00
Jonas DanielsandGitHub a80fc97b6a [SDK] - update prebuilt contract roles to match contracts (#195) 2022-09-23 18:49:41 +00:00
Adam MajmudarandGitHub 24f66e38c2 Switch to using XHR with timeout (#192) 2022-09-23 05:32:45 +00:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
b5bfaefa85 Version Packages (#157)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-09-22 17:11:41 -07:00
Adam MajmudarandGitHub 493ebf032e Add upload with gateway url for URIs (#190) 2022-09-22 17:06:59 -07:00
Adam MajmudarandGitHub d608cea197 Add progress tracking to file upload (#189) 2022-09-22 15:15:00 -07:00
Adam MajmudarandGitHub a9ec190ff9 Remove JSON types (#188) 2022-09-22 21:27:43 +00:00
3edec391be [CLI] thirdweb deploy redirects to contract deploy page if no contracts detected (#177)
Co-authored-by: Jonas Daniels <[email protected]>
Co-authored-by: Joaquim Verges <[email protected]>
2022-09-22 14:07:32 -07:00
bdec5a59af [CLI] Fix "Invalid Project name" when a project with a capital letter is entered (#184)
Co-authored-by: Jonas Daniels <[email protected]>
2022-09-22 20:20:15 +00:00
Jonas DanielsandGitHub 9d03619aab fix some lint issues (#187) 2022-09-22 19:02:22 +00:00
Jonas DanielsandGitHub 0ccbca78dc [Storage] - handle special chars (#185) 2022-09-22 11:29:42 -07:00
Adam MajmudarandGitHub 75c752cc14 Fix tests (#186) 2022-09-22 11:06:55 -07:00
Jonas DanielsandGitHub 3580182fa9 [Storage] - allow uploading the same file multiple times in uploadBatch if it is *the same file* (#183) 2022-09-22 09:33:24 +00:00
b54f95dc90 Update expected input types for storage (#163)
Co-authored-by: Jonas Daniels <[email protected]>
2022-09-22 08:12:52 +00:00
Adam MajmudarandGitHub f4074ddadc Replace schemes after replacing files (#180) 2022-09-22 06:43:22 +00:00
Jonas DanielsandGitHub 208b0389a5 [React] - use internal Signer (#168) 2022-09-22 06:04:27 +00:00
3b877ba221 [SDK] Ozdefender EOA forwarder option (#174)
Co-authored-by: Joaquim Verges <[email protected]>
2022-09-22 04:47:00 +00:00
Adam MajmudarandGitHub 45a400fd92 Change API for instantiating storage (#178) 2022-09-21 21:09:11 -07:00
Adam MajmudarandGitHub 3332fa5697 Split create contract flow into steps (#175) 2022-09-21 22:51:47 +00:00
Joaquim VergesandGitHub fdda564b1e [SOL] Fix browser build (#173) 2022-09-21 14:35:19 -07:00
Adam MajmudarandGitHub 89db3db7ff Add changeset for CLI (#172) 2022-09-21 20:15:09 +00:00
Adam MajmudarandGitHub bd9c705571 Add thirdweb deploy to end of thirdweb detect (#171) 2022-09-21 19:55:54 +00:00
Adam MajmudarandGitHub 0ad0f21bcd Display yarn build command at the end of thirdweb create --contract (#169) 2022-09-21 19:18:42 +00:00
Joaquim VergesandGitHub 4169b9428f [SDK] Syntax fixes for react native support (#167) 2022-09-21 09:35:22 -07:00
Jarrod WattsandGitHub 2f9e8aaab4 Jw/lazymintabletwfeature (#165) 2022-09-21 04:17:11 +00:00
Adam MajmudarandGitHub 421461ece0 Add README for Solana and Storage (#164) 2022-09-21 02:39:12 +00:00
Jarrod WattsandGitHub 485ebc8400 [REACT] - Feature snippet generation logic (#155) 2022-09-21 10:40:41 +10:00
Joaquim VergesandGitHub 772f8431e3 [SDK] Catch gas estimate errors in gasless tx (#162) 2022-09-20 23:00:12 +00:00
Joaquim VergesandGitHub 5345479922 [SDK] Allow forcing direct deploys to deploy implementations from release flow (#161) 2022-09-20 15:19:18 -07:00
Adam MajmudarandGitHub cfe8bbafa4 Add resolveScheme method to storage (#156) 2022-09-20 18:31:56 +00:00
Adam MajmudarandGitHub 18dc8d33ed Fix nft collection docs (#159) 2022-09-20 18:07:30 +00:00
Jonas DanielsandGitHub f8ab4779bb init paper checkout in v3 (#154) 2022-09-20 05:41:20 +00:00
Adam MajmudarandGitHub 6fe16c5bf2 Add documentation for Solana (#152) 2022-09-19 22:13:25 -07:00
Adam MajmudarandGitHub ba0e169654 Add support for string with name (#151) 2022-09-19 18:08:55 -07:00
Jarrod WattsandGitHub 27b869ed93 Update README.md (#150) 2022-09-20 00:25:11 +00:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
091ce2eb45 Version Packages (#149)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-09-19 16:08:23 -07:00
Joaquim Verges e24a371508 fix changeset 2022-09-19 16:03:14 -07:00
Adam MajmudarandGitHub b86afb4b09 Add documentation for storage (#147) 2022-09-19 22:26:44 +00:00
Joaquim VergesandGitHub 964add6f20 [CLI, Storage] Fix deploy/release uploads via CLI (#148) 2022-09-19 14:56:42 -07:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
ec414bbc42 Version Packages (#136)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-09-19 11:11:34 -07:00
Joaquim VergesandGitHub 51dde28224 [SDK, React] Fix using external signers with ThirdwebSDKProvider (#143) 2022-09-19 18:03:39 +00:00
Adam MajmudarandGitHub 57432d21c4 Update Storage SDK (#139) 2022-09-19 16:58:12 +00:00
Jarrod WattsandGitHub 19d87c2885 Jw/react readme (#144) 2022-09-19 07:20:08 +00:00
Joaquim VergesandGitHub 68812e9d27 Update README.md 2022-09-17 11:29:03 -07:00
Joaquim VergesandGitHub 90c53475f0 Update README.md 2022-09-17 11:23:54 -07:00
Joaquim VergesandGitHub 5a52585a7a [SOL] More solana integration (#140) 2022-09-16 19:15:35 -07:00
Joaquim VergesandGitHub 6b48b9288b [SOL] solana dashboard integration (#138) 2022-09-16 13:19:17 -07:00
Joaquim VergesandGitHub 9d74a43aac [SDK] Make contract events generic for better DX (#133) 2022-09-16 05:51:23 +00:00
Jonas DanielsandGitHub 9c7e5518ff [SOL] - enforce owner when deploying (#137) 2022-09-15 22:24:05 -07:00
Jonas DanielsandGitHub b234c58d44 [SDK] - less rpc calls = good (#135) 2022-09-16 03:43:24 +00:00
Adam MajmudarandGitHub b1ae844e8b Reorder CLI commands (#134) 2022-09-15 20:20:43 -07:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
78bae497ba Version Packages (#126)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-09-15 14:00:20 -07:00
Joaquim VergesandGitHub 3e425bf8e6 [SOL] get account type for a given address (#132) 2022-09-15 20:54:45 +00:00
Jarrod WattsandGitHub 4dca5b21a6 Change feature detection URLs (#128) 2022-09-15 13:45:04 -07:00
Joaquim VergesandGitHub 42c79e93dc [SDK] fix editing releases from the dashboard (#131) 2022-09-15 19:17:02 +00:00
Joaquim VergesandGitHub 2951b5533c [SOL] New return type for registry.getAccountsForWallet (#130) 2022-09-15 18:09:52 +00:00
Joaquim VergesandGitHub fe8751eeae [SDK] add ownable feature detection (#124) 2022-09-15 17:41:32 +00:00
Jonas DanielsandGitHub a0cd47ec35 [SOL] - explicitly export the missing schemata (#129) 2022-09-15 17:13:20 +00:00
Jarrod WattsandGitHub 3902e7aba4 Update read and write hook names for snippet generation (#127) 2022-09-15 17:36:23 +10:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
8717cb2c05 Version Packages (#118)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-09-14 19:38:16 -07:00
Jarrod WattsandGitHub d57ab03f5d Add useContract hook snippet back in (#125) 2022-09-15 12:34:57 +10:00
Joaquim VergesandGitHub 5aa3c433d7 [Solana] Add sdk.wallet.getBalance() (#123) 2022-09-14 19:27:52 +00:00
Joaquim VergesandGitHub 98dd64a375 [SDK] handle legacy claim method signature in extensions (#122) 2022-09-14 19:15:47 +00:00
Joaquim VergesandGitHub e25dd1ac8e [Solana] Handle WalletAdapter connections, internal cleanup (#120) 2022-09-14 05:04:08 +00:00
Jonas DanielsandGitHub e68f9685b3 fix snippets (#119) 2022-09-14 03:16:58 +00:00
Joaquim VergesandGitHub 84beda1cbb [Solana] Create account registry (#117) 2022-09-13 16:22:00 -07:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
32e7fb0c96 Version Packages (#92)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-09-13 15:24:45 -07:00
Joaquim VergesandGitHub 8202cc60d8 v3 release notes (#116) 2022-09-13 15:16:00 -07:00
Jonas DanielsandGitHub a37bc00991 [SDK] - add sales to token pre-built (#115) 2022-09-13 20:49:11 +00:00
b442c97080 [SDK] Fix get owned for Signature Drop (#111)
Co-authored-by: Jonas Daniels <[email protected]>
2022-09-13 11:49:42 -07:00
Jonas DanielsandGitHub 1df2dea18f [CLI, REACT] - small improvements (#114) 2022-09-13 17:51:01 +00:00
0fa6f3fcfb [REACT, SDK] universal use-contract & async pre-built contracts (#106)
Co-authored-by: Joaquim Verges <[email protected]>
2022-09-13 09:12:14 -07:00
Jonas DanielsandGitHub b900b8503f Create codeql-analysis.yml (#113) 2022-09-13 07:44:16 +00:00
5a5bc36150 addPackContents function (#108)
Co-authored-by: Joaquim Verges <[email protected]>
2022-09-12 22:21:16 -07:00
Nacho IacovinoandGitHub 8158a55e66 Remove incorrect await (#110) 2022-09-12 21:39:16 +00:00
Adam MajmudarandGitHub f7ccc30f9d Add enabled check to useUser (#109) 2022-09-12 21:11:08 +00:00
78b40a94df [Solana] Add support for NFT Drop (#78)
Co-authored-by: Joaquim Verges <[email protected]>
2022-09-12 18:10:36 +00:00
YashandGitHub 95d850515c Pass empty trustedForwarders for Pack (#107) 2022-09-11 20:20:21 -07:00
Joaquim VergesandGitHub 1244c38162 [Solana] Custom Program support (#94) 2022-09-10 22:34:33 -07:00
Joaquim Verges 7b923be499 add twfeature for non-ERC extensions 2022-09-10 21:23:00 -07:00
Adam MajmudarandGitHub 0912dc5f9c [Solana] Remove error catch from supplyOf (#105) 2022-09-10 22:57:21 +00:00
052aa23888 Add test case and supplyOf (#102)
Co-authored-by: Jarrod Watts <[email protected]>
Co-authored-by: Joaquim Verges <[email protected]>
2022-09-10 22:45:16 +00:00
Joaquim Verges 6c10ed93cb Fix standard function snippets 2022-09-09 21:04:53 -07:00
Joaquim VergesandGitHub 2327a9d611 generate feature snippets for all ERC standards (#101) 2022-09-10 12:29:20 +10:00
Nacho IacovinoandGitHub 27ffbc252b [Multi] Fix typo (#100) 2022-09-10 01:27:40 +00:00
Jonas DanielsandGitHub a70b590be1 [SDK] - do not prompt to deploy again if user rejected the first call (#99) 2022-09-09 17:15:54 -07:00
45818944e5 Add mint additional supply and balance (#97)
Co-authored-by: Joaquim Verges <[email protected]>
2022-09-09 23:21:06 +00:00
Adam MajmudarandGitHub fe047d551e [Solana] Add nft helper common interface (#79) 2022-09-09 20:56:03 +00:00
82627ea031 [SDK, REACT] Switch to simpler detection API for ERC20/721/1155 extensions (#19)
Co-authored-by: Nacho Iacovino <[email protected]>
Co-authored-by: Jonas Daniels <[email protected]>
Co-authored-by: Nacho Iacovino <[email protected]>
2022-09-09 13:45:51 -07:00
Adam MajmudarandGitHub 1de14730bc Update auth docs domain to example.com (#96) 2022-09-09 19:02:27 +00:00
2adb8ff667 [SDK, React] Support arbitrum and optimism goerli testnets (#91)
Co-authored-by: Joaquim Verges <[email protected]>
2022-09-09 07:07:08 +00:00
Joaquim VergesandGitHub 820a5191b5 [SDK] Expose ERC721Claimable/ERC1155Claimable features (#90) 2022-09-08 21:44:20 -07:00
Jarrod WattsandGitHub c94ef909d0 Update README.md (#89) 2022-09-08 08:28:27 +00:00
Joaquim VergesandGitHub 8aef97d6f1 apply prettier on everything (#80) 2022-09-08 04:38:16 +00:00
Jarrod WattsandGitHub 8a5501fb80 Add contributing.md and update readme (#77) 2022-09-07 19:58:39 -07:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
8696199849 Version Packages (#67)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-09-06 21:03:48 -07:00
Joaquim VergesandGitHub 05353fd8da Add support for chain agnostic gasless tx (#71) 2022-09-06 20:52:39 -07:00
baa87a1fbd Update Opezeppelin forwarder address for Goerli (#74)
Co-authored-by: Krishang Nadgauda <[email protected]>
Co-authored-by: Joaquim Verges <[email protected]>
2022-09-07 03:17:18 +00:00
Jonas DanielsandGitHub 407932680f [SDK] - fix FactoryDeploymentSchema initialize function by adding a default (#75) 2022-09-06 22:40:18 +00:00
Jarrod WattsandGitHub 6bec17c07a [SDK] more snippets (#73) 2022-09-06 02:21:39 +00:00
Jarrod WattsandGitHub c379d19dfb [SDK] - Document contract from ABI (#72) 2022-09-06 02:00:07 +00:00
Joaquim VergesandGitHub 3e2bec8a47 Fix links in README 2022-09-05 15:29:23 -07:00
Jarrod WattsandGitHub 8b028c29d5 Update erc-1155-claimable.ts (#69) 2022-09-05 15:43:21 +10:00
Jarrod WattsandGitHub cc286841e2 Update erc-1155-mintable.ts (#68) 2022-09-05 15:01:56 +10:00
576d968a89 [SDK] Doc - Remove "reveal" from "create reveal batch" snippet (#66)
Co-authored-by: Joaquim Verges <[email protected]>
2022-09-05 02:36:43 +00:00
f2bdf47b4f [Solana] deploy and manage tokens (#63)
Co-authored-by: Jonas Daniels <[email protected]>
2022-09-04 19:15:58 -07:00
Jonas Daniels b92d3f7006 release dev 2022-09-02 18:49:15 -07:00
Jonas Daniels c9457bc2e7 also allow contributors 2022-09-02 18:38:06 -07:00
Jonas Daniels 3308bddd70 fix release pr action? 2022-09-02 18:37:39 -07:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
8954b8c5b6 Version Packages (#62)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-09-02 17:31:37 -07:00
Jonas DanielsandGitHub 3287c2b0f2 Jd/add pre release (#61) 2022-09-02 17:26:47 -07:00
Jonas DanielsandGitHub a5cdd85c77 [SDK] - rapid testing (#64) 2022-09-02 16:17:18 -07:00
e9e71cfcd8 Add CLI tests (#56)
Co-authored-by: Jonas Daniels <[email protected]>
2022-09-02 13:44:43 -07:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
27b3475d64 Version Packages (#60)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-09-02 09:23:54 -07:00
6ba9cad8d8 fix contracts build (#59)
Co-authored-by: Joaquim Verges <[email protected]>
2022-09-02 09:14:34 -07:00
Jonas Daniels a5ad3d6828 fix e2e tests (re-instll playwright in CI for now 2022-09-02 09:07:52 -07:00
Joaquim Verges 9c817de558 fix GH action formatting 2022-09-01 21:01:08 -07:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
326f52ae4e Version Packages (#58)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-09-01 19:26:52 -07:00
Jonas Daniels beafc856cf change the file so workflow runs 2022-09-01 19:21:30 -07:00
Jonas Daniels a85b35e75f fix workflows 2022-09-01 19:19:44 -07:00
Jonas Daniels 5644ccd3ee bump versions 2022-09-01 19:13:49 -07:00
Jonas Daniels 14a5a31d70 Merge branch 'main' of github.com:thirdweb-dev/js 2022-09-01 19:10:26 -07:00
Jonas Daniels 83417c23d8 fix workflows? 2022-09-01 19:10:17 -07:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
b6ebc82d5a Version Packages (#57)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-09-01 18:49:01 -07:00
Jonas Daniels 091f175860 bump & release all 2022-09-01 18:43:13 -07:00
Jonas Daniels 8811b99ad5 fix release workflow? 2022-09-01 18:35:02 -07:00
Jonas DanielsandGitHub 711783dbbf add workflow files (#55) 2022-09-01 18:09:16 -07:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
cd44d4d0c2 Version Packages (#54)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-09-01 17:35:59 -07:00
Jonas DanielsandGitHub 924247a8ed [MULTI] - automatically bump & pin versions of dependent packages + do not check in ABIs (#53) 2022-09-01 17:29:15 -07:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
97c6041b15 Version Packages (#51)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-09-01 15:50:44 -07:00
Joaquim VergesandGitHub 2eb7e945b1 Pack contract updates (#52) 2022-09-01 15:42:00 -07:00
Joaquim VergesandGitHub e59735b6a2 [SDK] add contract encoder to custom contracts (#49) 2022-09-01 13:40:36 -07:00
Jonas DanielsandGitHub c903ca8af9 [REACT] - apply sandboxing to MediaRenderer (#50) 2022-09-01 19:52:10 +00:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
600a51d509 Version Packages (#35)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-09-01 11:50:23 -07:00
Jonas Daniels 32d923bd70 fix changeset config 2022-09-01 05:10:35 -07:00
Jonas DanielsandGitHub 82156cc727 [AUTH] - fix build (#47) 2022-09-01 04:55:20 -07:00
Jonas DanielsandGitHub 349b5c1e02 better babel transforms (#46) 2022-09-01 11:00:09 +00:00
Jonas DanielsandGitHub ed639d659d [MULTI] - switch back to preconstruct (#45) 2022-09-01 02:50:08 -07:00
Jonas Daniels a8d93a34c0 add missing dep 2022-09-01 00:42:31 -07:00
Jonas DanielsandGitHub f6b24a5b06 [AUTH] - fix build? (#44) 2022-09-01 00:40:00 -07:00
Jonas DanielsandGitHub 821383e26f attempt to fix auth build (#43) 2022-09-01 00:14:00 -07:00
Jonas DanielsandGitHub 46ad691a16 [MULTI] switch build to tsup to fix dependency build issues (#42) 2022-08-31 23:35:26 -07:00
Mahesh VagicherlaandGitHub f9067eb79b Fix typo in CLI (#41) 2022-08-31 22:32:40 -07:00
Joaquim VergesandGitHub 5bb92c3163 [Solana] add balance/transfer functions (#40) 2022-08-31 21:04:11 -07:00
Adam MajmudarandGitHub 00015fa958 Fix auth build CI (#39) 2022-09-01 01:55:50 +00:00
74640b23bf Add callbacks to next and express auth (#38)
Co-authored-by: Jonas Daniels <[email protected]>
2022-08-31 18:23:40 -07:00
Jonas Daniels 280b786e07 fix e2e job name 2022-08-31 17:28:15 -07:00
Joaquim VergesandGitHub 127bc50217 update to Contracts SDK 3.1.5 (#36) 2022-09-01 00:01:59 +00:00
Jonas Daniels 81e9a4a475 remove prepublishOnly steps 2022-08-31 16:34:47 -07:00
Jonas Daniels 6b10e0d9e6 add back independent nightly workflow 2022-08-31 16:31:48 -07:00
Jonas Daniels c075435be6 fix nightly snapshot 2022-08-31 16:23:42 -07:00
Jonas DanielsandGitHub 5731ac2f50 [MULTI] - add e2e tests in react, sdk & cli (#34) 2022-08-31 16:11:41 -07:00
Adam MajmudarandGitHub 5d8654a222 Update next endpoints (#37) 2022-08-31 14:27:24 -07:00
Adam MajmudarandGitHub c52a43c886 [Auth] Add support for next-auth integration (#30) 2022-08-31 19:43:52 +00:00
Joaquim VergesandGitHub 5f2c7063f3 add cache output for solana builds (#33) 2022-08-30 21:07:09 -07:00
Jarrod WattsandGitHub c7850ac4f7 [MULTI] - Update Old references to SDK URLs across readme files (#32) 2022-08-30 20:28:47 -07:00
Joaquim VergesandGitHub 3f77fc8b59 [Solana] setup basic testing for Solana (#31) 2022-08-30 17:47:31 -07:00
Jonas Daniels c4556a8026 fix workflow file 2022-08-29 23:14:42 -07:00
Jonas DanielsandGitHub a5de09b513 make nightly version part of the Release worfklow (#29) 2022-08-29 23:12:17 -07:00
1256 changed files with 64377 additions and 225350 deletions
+9 -3
View File
@@ -1,13 +1,19 @@
{
"$schema": "https://unpkg.com/@changesets/[email protected]/schema.json",
"changelog": "@changesets/cli/changelog",
"changelog": ["@changesets/changelog-github", { "repo": "thirdweb-dev/js" }],
"commit": false,
"fixed": [],
"fixed": [
["@thirdweb-dev/sdk", "@thirdweb-dev/react-core", "@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
},
"snapshot": {
"useCalculatedVersion": true,
"prereleaseTemplate": "{tag}-{commit}"
@@ -0,0 +1,18 @@
name: "Install"
description: "Sets up Node.js and runs install"
runs:
using: composite
steps:
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: 16
registry-url: "https://registry.npmjs.org"
cache: "yarn"
- name: Install dependencies
shell: bash
run: yarn install --frozen-lockfile
env:
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: "1"
+136
View File
@@ -0,0 +1,136 @@
# Contributing to thirdweb
You can find a full video tutorial on how to contribute to thirdweb below:
https://www.youtube.com/watch?v=TXsQ3qok3B0
## Getting Started
To get started, read the [How this repo works](#how-this-repo-works) section below to learn about the structure of this repo.
From there, you can take a look at our [Good First Issues](https://github.com/thirdweb-dev/js/labels/good%20first%20issue) board and find an issue that interests you!
If you have any questions about the issue, feel free to ask on our [Discord server](https://discord.gg/thirdweb) in the `#contributors` channel; where you'll be able to get help from our team and other contributors.
<br/>
## How this repo works
[@thirdweb-dev/js](https://github.com/thirdweb-dev/js) is a monorepo, meaning it contains many projects within it.
We use [Turborepo](https://turborepo.org/docs/getting-started) to manage the monorepo, and help speed up the [CI/CD](https://www.atlassian.com/continuous-delivery/principles/continuous-integration-vs-delivery-vs-deployment) pipeline to ship to production faster 🚢.
You can see a quick outline of each of the projects within this repo below, each living within the [/packages](/packages) directory:
| Package | Description | Latest Version |
| ------------------------------ | -------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [/sdk](./packages/sdk) | Best in class web3 SDK for Browser, Node and Mobile apps | <a href="https://www.npmjs.com/package/@thirdweb-dev/sdk"><img src="https://img.shields.io/npm/v/@thirdweb-dev/sdk?color=red&label=npm&logo=npm" alt="npm version"/></a> |
| [/react](./packages/react) | Ultimate collection of React hooks for your web3 apps | <a href="https://www.npmjs.com/package/@thirdweb-dev/react"><img src="https://img.shields.io/npm/v/@thirdweb-dev/react?color=red&label=npm&logo=npm" alt="npm version"/></a> |
| [/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> |
## How to contribute
Let's explore how you can set up the repo on your local machine and start contributing!
This section requires some existing knowledge of [Git](https://git-scm.com/), [Node.js](https://nodejs.org/en/) and [Yarn](https://yarnpkg.com/).
<br/>
### Getting the repo
For OSS contributions, we use a [Forking Workflow](https://www.atlassian.com/git/tutorials/comparing-workflows/forking-workflow), meaning each developer will fork the repo and work on their own fork; and then submit a PR to the main repo when they're ready to merge their changes.
To begin:
1. [Create a fork](https://github.com/thirdweb-dev/js/fork) of this repository to your own GitHub account.
2. [Clone your fork](https://help.github.com/articles/cloning-a-repository/) to your local device.
3. Create a new branch on your fork to start working on your changes:
```
git checkout -b MY_BRANCH_NAME
```
4. Install the dependencies:
```
yarn
```
Now you have got the repo on your local machine, and you're ready to start making your changes!
### Test Your Changes
We use [yalc](https://github.com/wclr/yalc) to test changes locally.
Install the yalc CLI globally:
```bash
yarn global add yalc
```
First, create a test project where you can experiment with your changes:
You can create a basic starter project with the `sdk` and `react` packages installed using the CLI:
```bash
npx thirdweb create --app
```
Use `yalc` to link your local changes in the monorepo to the test project, by running the following command from your test repo:
```bash
yalc add @thirdweb-dev/react # Link the react package
yalc add @thirdweb-dev/sdk # Link the sdk package
# etc...
```
From the monorepo, run the following command to publish your local changes to the test project:
```bash
yarn push
```
Now, each time you make a change to the monorepo, you can run `yarn push` to publish your changes to the test project.
In your test project, you need to:
1. Delete the dependencies cache. If you're using Next.js, that is the `.next` directory, and if you're using CRA, that is the `node_modules/.cache` directory.
2. Restart the development server.
<br/>
### Publish Your Changes
Once you're satisfied with your changes, you are ready to submit them for review!
1. Use [changeset](https://github.com/changesets/changesets) to generate a changeset file:
```
yarn changeset
```
We follow [semantic versioning](https://semver.org/) for generating versioned releases of our packages (i.e. version = `MAJOR.MINOR.PATCH`)
- Update `major` for breaking changes
- Update `minor` for new features,
- Update `patch` for non-breaking bug fixes, etc)
2. Commit the changeset along with your changes:
```
git commit -am "My commit message"
```
3. Push your changes to the SDK:
```
git push origin MY_BRANCH_NAME
```
4. Create a [pull request](https://www.atlassian.com/git/tutorials/making-a-pull-request) to the `main` branch of the official (not your fork) SDK repo.
It's helpful to tag PRs with `[SDK]`, `[REACT]`, `[AUTH]`, (the name of the package you're modifying) to indicate the package that you are engaging with.
+17
View File
@@ -0,0 +1,17 @@
# Number of days of inactivity before an issue becomes stale
daysUntilStale: 60
# Number of days of inactivity before a stale issue is closed
daysUntilClose: 7
# Issues with these labels will never be considered stale
exemptLabels:
- pinned
- security
# Label to use when marking an issue as stale
staleLabel: wontfix
# Comment to post when marking an issue as stale. Set to `false` to disable
markComment: >
This issue has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs. Thank you
for your contributions.
# Comment to post when closing a stale issue. Set to `false` to disable
closeComment: false
+9 -17
View File
@@ -7,14 +7,14 @@ on:
types: [opened, synchronize]
concurrency:
group: ${{ github.ref }}
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
build:
name: Build and Test
# timeout-minutes: 15
runs-on: ubuntu-latest
timeout-minutes: 30
runs-on: ubuntu-latest-32
# To use Remote Caching, uncomment the next lines and follow the steps below.
env:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
@@ -22,21 +22,13 @@ jobs:
steps:
- name: Check out code
uses: actions/checkout@v2
uses: actions/checkout@v3
with:
fetch-depth: 2
fetch-depth: 0
- name: Setup Node.js environment
uses: actions/setup-node@v2
with:
node-version: 16
cache: "yarn"
- name: Install
uses: ./.github/composite-actions/install
- name: Install Dependencies
run: yarn
- name: Build
run: yarn build
- name: Test
# this already builds anything that changed...
- name: Build & Test
run: yarn test
-41
View File
@@ -1,41 +0,0 @@
name: Release Nightly
on:
push:
branches:
- main
concurrency: ${{ github.workflow }}-${{ github.ref }}
jobs:
release:
name: Release Nightly
runs-on: ubuntu-latest
# To use Remote Caching, uncomment the next lines and follow the steps below.
env:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
steps:
- name: Checkout Repo
uses: actions/checkout@v2
- name: Setup Node.js environment
uses: actions/setup-node@v2
with:
node-version: 16
- name: Install Dependencies
run: yarn
- name: Create an .npmrc
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
run: |
cat << EOF > "$HOME/.npmrc"
//registry.npmjs.org/:_authToken=$NPM_TOKEN
EOF
- name: Publish Nightly version to NPM
run: yarn ci:release:nightly
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-38
View File
@@ -1,38 +0,0 @@
name: Release
on:
push:
branches:
- main
concurrency: ${{ github.workflow }}-${{ github.ref }}
jobs:
release:
name: Release
runs-on: ubuntu-latest
# To use Remote Caching, uncomment the next lines and follow the steps below.
env:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
steps:
- name: Checkout Repo
uses: actions/checkout@v2
- name: Setup Node.js environment
uses: actions/setup-node@v2
with:
node-version: 16
- name: Install Dependencies
run: yarn
- name: Create Release Pull Request or Publish to npm
id: changesets
uses: changesets/action@v1
with:
# This expects you to have a script called release which does a build for your packages and calls changeset publish
publish: yarn release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
+72
View File
@@ -0,0 +1,72 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
on:
push:
branches: [ "main" ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ "main" ]
schedule:
- cron: '37 9 * * 3'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'javascript' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
steps:
- name: Checkout repository
uses: actions/checkout@v3
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
# queries: security-extended,security-and-quality
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v2
# ℹ️ Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
# If the Autobuild fails above, remove it and uncomment the following three lines.
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
# - run: |
# echo "Run, Build Application using script"
# ./location_of_script_within_repo/buildscript.sh
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
+29
View File
@@ -0,0 +1,29 @@
name: Integration tests
on:
push:
branches: ["main"]
pull_request:
types: [opened, synchronize]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
integration-tests:
timeout-minutes: 30
runs-on: ubuntu-latest-32
steps:
- uses: actions/checkout@v3
- name: Install
uses: ./.github/composite-actions/install
- name: Install Playwright
run: npx playwright install --with-deps
- name: Run e2e tests
run: yarn e2e
env:
NODE_OPTIONS: "--max_old_space_size=4096"
+33
View File
@@ -0,0 +1,33 @@
name: Lint
on:
push:
branches: ["main"]
pull_request:
types: [opened, synchronize]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
lint:
name: Lint
timeout-minutes: 15
runs-on: ubuntu-latest
# To use Remote Caching, uncomment the next lines and follow the steps below.
env:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
steps:
- name: Check out code
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Install
uses: ./.github/composite-actions/install
- name: Lint
run: yarn lint
+83
View File
@@ -0,0 +1,83 @@
# pulled from: https://github.com/changesets/changesets/pull/909
name: release-pr
on:
issue_comment:
types: [created]
jobs:
deploy-check:
# this just checks if ur allowed to do the action so does not need to run on a fast machine
runs-on: ubuntu-latest
steps:
- name: acknowledge deployment request to commenter
id: check
uses: khan/pull-request-comment-trigger@master
with:
trigger: "/release-pr"
reaction: rocket
env:
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
- name: validate user
id: validate_user
if: ${{ steps.check.outputs.triggered == 'true' }}
run: |
if [[ "${AUTHOR_ASSOCIATION}" != 'MEMBER' && "${AUTHOR_ASSOCIATION}" != 'OWNER' && "${AUTHOR_ASSOCIATION}" != 'CONTRIBUTOR' ]]
then
echo "User authorization failed"
exit 1
else
echo "User authorization successful"
fi
env:
AUTHOR_ASSOCIATION: ${{ github.event.comment.author_association }}
- name: check outputs
run: echo ${{ steps.check.outputs.triggered }}
outputs:
triggered: ${{ steps.check.outputs.triggered }}
comment_body: ${{ github.event.comment.body }}
deploy:
runs-on: ubuntu-latest-32
timeout-minutes: 30
needs: deploy-check
if: needs.deploy-check.outputs.triggered == 'true'
steps:
- name: get pull request ref
id: get_pull_request_ref
uses: octokit/[email protected]
with:
route: GET /repos/{owner}/{repository}/pulls/{issue_id}
owner: ${{ github.repository_owner }}
repository: ${{ github.event.repository.name }}
issue_id: ${{ github.event.issue.number }}
env:
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
- name: pull the repo.
uses: actions/checkout@v3
with:
persist-credentials: true
repository: ${{ fromJson(steps.get_pull_request_ref.outputs.data).head.repo.full_name }}
ref: ${{ fromJson(steps.get_pull_request_ref.outputs.data).head.ref }}
- name: Install
uses: ./.github/composite-actions/install
- name: Build
run: yarn build:release
- name: Configure npm
run: |
echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > .npmrc
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Deploy pre-release
run: |
yarn changeset version --snapshot dev
yarn changeset publish --tag dev --no-git-tag
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+50
View File
@@ -0,0 +1,50 @@
name: Release Nightly & Stable
on:
push:
paths:
- ".changeset/**"
- "packages/**"
branches:
- main
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
jobs:
release:
name: Release
timeout-minutes: 30
runs-on: ubuntu-latest-32
steps:
- name: Checkout branch
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Install
uses: ./.github/composite-actions/install
- name: Build
run: yarn build:release
- name: Create release Pull Request or publish to NPM
id: changesets
uses: changesets/action@v1
with:
publish: yarn release
version: yarn version-packages
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Create @nightly release
if: steps.changesets.outputs.published != 'true'
run: |
git checkout main
yarn version-packages:nightly
yarn release:nightly
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+20
View File
@@ -0,0 +1,20 @@
name: Compressed Size
on: [pull_request]
jobs:
build:
runs-on: ubuntu-latest-32
timeout-minutes: 30
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/
+4 -2
View File
@@ -1,2 +1,4 @@
lib
node_modules
node_modules
dist
contracts-js
*.d.ts
+15 -15
View File
@@ -10,19 +10,19 @@ We pledge to act and interact in ways that contribute to an open, welcoming, div
Examples of behavior that contributes to a positive environment for our community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience
* Focusing on what is best not just for us as individuals, but for the overall community
- Demonstrating empathy and kindness toward other people
- Being respectful of differing opinions, viewpoints, and experiences
- Giving and gracefully accepting constructive feedback
- Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience
- Focusing on what is best not just for us as individuals, but for the overall community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or advances of any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email address, without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a professional setting
- The use of sexualized language or imagery, and sexual attention or advances of any kind
- Trolling, insulting or derogatory comments, and personal or political attacks
- Public or private harassment
- Publishing others' private information, such as a physical or email address, without their explicit permission
- Other conduct which could reasonably be considered inappropriate in a professional setting
## Enforcement Responsibilities
@@ -64,7 +64,7 @@ Community leaders will follow these Community Impact Guidelines in determining t
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals.
**Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within the community.
@@ -72,12 +72,12 @@ Community leaders will follow these Community Impact Guidelines in determining t
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.0, available at [https://www.contributor-covenant.org/version/2/0/code_of_conduct.html][v2.0].
Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder][Mozilla CoC].
Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder][mozilla coc].
For answers to common questions about this code of conduct, see the FAQ at [https://www.contributor-covenant.org/faq][FAQ]. Translations are available at [https://www.contributor-covenant.org/translations][translations].
For answers to common questions about this code of conduct, see the FAQ at [https://www.contributor-covenant.org/faq][faq]. Translations are available at [https://www.contributor-covenant.org/translations][translations].
[homepage]: https://www.contributor-covenant.org
[v2.0]: https://www.contributor-covenant.org/version/2/0/code_of_conduct.html
[Mozilla CoC]: https://github.com/mozilla/diversity
[FAQ]: https://www.contributor-covenant.org/faq
[mozilla coc]: https://github.com/mozilla/diversity
[faq]: https://www.contributor-covenant.org/faq
[translations]: https://www.contributor-covenant.org/translations
+35 -28
View File
@@ -1,44 +1,51 @@
<p align="center">
<br />
<a href="https://thirdweb.com"><img src="https://github.com/thirdweb-dev/typescript-sdk/blob/main/logo.svg?raw=true" width="200" alt=""/></a>
<br />
<br />
<a href="https://thirdweb.com">
<img src="https://github.com/thirdweb-dev/js/blob/main/packages/sdk/logo.svg?raw=true" width="200" alt=""/></a>
<br />
</p>
<h1 align="center">thirdweb JavaScript/TypeScript monorepo</h1>
<h1 align="center"><a href='http://thirdweb.com/'>thirdweb</a> JavaScript/TypeScript monorepo</h1>
<p align="center">
<a href="https://github.com/thirdweb-dev/js/actions/workflows/CI.yml"><img alt="Build Status" src="https://github.com/thirdweb-dev/js/actions/workflows/CI.yml/badge.svg"/></a>
<a href="https://discord.gg/thirdweb"><img alt="Join our Discord!" src="https://img.shields.io/discord/834227967404146718.svg?color=7289da&label=discord&logo=discord&style=flat"/></a>
<a href="https://github.com/thirdweb-dev/js/actions/workflows/CI.yml">
<img alt="Build Status" src="https://github.com/thirdweb-dev/js/actions/workflows/CI.yml/badge.svg"/>
</a>
<a href="https://discord.gg/thirdweb">
<img alt="Join our Discord!" src="https://img.shields.io/discord/834227967404146718.svg?color=7289da&label=discord&logo=discord&style=flat"/>
</a>
</p>
<p align="center"><strong>Best in class web3 SDKs for Browser, Node and Mobile apps</strong></p>
<br />
## Packages
| Package | Description | Latest Version |
| ---------------------------------------- | -------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [/sdk](./packages/sdk/README.md) | Best in class web3 SDK for Browser, Node and Mobile apps | <a href="https://www.npmjs.com/package/@thirdweb-dev/sdk"><img src="https://img.shields.io/npm/v/@thirdweb-dev/sdk?color=red&label=npm&logo=npm" alt="npm version"/></a> |
| [/react](./packages/react/README.md) | Ultimate collection of React hooks for your web3 apps | <a href="https://www.npmjs.com/package/@thirdweb-dev/react"><img src="https://img.shields.io/npm/v/@thirdweb-dev/react?color=red&label=npm&logo=npm" alt="npm version"/></a> |
| [/auth](./packages/auth/README.md) | 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/README.md) | 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/README.md) | 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/cli/README.md) | 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> |
| Package | Description | Latest Version |
| ------------------------------ | -------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [/sdk](./packages/sdk) | Best in class web3 SDK for Browser, Node and Mobile apps | <a href="https://www.npmjs.com/package/@thirdweb-dev/sdk"><img src="https://img.shields.io/npm/v/@thirdweb-dev/sdk?color=red&label=npm&logo=npm" alt="npm version"/></a> |
| [/react](./packages/react) | Ultimate collection of React hooks for your web3 apps | <a href="https://www.npmjs.com/package/@thirdweb-dev/react"><img src="https://img.shields.io/npm/v/@thirdweb-dev/react?color=red&label=npm&logo=npm" alt="npm version"/></a> |
| [/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> |
## How this monorepo functions
## Documentation
### Contributing
Visit [https://portal.thirdweb.com/](https://portal.thirdweb.com/) to view the full documentation.
1. Create PRs to the monorepo
2. Tag PRs with `[SDK]`, `[REACT]`, `[AUTH]`, etc to indicate the package that you are engaging with (TBD a better process for this / if it is necessary)
3. Create a `changeset` (with `yarn changeset`) for every **user impacting** change and describe what changed (try to focus on the end-user impact as much as possible -- use `major` for breaking changes, `minor` for new features, `patch` for non-breaking bug fixes, etc)
4. when the PR builds and tests pass merge to main
<br />
### Releases
## Contributing
#### Nightly
We welcome contributions from all developers, regardless of experience level. If you are interested in contributing, please read our [Contributing Guide](.github/contributing.md) where you'll learn how the repo works, how to test your changes, and how to submit a pull request.
- every push to main automatically gets published to the `@nightly` tag as a snapshot version (based on the commit hash)
- nightly versions are published to npm under the `@nightly` tag
<br />
#### Stable
## Community
- every push to main that contains a changeset automatically gets added to the [Version Packages](https://github.com/thirdweb-dev/js/tree/changeset-release/main) PR
- to release a stable version of the code that is on main (with the change sets as the release notes / changelog) merge the `Version Packages` PR to main, this will automatically create new `@latest` packages for all of the packages in the monorepo that have changesets
The best place to discuss your ideas, ask questions, and troubleshoot issues is our [Discord server](https://discord.gg/thirdweb).
<br/>
## Security
If you believe you have found a security vulnerability in any of our packages, we kindly ask you not to open a public issue; and to disclose this to us by emailing `[email protected]`.
+29 -20
View File
@@ -2,39 +2,48 @@
"name": "@thirdweb-dev/js",
"version": "1.0.0",
"main": "index.js",
"repository": "git@github.com:thirdweb-dev/js.git",
"repository": "https://github.com/thirdweb-dev/js",
"author": "thirdweb eng <[email protected]>",
"license": "MIT",
"private": true,
"workspaces": {
"packages": [
"packages/*"
"packages/*",
"test/**/*"
]
},
"devDependencies": {
"@changesets/cli": "^2.24.3",
"@trivago/prettier-plugin-sort-imports": "^3.3.0",
"eslint": "^8.23.0",
"eslint-config-thirdweb": "*",
"prettier": "^2.7.1",
"turbo": "latest",
"yarn-deduplicate": "^5.0.2"
},
"scripts": {
"postinstall": "yarn-deduplicate yarn.lock",
"test": "turbo run test",
"build": "turbo run build",
"test": "turbo run test test:evm test:solana",
"e2e": "turbo run e2e",
"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",
"fix": "turbo run fix",
"clean": "turbo run clean && rm -rf node_modules",
"format": "prettier --write \"**/*.{ts,tsx,md}\"",
"changeset": "changeset",
"release": "turbo run build && changeset publish",
"ci:release:nightly": "turbo run build && yarn changeset version --snapshot nightly && yarn changeset publish --tag nightly --no-git-tag"
"version-packages": "changeset version",
"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 --filter=./packages/*"
},
"preconstruct": {
"packages": [
"packages/*"
]
"packageManager": "[email protected]",
"dependencies": {
"@changesets/changelog-github": "^0.4.6",
"@changesets/cli": "^2.24.3",
"@manypkg/cli": "^0.19.1",
"@manypkg/get-packages": "^1.1.3",
"@trivago/prettier-plugin-sort-imports": "^4.0.0",
"eslint": "^8.21.0",
"postinstall-postinstall": "^2.1.0",
"prettier": "^2.7.1",
"turbo": "^1.4.6",
"yalc": "^1.0.0-pre.53"
},
"manypkg": {
"defaultBranch": "main"
}
}
+2 -2
View File
@@ -1,6 +1,6 @@
{
"presets": [
"@babel/preset-typescript",
"@babel/preset-env"
["@babel/preset-env", { "targets": "defaults, not ie 11" }]
]
}
}
+255
View File
@@ -1,5 +1,260 @@
# @thirdweb-dev/auth
## 3.0.2
## 3.0.1
### Patch Changes
- [#520](https://github.com/thirdweb-dev/js/pull/520) [`8c81ca5`](https://github.com/thirdweb-dev/js/commit/8c81ca5c3033b04b1f64e3a1134a72e7e3ec03b1) Thanks [@adam-maj](https://github.com/adam-maj)! - Update auth and react-core dependencies
## 3.0.0
### Major Changes
- [#460](https://github.com/thirdweb-dev/js/pull/460) [`a6c074c`](https://github.com/thirdweb-dev/js/commit/a6c074c3f33148cd17f5a66a58df9272a4381bab) Thanks [@adam-maj](https://github.com/adam-maj)! - Complete Auth redesign and update to add a number of major and quality of life improvements, including the following:
- Ability to use Auth APIs with both cookies and JWTs, allowing non browser clients to interact with Auth (mobile, gaming, scripts, etc.)
- Ability to store session data and other data on the Auth user
- Callbacks to run side-effects on login, logout, and requesting user data
- Ability to configure cookies for custom domains and backend setups
- Support for validation of the entire EIP4361/CAIP122 specification
- No more need for redirects or payload encoding on Auth requests
- and more...
See the new documentation to view the new changes and usage: [Auth Documentation](https://portal.thirdweb.com/auth).
## How to upgrade
The `ThirdwebAuth` constructor now takes the `domain` in the constructor, and takes a more generic `wallet` interface as input. The `wallet` can be imported from the `@thirdweb-dev/wallets` package, or for more simpler use cases, from the `@thirdweb-dev/auth/evm` and `@thirdweb-dev/auth/solana` entrypoints.
```js
import { PrivateKeyWallet } from "@thirdweb-dev/auth/evm";
// Pass in domain and wallet to the constructor
const wallet = new PrivateKeyWallet("0x...");
const auth = new ThirdwebAuth(wallet, "example.com");
// Auth functions no longer require domain to be passed in
const payload = await auth.login();
```
## 2.0.41
## 2.0.40
## 2.0.39
## 2.0.38
## 2.0.37
## 2.0.36
## 2.0.35
## 2.0.34
### Patch Changes
- [#425](https://github.com/thirdweb-dev/js/pull/425) [`f545a67`](https://github.com/thirdweb-dev/js/commit/f545a67e9fb597d27bb39ca515d24d306fbb121a) Thanks [@adam-maj](https://github.com/adam-maj)! - Add sameSite none and include credentials
## 2.0.33
## 2.0.32
## 2.0.31
### Patch Changes
- [#397](https://github.com/thirdweb-dev/js/pull/397) [`72589ec`](https://github.com/thirdweb-dev/js/commit/72589ec234d86939b67b0ca8b3b789f2e9c54cc2) Thanks [@adam-maj](https://github.com/adam-maj)! - Call user callback in getUser
## 2.0.30
## 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
## 2.0.9
## 2.0.8
### Patch Changes
- Updated dependencies [[`ce05bfd`](https://github.com/thirdweb-dev/js/commit/ce05bfd8615a9c79664856bce53de8b43bed5c87)]:
- @thirdweb-dev/[email protected]
## 2.0.7
### Patch Changes
- Updated dependencies [[`5f5ab01`](https://github.com/thirdweb-dev/js/commit/5f5ab015e1dd3c471d6affe995ef36ec88932b3c)]:
- @thirdweb-dev/[email protected]
## 2.0.6
### Patch Changes
- Updated dependencies [[`a80fc97`](https://github.com/thirdweb-dev/js/commit/a80fc97b6a1e72ed46a400b4b602e180947fb870)]:
- @thirdweb-dev/[email protected]
## 2.0.5
### Patch Changes
- Updated dependencies [[`4169b94`](https://github.com/thirdweb-dev/js/commit/4169b9428f9001b7cad259a4e56fe610316cd191), [`3b877ba`](https://github.com/thirdweb-dev/js/commit/3b877ba221acfd85f80b99e1bc382055217f0a39), [`b54f95d`](https://github.com/thirdweb-dev/js/commit/b54f95dc906928ff2f9251748f254a16fe1f2cee), [`f8ab477`](https://github.com/thirdweb-dev/js/commit/f8ab4779bb2d6d66200e1e8fd558e0ac069a2f54), [`772f843`](https://github.com/thirdweb-dev/js/commit/772f8431e3a62d0ded62dae90a43e9a7edd5b1a2), [`a9ec190`](https://github.com/thirdweb-dev/js/commit/a9ec190ff99d2714cef2500d20ea0f3f73f07be3), [`208b038`](https://github.com/thirdweb-dev/js/commit/208b0389a50ea48bbb9600fec60fec2f1671d4b9), [`5345479`](https://github.com/thirdweb-dev/js/commit/534547992243bdd3a77e34ec2b2487b5adab366a)]:
- @thirdweb-dev/[email protected]
## 2.0.4
### Patch Changes
- Updated dependencies []:
- @thirdweb-dev/[email protected]
## 2.0.3
### Patch Changes
- Updated dependencies [[`51dde28`](https://github.com/thirdweb-dev/js/commit/51dde28224209f1b8b26f436c204a5e702281564), [`9d74a43`](https://github.com/thirdweb-dev/js/commit/9d74a43aac21448beba63ba4e2637945965a3634), [`b234c58`](https://github.com/thirdweb-dev/js/commit/b234c58d44d8322e44b2d2ba87ad4ec7d699e961)]:
- @thirdweb-dev/[email protected]
## 2.0.2
### Patch Changes
- Updated dependencies [[`42c79e9`](https://github.com/thirdweb-dev/js/commit/42c79e93dc958ca46a55d705aeea44ffdbbcc5f6), [`fe8751e`](https://github.com/thirdweb-dev/js/commit/fe8751eeae7ad013b890a8092ddbd091ecbd6708)]:
- @thirdweb-dev/[email protected]
## 2.0.1
### Patch Changes
- Updated dependencies [[`98dd64a`](https://github.com/thirdweb-dev/js/commit/98dd64a375c302a879aab3c628ecfb84b4dd19da)]:
- @thirdweb-dev/[email protected]
## 2.0.0
### Patch Changes
- Updated dependencies [[`a70b590`](https://github.com/thirdweb-dev/js/commit/a70b590be1efa7c0ad93a724afb24870439558ed), [`a37bc00`](https://github.com/thirdweb-dev/js/commit/a37bc00991bf1a359f5f8aa8e24e2c388dcd99d8), [`b442c97`](https://github.com/thirdweb-dev/js/commit/b442c970808f6cb7457d29542bd826dba711579c), [`0fa6f3f`](https://github.com/thirdweb-dev/js/commit/0fa6f3fcfbd571579baf9d2a0dbeee556ddbd5fe), [`2adb8ff`](https://github.com/thirdweb-dev/js/commit/2adb8ff6673768a91fa411c2d069245190ad9397), [`5a5bc36`](https://github.com/thirdweb-dev/js/commit/5a5bc361507bd8707dc12e9000bb9a218221cf61), [`820a519`](https://github.com/thirdweb-dev/js/commit/820a5191b5e7af5aba5e4d1cc90cd895c0dade11), [`0fa6f3f`](https://github.com/thirdweb-dev/js/commit/0fa6f3fcfbd571579baf9d2a0dbeee556ddbd5fe), [`82627ea`](https://github.com/thirdweb-dev/js/commit/82627ea0311f612119d0596ed0f568267a7af16b)]:
- @thirdweb-dev/[email protected]
## 1.0.8
### Patch Changes
- Updated dependencies [[`baa87a1`](https://github.com/thirdweb-dev/js/commit/baa87a1fbd7eee24ce9a95e16028de8435f85e69), [`4079326`](https://github.com/thirdweb-dev/js/commit/407932680fb024f17f12f578aa22c7f8c0c13339), [`05353fd`](https://github.com/thirdweb-dev/js/commit/05353fd8da82f77fb642bb38a533fb99801aed30)]:
- @thirdweb-dev/[email protected]
## 1.0.7
### Patch Changes
- [#61](https://github.com/thirdweb-dev/js/pull/61) [`3287c2b`](https://github.com/thirdweb-dev/js/commit/3287c2b0f233332fe4a095f973deed8efab91db6) Thanks [@jnsdls](https://github.com/jnsdls)! - fix versions in dependencies before releasing stable
- Updated dependencies [[`3287c2b`](https://github.com/thirdweb-dev/js/commit/3287c2b0f233332fe4a095f973deed8efab91db6)]:
- @thirdweb-dev/[email protected]
## 1.0.6
### Patch Changes
- Updated dependencies [[`6ba9cad`](https://github.com/thirdweb-dev/js/commit/6ba9cad8d8b933256599dc3b147601cd4828c89b)]:
- @thirdweb-dev/[email protected]
## 1.0.5
### Patch Changes
- [`5644ccd`](https://github.com/thirdweb-dev/js/commit/5644ccd3ee2ff330e4e5840d3266033376750117) Thanks [@jnsdls](https://github.com/jnsdls)! - bump versions again
- Updated dependencies [[`5644ccd`](https://github.com/thirdweb-dev/js/commit/5644ccd3ee2ff330e4e5840d3266033376750117)]:
- @thirdweb-dev/[email protected]
## 1.0.4
### Patch Changes
- [`091f175`](https://github.com/thirdweb-dev/js/commit/091f1758604d40e825ea28a13c2699d67bc75d8c) Thanks [@jnsdls](https://github.com/jnsdls)! - release-all-packages
- Updated dependencies [[`091f175`](https://github.com/thirdweb-dev/js/commit/091f1758604d40e825ea28a13c2699d67bc75d8c)]:
- @thirdweb-dev/[email protected]
## 1.0.3
### Patch Changes
- Updated dependencies []:
- @thirdweb-dev/[email protected]
## 1.0.2
### Patch Changes
- [#38](https://github.com/thirdweb-dev/js/pull/38) [`74640b2`](https://github.com/thirdweb-dev/js/commit/74640b23bf2c537f873186d3537e49b3289e8197) Thanks [@adam-maj](https://github.com/adam-maj)! - Add callbacks to express and next auth
- [#43](https://github.com/thirdweb-dev/js/pull/43) [`821383e`](https://github.com/thirdweb-dev/js/commit/821383e26f0e9f489e49be708a47823ac2de1473) Thanks [@jnsdls](https://github.com/jnsdls)! - export all entrypoints from main package
- [#39](https://github.com/thirdweb-dev/js/pull/39) [`00015fa`](https://github.com/thirdweb-dev/js/commit/00015fa958fbaea72526f42b95d919382694bbcd) Thanks [@adam-maj](https://github.com/adam-maj)! - Update auth build CI
- [#44](https://github.com/thirdweb-dev/js/pull/44) [`f6b24a5`](https://github.com/thirdweb-dev/js/commit/f6b24a5b06b034435785a2b788096e834bcefef9) Thanks [@jnsdls](https://github.com/jnsdls)! - attempt to fix auth build
- [#46](https://github.com/thirdweb-dev/js/pull/46) [`349b5c1`](https://github.com/thirdweb-dev/js/commit/349b5c1e028a06616d40de84257fd8d1cf05df83) Thanks [@jnsdls](https://github.com/jnsdls)! - imrprove babel & tsconfig settings
- [#47](https://github.com/thirdweb-dev/js/pull/47) [`82156cc`](https://github.com/thirdweb-dev/js/commit/82156cc727166230d301eeaf8fdc422332b06a8e) Thanks [@jnsdls](https://github.com/jnsdls)! - re-enable cache for auth build output
- [#37](https://github.com/thirdweb-dev/js/pull/37) [`5d8654a`](https://github.com/thirdweb-dev/js/commit/5d8654a22231c6205829f1731f91fb47d3410e98) Thanks [@adam-maj](https://github.com/adam-maj)! - Update user endpoints for express and next
- [#30](https://github.com/thirdweb-dev/js/pull/30) [`c52a43c`](https://github.com/thirdweb-dev/js/commit/c52a43c8863052b6d1bc9b8c3e1e86d0e1759d39) Thanks [@adam-maj](https://github.com/adam-maj)! - Add support for next-auth
- Updated dependencies [[`ed639d6`](https://github.com/thirdweb-dev/js/commit/ed639d659d9d746321fb8858212d22cc16d9cd19), [`349b5c1`](https://github.com/thirdweb-dev/js/commit/349b5c1e028a06616d40de84257fd8d1cf05df83), [`46ad691`](https://github.com/thirdweb-dev/js/commit/46ad691a1636dbc7915ade22067ccfa1d39f7851), [`5731ac2`](https://github.com/thirdweb-dev/js/commit/5731ac2f50ef63c243d3a6c2516e85920c325a95), [`5731ac2`](https://github.com/thirdweb-dev/js/commit/5731ac2f50ef63c243d3a6c2516e85920c325a95)]:
- @thirdweb-dev/sdk@2.4.2
## 1.0.1
### Patch Changes
+2 -1
View File
@@ -1,6 +1,6 @@
<p align="center">
<br />
<a href="https://thirdweb.com"><img src="https://github.com/thirdweb-dev/typescript-sdk/blob/main/logo.svg?raw=true" width="200" alt=""/></a>
<a href="https://thirdweb.com"><img src="https://github.com/thirdweb-dev/js/blob/main/packages/sdk/logo.svg?raw=true" width="200" alt=""/></a>
<br />
</p>
<h1 align="center">thirdweb Auth</h1>
@@ -18,6 +18,7 @@ Install the latest version of the SDK with either `npm` or `yarn`:
```shell
npm install @thirdweb-dev/auth @thirdweb-dev/sdk ethers
```
```shell
yarn add @thirdweb-dev/auth @thirdweb-dev/sdk ethers
```
+4
View File
@@ -0,0 +1,4 @@
{
"main": "dist/thirdweb-dev-auth-evm.cjs.js",
"module": "dist/thirdweb-dev-auth-evm.esm.js"
}
+2 -1
View File
@@ -1,3 +1,4 @@
{
"main": "dist/thirdweb-dev-auth-express.cjs.js"
"main": "dist/thirdweb-dev-auth-express.cjs.js",
"module": "dist/thirdweb-dev-auth-express.esm.js"
}
+4
View File
@@ -0,0 +1,4 @@
{
"main": "dist/thirdweb-dev-auth-next-auth.cjs.js",
"module": "dist/thirdweb-dev-auth-next-auth.esm.js"
}
+2 -1
View File
@@ -1,3 +1,4 @@
{
"main": "dist/thirdweb-dev-auth-next.cjs.js"
"main": "dist/thirdweb-dev-auth-next.cjs.js",
"module": "dist/thirdweb-dev-auth-next.esm.js"
}
+100 -18
View File
@@ -1,61 +1,143 @@
{
"name": "@thirdweb-dev/auth",
"version": "1.0.1",
"repository": {
"directory": "packages/auth",
"type": "git",
"url": "https://github.com/thirdweb-dev/js.git"
"version": "3.0.2",
"main": "dist/thirdweb-dev-auth.cjs.js",
"module": "dist/thirdweb-dev-auth.esm.js",
"exports": {
".": {
"module": "./dist/thirdweb-dev-auth.esm.js",
"default": "./dist/thirdweb-dev-auth.cjs.js"
},
"./evm": {
"module": "./evm/dist/thirdweb-dev-auth-evm.esm.js",
"default": "./evm/dist/thirdweb-dev-auth-evm.cjs.js"
},
"./next": {
"module": "./next/dist/thirdweb-dev-auth-next.esm.js",
"default": "./next/dist/thirdweb-dev-auth-next.cjs.js"
},
"./solana": {
"module": "./solana/dist/thirdweb-dev-auth-solana.esm.js",
"default": "./solana/dist/thirdweb-dev-auth-solana.cjs.js"
},
"./express": {
"module": "./express/dist/thirdweb-dev-auth-express.esm.js",
"default": "./express/dist/thirdweb-dev-auth-express.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"
},
"./package.json": "./package.json"
},
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/auth",
"author": "thirdweb eng <[email protected]>",
"license": "Apache-2.0",
"files": [
"dist/",
"next/",
"express/"
],
"scripts": {
"build": "preconstruct build",
"lint": "eslint"
"format": "prettier --write 'src/**/*'",
"lint": "eslint src/",
"fix": "eslint src/ --fix",
"clean": "rm -rf dist/ && rm -rf node_modules/",
"test": "mocha --config './test/.mocharc.json' --timeout 30000 --parallel './test/**/*.test.ts'"
},
"preconstruct": {
"entrypoints": [
"index.ts",
"express/index.ts",
"next/index.ts",
"express/index.ts"
]
"next-auth/index.ts",
"evm/index.ts",
"solana/index.ts"
],
"___experimentalFlags_WILL_CHANGE_IN_PATCH": {
"exports": true
},
"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",
"@microsoft/api-extractor": "^7.29.2",
"@microsoft/tsdoc": "^0.14.1",
"@preconstruct/cli": "^2.2.1",
"@solana/web3.js": "^1.73.0",
"@swc-node/register": "^1.5.4",
"@swc/core": "^1.3.23",
"@thirdweb-dev/sdk": "*",
"@thirdweb-dev/wallets": "*",
"@types/bs58": "^4.0.1",
"@types/chai": "^4.3.4",
"@types/cookie": "^0.5.1",
"@types/cookie-parser": "^1.4.3",
"@types/express": "^4.17.13",
"@types/mocha": "^10.0.1",
"@typescript-eslint/eslint-plugin": "^5.33.0",
"@typescript-eslint/parser": "^5.33.0",
"@wagmi/core": "^0.8.18",
"chai": "^4.3.7",
"eslint": "^8.21.0",
"eslint-config-prettier": "^8.5.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-inclusive-language": "^2.2.0",
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-tsdoc": "^0.2.16",
"ethers": ">=5.5.1",
"ethers": "^5.7.2",
"express": "^4.18.1",
"next": "12.2.0",
"mocha": "^10.2.0",
"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": {
"@noble/ed25519": "^1.7.1",
"@solana/web3.js": "^1.73.0",
"@thirdweb-dev/sdk": "*",
"bs58": "^5.0.0",
"cookie-parser": "^1.4.6",
"ethers": ">=5.5.1",
"express": "^4.18.1",
"next": "12.2.0"
"next": "^12.2.0",
"next-auth": "^4.10.3",
"tweetnacl": "^1.0.3"
},
"peerDependenciesMeta": {
"@noble/ed25519": {
"optional": true
},
"@solana/web3.js": {
"optional": true
},
"bs58": {
"optional": true
},
"cookie-parser": {
"optional": true
},
"tweetnacl": {
"optional": true
},
"ethers": {
"optional": true
},
"express": {
"optional": true
},
"next": {
"optional": true
},
"next-auth": {
"optional": true
}
},
"dependencies": {
"cookie": "^0.5.0",
"cookie-parser": "^1.4.6"
"uuid": "^9.0.0",
"zod": "^3.20.2"
}
}
+4
View File
@@ -0,0 +1,4 @@
{
"main": "dist/thirdweb-dev-auth-solana.cjs.js",
"module": "dist/thirdweb-dev-auth-solana.esm.js"
}
+412
View File
@@ -0,0 +1,412 @@
import {
LoginOptions,
LoginPayload,
GenerateOptions,
LoginPayloadData,
LoginPayloadDataSchema,
AuthenticationPayloadDataSchema,
AuthenticationPayloadData,
LoginOptionsSchema,
VerifyOptionsSchema,
VerifyOptions,
GenerateOptionsSchema,
AuthenticateOptionsSchema,
AuthenticateOptions,
User,
Json,
} from "./schema";
import { isBrowser } from "./utils";
import type { GenericAuthWallet } from "@thirdweb-dev/wallets";
export class ThirdwebAuth {
private domain: string;
private wallet: GenericAuthWallet;
constructor(wallet: GenericAuthWallet, domain: string) {
this.wallet = wallet;
this.domain = domain;
}
public updateWallet(wallet: GenericAuthWallet) {
this.wallet = wallet;
}
public async login(options?: LoginOptions): Promise<LoginPayload> {
const parsedOptions = LoginOptionsSchema.parse(options);
let chainId: string | undefined = parsedOptions?.chainId;
if (!chainId && this.wallet.getChainId) {
try {
chainId = (await this.wallet.getChainId()).toString();
} catch {
// ignore error
}
}
const payloadData = LoginPayloadDataSchema.parse({
type: this.wallet.type,
domain: parsedOptions?.domain || this.domain,
address: await this.wallet.getAddress(),
statement: parsedOptions?.statement,
version: parsedOptions?.version,
uri:
parsedOptions?.uri || (isBrowser() ? window.location.href : undefined),
chain_id: chainId,
nonce: parsedOptions?.nonce,
expiration_time:
parsedOptions?.expirationTime || new Date(Date.now() + 1000 * 60 * 5),
invalid_before: parsedOptions?.invalidBefore,
resources: parsedOptions?.resources,
});
const message = this.generateMessage(payloadData);
const signature = await this.wallet.signMessage(message);
return {
payload: payloadData,
signature,
};
}
public async verify(
payload: LoginPayload,
options?: VerifyOptions,
): Promise<string> {
const parsedOptions = VerifyOptionsSchema.parse(options);
if (payload.payload.type !== this.wallet.type) {
throw new Error(
`Expected chain type '${this.wallet.type}' does not match chain type on payload '${payload.payload.type}'`,
);
}
// Check that the intended domain matches the domain of the payload
const domain = parsedOptions?.domain || this.domain;
if (payload.payload.domain !== domain) {
throw new Error(
`Expected domain '${domain}' does not match domain on payload '${payload.payload.domain}'`,
);
}
// Check that the payload statement matches the expected statement
if (parsedOptions?.statement) {
if (payload.payload.statement !== parsedOptions.statement) {
throw new Error(
`Expected statement '${parsedOptions.statement}' does not match statement on payload '${payload.payload.statement}'`,
);
}
}
// Check that the intended URI matches the URI of the payload
if (parsedOptions?.uri) {
if (payload.payload.uri !== parsedOptions.uri) {
throw new Error(
`Expected URI '${parsedOptions.uri}' does not match URI on payload '${payload.payload.uri}'`,
);
}
}
// Check that the intended version matches the version of the payload
if (parsedOptions?.version) {
if (payload.payload.version !== parsedOptions.version) {
throw new Error(
`Expected version '${parsedOptions.version}' does not match version on payload '${payload.payload.version}'`,
);
}
}
// Check that the intended chain ID matches the chain ID of the payload
if (parsedOptions?.chainId) {
if (payload.payload.chain_id !== parsedOptions.chainId) {
throw new Error(
`Expected chain ID '${parsedOptions.chainId}' does not match chain ID on payload '${payload.payload.chain_id}'`,
);
}
}
// Check that the payload nonce is valid
if (parsedOptions?.validateNonce !== undefined) {
try {
await parsedOptions.validateNonce(payload.payload.nonce);
} catch (err) {
console.log(err);
throw new Error(`Login request nonce is invalid`);
}
}
// Check that it isn't before the invalid before time
const currentTime = new Date();
if (currentTime < new Date(payload.payload.invalid_before)) {
throw new Error(`Login request is not yet valid`);
}
// Check that the payload hasn't expired
if (currentTime > new Date(payload.payload.expiration_time)) {
throw new Error(`Login request has expired`);
}
// Check that the specified resources are present on the payload
if (parsedOptions?.resources) {
const missingResources = parsedOptions.resources.filter(
(resource) => !payload.payload.resources?.includes(resource),
);
if (missingResources.length > 0) {
throw new Error(
`Login request is missing required resources: ${missingResources.join(
", ",
)}`,
);
}
}
// Check that the signing address is the claimed wallet address
const message = this.generateMessage(payload.payload);
const chainId =
this.wallet.type === "evm" && payload.payload.chain_id
? parseInt(payload.payload.chain_id)
: undefined;
const verified = await this.verifySignature(
message,
payload.signature,
payload.payload.address,
chainId,
);
if (!verified) {
throw new Error(
`Signer address does not match payload address '${payload.payload.address.toLowerCase()}'`,
);
}
return payload.payload.address;
}
public async generate(
payload: LoginPayload,
options?: GenerateOptions,
): Promise<string> {
if (isBrowser()) {
throw new Error(
"Authentication tokens should not be generated in the browser, as they must be signed by a server-side admin wallet.",
);
}
const parsedOptions = GenerateOptionsSchema.parse(options);
const domain = parsedOptions?.domain || this.domain;
const userAddress = await this.verify(payload, {
domain,
...parsedOptions?.verifyOptions,
});
let session: Json | undefined = undefined;
if (typeof parsedOptions?.session === "function") {
const sessionTrigger = (await parsedOptions.session(userAddress)) as Json;
if (sessionTrigger) {
session = sessionTrigger;
}
} else {
session = parsedOptions?.session;
}
const adminAddress = await this.wallet.getAddress();
const payloadData = AuthenticationPayloadDataSchema.parse({
iss: adminAddress,
sub: userAddress,
aud: domain,
nbf: parsedOptions?.invalidBefore || new Date(),
exp:
parsedOptions?.expirationTime ||
new Date(Date.now() + 1000 * 60 * 60 * 5),
iat: new Date(),
jti: parsedOptions?.tokenId,
ctx: session,
});
const message = JSON.stringify(payloadData);
const signature = await this.wallet.signMessage(message);
// Header used for JWT token specifying hash algorithm
const header = {
// Specify ECDSA with SHA-256 for hashing algorithm
alg: "ES256",
typ: "JWT",
};
const encodedHeader = Buffer.from(JSON.stringify(header)).toString(
"base64",
);
const encodedData = Buffer.from(JSON.stringify(payloadData))
.toString("base64")
.replace(/=/g, "");
const encodedSignature = Buffer.from(signature).toString("base64");
// Generate a JWT token with base64 encoded header, payload, and signature
const token = `${encodedHeader}.${encodedData}.${encodedSignature}`;
return token;
}
/**
* Authenticate With Token
* @remarks Server-side function that authenticates the provided JWT token. This function verifies that
* the provided authentication token is valid and returns the address of the authenticated wallet.
*
* @param domain - The domain of the server-side application doing authentication
* @param token - The authentication token being used
* @returns The address of the authenticated wallet
*
* @example
* ```javascript
* const domain = "example.com";
* const loginPayload = await sdk.auth.login(domain);
* const token = await sdk.auth.generateAuthToken(domain, loginPayload);
*
* // Authenticate the token and get the address of authenticating users wallet
* const address = sdk.auth.authenticate(domain, token);
* ```
*/
public async authenticate<TSession extends Json = Json>(
token: string,
options?: AuthenticateOptions,
): Promise<User<TSession>> {
if (isBrowser()) {
throw new Error(
"Should not authenticate tokens in the browser, as they must be verified by the server-side admin wallet.",
);
}
const parsedOptions = AuthenticateOptionsSchema.parse(options);
const domain = parsedOptions?.domain || this.domain;
const encodedPayload = token.split(".")[1];
const encodedSignature = token.split(".")[2];
const payload: AuthenticationPayloadData = JSON.parse(
Buffer.from(encodedPayload, "base64").toString(),
);
const signature = Buffer.from(encodedSignature, "base64").toString();
// Check that the payload unique ID is valid
if (parsedOptions?.validateTokenId !== undefined) {
try {
await parsedOptions.validateTokenId(payload.jti);
} catch (err) {
throw new Error(`Token ID is invalid`);
}
}
// Check that the token audience matches the domain
if (payload.aud !== domain) {
throw new Error(
`Expected token to be for the domain '${domain}', but found token with domain '${payload.aud}'`,
);
}
// Check that the token is past the invalid before time
const currentTime = Math.floor(new Date().getTime() / 1000);
if (currentTime < payload.nbf) {
throw new Error(
`This token is invalid before epoch time '${payload.nbf}', current epoch time is '${currentTime}'`,
);
}
// Check that the token hasn't expired
if (currentTime > payload.exp) {
throw new Error(
`This token expired at epoch time '${payload.exp}', current epoch time is '${currentTime}'`,
);
}
// Check that the connected wallet matches the token issuer
const connectedAddress = await this.wallet.getAddress();
if (connectedAddress.toLowerCase() !== payload.iss.toLowerCase()) {
throw new Error(
`Expected the connected wallet address '${connectedAddress}' to match the token issuer address '${payload.iss}'`,
);
}
let chainId: number | undefined = undefined;
if (this.wallet.getChainId) {
try {
chainId = await this.wallet.getChainId();
} catch {
// ignore error
}
}
const verified = await this.verifySignature(
JSON.stringify(payload),
signature,
connectedAddress,
chainId,
);
if (!verified) {
throw new Error(
`The connected wallet address '${connectedAddress}' did not sign the token`,
);
}
return {
address: payload.sub,
session: payload.ctx as TSession | undefined,
};
}
private async verifySignature(
message: string,
signature: string,
address: string,
chainId?: number,
) {
return this.wallet.verifySignature(message, signature, address, chainId);
}
/**
* Generates a EIP-4361 & CAIP-122 compliant message to sign based on the login payload
*/
private generateMessage(payload: LoginPayloadData): string {
const typeField = payload.type === "evm" ? "Ethereum" : "Solana";
const header = `${payload.domain} wants you to sign in with your ${typeField} account:`;
let prefix = [header, payload.address].join("\n");
prefix = [prefix, payload.statement].join("\n\n");
if (payload.statement) {
prefix += "\n";
}
const suffixArray = [];
if (payload.uri) {
const uriField = `URI: ${payload.uri}`;
suffixArray.push(uriField);
}
const versionField = `Version: ${payload.version}`;
suffixArray.push(versionField);
if (payload.chain_id) {
const chainField = `Chain ID: ` + payload.chain_id || "1";
suffixArray.push(chainField);
}
const nonceField = `Nonce: ${payload.nonce}`;
suffixArray.push(nonceField);
const issuedAtField = `Issued At: ${payload.issued_at}`;
suffixArray.push(issuedAtField);
const expiryField = `Expiration Time: ${payload.expiration_time}`;
suffixArray.push(expiryField);
if (payload.invalid_before) {
const invalidBeforeField = `Not Before: ${payload.invalid_before}`;
suffixArray.push(invalidBeforeField);
}
if (payload.resources) {
suffixArray.push(
[`Resources:`, ...payload.resources.map((x) => `- ${x}`)].join("\n"),
);
}
const suffix = suffixArray.join("\n");
return [prefix, suffix].join("\n");
}
}
+2
View File
@@ -0,0 +1,2 @@
export { ThirdwebAuth } from "./auth";
export * from "./schema";
+208
View File
@@ -0,0 +1,208 @@
import { utils, BigNumber } from "ethers";
import { v4 as uuidv4 } from "uuid";
import { z } from "zod";
export const AddressSchema = z.string().refine(
(arg) => utils.isAddress(arg),
(out) => {
return {
message: `${out} is not a valid address`,
};
},
);
export const RawDateSchema = z.date().transform((i) => {
return BigNumber.from(Math.floor(i.getTime() / 1000));
});
export const AccountTypeSchema = z.union([
z.literal("evm"),
z.literal("solana"),
]);
const literalSchema = z.union([z.string(), z.number(), z.boolean(), z.null()]);
type Literal = z.infer<typeof literalSchema>;
export type Json = Literal | { [key: string]: Json } | Json[];
const JsonSchema: z.ZodType<Json> = z.lazy(
() => z.union([literalSchema, z.array(JsonSchema), z.record(JsonSchema)]),
{ invalid_type_error: "Provided value was not valid JSON" },
);
/**
* @internal
*/
export const LoginOptionsSchema = z
.object({
domain: z.string().optional(),
statement: z.string().optional(),
uri: z.string().optional(),
version: z.string().optional(),
chainId: z.string().optional(),
nonce: z.string().optional(),
expirationTime: z.date().optional(),
invalidBefore: z.date().optional(),
resources: z.array(z.string()).optional(),
})
.optional();
/**
* @internal
*/
export const LoginPayloadDataSchema = z.object({
type: AccountTypeSchema,
domain: z.string(),
address: z.string(),
statement: z
.string()
.default(
"Please ensure that the domain above matches the URL of the current website.",
),
uri: z.string().optional(),
version: z.string().default("1"),
chain_id: z.string().optional(),
nonce: z.string().default(uuidv4()),
issued_at: z
.date()
.default(new Date())
.transform((d) => d.toISOString()),
expiration_time: z.date().transform((d) => d.toISOString()),
invalid_before: z
.date()
.default(new Date())
.transform((d) => d.toISOString()),
resources: z.array(z.string()).optional(),
});
/**
* @internal
*/
export const LoginPayloadSchema = z.object({
payload: LoginPayloadDataSchema,
signature: z.string(),
});
/**
* @internal
*/
const VerifyOptionsSchemaRequired = z.object({
domain: z.string().optional(),
statement: z.string().optional(),
uri: z.string().optional(),
version: z.string().optional(),
chainId: z.string().optional(),
validateNonce: z.function().args(z.string()).optional(),
resources: z.array(z.string()).optional(),
});
/**
* @internal
*/
export const VerifyOptionsSchema = VerifyOptionsSchemaRequired.optional();
/**
* @internal
*/
export const GenerateOptionsSchema = z
.object({
domain: z.string().optional(),
tokenId: z.string().optional(),
expirationTime: z.date().optional(),
invalidBefore: z.date().optional(),
session: z.union([JsonSchema, z.function().args(z.string())]).optional(),
verifyOptions: VerifyOptionsSchemaRequired.omit({
domain: true,
}).optional(),
})
.optional();
/**
* @internal
*/
export const AuthenticationPayloadDataSchema = z.object({
iss: z.string(),
sub: z.string(),
aud: z.string(),
exp: RawDateSchema.transform((b) => b.toNumber()),
nbf: RawDateSchema.transform((b) => b.toNumber()),
iat: RawDateSchema.transform((b) => b.toNumber()),
jti: z.string().default(uuidv4()),
ctx: JsonSchema.optional(),
});
/**
* @internal
*/
export const AuthenticationPayloadSchema = z.object({
payload: AuthenticationPayloadDataSchema,
signature: z.string(),
});
/**
* @internal
*/
export const AuthenticateOptionsSchema = z
.object({
domain: z.string().optional(),
validateTokenId: z.function().args(z.string()).optional(),
})
.optional();
/**
* @public
*/
export type LoginOptions = z.input<typeof LoginOptionsSchema>;
/**
* @public
*/
export type LoginPayloadData = z.output<typeof LoginPayloadDataSchema>;
/**
* @public
*/
export type LoginPayload = z.output<typeof LoginPayloadSchema>;
/**
* @public
*/
export type VerifyOptions = z.input<typeof VerifyOptionsSchema>;
/**
* @public
*/
export type GenerateOptions = z.input<typeof GenerateOptionsSchema>;
/**
* @public
*/
export type AuthenticationPayloadData = z.output<
typeof AuthenticationPayloadDataSchema
>;
/**
* @public
*/
export type AuthenticationPayload = z.output<
typeof AuthenticationPayloadSchema
>;
/**
* @public
*/
export type AuthenticateOptions = z.output<typeof AuthenticateOptionsSchema>;
/**
* @public
*/
export type User<TContext extends Json = Json> = {
address: string;
session?: TContext;
};
export const LoginPayloadOutputSchema = LoginPayloadSchema.extend({
payload: LoginPayloadDataSchema.extend({
issued_at: z.string(),
expiration_time: z.string(),
invalid_before: z.string(),
}),
});
@@ -1,7 +1,6 @@
/**
* @internal
*/
// @ts-expext-error window is not defined
export const isBrowser = () => typeof window !== "undefined";
/**
+613
View File
@@ -0,0 +1,613 @@
import type { Chain } from "@wagmi/core";
const arbitrum: Chain = {
id: 42_161,
name: "Arbitrum One",
network: "arbitrum",
nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
rpcUrls: {
alchemy: {
http: ["https://arb-mainnet.g.alchemy.com/v2"],
webSocket: ["wss://arb-mainnet.g.alchemy.com/v2"],
},
infura: {
http: ["https://arbitrum-mainnet.infura.io/v3"],
webSocket: ["wss://arbitrum-mainnet.infura.io/ws/v3"],
},
default: {
http: ["https://arb1.arbitrum.io/rpc"],
},
public: {
http: ["https://arb1.arbitrum.io/rpc"],
},
},
blockExplorers: {
etherscan: { name: "Arbiscan", url: "https://arbiscan.io" },
default: { name: "Arbiscan", url: "https://arbiscan.io" },
},
contracts: {
multicall3: {
address: "0xca11bde05977b3631167028862be2a173976ca11",
blockCreated: 7654707,
},
},
};
const arbitrumGoerli: Chain = {
id: 421_613,
name: "Arbitrum Goerli",
network: "arbitrum-goerli",
nativeCurrency: {
name: "Arbitrum Goerli Ether",
symbol: "ETH",
decimals: 18,
},
rpcUrls: {
alchemy: {
http: ["https://arb-goerli.g.alchemy.com/v2"],
webSocket: ["wss://arb-goerli.g.alchemy.com/v2"],
},
infura: {
http: ["https://arbitrum-goerli.infura.io/v3"],
webSocket: ["wss://arbitrum-goerli.infura.io/ws/v3"],
},
default: {
http: ["https://goerli-rollup.arbitrum.io/rpc"],
},
public: {
http: ["https://goerli-rollup.arbitrum.io/rpc"],
},
},
blockExplorers: {
etherscan: { name: "Arbiscan", url: "https://goerli.arbiscan.io/" },
default: { name: "Arbiscan", url: "https://goerli.arbiscan.io/" },
},
contracts: {
multicall3: {
address: "0xca11bde05977b3631167028862be2a173976ca11",
blockCreated: 88114,
},
},
testnet: true,
};
const avalanche: Chain = {
id: 43_114,
name: "Avalanche",
network: "avalanche",
nativeCurrency: {
decimals: 18,
name: "Avalanche",
symbol: "AVAX",
},
rpcUrls: {
default: { http: ["https://api.avax.network/ext/bc/C/rpc"] },
public: { http: ["https://api.avax.network/ext/bc/C/rpc"] },
},
blockExplorers: {
etherscan: { name: "SnowTrace", url: "https://snowtrace.io" },
default: { name: "SnowTrace", url: "https://snowtrace.io" },
},
contracts: {
multicall3: {
address: "0xca11bde05977b3631167028862be2a173976ca11",
blockCreated: 11907934,
},
},
};
const avalancheFuji: Chain = {
id: 43_113,
name: "Avalanche Fuji",
network: "avalanche-fuji",
nativeCurrency: {
decimals: 18,
name: "Avalanche Fuji",
symbol: "AVAX",
},
rpcUrls: {
default: { http: ["https://api.avax-test.network/ext/bc/C/rpc"] },
public: { http: ["https://api.avax-test.network/ext/bc/C/rpc"] },
},
blockExplorers: {
etherscan: { name: "SnowTrace", url: "https://testnet.snowtrace.io" },
default: { name: "SnowTrace", url: "https://testnet.snowtrace.io" },
},
contracts: {
multicall3: {
address: "0xca11bde05977b3631167028862be2a173976ca11",
blockCreated: 7096959,
},
},
testnet: true,
};
const bsc: Chain = {
id: 56,
name: "BNB Smart Chain",
network: "bsc",
nativeCurrency: {
decimals: 18,
name: "BNB",
symbol: "BNB",
},
rpcUrls: {
default: { http: ["https://rpc.ankr.com/bsc"] },
public: { http: ["https://rpc.ankr.com/bsc"] },
},
blockExplorers: {
etherscan: { name: "BscScan", url: "https://bscscan.com" },
default: { name: "BscScan", url: "https://bscscan.com" },
},
contracts: {
multicall3: {
address: "0xca11bde05977b3631167028862be2a173976ca11",
blockCreated: 15921452,
},
},
};
const bscTestnet: Chain = {
id: 97,
name: "Binance Smart Chain Testnet",
network: "bsc-testnet",
nativeCurrency: {
decimals: 18,
name: "BNB",
symbol: "tBNB",
},
rpcUrls: {
default: { http: ["https://bsc-testnet.public.blastapi.io"] },
public: { http: ["https://bsc-testnet.public.blastapi.io"] },
},
blockExplorers: {
etherscan: { name: "BscScan", url: "https://testnet.bscscan.com" },
default: { name: "BscScan", url: "https://testnet.bscscan.com" },
},
contracts: {
multicall3: {
address: "0xca11bde05977b3631167028862be2a173976ca11",
blockCreated: 17422483,
},
},
testnet: true,
};
const fantom: Chain = {
id: 250,
name: "Fantom",
network: "fantom",
nativeCurrency: {
decimals: 18,
name: "Fantom",
symbol: "FTM",
},
rpcUrls: {
default: { http: ["https://rpc.ankr.com/fantom"] },
public: { http: ["https://rpc.ankr.com/fantom"] },
},
blockExplorers: {
etherscan: { name: "FTMScan", url: "https://ftmscan.com" },
default: { name: "FTMScan", url: "https://ftmscan.com" },
},
contracts: {
multicall3: {
address: "0xca11bde05977b3631167028862be2a173976ca11",
blockCreated: 33001987,
},
},
};
const fantomTestnet: Chain = {
id: 4002,
name: "Fantom Testnet",
network: "fantom-testnet",
nativeCurrency: {
decimals: 18,
name: "Fantom",
symbol: "FTM",
},
rpcUrls: {
default: { http: ["https://rpc.testnet.fantom.network"] },
public: { http: ["https://rpc.testnet.fantom.network"] },
},
blockExplorers: {
etherscan: { name: "FTMScan", url: "https://testnet.ftmscan.com" },
default: { name: "FTMScan", url: "https://testnet.ftmscan.com" },
},
contracts: {
multicall3: {
address: "0xca11bde05977b3631167028862be2a173976ca11",
blockCreated: 8328688,
},
},
};
const goerli: Chain = {
id: 5,
network: "goerli",
name: "Goerli",
nativeCurrency: { name: "Goerli Ether", symbol: "ETH", decimals: 18 },
rpcUrls: {
alchemy: {
http: ["https://eth-goerli.g.alchemy.com/v2"],
webSocket: ["wss://eth-goerli.g.alchemy.com/v2"],
},
infura: {
http: ["https://goerli.infura.io/v3"],
webSocket: ["wss://goerli.infura.io/ws/v3"],
},
default: {
http: ["https://rpc.ankr.com/eth_goerli"],
},
public: {
http: ["https://rpc.ankr.com/eth_goerli"],
},
},
blockExplorers: {
etherscan: {
name: "Etherscan",
url: "https://goerli.etherscan.io",
},
default: {
name: "Etherscan",
url: "https://goerli.etherscan.io",
},
},
contracts: {
ensRegistry: {
address: "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e",
},
multicall3: {
address: "0xca11bde05977b3631167028862be2a173976ca11",
blockCreated: 6507670,
},
},
testnet: true,
};
const mainnet: Chain = {
id: 1,
network: "homestead",
name: "Ethereum",
nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
rpcUrls: {
alchemy: {
http: ["https://eth-mainnet.g.alchemy.com/v2"],
webSocket: ["wss://eth-mainnet.g.alchemy.com/v2"],
},
infura: {
http: ["https://mainnet.infura.io/v3"],
webSocket: ["wss://mainnet.infura.io/ws/v3"],
},
default: {
http: ["https://cloudflare-eth.com"],
},
public: {
http: ["https://cloudflare-eth.com"],
},
},
blockExplorers: {
etherscan: {
name: "Etherscan",
url: "https://etherscan.io",
},
default: {
name: "Etherscan",
url: "https://etherscan.io",
},
},
contracts: {
ensRegistry: {
address: "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e",
},
multicall3: {
address: "0xca11bde05977b3631167028862be2a173976ca11",
blockCreated: 14353601,
},
},
};
const optimism: Chain = {
id: 10,
name: "Optimism",
network: "optimism",
nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
rpcUrls: {
alchemy: {
http: ["https://opt-mainnet.g.alchemy.com/v2"],
webSocket: ["wss://opt-mainnet.g.alchemy.com/v2"],
},
infura: {
http: ["https://optimism-mainnet.infura.io/v3"],
webSocket: ["wss://optimism-mainnet.infura.io/ws/v3"],
},
default: {
http: ["https://mainnet.optimism.io"],
},
public: {
http: ["https://mainnet.optimism.io"],
},
},
blockExplorers: {
etherscan: {
name: "Etherscan",
url: "https://optimistic.etherscan.io",
},
default: {
name: "Etherscan",
url: "https://optimistic.etherscan.io",
},
},
contracts: {
multicall3: {
address: "0xca11bde05977b3631167028862be2a173976ca11",
blockCreated: 4286263,
},
},
};
const optimismGoerli: Chain = {
id: 420,
name: "Optimism Goerli",
network: "optimism-goerli",
nativeCurrency: { name: "Goerli Ether", symbol: "ETH", decimals: 18 },
rpcUrls: {
alchemy: {
http: ["https://opt-goerli.g.alchemy.com/v2"],
webSocket: ["wss://opt-goerli.g.alchemy.com/v2"],
},
infura: {
http: ["https://optimism-goerli.infura.io/v3"],
webSocket: ["wss://optimism-goerli.infura.io/ws/v3"],
},
default: {
http: ["https://goerli.optimism.io"],
},
public: {
http: ["https://goerli.optimism.io"],
},
},
blockExplorers: {
etherscan: {
name: "Etherscan",
url: "https://goerli-optimism.etherscan.io",
},
default: {
name: "Etherscan",
url: "https://goerli-optimism.etherscan.io",
},
},
contracts: {
multicall3: {
address: "0xca11bde05977b3631167028862be2a173976ca11",
blockCreated: 49461,
},
},
testnet: true,
};
const polygon: Chain = {
id: 137,
name: "Polygon",
network: "matic",
nativeCurrency: { name: "MATIC", symbol: "MATIC", decimals: 18 },
rpcUrls: {
alchemy: {
http: ["https://polygon-mainnet.g.alchemy.com/v2"],
webSocket: ["wss://polygon-mainnet.g.alchemy.com/v2"],
},
infura: {
http: ["https://polygon-mainnet.infura.io/v3"],
webSocket: ["wss://polygon-mainnet.infura.io/ws/v3"],
},
default: {
http: ["https://polygon-rpc.com"],
},
public: {
http: ["https://polygon-rpc.com"],
},
},
blockExplorers: {
etherscan: {
name: "PolygonScan",
url: "https://polygonscan.com",
},
default: {
name: "PolygonScan",
url: "https://polygonscan.com",
},
},
contracts: {
multicall3: {
address: "0xca11bde05977b3631167028862be2a173976ca11",
blockCreated: 25770160,
},
},
};
const polygonMumbai: Chain = {
id: 80_001,
name: "Polygon Mumbai",
network: "maticmum",
nativeCurrency: { name: "MATIC", symbol: "MATIC", decimals: 18 },
rpcUrls: {
alchemy: {
http: ["https://polygon-mumbai.g.alchemy.com/v2"],
webSocket: ["wss://polygon-mumbai.g.alchemy.com/v2"],
},
infura: {
http: ["https://polygon-mumbai.infura.io/v3"],
webSocket: ["wss://polygon-mumbai.infura.io/ws/v3"],
},
default: {
http: ["https://matic-mumbai.chainstacklabs.com"],
},
public: {
http: ["https://matic-mumbai.chainstacklabs.com"],
},
},
blockExplorers: {
etherscan: {
name: "PolygonScan",
url: "https://mumbai.polygonscan.com",
},
default: {
name: "PolygonScan",
url: "https://mumbai.polygonscan.com",
},
},
contracts: {
multicall3: {
address: "0xca11bde05977b3631167028862be2a173976ca11",
blockCreated: 25770160,
},
},
testnet: true,
};
/**
* @public
*/
export enum ChainId {
Mainnet = 1,
Goerli = 5,
Polygon = 137,
Mumbai = 80001,
Fantom = 250,
FantomTestnet = 4002,
Avalanche = 43114,
AvalancheFujiTestnet = 43113,
Optimism = 10,
OptimismGoerli = 420,
Arbitrum = 42161,
ArbitrumGoerli = 421613,
BinanceSmartChainMainnet = 56,
BinanceSmartChainTestnet = 97,
}
/**
* @public
*/
export type SUPPORTED_CHAIN_ID =
| ChainId.Mainnet
| ChainId.Goerli
| ChainId.Mumbai
| ChainId.Polygon
| ChainId.Fantom
| ChainId.FantomTestnet
| ChainId.Avalanche
| ChainId.AvalancheFujiTestnet
| ChainId.Optimism
| ChainId.OptimismGoerli
| ChainId.Arbitrum
| ChainId.ArbitrumGoerli
| ChainId.BinanceSmartChainMainnet
| ChainId.BinanceSmartChainTestnet;
/**
* @public
*/
export const SUPPORTED_CHAIN_IDS: SUPPORTED_CHAIN_ID[] = [
ChainId.Mainnet,
ChainId.Goerli,
ChainId.Polygon,
ChainId.Mumbai,
ChainId.Fantom,
ChainId.FantomTestnet,
ChainId.Avalanche,
ChainId.AvalancheFujiTestnet,
ChainId.Optimism,
ChainId.OptimismGoerli,
ChainId.Arbitrum,
ChainId.ArbitrumGoerli,
ChainId.BinanceSmartChainMainnet,
ChainId.BinanceSmartChainTestnet,
];
/**
* @public
*/
export const NATIVE_TOKEN_ADDRESS =
"0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee";
export const DEFAULT_API_KEY =
"c6634ad2d97b74baf15ff556016830c251050e6c36b9da508ce3ec80095d3dc1";
function getRpcNameFromChainId(chainId: SUPPORTED_CHAIN_ID): string {
switch (chainId) {
case ChainId.Mainnet:
return "mainnet";
case ChainId.Goerli:
return "goerli";
case ChainId.Polygon:
return "polygon";
case ChainId.Mumbai:
return "mumbai";
case ChainId.Avalanche:
return "avalanche";
case ChainId.AvalancheFujiTestnet:
return "avalanche-fuji";
case ChainId.Fantom:
return "fantom";
case ChainId.FantomTestnet:
return "fantom-testnet";
case ChainId.Arbitrum:
return "arbitrum";
case ChainId.ArbitrumGoerli:
return "arbitrum-goerli";
case ChainId.Optimism:
return "optimism";
case ChainId.OptimismGoerli:
return "optimism-goerli";
case ChainId.BinanceSmartChainMainnet:
return "bsc";
case ChainId.BinanceSmartChainTestnet:
return "bsc-testnet";
default:
throw new Error("Unsupported chain id");
}
}
export function getRpcUrl(chainId: SUPPORTED_CHAIN_ID) {
return `https://${getRpcNameFromChainId(
chainId,
)}.rpc.thirdweb.com/${DEFAULT_API_KEY}`;
}
function enhanceChain<TChain extends Chain>(chain: TChain) {
const twRPC = getRpcUrl(chain.id);
return {
...chain,
rpcUrls: {
...chain.rpcUrls,
default: {
...chain.rpcUrls.default,
http: [twRPC, ...chain.rpcUrls.default.http],
},
public: {
...chain.rpcUrls.public,
http: [twRPC, ...(chain.rpcUrls.public?.http || [])],
},
},
} as TChain;
}
export const supportedChains: Record<SUPPORTED_CHAIN_ID, Chain> = {
[ChainId.Mainnet]: enhanceChain(mainnet),
[ChainId.Goerli]: enhanceChain(goerli),
[ChainId.Polygon]: enhanceChain(polygon),
[ChainId.Mumbai]: enhanceChain(polygonMumbai),
[ChainId.Avalanche]: enhanceChain(avalanche),
[ChainId.AvalancheFujiTestnet]: enhanceChain(avalancheFuji),
[ChainId.Fantom]: enhanceChain(fantom),
[ChainId.FantomTestnet]: enhanceChain(fantomTestnet),
[ChainId.Arbitrum]: enhanceChain(arbitrum),
[ChainId.ArbitrumGoerli]: enhanceChain(arbitrumGoerli),
[ChainId.Optimism]: enhanceChain(optimism),
[ChainId.OptimismGoerli]: enhanceChain(optimismGoerli),
[ChainId.BinanceSmartChainMainnet]: enhanceChain(bsc),
[ChainId.BinanceSmartChainTestnet]: enhanceChain(bscTestnet),
};
export const thirdwebChains: Chain[] = Object.values(supportedChains);
+93
View File
@@ -0,0 +1,93 @@
import { SUPPORTED_CHAIN_ID, supportedChains } from "./evm";
import type { Ecosystem, GenericAuthWallet } from "@thirdweb-dev/wallets";
import { ethers } from "ethers";
const EIP1271_ABI = [
"function isValidSignature(bytes32 _message, bytes _signature) public view returns (bytes4)",
];
const EIP1271_MAGICVALUE = "0x1626ba7e";
export const checkContractWalletSignature = async (
message: string,
signature: string,
address: string,
chainId: number,
): Promise<boolean> => {
const rpcUrl =
supportedChains[chainId as SUPPORTED_CHAIN_ID]?.rpcUrls.default.http[0];
if (!rpcUrl) {
return false;
}
const provider = new ethers.providers.JsonRpcProvider(rpcUrl);
const walletContract = new ethers.Contract(address, EIP1271_ABI, provider);
const hashMessage = ethers.utils.hashMessage(message);
try {
const res = await walletContract.isValidSignature(hashMessage, signature);
return res === EIP1271_MAGICVALUE;
} catch {
return false;
}
};
export class SignerWallet implements GenericAuthWallet {
type: Ecosystem = "evm";
#signer: ethers.Signer;
constructor(signer: ethers.Signer) {
this.#signer = signer;
}
public async getAddress(): Promise<string> {
return this.#signer.getAddress();
}
public async getChainId(): Promise<number> {
return this.#signer.getChainId();
}
public async signMessage(message: string): Promise<string> {
return await this.#signer.signMessage(message);
}
public async verifySignature(
message: string,
signature: string,
address: string,
chainId?: number,
): Promise<boolean> {
const messageHash = ethers.utils.hashMessage(message);
const messageHashBytes = ethers.utils.arrayify(messageHash);
const recoveredAddress = ethers.utils.recoverAddress(
messageHashBytes,
signature,
);
if (recoveredAddress === address) {
return true;
}
// Check if the address is a smart contract wallet
if (chainId !== undefined) {
try {
const isValid = await checkContractWalletSignature(
message,
signature,
address,
chainId || 1,
);
return isValid;
} catch {
// no-op
}
}
return false;
}
}
export class PrivateKeyWallet extends SignerWallet {
constructor(privateKey: string) {
super(new ethers.Wallet(privateKey));
}
}
+60
View File
@@ -0,0 +1,60 @@
import { Json } from "../../core/schema";
import { ThirdwebAuthContext, ThirdwebAuthUser } from "../types";
import { Request } from "express";
function getToken(req: Request): string | undefined {
if (req.headers["authorization"]) {
const authorizationHeader = req.headers["authorization"].split(" ");
if (authorizationHeader?.length === 2) {
return authorizationHeader[1];
}
return undefined;
}
const cookie: string | undefined = !req.cookies
? undefined
: typeof req.cookies.get === "function"
? (req.cookies as any).get("thirdweb_auth_token")
: (req.cookies as any).thirdweb_auth_token;
return cookie;
}
export async function getUser<
TData extends Json = Json,
TSession extends Json = Json,
>(
req: Request,
ctx: ThirdwebAuthContext<TData, TSession>,
): Promise<ThirdwebAuthUser<TData, TSession> | null> {
const token = getToken(req);
if (!token) {
return null;
}
let authenticatedUser: ThirdwebAuthUser<TData, TSession>;
try {
authenticatedUser = await ctx.auth.authenticate<TSession>(token, {
validateTokenId: async (tokenId: string) => {
if (ctx.authOptions?.validateTokenId) {
await ctx.authOptions?.validateTokenId(tokenId);
}
},
});
} catch (err) {
return null;
}
if (!ctx.callbacks?.onUser) {
return authenticatedUser;
}
const data = await ctx.callbacks.onUser(authenticatedUser);
if (!data) {
return authenticatedUser;
}
return { ...authenticatedUser, data: data };
}
+47 -47
View File
@@ -1,63 +1,63 @@
import { Json, ThirdwebAuth as ThirdwebAuthSDK } from "../core";
import { getUser } from "./helpers/user";
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 { ThirdwebAuthConfig, ThirdwebAuthContext } from "./types";
import cookieParser from "cookie-parser";
import { Express, NextFunction, Request, Response } from "express";
import express, { 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 asyncHandler =
(fn: CallableFunction) =>
(...args: any[]) => {
const fnReturn = fn(...args);
const next = args[args.length - 1];
return Promise.resolve(fnReturn).catch(next);
};
const authUrl = cfg.authUrl?.replace(/\/$/, "") || "/auth";
export function ThirdwebAuth<
TData extends Json = Json,
TSession extends Json = Json,
>(cfg: ThirdwebAuthConfig<TData, TSession>) {
const ctx = {
...cfg,
auth: new ThirdwebAuthSDK(cfg.wallet, cfg.domain),
};
app.use(cookieParser());
const router = express.Router();
const cookieMiddleware = cookieParser();
app.use(async (req: Request, _: Response, next: NextFunction) => {
const { sdk, domain } = ctx;
let user = null;
const token = req.cookies.thirdweb_auth_token;
router.use(express.json());
router.use(cookieMiddleware);
if (token) {
try {
const address = await sdk.auth.authenticate(domain, token);
user = { address };
} catch {
// No-op
}
}
router.post(
"/login",
asyncHandler((req: Request, res: Response) =>
loginHandler(req, res, ctx as ThirdwebAuthContext),
),
);
req.user = user;
next();
});
router.get(
"/user",
asyncHandler((req: Request, res: Response) =>
userHandler(req, res, ctx as ThirdwebAuthContext),
),
);
app.get(`${authUrl}/:route`, (req: Request, res: Response) => {
const action = req.params.route as ThirdwebAuthRoute;
router.post(
"/logout",
asyncHandler((req: Request, res: Response) =>
logoutHandler(req, res, ctx as ThirdwebAuthContext),
),
);
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.",
});
}
});
return {
authRouter: router,
authMiddleware: cookieMiddleware,
getUser: (req: Request) => {
return getUser<TData, TSession>(req, ctx);
},
};
}
+55 -24
View File
@@ -1,38 +1,67 @@
import { ThirdwebAuthContext } from "../types";
import { LoginPayload } from "@thirdweb-dev/sdk/dist/src/schema";
import { GenerateOptions } from "../../core";
import { LoginPayloadBodySchema, ThirdwebAuthContext } from "../types";
import { serialize } from "cookie";
import { Request, Response } from "express";
function redirectWithError(req: Request, res: Response, 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: Request,
res: Response,
ctx: ThirdwebAuthContext
ctx: ThirdwebAuthContext,
) {
if (req.method !== "GET") {
return redirectWithError(req, res, "INVALID_METHOD");
if (req.method !== "POST") {
return res.status(405).json({ error: "Method not allowed" });
}
const { sdk, domain } = ctx;
const parsedPayload = LoginPayloadBodySchema.safeParse(req.body);
// 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");
if (!parsedPayload.success) {
return res.status(400).json({ error: "Invalid login payload" });
}
let token;
const payload = parsedPayload.data.payload;
const validateNonce = async (nonce: string) => {
if (ctx.authOptions?.validateNonce) {
await ctx.authOptions?.validateNonce(nonce);
}
};
const getSession = async (address: string) => {
if (ctx.callbacks?.onLogin) {
return ctx.callbacks.onLogin(address, req);
}
};
const expirationTime = ctx.authOptions?.tokenDurationInSeconds
? new Date(Date.now() + 1000 * ctx.authOptions.tokenDurationInSeconds)
: undefined;
const generateOptions: GenerateOptions = {
verifyOptions: {
statement: ctx.authOptions?.statement,
uri: ctx.authOptions?.uri,
version: ctx.authOptions?.version,
chainId: ctx.authOptions?.chainId,
validateNonce,
resources: ctx.authOptions?.resources,
},
expirationTime,
session: getSession,
};
let token: string;
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");
token = await ctx.auth.generate(payload, generateOptions);
} catch (err: any) {
if (err.message) {
return res.status(400).json({ error: err.message });
} else if (typeof err === "string") {
return res.status(400).json({ error: err });
} else {
return res.status(400).json({ error: "Invalid login payload" });
}
}
// Securely set httpOnly cookie on request to prevent XSS on frontend
@@ -40,12 +69,14 @@ export default async function handler(
res.setHeader(
"Set-Cookie",
serialize("thirdweb_auth_token", token, {
path: "/",
domain: ctx.cookieOptions?.domain,
path: ctx.cookieOptions?.path || "/",
sameSite: ctx.cookieOptions?.sameSite || "none",
httpOnly: true,
secure: true,
sameSite: "strict",
})
}),
);
return res.status(301).redirect(req.query.redirect as string);
// Send token in body and as cookie for frontend and backend use cases
return res.status(200).json({ token });
}
+21 -7
View File
@@ -1,21 +1,35 @@
import { getUser } from "../helpers/user";
import { ThirdwebAuthContext } from "../types";
import { serialize } from "cookie";
import { Request, Response } from "express";
export default async function handler(req: Request, res: Response) {
if (req.method !== "GET") {
return res.status(400).json({
error: "Invalid method. Only GET supported.",
export default async function handler(
req: Request,
res: Response,
ctx: ThirdwebAuthContext,
) {
if (req.method !== "POST") {
return res.status(405).json({
error: "Invalid method. Only POST supported.",
});
}
if (ctx.callbacks?.onLogout) {
const user = await getUser(req, ctx);
if (user) {
await ctx.callbacks.onLogout(user, req);
}
}
// Set the access token to 'none' and expire in 5 seconds
res.setHeader(
"Set-Cookie",
serialize("thirdweb_auth_token", "", {
path: "/",
domain: ctx.cookieOptions?.domain,
path: ctx.cookieOptions?.path || "/",
expires: new Date(Date.now() + 5 * 1000),
})
}),
);
return res.status(301).redirect(req.headers.referer as string);
return res.status(200).json({ message: "Succesfully logged out" });
}
+15 -2
View File
@@ -1,5 +1,18 @@
import { getUser } from "../helpers/user";
import { ThirdwebAuthContext } from "../types";
import { Request, Response } from "express";
export default async function handler(req: Request, res: Response) {
return res.status(200).json(req.user);
export default async function handler(
req: Request,
res: Response,
ctx: ThirdwebAuthContext,
) {
if (req.method !== "GET") {
return res.status(400).json({
error: "Invalid method. Only GET supported.",
});
}
const user = await getUser(req, ctx);
return res.status(200).json(user);
}
-12
View File
@@ -1,12 +0,0 @@
import { ThirdwebAuthUser } from "..";
export {};
declare global {
// eslint-disable-next-line @typescript-eslint/no-namespace
namespace Express {
export interface Request {
user: ThirdwebAuthUser | null;
}
}
}
+55 -15
View File
@@ -1,23 +1,63 @@
import { ThirdwebSDK } from "@thirdweb-dev/sdk";
import { ThirdwebAuth } from "../../core";
import { Json, LoginPayloadOutputSchema, User } from "../../core/schema";
import { GenericAuthWallet } from "@thirdweb-dev/wallets";
import { Request } from "express";
import { z } from "zod";
export const LoginPayloadBodySchema = z.object({
payload: LoginPayloadOutputSchema,
});
export type ThirdwebAuthRoute = "login" | "user" | "logout";
export type ThirdwebAuthConfig = {
privateKey: string;
export type ThirdwebAuthUser<
TData extends Json = Json,
TSession extends Json = Json,
> = User<TSession> & {
data?: TData;
};
export type ThirdwebAuthConfig<
TData extends Json = Json,
TSession extends Json = Json,
> = {
domain: string;
authUrl?: string;
wallet: GenericAuthWallet;
authOptions?: {
statement?: string;
uri?: string;
version?: string;
chainId?: string;
resources?: string[];
validateNonce?:
| ((nonce: string) => void)
| ((nonce: string) => Promise<void>);
validateTokenId?:
| ((tokenId: string) => void)
| ((tokenId: string) => Promise<void>);
tokenDurationInSeconds?: number;
};
cookieOptions?: {
domain?: string;
path?: string;
sameSite?: "lax" | "strict" | "none";
};
callbacks?: {
onLogin?:
| ((address: string, req?: Request) => void | TSession)
| ((address: string, req?: Request) => Promise<void | TSession>);
onUser?:
| ((user: User<TSession>, req?: Request) => void | TData)
| ((user: User<TSession>, req?: Request) => Promise<void | TData>);
onLogout?:
| ((user: User, req?: Request) => void)
| ((user: User, req?: Request) => Promise<void>);
};
};
export type ThirdwebAuthContext = {
sdk: ThirdwebSDK;
domain: string;
};
export type ThirdwebAuthUser = {
address: string;
};
export type RequestWithUser = Request & {
user: ThirdwebAuthUser | null;
export type ThirdwebAuthContext<
TData extends Json = Json,
TSession extends Json = Json,
> = Omit<Omit<ThirdwebAuthConfig<TData, TSession>, "wallet">, "domain"> & {
auth: ThirdwebAuth;
};
+1
View File
@@ -0,0 +1 @@
export * from "./core";
+145
View File
@@ -0,0 +1,145 @@
import { ThirdwebAuth } from "../core";
import { ThirdwebNextAuthConfig } from "./types";
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 auth = new ThirdwebAuth(cfg.wallet, cfg.domain);
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 auth.generate(parsed);
const user = await auth.authenticate(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: "none",
}),
);
return { id: user.address, address: user.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 auth.authenticate(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,
};
}
@@ -0,0 +1,8 @@
import { GenericAuthWallet } from "@thirdweb-dev/wallets";
import { NextAuthOptions } from "next-auth";
export type ThirdwebNextAuthConfig = {
domain: string;
wallet: GenericAuthWallet;
nextOptions: NextAuthOptions;
};
+65
View File
@@ -0,0 +1,65 @@
import { Json } from "../../core/schema";
import { ThirdwebAuthContext, ThirdwebAuthUser } from "../types";
import { GetServerSidePropsContext, NextApiRequest } from "next";
import { NextRequest } from "next/server";
function getToken(
req: GetServerSidePropsContext["req"] | NextRequest | NextApiRequest,
): string | undefined {
if (!!(req as NextApiRequest).headers["authorization"]) {
const authorizationHeader = (req as NextApiRequest).headers[
"authorization"
]?.split(" ");
if (authorizationHeader?.length === 2) {
return authorizationHeader[1];
}
return undefined;
}
const cookie: string | undefined = !req.cookies
? undefined
: typeof req.cookies.get === "function"
? (req.cookies as any).get("thirdweb_auth_token")
: (req.cookies as any).thirdweb_auth_token;
return cookie;
}
export async function getUser<
TData extends Json = Json,
TSession extends Json = Json,
>(
req: GetServerSidePropsContext["req"] | NextRequest | NextApiRequest,
ctx: ThirdwebAuthContext<TData, TSession>,
): Promise<ThirdwebAuthUser<TData, TSession> | null> {
const token = getToken(req);
if (!token) {
return null;
}
let authenticatedUser: ThirdwebAuthUser<TData, TSession>;
try {
authenticatedUser = await ctx.auth.authenticate<TSession>(token, {
validateTokenId: async (tokenId: string) => {
if (ctx.authOptions?.validateTokenId) {
await ctx.authOptions?.validateTokenId(tokenId);
}
},
});
} catch (err) {
return null;
}
if (!ctx.callbacks?.onUser) {
return authenticatedUser;
}
const data = await ctx.callbacks.onUser(authenticatedUser);
if (!data) {
return authenticatedUser;
}
return { ...authenticatedUser, data: data };
}
+19 -31
View File
@@ -1,3 +1,5 @@
import { Json, ThirdwebAuth as ThirdwebAuthSDK } from "../core";
import { getUser } from "./helpers/user";
import loginHandler from "./routes/login";
import logoutHandler from "./routes/logout";
import userHandler from "./routes/user";
@@ -5,9 +7,7 @@ import {
ThirdwebAuthConfig,
ThirdwebAuthContext,
ThirdwebAuthRoute,
ThirdwebAuthUser,
} from "./types";
import { ThirdwebSDK } from "@thirdweb-dev/sdk";
import { NextRequest } from "next/server";
import {
GetServerSidePropsContext,
@@ -20,7 +20,7 @@ export * from "./types";
async function ThirdwebAuthRouter(
req: NextApiRequest,
res: NextApiResponse,
ctx: ThirdwebAuthContext
ctx: ThirdwebAuthContext,
) {
// Catch-all route must be named with [...thirdweb]
const { thirdweb } = req.query;
@@ -32,7 +32,7 @@ async function ThirdwebAuthRouter(
case "user":
return await userHandler(req, res, ctx);
case "logout":
return await logoutHandler(req, res);
return await logoutHandler(req, res, ctx);
default:
return res.status(400).json({
message: "Invalid route for authentication.",
@@ -40,10 +40,13 @@ async function ThirdwebAuthRouter(
}
}
export function ThirdwebAuth(cfg: ThirdwebAuthConfig) {
export function ThirdwebAuth<
TData extends Json = Json,
TSession extends Json = Json,
>(cfg: ThirdwebAuthConfig<TData, TSession>) {
const ctx = {
...cfg,
sdk: ThirdwebSDK.fromPrivateKey(cfg.privateKey, "mainnet"),
auth: new ThirdwebAuthSDK(cfg.wallet, cfg.domain),
};
function ThirdwebAuthHandler(
@@ -51,33 +54,18 @@ export function ThirdwebAuth(cfg: ThirdwebAuthConfig) {
) {
if (args.length === 0) {
return async (req: NextApiRequest, res: NextApiResponse) =>
await ThirdwebAuthRouter(req, res, ctx);
await ThirdwebAuthRouter(req, res, ctx as ThirdwebAuthContext);
}
return ThirdwebAuthRouter(args[0], args[1], ctx);
return ThirdwebAuthRouter(args[0], args[1], ctx as ThirdwebAuthContext);
}
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 };
return {
ThirdwebAuthHandler,
getUser: (
req: GetServerSidePropsContext["req"] | NextRequest | NextApiRequest,
) => {
return getUser<TData, TSession>(req, ctx);
},
};
}
+55 -28
View File
@@ -1,42 +1,67 @@
import { ThirdwebAuthContext } from "../types";
import { LoginPayload } from "@thirdweb-dev/sdk/dist/src/schema";
import { GenerateOptions } from "../../core";
import { LoginPayloadBodySchema, ThirdwebAuthContext } from "../types";
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
ctx: ThirdwebAuthContext,
) {
if (req.method !== "GET") {
return redirectWithError(req, res, "INVALID_METHOD");
if (req.method !== "POST") {
return res.status(405).json({ error: "Method not allowed" });
}
const { sdk, domain } = ctx;
const parsedPayload = LoginPayloadBodySchema.safeParse(req.body);
// 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");
if (!parsedPayload.success) {
return res.status(400).json({ error: "Invalid login payload" });
}
let token;
const payload = parsedPayload.data.payload;
const validateNonce = async (nonce: string) => {
if (ctx.authOptions?.validateNonce) {
await ctx.authOptions?.validateNonce(nonce);
}
};
const getSession = async (address: string) => {
if (ctx.callbacks?.onLogin) {
return ctx.callbacks.onLogin(address, req);
}
};
const expirationTime = ctx.authOptions?.tokenDurationInSeconds
? new Date(Date.now() + 1000 * ctx.authOptions.tokenDurationInSeconds)
: undefined;
const generateOptions: GenerateOptions = {
verifyOptions: {
statement: ctx.authOptions?.statement,
uri: ctx.authOptions?.uri,
version: ctx.authOptions?.version,
chainId: ctx.authOptions?.chainId,
validateNonce,
resources: ctx.authOptions?.resources,
},
expirationTime,
session: getSession,
};
let token: string;
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");
token = await ctx.auth.generate(payload, generateOptions);
} catch (err: any) {
if (err.message) {
return res.status(400).json({ error: err.message });
} else if (typeof err === "string") {
return res.status(400).json({ error: err });
} else {
return res.status(400).json({ error: "Invalid login payload" });
}
}
// Securely set httpOnly cookie on request to prevent XSS on frontend
@@ -44,12 +69,14 @@ export default async function handler(
res.setHeader(
"Set-Cookie",
serialize("thirdweb_auth_token", token, {
path: "/",
domain: ctx.cookieOptions?.domain,
path: ctx.cookieOptions?.path || "/",
sameSite: ctx.cookieOptions?.sameSite || "none",
httpOnly: true,
secure: true,
sameSite: "strict",
})
}),
);
return res.status(301).redirect(req.query.redirect as string);
// Send token in body and as cookie for frontend and backend use cases
return res.status(200).json({ token });
}
+18 -7
View File
@@ -1,24 +1,35 @@
import { getUser } from "../helpers/user";
import { ThirdwebAuthContext } from "../types";
import { serialize } from "cookie";
import { NextApiRequest, NextApiResponse } from "next";
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
res: NextApiResponse,
ctx: ThirdwebAuthContext,
) {
if (req.method !== "GET") {
return res.status(400).json({
error: "Invalid method. Only GET supported.",
if (req.method !== "POST") {
return res.status(405).json({
error: "Invalid method. Only POST supported.",
});
}
if (ctx.callbacks?.onLogout) {
const user = await getUser(req, ctx);
if (user) {
await ctx.callbacks.onLogout(user, req);
}
}
// Set the access token to 'none' and expire in 5 seconds
res.setHeader(
"Set-Cookie",
serialize("thirdweb_auth_token", "", {
path: "/",
domain: ctx.cookieOptions?.domain,
path: ctx.cookieOptions?.path || "/",
expires: new Date(Date.now() + 5 * 1000),
})
}),
);
return res.status(301).redirect(req.headers.referer as string);
return res.status(200).json({ message: "Succesfully logged out" });
}
+7 -12
View File
@@ -1,23 +1,18 @@
import { getUser } from "../helpers/user";
import { ThirdwebAuthContext } from "../types";
import { NextApiRequest, NextApiResponse } from "next";
export default async function handler(
req: NextApiRequest,
res: NextApiResponse,
ctx: ThirdwebAuthContext
ctx: ThirdwebAuthContext,
) {
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);
user = { address };
} catch {
// No-op
}
if (req.method !== "GET") {
return res.status(400).json({
error: "Invalid method. Only GET supported.",
});
}
const user = await getUser(req, ctx);
return res.status(200).json(user);
}
+66 -8
View File
@@ -1,17 +1,75 @@
import { ThirdwebSDK } from "@thirdweb-dev/sdk";
import { ThirdwebAuth } from "../../core";
import { Json, LoginPayloadOutputSchema, User } from "../../core/schema";
import { GenericAuthWallet } from "@thirdweb-dev/wallets";
import { GetServerSidePropsContext, NextApiRequest } from "next";
import { NextRequest } from "next/server";
import { z } from "zod";
export const LoginPayloadBodySchema = z.object({
payload: LoginPayloadOutputSchema,
});
type RequestType =
| GetServerSidePropsContext["req"]
| NextRequest
| NextApiRequest;
export type ThirdwebAuthRoute = "login" | "logout" | "user";
export type ThirdwebAuthConfig = {
privateKey: string;
domain: string;
export type ThirdwebAuthUser<
TData extends Json = Json,
TSession extends Json = Json,
> = User<TSession> & {
data?: TData;
};
export type ThirdwebAuthContext = {
sdk: ThirdwebSDK;
export type ThirdwebAuthConfig<
TData extends Json = Json,
TSession extends Json = Json,
> = {
domain: string;
wallet: GenericAuthWallet;
authOptions?: {
statement?: string;
uri?: string;
version?: string;
chainId?: string;
resources?: string[];
validateNonce?:
| ((nonce: string) => void)
| ((nonce: string) => Promise<void>);
validateTokenId?:
| ((tokenId: string) => void)
| ((tokenId: string) => Promise<void>);
tokenDurationInSeconds?: number;
};
cookieOptions?: {
domain?: string;
path?: string;
sameSite?: "lax" | "strict" | "none";
};
callbacks?: {
onLogin?:
| ((address: string, req?: NextApiRequest) => void | TSession)
| ((address: string, req?: NextApiRequest) => Promise<void | TSession>);
onUser?:
| (<TRequestType extends RequestType = RequestType>(
user: User<TSession>,
req?: TRequestType,
) => void | TData)
| (<TRequestType extends RequestType = RequestType>(
user: User<TSession>,
req?: TRequestType,
) => Promise<void | TData>);
onLogout?:
| ((user: User, req?: NextApiRequest) => void)
| ((user: User, req?: NextApiRequest) => Promise<void>);
};
};
export type ThirdwebAuthUser = {
address: string;
export type ThirdwebAuthContext<
TData extends Json = Json,
TSession extends Json = Json,
> = Omit<Omit<ThirdwebAuthConfig<TData, TSession>, "wallet">, "domain"> & {
auth: ThirdwebAuth;
};
+69
View File
@@ -0,0 +1,69 @@
import * as ed25519 from "@noble/ed25519";
import { Signer, Keypair, PublicKey } from "@solana/web3.js";
import type { Ecosystem, GenericAuthWallet } from "@thirdweb-dev/wallets";
import bs58 from "bs58";
import nacl from "tweetnacl";
export interface SolanaSigner {
publicKey: PublicKey;
signMessage(message: Uint8Array): Promise<Uint8Array>;
}
export class SignerWallet implements GenericAuthWallet {
type: Ecosystem = "solana";
private signer: SolanaSigner;
constructor(signer: SolanaSigner) {
this.signer = signer;
}
public async getAddress(): Promise<string> {
return this.signer.publicKey.toBase58();
}
public async signMessage(message: string): Promise<string> {
const encodedMessage = new TextEncoder().encode(message);
const signedMessage = await this.signer.signMessage(encodedMessage);
const signature = bs58.encode(signedMessage);
return signature;
}
public async verifySignature(
message: string,
signature: string,
address: string,
): Promise<boolean> {
return nacl.sign.detached.verify(
new TextEncoder().encode(message),
bs58.decode(signature),
bs58.decode(address),
);
}
}
class KeypairSigner implements SolanaSigner {
private keypair: Signer;
public publicKey: PublicKey;
constructor(keypair: Signer) {
this.keypair = keypair;
this.publicKey = keypair.publicKey;
}
public async signMessage(message: Uint8Array): Promise<Uint8Array> {
return ed25519.sync.sign(message, this.keypair.secretKey.slice(0, 32));
}
}
export class KeypairWallet extends SignerWallet {
constructor(keypair: Keypair) {
super(new KeypairSigner(keypair));
}
}
export class PrivateKeyWallet extends KeypairWallet {
constructor(privateKey: string) {
super(Keypair.fromSecretKey(bs58.decode(privateKey)));
}
}
+8
View File
@@ -0,0 +1,8 @@
{
"extension": [
"ts"
],
"require": [
"@swc-node/register"
]
}
+353
View File
@@ -0,0 +1,353 @@
import { ThirdwebAuth } from "../src/core";
import { SignerWallet } from "../src/evm";
import { expect } from "chai";
import { Wallet } from "ethers";
describe("Wallet Authentication", async () => {
let adminWallet: any, signerWallet: any, attackerWallet: any;
let auth: ThirdwebAuth;
before(async () => {
const [adminSigner, signerSigner, attackerSigner] = [
Wallet.createRandom(),
Wallet.createRandom(),
Wallet.createRandom(),
];
adminWallet = new SignerWallet(adminSigner);
signerWallet = new SignerWallet(signerSigner);
attackerWallet = new SignerWallet(attackerSigner);
auth = new ThirdwebAuth(signerWallet, "thirdweb.com");
});
beforeEach(async () => {
auth.updateWallet(signerWallet);
});
it("Should verify logged in wallet", async () => {
const payload = await auth.login();
auth.updateWallet(adminWallet);
const address = await auth.verify(payload);
expect(address).to.equal(await signerWallet.getAddress());
});
it("Should verify logged in wallet with chain ID and expiration", async () => {
const payload = await auth.login({
expirationTime: new Date(Date.now() + 1000 * 60 * 5),
chainId: "137",
});
auth.updateWallet(adminWallet);
const address = await auth.verify(payload, {
chainId: "137",
});
expect(address).to.equal(await signerWallet.getAddress());
});
it("Should verify payload with resources", async () => {
const payload = await auth.login({
resources: ["https://example.com", "https://test.com"],
});
auth.updateWallet(adminWallet);
const address = await auth.verify(payload, {
resources: ["https://example.com", "https://test.com"],
});
expect(address).to.equal(await signerWallet.getAddress());
});
it("Should reject payload without necessary resources", async () => {
const payload = await auth.login({
resources: ["https://example.com"],
});
auth.updateWallet(adminWallet);
try {
await auth.verify(payload, {
resources: ["https://example.com", "https://test.com"],
});
expect.fail();
} catch (err: any) {
expect(err.message).to.equal(
"Login request is missing required resources: https://test.com",
);
}
});
it("Should verify payload with customized statement", async () => {
const payload = await auth.login({
statement: "Please sign!",
});
auth.updateWallet(adminWallet);
const address = await auth.verify(payload, {
statement: "Please sign!",
});
expect(address).to.equal(await signerWallet.getAddress());
});
it("Should reject payload with incorrect statement", async () => {
const payload = await auth.login({
statement: "Please sign!",
});
auth.updateWallet(adminWallet);
try {
await auth.verify(payload, {
statement: "Please sign again!",
});
expect.fail();
} catch (err: any) {
expect(err.message).to.include(
"Expected statement 'Please sign again!' does not match statement on payload",
);
}
});
it("Should reject invalid nonce", async () => {
const payload = await auth.login();
auth.updateWallet(adminWallet);
try {
await auth.verify(payload, {
validateNonce: (nonce: string) => {
if (nonce === payload.payload.nonce) {
throw new Error();
}
},
});
expect.fail();
} catch (err: any) {
expect(err.message).to.equal("Login request nonce is invalid");
}
});
it("Should accept valid nonce", async () => {
const payload = await auth.login();
auth.updateWallet(adminWallet);
const address = await auth.verify(payload, {
validateNonce: (nonce: string) => {
if (nonce !== payload.payload.nonce) {
throw new Error();
}
},
});
expect(address).to.equal(await signerWallet.getAddress());
});
it("Should reject payload with incorrect domain", async () => {
const payload = await auth.login();
auth.updateWallet(adminWallet);
try {
await auth.verify(payload, { domain: "test.thirdweb.com" });
expect.fail();
} catch (err: any) {
expect(err.message).to.equal(
"Expected domain 'test.thirdweb.com' does not match domain on payload 'thirdweb.com'",
);
}
});
it("Should reject expired login payload", async () => {
const payload = await auth.login({
expirationTime: new Date(Date.now() - 1000 * 60 * 5),
});
auth.updateWallet(adminWallet);
try {
await auth.verify(payload);
expect.fail();
} catch (err: any) {
expect(err.message).to.equal("Login request has expired");
}
});
it("Should reject payload with incorrect chain ID", async () => {
const payload = await auth.login({
chainId: "1",
});
auth.updateWallet(adminWallet);
try {
await auth.verify(payload, {
chainId: "137",
});
expect.fail();
} catch (err: any) {
expect(err.message).to.equal(
"Expected chain ID '137' does not match chain ID on payload '1'",
);
}
});
it("Should reject payload with incorrect signer", async () => {
const payload = await auth.login();
payload.payload.address = await attackerWallet.getAddress();
auth.updateWallet(adminWallet);
try {
await auth.verify(payload);
expect.fail();
} catch (err: any) {
expect(err.message).to.contain("does not match payload address");
}
});
it("Should generate valid authentication token", async () => {
const payload = await auth.login();
auth.updateWallet(adminWallet);
const token = await auth.generate(payload);
const user = await auth.authenticate(token);
expect(user.address).to.equal(await signerWallet.getAddress());
});
it("Should reject token with incorrect domain", async () => {
const payload = await auth.login();
auth.updateWallet(adminWallet);
const token = await auth.generate(payload);
try {
await auth.authenticate(token, { domain: "test.thirdweb.com" });
expect.fail();
} catch (err: any) {
expect(err.message).to.contain(
"Expected token to be for the domain 'test.thirdweb.com', but found token with domain 'thirdweb.com'",
);
}
});
it("Should reject token before invalid before", async () => {
const payload = await auth.login();
auth.updateWallet(adminWallet);
const token = await auth.generate(payload, {
invalidBefore: new Date(Date.now() + 1000 * 60 * 5),
});
try {
await auth.authenticate(token);
expect.fail();
} catch (err: any) {
expect(err.message).to.contain("This token is invalid before");
}
});
it("Should reject expired authentication token", async () => {
const payload = await auth.login();
auth.updateWallet(adminWallet);
const token = await auth.generate(payload, {
expirationTime: new Date(Date.now() - 1000 * 60 * 5),
});
try {
await auth.authenticate(token);
expect.fail();
} catch (err: any) {
expect(err.message).to.contain("This token expired");
}
});
it("Should reject if admin address is not connected wallet address", async () => {
const payload = await auth.login();
auth.updateWallet(adminWallet);
const token = await auth.generate(payload);
auth.updateWallet(signerWallet);
try {
await auth.authenticate(token);
expect.fail();
} catch (err: any) {
expect(err.message).to.contain(
`Expected the connected wallet address '${await signerWallet.getAddress()}' to match the token issuer address '${await adminWallet.getAddress()}'`,
);
}
});
it("Should accept token with valid token ID", async () => {
const payload = await auth.login();
auth.updateWallet(adminWallet);
const token = await auth.generate(payload, {
tokenId: "test",
});
const user = await auth.authenticate(token, {
validateTokenId: (tokenId: string) => {
if (tokenId !== "test") {
throw new Error();
}
},
});
expect(user.address).to.equal(await signerWallet.getAddress());
});
it("Should reject token with invalid token ID", async () => {
const payload = await auth.login();
auth.updateWallet(adminWallet);
const token = await auth.generate(payload, {
tokenId: "test",
});
try {
await auth.authenticate(token, {
validateTokenId: (tokenId: string) => {
if (tokenId !== "invalid") {
throw new Error();
}
},
});
expect.fail();
} catch (err: any) {
expect(err.message).to.contain("Token ID is invalid");
}
});
it("Should propagate session on token", async () => {
const payload = await auth.login();
auth.updateWallet(adminWallet);
const token = await auth.generate(payload, {
session: { role: "admin" },
});
const user = await auth.authenticate(token);
expect(user.address).to.equal(await signerWallet.getAddress());
expect(user.session).to.deep.equal({ role: "admin" });
});
it("Should call session callback function", async () => {
const payload = await auth.login();
auth.updateWallet(adminWallet);
const token = await auth.generate(payload, {
session: (address: string) => {
return { address, role: "admin" };
},
});
const user = await auth.authenticate(token);
expect(user.address).to.equal(await signerWallet.getAddress());
expect(user.session).to.deep.equal({
address: await signerWallet.getAddress(),
role: "admin",
});
});
});
+353
View File
@@ -0,0 +1,353 @@
import { ThirdwebAuth } from "../src/core";
import { KeypairWallet } from "../src/solana";
import { Keypair } from "@solana/web3.js";
import { expect } from "chai";
describe("Wallet Authentication", async () => {
let adminWallet: any, signerWallet: any, attackerWallet: any;
let auth: ThirdwebAuth;
before(async () => {
const [adminSigner, signerSigner, attackerSigner] = [
Keypair.generate(),
Keypair.generate(),
Keypair.generate(),
];
adminWallet = new KeypairWallet(adminSigner);
signerWallet = new KeypairWallet(signerSigner);
attackerWallet = new KeypairWallet(attackerSigner);
auth = new ThirdwebAuth(signerWallet, "thirdweb.com");
});
beforeEach(async () => {
auth.updateWallet(signerWallet);
});
it("Should verify logged in wallet", async () => {
const payload = await auth.login();
auth.updateWallet(adminWallet);
const address = await auth.verify(payload);
expect(address).to.equal(await signerWallet.getAddress());
});
it("Should verify logged in wallet with chain ID and expiration", async () => {
const payload = await auth.login({
expirationTime: new Date(Date.now() + 1000 * 60 * 5),
chainId: "137",
});
auth.updateWallet(adminWallet);
const address = await auth.verify(payload, {
chainId: "137",
});
expect(address).to.equal(await signerWallet.getAddress());
});
it("Should verify payload with resources", async () => {
const payload = await auth.login({
resources: ["https://example.com", "https://test.com"],
});
auth.updateWallet(adminWallet);
const address = await auth.verify(payload, {
resources: ["https://example.com", "https://test.com"],
});
expect(address).to.equal(await signerWallet.getAddress());
});
it("Should reject payload without necessary resources", async () => {
const payload = await auth.login({
resources: ["https://example.com"],
});
auth.updateWallet(adminWallet);
try {
await auth.verify(payload, {
resources: ["https://example.com", "https://test.com"],
});
expect.fail();
} catch (err: any) {
expect(err.message).to.equal(
"Login request is missing required resources: https://test.com",
);
}
});
it("Should verify payload with customized statement", async () => {
const payload = await auth.login({
statement: "Please sign!",
});
auth.updateWallet(adminWallet);
const address = await auth.verify(payload, {
statement: "Please sign!",
});
expect(address).to.equal(await signerWallet.getAddress());
});
it("Should reject payload with incorrect statement", async () => {
const payload = await auth.login({
statement: "Please sign!",
});
auth.updateWallet(adminWallet);
try {
await auth.verify(payload, {
statement: "Please sign again!",
});
expect.fail();
} catch (err: any) {
expect(err.message).to.include(
"Expected statement 'Please sign again!' does not match statement on payload",
);
}
});
it("Should reject invalid nonce", async () => {
const payload = await auth.login();
auth.updateWallet(adminWallet);
try {
await auth.verify(payload, {
validateNonce: (nonce: string) => {
if (nonce === payload.payload.nonce) {
throw new Error();
}
},
});
expect.fail();
} catch (err: any) {
expect(err.message).to.equal("Login request nonce is invalid");
}
});
it("Should accept valid nonce", async () => {
const payload = await auth.login();
auth.updateWallet(adminWallet);
const address = await auth.verify(payload, {
validateNonce: (nonce: string) => {
if (nonce !== payload.payload.nonce) {
throw new Error();
}
},
});
expect(address).to.equal(await signerWallet.getAddress());
});
it("Should reject payload with incorrect domain", async () => {
const payload = await auth.login();
auth.updateWallet(adminWallet);
try {
await auth.verify(payload, { domain: "test.thirdweb.com" });
expect.fail();
} catch (err: any) {
expect(err.message).to.equal(
"Expected domain 'test.thirdweb.com' does not match domain on payload 'thirdweb.com'",
);
}
});
it("Should reject expired login payload", async () => {
const payload = await auth.login({
expirationTime: new Date(Date.now() - 1000 * 60 * 5),
});
auth.updateWallet(adminWallet);
try {
await auth.verify(payload);
expect.fail();
} catch (err: any) {
expect(err.message).to.equal("Login request has expired");
}
});
it("Should reject payload with incorrect chain ID", async () => {
const payload = await auth.login({
chainId: "1",
});
auth.updateWallet(adminWallet);
try {
await auth.verify(payload, {
chainId: "137",
});
expect.fail();
} catch (err: any) {
expect(err.message).to.equal(
"Expected chain ID '137' does not match chain ID on payload '1'",
);
}
});
it("Should reject payload with incorrect signer", async () => {
const payload = await auth.login();
payload.payload.address = await attackerWallet.getAddress();
auth.updateWallet(adminWallet);
try {
await auth.verify(payload);
expect.fail();
} catch (err: any) {
expect(err.message).to.contain("does not match payload address");
}
});
it("Should generate valid authentication token", async () => {
const payload = await auth.login();
auth.updateWallet(adminWallet);
const token = await auth.generate(payload);
const user = await auth.authenticate(token);
expect(user.address).to.equal(await signerWallet.getAddress());
});
it("Should reject token with incorrect domain", async () => {
const payload = await auth.login();
auth.updateWallet(adminWallet);
const token = await auth.generate(payload);
try {
await auth.authenticate(token, { domain: "test.thirdweb.com" });
expect.fail();
} catch (err: any) {
expect(err.message).to.contain(
"Expected token to be for the domain 'test.thirdweb.com', but found token with domain 'thirdweb.com'",
);
}
});
it("Should reject token before invalid before", async () => {
const payload = await auth.login();
auth.updateWallet(adminWallet);
const token = await auth.generate(payload, {
invalidBefore: new Date(Date.now() + 1000 * 60 * 5),
});
try {
await auth.authenticate(token);
expect.fail();
} catch (err: any) {
expect(err.message).to.contain("This token is invalid before");
}
});
it("Should reject expired authentication token", async () => {
const payload = await auth.login();
auth.updateWallet(adminWallet);
const token = await auth.generate(payload, {
expirationTime: new Date(Date.now() - 1000 * 60 * 5),
});
try {
await auth.authenticate(token);
expect.fail();
} catch (err: any) {
expect(err.message).to.contain("This token expired");
}
});
it("Should reject if admin address is not connected wallet address", async () => {
const payload = await auth.login();
auth.updateWallet(adminWallet);
const token = await auth.generate(payload);
auth.updateWallet(signerWallet);
try {
await auth.authenticate(token);
expect.fail();
} catch (err: any) {
expect(err.message).to.contain(
`Expected the connected wallet address '${await signerWallet.getAddress()}' to match the token issuer address '${await adminWallet.getAddress()}'`,
);
}
});
it("Should accept token with valid token ID", async () => {
const payload = await auth.login();
auth.updateWallet(adminWallet);
const token = await auth.generate(payload, {
tokenId: "test",
});
const user = await auth.authenticate(token, {
validateTokenId: (tokenId: string) => {
if (tokenId !== "test") {
throw new Error();
}
},
});
expect(user.address).to.equal(await signerWallet.getAddress());
});
it("Should reject token with invalid token ID", async () => {
const payload = await auth.login();
auth.updateWallet(adminWallet);
const token = await auth.generate(payload, {
tokenId: "test",
});
try {
await auth.authenticate(token, {
validateTokenId: (tokenId: string) => {
if (tokenId !== "invalid") {
throw new Error();
}
},
});
expect.fail();
} catch (err: any) {
expect(err.message).to.contain("Token ID is invalid");
}
});
it("Should propagate session on token", async () => {
const payload = await auth.login();
auth.updateWallet(adminWallet);
const token = await auth.generate(payload, {
session: { role: "admin" },
});
const user = await auth.authenticate(token);
expect(user.address).to.equal(await signerWallet.getAddress());
expect(user.session).to.deep.equal({ role: "admin" });
});
it("Should call session callback function", async () => {
const payload = await auth.login();
auth.updateWallet(adminWallet);
const token = await auth.generate(payload, {
session: (address: string) => {
return { address, role: "admin" };
},
});
const user = await auth.authenticate(token);
expect(user.address).to.equal(await signerWallet.getAddress());
expect(user.session).to.deep.equal({
address: await signerWallet.getAddress(),
role: "admin",
});
});
});
+1 -1
View File
@@ -1,5 +1,5 @@
{
"extends": "@thirdweb-dev/tsconfig/base.json",
"extends": "@thirdweb-dev/tsconfig/sdk.json",
"include": ["."],
"exclude": ["dist", "build", "node_modules"]
}
+454
View File
@@ -1,5 +1,459 @@
# thirdweb
## 0.9.13
### Patch Changes
- Updated dependencies [[`19b8e18`](https://github.com/thirdweb-dev/js/commit/19b8e18b6bf68e374c817fefbf2fd29ac5573052)]:
- @thirdweb-dev/sdk@3.7.2
## 0.9.12
### Patch Changes
- Updated dependencies []:
- @thirdweb-dev/sdk@3.7.1
## 0.9.11
### Patch Changes
- [#517](https://github.com/thirdweb-dev/js/pull/517) [`9e591b3`](https://github.com/thirdweb-dev/js/commit/9e591b32b78f1300b2014d44d591d297249a1f84) Thanks [@avneesh0612](https://github.com/avneesh0612)! - add express, node.js, and react native templates
- Updated dependencies [[`f37e86b`](https://github.com/thirdweb-dev/js/commit/f37e86b7d21f7da0df6ab549cb903dc99db10a79), [`f15733f`](https://github.com/thirdweb-dev/js/commit/f15733f34c5985eec5593b3d9196e5528ba96443)]:
- @thirdweb-dev/sdk@3.7.0
## 0.9.10
### Patch Changes
- Updated dependencies [[`7cab8d1`](https://github.com/thirdweb-dev/js/commit/7cab8d1679f8d007091aa03adb83add3822a504a)]:
- @thirdweb-dev/sdk@3.6.11
## 0.9.9
### Patch Changes
- [#483](https://github.com/thirdweb-dev/js/pull/483) [`4e8ff58`](https://github.com/thirdweb-dev/js/commit/4e8ff582084c3d201314e6c5dff5b79f15eb6292) Thanks [@jnsdls](https://github.com/jnsdls)! - `thirdweb create` with usage of `forge` should now function correctly in windows CMD shell
- Updated dependencies [[`5fba324`](https://github.com/thirdweb-dev/js/commit/5fba324ac17ab02bc8f13d82a232bc5c6970c8e5), [`894cbef`](https://github.com/thirdweb-dev/js/commit/894cbefc6361f23fc528a6c6819f5c71793d46e4), [`557429b`](https://github.com/thirdweb-dev/js/commit/557429b5cfb3af2983ee01cf7d12d41ee0557593), [`73dc026`](https://github.com/thirdweb-dev/js/commit/73dc026ff9d0ac9099bd5a9a9cab8fdbfc0ae723), [`9eaa21d`](https://github.com/thirdweb-dev/js/commit/9eaa21d09ab9c700aea61a2a25f8ca9859d20857), [`68c8e3c`](https://github.com/thirdweb-dev/js/commit/68c8e3c4d79f5d56dc4414241bcca0d88285fcca), [`bff433e`](https://github.com/thirdweb-dev/js/commit/bff433e12150dc029e33a578219c8437a510da99)]:
- @thirdweb-dev/sdk@3.6.10
## 0.9.8
### Patch Changes
- Updated dependencies [[`e913e0d`](https://github.com/thirdweb-dev/js/commit/e913e0daa9ece9a6274f7ffa4e66bdcbf32e6ada), [`5862c55`](https://github.com/thirdweb-dev/js/commit/5862c558fb48604b5aca4defd1ccc06fc3536358), [`73883f5`](https://github.com/thirdweb-dev/js/commit/73883f56d0ed0a35ace8b98b96caa782443be22e), [`f7a74a3`](https://github.com/thirdweb-dev/js/commit/f7a74a3b7c489ada5b1ec435632a326334a33c9b)]:
- @thirdweb-dev/sdk@3.6.9
## 0.9.7
### Patch Changes
- [#454](https://github.com/thirdweb-dev/js/pull/454) [`c673e39`](https://github.com/thirdweb-dev/js/commit/c673e39f23ef082097d73d62910580e8fad400a0) Thanks [@jnsdls](https://github.com/jnsdls)! - upgraded dependencies
- [#461](https://github.com/thirdweb-dev/js/pull/461) [`af85389`](https://github.com/thirdweb-dev/js/commit/af85389dc07fb163b8f9d70093eb1b691e47561a) Thanks [@nachoiacovino](https://github.com/nachoiacovino)! - Fix deprecated links
- Updated dependencies [[`c673e39`](https://github.com/thirdweb-dev/js/commit/c673e39f23ef082097d73d62910580e8fad400a0), [`ea95c5f`](https://github.com/thirdweb-dev/js/commit/ea95c5f609e306e333ee0f73f7920503358ca848), [`699a2b1`](https://github.com/thirdweb-dev/js/commit/699a2b16fb991c474ec57db8f178e2601d631f39), [`4cdd0bd`](https://github.com/thirdweb-dev/js/commit/4cdd0bd6348494a256d7c6a2bdf8f7b5c20f6877), [`a8267f9`](https://github.com/thirdweb-dev/js/commit/a8267f912df84c58d3fe3f47b90bd474f73c84ca)]:
- @thirdweb-dev/sdk@3.6.8
## 0.9.6
### Patch Changes
- [#441](https://github.com/thirdweb-dev/js/pull/441) [`64f24f1`](https://github.com/thirdweb-dev/js/commit/64f24f1bb167c3d0cb3beca51fd300eaec21cc27) Thanks [@adam-maj](https://github.com/adam-maj)! - CLI autoupdates on run
- [#447](https://github.com/thirdweb-dev/js/pull/447) [`e7740a5`](https://github.com/thirdweb-dev/js/commit/e7740a59db022a3396194ac66cd195d8ae754d57) Thanks [@adam-maj](https://github.com/adam-maj)! - Detect whether forge is installed
- Updated dependencies [[`3731459`](https://github.com/thirdweb-dev/js/commit/3731459d33f1ded7ebb69124809449b901b3ad3d), [`cac6c30`](https://github.com/thirdweb-dev/js/commit/cac6c30bca5e17df81d746ef81316af47d5e252e), [`7a37e56`](https://github.com/thirdweb-dev/js/commit/7a37e564fd5d5a9df84c8da44ecaf6c42f67a0e2)]:
- @thirdweb-dev/sdk@3.6.7
## 0.9.5
### Patch Changes
- Updated dependencies [[`8c6cdaa`](https://github.com/thirdweb-dev/js/commit/8c6cdaa2887fb2cc40d3ee6991233d195d103805)]:
- @thirdweb-dev/sdk@3.6.6
## 0.9.4
### Patch Changes
- [#444](https://github.com/thirdweb-dev/js/pull/444) [`94f49b1`](https://github.com/thirdweb-dev/js/commit/94f49b1294bc0208171b16ed413cbb44644af017) Thanks [@adam-maj](https://github.com/adam-maj)! - Add changeset
## 0.9.3
### Patch Changes
- Updated dependencies [[`efc56fa`](https://github.com/thirdweb-dev/js/commit/efc56fa5802490ac8ef50037658d046afd89e9a1)]:
- @thirdweb-dev/sdk@3.6.5
## 0.9.2
### Patch Changes
- [#430](https://github.com/thirdweb-dev/js/pull/430) [`e684d7e`](https://github.com/thirdweb-dev/js/commit/e684d7e1ca16f633bce8698529d16bf60c264aa4) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Fix uploading contracts with special characters in the comments
- [#435](https://github.com/thirdweb-dev/js/pull/435) [`bef97f7`](https://github.com/thirdweb-dev/js/commit/bef97f72f4860596b7b70b05e67fcadf2c9a012e) Thanks [@adam-maj](https://github.com/adam-maj)! - Make solc default backup compilation
- [#432](https://github.com/thirdweb-dev/js/pull/432) [`3e87dd3`](https://github.com/thirdweb-dev/js/commit/3e87dd3ccb04a2b73437357c939a251e83b783b2) Thanks [@adam-maj](https://github.com/adam-maj)! - Display file name on duplicate contract names
- [#431](https://github.com/thirdweb-dev/js/pull/431) [`c42545a`](https://github.com/thirdweb-dev/js/commit/c42545a64725492136025fa1dcdb4d46ba674a30) Thanks [@adam-maj](https://github.com/adam-maj)! - Add changeset
- [#428](https://github.com/thirdweb-dev/js/pull/428) [`6e20d1f`](https://github.com/thirdweb-dev/js/commit/6e20d1f21a622f102e94b956f700d0871c0dc779) Thanks [@adam-maj](https://github.com/adam-maj)! - Update CLI to enable create contract inside contracts project
- Updated dependencies [[`f451da6`](https://github.com/thirdweb-dev/js/commit/f451da6395689a5f89800ee63f34b6175b61f703), [`af3acc6`](https://github.com/thirdweb-dev/js/commit/af3acc6b10751b840e56aef6400da5eea6040df2), [`1bfb91d`](https://github.com/thirdweb-dev/js/commit/1bfb91dbf3a39160c987a425813ac5dbb84703ad), [`ee47840`](https://github.com/thirdweb-dev/js/commit/ee478407673b0416e0c1cfe2be11bd6963395348), [`def4251`](https://github.com/thirdweb-dev/js/commit/def42511ff5a20d83f9094164dafb87e412571b5), [`034a257`](https://github.com/thirdweb-dev/js/commit/034a257442314c67729f7bafcbe3740cff33fa32), [`eca4776`](https://github.com/thirdweb-dev/js/commit/eca47763cd89cc3b7aa57b542971837987540b55), [`639e535`](https://github.com/thirdweb-dev/js/commit/639e535ed55280ad9d081001aab3f5af72bb3e45)]:
- @thirdweb-dev/sdk@3.6.4
## 0.9.1
### Patch Changes
- [#415](https://github.com/thirdweb-dev/js/pull/415) [`48d91e4`](https://github.com/thirdweb-dev/js/commit/48d91e4431a3740d0f98f730ed5abf5b287f92c1) Thanks [@nachoiacovino](https://github.com/nachoiacovino)! - Fix mappings for prebuilts redirect on deploy
## 0.9.0
### Minor Changes
- [#403](https://github.com/thirdweb-dev/js/pull/403) [`27996cb`](https://github.com/thirdweb-dev/js/commit/27996cb4bc6f054f45b0a66b0a3aca732e0ef610) Thanks [@avneesh0612](https://github.com/avneesh0612)! - Add CLI Support for solana with --solana flag on create command
- [#283](https://github.com/thirdweb-dev/js/pull/283) [`2eaa67a`](https://github.com/thirdweb-dev/js/commit/2eaa67acbe21464e7715d0b696afdc024460a44d) Thanks [@adam-maj](https://github.com/adam-maj)! - add capability to deploy apps
### Patch Changes
- [#413](https://github.com/thirdweb-dev/js/pull/413) [`1b0bf99`](https://github.com/thirdweb-dev/js/commit/1b0bf99da796df88ff307dd004cdaf239fb2b1ba) Thanks [@furqanrydhan](https://github.com/furqanrydhan)! - small interface tweaks
- Updated dependencies [[`48797c7`](https://github.com/thirdweb-dev/js/commit/48797c7cf0695965e33eb6bf602be4652f7085a8), [`83dcaf7`](https://github.com/thirdweb-dev/js/commit/83dcaf70584af8c5cdf0427f9fc1ba82d89f0887), [`f03be39`](https://github.com/thirdweb-dev/js/commit/f03be398e0d7b75c9c5b4fb643f35903e89a17e7), [`2eaa67a`](https://github.com/thirdweb-dev/js/commit/2eaa67acbe21464e7715d0b696afdc024460a44d), [`47f8945`](https://github.com/thirdweb-dev/js/commit/47f8945325447874b1ace0359919c1d54fd55436)]:
- @thirdweb-dev/sdk@3.6.3
## 0.8.18
### Patch Changes
- Updated dependencies [[`ccb7db4`](https://github.com/thirdweb-dev/js/commit/ccb7db48739b8dddcb2c032b3b6e3e5200485715), [`9151aa7`](https://github.com/thirdweb-dev/js/commit/9151aa7dc80754a7aa4fd3f6ce0207bccbb6451f), [`5a01c53`](https://github.com/thirdweb-dev/js/commit/5a01c537e30bd7ef562aad1012413990de6faedf)]:
- @thirdweb-dev/sdk@3.6.2
## 0.8.17
### Patch Changes
- Updated dependencies [[`15514f1`](https://github.com/thirdweb-dev/js/commit/15514f190a844d809ae5b1edb580aab46a682485), [`7c0744a`](https://github.com/thirdweb-dev/js/commit/7c0744a006e78987ad8b271b2c64f4bac7759510), [`f8bceec`](https://github.com/thirdweb-dev/js/commit/f8bceec10c60b30dc3d6f45926e2a9e018202578), [`887af24`](https://github.com/thirdweb-dev/js/commit/887af24450e316c2ea390cd49c0563cebe793c0f), [`96bd92e`](https://github.com/thirdweb-dev/js/commit/96bd92e724ec7d880d23348071b26ab8c21dd868)]:
- @thirdweb-dev/sdk@3.6.1
## 0.8.16
### Patch Changes
- Updated dependencies [[`7cb8e59`](https://github.com/thirdweb-dev/js/commit/7cb8e59be48dc923316eb36ab43a1bef7364f6b1), [`0ed77d2`](https://github.com/thirdweb-dev/js/commit/0ed77d2c9e7526444ef660aab82a950865234aa6), [`04a47e8`](https://github.com/thirdweb-dev/js/commit/04a47e8971d28da2532ebd9db824eeb459c17bd4)]:
- @thirdweb-dev/sdk@3.6.0
## 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
- [#103](https://github.com/thirdweb-dev/js/pull/103) [`e1bcc67`](https://github.com/thirdweb-dev/js/commit/e1bcc67cebfc95fce615439ff03871b4fef0eed9) Thanks [@b4s36t4](https://github.com/b4s36t4)! - automatically re-name the created example repo to the user provided app name
- Updated dependencies [[`8239763`](https://github.com/thirdweb-dev/js/commit/82397636f41faa2b44cfe70e75212f0f42291092), [`27a30e3`](https://github.com/thirdweb-dev/js/commit/27a30e3ffb56dd7fa8412a066bc3ac0977aca8e2), [`f2a6211`](https://github.com/thirdweb-dev/js/commit/f2a62110c43e7b8f35c86a197730e732f8fcc786), [`d26b768`](https://github.com/thirdweb-dev/js/commit/d26b76872a6c85651ee06aa0732ee2967f70db27), [`ba7dcbb`](https://github.com/thirdweb-dev/js/commit/ba7dcbbad8db2b73baf1435f4175d19933cb7d4f), [`9c8a3fb`](https://github.com/thirdweb-dev/js/commit/9c8a3fb6d4520dd6cdf2d1c17f33b764e871599e)]:
- @thirdweb-dev/sdk@3.1.2
## 0.7.21
### Patch Changes
- Updated dependencies [[`666ff5f`](https://github.com/thirdweb-dev/js/commit/666ff5f694f1e146150ddb9fa3233f00533b6d60), [`0dade96`](https://github.com/thirdweb-dev/js/commit/0dade96d0d2553d25b386d822e534eb7ed0aefa9), [`c295a47`](https://github.com/thirdweb-dev/js/commit/c295a47144cd722c6f8861c1ec567b53a05ea0bf)]:
- @thirdweb-dev/sdk@3.1.1
## 0.7.20
### Patch Changes
- Updated dependencies [[`3c8f620`](https://github.com/thirdweb-dev/js/commit/3c8f6205e9b49ae9fea1f9629678210c6c9c36e6), [`e187d21`](https://github.com/thirdweb-dev/js/commit/e187d21e123a506fac0459da18f2d4fc94abae29), [`acbabc8`](https://github.com/thirdweb-dev/js/commit/acbabc8999b2b7b6e0eb89cae97e2f1ef7143501), [`b030a86`](https://github.com/thirdweb-dev/js/commit/b030a866080953bbf4e504ddc41d7050b250d2d7)]:
- @thirdweb-dev/sdk@3.1.0
## 0.7.19
### Patch Changes
- Updated dependencies [[`ce05bfd`](https://github.com/thirdweb-dev/js/commit/ce05bfd8615a9c79664856bce53de8b43bed5c87)]:
- @thirdweb-dev/sdk@3.0.8
## 0.7.18
### Patch Changes
- Updated dependencies [[`5f5ab01`](https://github.com/thirdweb-dev/js/commit/5f5ab015e1dd3c471d6affe995ef36ec88932b3c)]:
- @thirdweb-dev/sdk@3.0.7
## 0.7.17
### Patch Changes
- Updated dependencies [[`a80fc97`](https://github.com/thirdweb-dev/js/commit/a80fc97b6a1e72ed46a400b4b602e180947fb870)]:
- @thirdweb-dev/sdk@3.0.6
## 0.7.16
### Patch Changes
- [#172](https://github.com/thirdweb-dev/js/pull/172) [`89db3db`](https://github.com/thirdweb-dev/js/commit/89db3db7ff3d5af7e7a7b5f25e299b9faad44eb0) Thanks [@adam-maj](https://github.com/adam-maj)! - Add thirdweb deploy to end of thirdweb detect and yarn build to thirdweb create
- [#175](https://github.com/thirdweb-dev/js/pull/175) [`3332fa5`](https://github.com/thirdweb-dev/js/commit/3332fa5697b9ff2a4ded1f2c508304f59f434be2) Thanks [@adam-maj](https://github.com/adam-maj)! - Split create contract flow into two steps
- [#177](https://github.com/thirdweb-dev/js/pull/177) [`3edec39`](https://github.com/thirdweb-dev/js/commit/3edec391be169453aaee412846005a820939fab0) Thanks [@adam-maj](https://github.com/adam-maj)! - thirdweb deploy redirects to contract deploy page if no contracts detected
- [#184](https://github.com/thirdweb-dev/js/pull/184) [`bdec5a5`](https://github.com/thirdweb-dev/js/commit/bdec5a59af680802e206e3025b2c463fc7dcb740) Thanks [@aeither](https://github.com/aeither)! - Fix "Invalid Project name" when a project with a capital letter is entered
- Updated dependencies [[`4169b94`](https://github.com/thirdweb-dev/js/commit/4169b9428f9001b7cad259a4e56fe610316cd191), [`3b877ba`](https://github.com/thirdweb-dev/js/commit/3b877ba221acfd85f80b99e1bc382055217f0a39), [`b54f95d`](https://github.com/thirdweb-dev/js/commit/b54f95dc906928ff2f9251748f254a16fe1f2cee), [`f8ab477`](https://github.com/thirdweb-dev/js/commit/f8ab4779bb2d6d66200e1e8fd558e0ac069a2f54), [`772f843`](https://github.com/thirdweb-dev/js/commit/772f8431e3a62d0ded62dae90a43e9a7edd5b1a2), [`a9ec190`](https://github.com/thirdweb-dev/js/commit/a9ec190ff99d2714cef2500d20ea0f3f73f07be3), [`208b038`](https://github.com/thirdweb-dev/js/commit/208b0389a50ea48bbb9600fec60fec2f1671d4b9), [`5345479`](https://github.com/thirdweb-dev/js/commit/534547992243bdd3a77e34ec2b2487b5adab366a)]:
- @thirdweb-dev/sdk@3.0.5
## 0.7.15
### Patch Changes
- [#148](https://github.com/thirdweb-dev/js/pull/148) [`964add6`](https://github.com/thirdweb-dev/js/commit/964add6f205577298b8f4b9ce7298e5bf09e88e7) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Fix deploy/release CLI uploads
- Updated dependencies []:
- @thirdweb-dev/sdk@3.0.4
## 0.7.14
### Patch Changes
- [#134](https://github.com/thirdweb-dev/js/pull/134) [`b1ae844`](https://github.com/thirdweb-dev/js/commit/b1ae844e8bbed59c0225b32f4115ae384fa23ef5) Thanks [@adam-maj](https://github.com/adam-maj)! - Update ordering of commands in npx thirdweb
- Updated dependencies [[`51dde28`](https://github.com/thirdweb-dev/js/commit/51dde28224209f1b8b26f436c204a5e702281564), [`9d74a43`](https://github.com/thirdweb-dev/js/commit/9d74a43aac21448beba63ba4e2637945965a3634), [`b234c58`](https://github.com/thirdweb-dev/js/commit/b234c58d44d8322e44b2d2ba87ad4ec7d699e961)]:
- @thirdweb-dev/sdk@3.0.3
## 0.7.13
### Patch Changes
- [#128](https://github.com/thirdweb-dev/js/pull/128) [`4dca5b2`](https://github.com/thirdweb-dev/js/commit/4dca5b21a66fe8ff4c4b24663e1b9ad6c446b7e2) Thanks [@jarrodwatts](https://github.com/jarrodwatts)! - Add new documentation for detectable features
- Updated dependencies [[`42c79e9`](https://github.com/thirdweb-dev/js/commit/42c79e93dc958ca46a55d705aeea44ffdbbcc5f6), [`fe8751e`](https://github.com/thirdweb-dev/js/commit/fe8751eeae7ad013b890a8092ddbd091ecbd6708)]:
- @thirdweb-dev/sdk@3.0.2
## 0.7.12
### Patch Changes
- Updated dependencies [[`98dd64a`](https://github.com/thirdweb-dev/js/commit/98dd64a375c302a879aab3c628ecfb84b4dd19da)]:
- @thirdweb-dev/sdk@3.0.1
## 0.7.11
### Patch Changes
- [#114](https://github.com/thirdweb-dev/js/pull/114) [`1df2dea`](https://github.com/thirdweb-dev/js/commit/1df2dea18f85f6760040c9000f2eb8aee8a6011b) Thanks [@jnsdls](https://github.com/jnsdls)! - remove deprecacted `publish` cli command (use `release` instead)
- Updated dependencies [[`a70b590`](https://github.com/thirdweb-dev/js/commit/a70b590be1efa7c0ad93a724afb24870439558ed), [`a37bc00`](https://github.com/thirdweb-dev/js/commit/a37bc00991bf1a359f5f8aa8e24e2c388dcd99d8), [`b442c97`](https://github.com/thirdweb-dev/js/commit/b442c970808f6cb7457d29542bd826dba711579c), [`0fa6f3f`](https://github.com/thirdweb-dev/js/commit/0fa6f3fcfbd571579baf9d2a0dbeee556ddbd5fe), [`2adb8ff`](https://github.com/thirdweb-dev/js/commit/2adb8ff6673768a91fa411c2d069245190ad9397), [`5a5bc36`](https://github.com/thirdweb-dev/js/commit/5a5bc361507bd8707dc12e9000bb9a218221cf61), [`820a519`](https://github.com/thirdweb-dev/js/commit/820a5191b5e7af5aba5e4d1cc90cd895c0dade11), [`0fa6f3f`](https://github.com/thirdweb-dev/js/commit/0fa6f3fcfbd571579baf9d2a0dbeee556ddbd5fe), [`82627ea`](https://github.com/thirdweb-dev/js/commit/82627ea0311f612119d0596ed0f568267a7af16b)]:
- @thirdweb-dev/sdk@3.0.0
## 0.7.10
### Patch Changes
- Updated dependencies [[`baa87a1`](https://github.com/thirdweb-dev/js/commit/baa87a1fbd7eee24ce9a95e16028de8435f85e69), [`4079326`](https://github.com/thirdweb-dev/js/commit/407932680fb024f17f12f578aa22c7f8c0c13339), [`05353fd`](https://github.com/thirdweb-dev/js/commit/05353fd8da82f77fb642bb38a533fb99801aed30)]:
- @thirdweb-dev/sdk@2.4.9
## 0.7.9
### Patch Changes
- [#61](https://github.com/thirdweb-dev/js/pull/61) [`3287c2b`](https://github.com/thirdweb-dev/js/commit/3287c2b0f233332fe4a095f973deed8efab91db6) Thanks [@jnsdls](https://github.com/jnsdls)! - fix versions in dependencies before releasing stable
- [#56](https://github.com/thirdweb-dev/js/pull/56) [`e9e71cf`](https://github.com/thirdweb-dev/js/commit/e9e71cfcd892611cd653431dc433b27532e9e3cc) Thanks [@nachoiacovino](https://github.com/nachoiacovino)! - Add basic tests
- Updated dependencies [[`3287c2b`](https://github.com/thirdweb-dev/js/commit/3287c2b0f233332fe4a095f973deed8efab91db6)]:
- @thirdweb-dev/sdk@2.4.8
## 0.7.8
### Patch Changes
- Updated dependencies [[`6ba9cad`](https://github.com/thirdweb-dev/js/commit/6ba9cad8d8b933256599dc3b147601cd4828c89b)]:
- @thirdweb-dev/sdk@2.4.7
## 0.7.7
### Patch Changes
- [`5644ccd`](https://github.com/thirdweb-dev/js/commit/5644ccd3ee2ff330e4e5840d3266033376750117) Thanks [@jnsdls](https://github.com/jnsdls)! - bump versions again
- Updated dependencies [[`5644ccd`](https://github.com/thirdweb-dev/js/commit/5644ccd3ee2ff330e4e5840d3266033376750117)]:
- @thirdweb-dev/sdk@2.4.6
## 0.7.6
### Patch Changes
- [`091f175`](https://github.com/thirdweb-dev/js/commit/091f1758604d40e825ea28a13c2699d67bc75d8c) Thanks [@jnsdls](https://github.com/jnsdls)! - release-all-packages
- Updated dependencies [[`091f175`](https://github.com/thirdweb-dev/js/commit/091f1758604d40e825ea28a13c2699d67bc75d8c)]:
- @thirdweb-dev/sdk@2.4.5
## 0.7.5
### Patch Changes
- Updated dependencies []:
- @thirdweb-dev/sdk@2.4.4
## 0.7.4
### Patch Changes
- [#42](https://github.com/thirdweb-dev/js/pull/42) [`46ad691`](https://github.com/thirdweb-dev/js/commit/46ad691a1636dbc7915ade22067ccfa1d39f7851) Thanks [@jnsdls](https://github.com/jnsdls)! - switch build to tsup
- [#34](https://github.com/thirdweb-dev/js/pull/34) [`5731ac2`](https://github.com/thirdweb-dev/js/commit/5731ac2f50ef63c243d3a6c2516e85920c325a95) Thanks [@jnsdls](https://github.com/jnsdls)! - add e2e tests
- Updated dependencies [[`ed639d6`](https://github.com/thirdweb-dev/js/commit/ed639d659d9d746321fb8858212d22cc16d9cd19), [`349b5c1`](https://github.com/thirdweb-dev/js/commit/349b5c1e028a06616d40de84257fd8d1cf05df83), [`46ad691`](https://github.com/thirdweb-dev/js/commit/46ad691a1636dbc7915ade22067ccfa1d39f7851), [`5731ac2`](https://github.com/thirdweb-dev/js/commit/5731ac2f50ef63c243d3a6c2516e85920c325a95), [`5731ac2`](https://github.com/thirdweb-dev/js/commit/5731ac2f50ef63c243d3a6c2516e85920c325a95)]:
- @thirdweb-dev/sdk@2.4.2
## 0.7.3
### Patch Changes
+13 -4
View File
@@ -1,6 +1,6 @@
<p align="center">
<br />
<a href="https://thirdweb.com"><img src="https://github.com/thirdweb-dev/typescript-sdk/blob/main/logo.svg?raw=true" width="200" alt=""/></a>
<a href="https://thirdweb.com"><img src="https://github.com/thirdweb-dev/js/blob/main/packages/sdk/logo.svg?raw=true" width="200" alt=""/></a>
<br />
</p>
<h1 align="center">thirdweb CLI</h1>
@@ -62,7 +62,7 @@ Deploying released contracts give deployers access to automatic SDKs to integrat
npx thirdweb@latest detect
```
As you're developing your contracts, you may want to implement [Extensions](https://portal.thirdweb.com/extensions) to unlock functionality on the SDKs (ie. nft minting with automatic upload to IPFS) and the dashboard (ie. generated UI to manage permissions). This command will show what extensions were detected on your contract, unlocking the corresponding functionality on the SDKs and dashboard.
As you're developing your contracts, you may want to implement [Extensions](https://portal.thirdweb.com/contractkit) to unlock functionality on the SDKs (ie. nft minting with automatic upload to IPFS) and the dashboard (ie. generated UI to manage permissions). This command will show what extensions were detected on your contract, unlocking the corresponding functionality on the SDKs and dashboard.
---
@@ -101,8 +101,17 @@ $ npx thirdweb@latest release
The simplest way to work on the CLI locally is to:
1. Install the package locally
2. Run the `build:watch` command to compile any changes in realtime
1. Install the package locally by running
```bash
git clone https://github.com/thirdweb-dev/js.git
```
2. From the project root run
```bash
yarn && yarn build
```
3. From inside `js/packages/cli` Run the `build:watch` command to compile any changes in real time
```bash
$ npm install -g ./
+15
View File
@@ -0,0 +1,15 @@
export const CREATE_MESSAGES = {
typeOfProject: "What type of project do you want to create?",
projectName: "What is your project named?",
contractName: "What will be the name of your new smart contract?",
chain: "Which blockchain do you want to use?",
framework: "What framework do you want to use?",
language: "What language do you want to use?",
contract: "What type of contract do you want to start from?",
extensions: "What extensions do you want to add to your contract?",
} as const;
export const ERROR_MESSAGES = {
noConfiguration:
"No contract project configuration file found in current directory",
};
+39
View File
@@ -0,0 +1,39 @@
import { CREATE_MESSAGES } from "../constants/constants";
import { prepareEnvironment } from "@gmrchk/cli-testing-library";
// this creates an app, can take some time that's fine
jest.setTimeout(150_000);
describe("npx thirdweb create", () => {
it("should create app (CRA) successfully", async () => {
const { spawn, cleanup, exists } = await prepareEnvironment();
const { waitForText, waitForFinish, getExitCode, pressKey } = await spawn(
"node",
"./dist/cli/index.js create",
);
await waitForText(CREATE_MESSAGES.typeOfProject);
await pressKey("enter");
await waitForText(CREATE_MESSAGES.projectName);
await pressKey("enter");
await waitForText(CREATE_MESSAGES.chain);
await pressKey("enter");
await waitForText(CREATE_MESSAGES.framework);
// select CRA
await pressKey("arrowDown");
await pressKey("enter");
await waitForText(CREATE_MESSAGES.language);
await pressKey("enter");
// wait for program to finish
await waitForFinish();
// check if the app was created
expect(await exists("thirdweb-app/package.json")).toEqual(true);
// the process should exit with code 0
expect(getExitCode()).toEqual(0);
await cleanup(); // cleanup after test
});
});
+38
View File
@@ -0,0 +1,38 @@
import { ERROR_MESSAGES } from "../constants/constants";
import { prepareEnvironment } from "@gmrchk/cli-testing-library";
import { copyFile } from "fs/promises";
import { resolve } from "path";
// this creates an app, can take some time that's fine
jest.setTimeout(120_000);
describe("npx thirdweb deploy", () => {
it("should return deploy page url", async () => {
const { spawn, cleanup, exists, path } = await prepareEnvironment();
await copyFile(
resolve("./e2e/files/BasicContract.sol"),
`${path}/BasicContract.sol`,
);
const { waitForText, waitForFinish, getExitCode, writeText, getStderr } =
await spawn("node", "./dist/cli/index.js deploy --contract");
expect(await exists("BasicContract.sol")).toEqual(true);
await waitForText(ERROR_MESSAGES.noConfiguration);
await writeText("y");
// wait for program to finish
await waitForFinish();
expect(getStderr().at(-1)).toContain(
"https://thirdweb.com/contracts/deploy/",
);
// the process should exit with code 0
expect(getExitCode()).toEqual(0);
await cleanup(); // cleanup after test
});
});
+94
View File
@@ -0,0 +1,94 @@
import { CREATE_MESSAGES } from "../constants/constants";
import { prepareEnvironment } from "@gmrchk/cli-testing-library";
// this creates an app, can take some time that's fine
jest.setTimeout(120_000);
describe("npx thirdweb detect", () => {
it("should detect ERC721Base extensions", async () => {
const { spawn, cleanup, exists } = await prepareEnvironment();
const create = await spawn("node", "./dist/cli/index.js create");
await create.waitForText(CREATE_MESSAGES.typeOfProject);
// select contract
await create.pressKey("arrowDown");
await create.pressKey("enter");
await create.waitForText(CREATE_MESSAGES.projectName);
// use default name
await create.pressKey("enter");
await create.waitForText(CREATE_MESSAGES.framework);
// select hardhat
await create.pressKey("enter");
await create.waitForText(CREATE_MESSAGES.contractName);
// set contract name
await create.writeText("Contract");
await create.pressKey("enter");
await create.waitForText(CREATE_MESSAGES.contract);
// select ERC721
await create.pressKey("arrowDown");
await create.pressKey("enter");
await create.waitForText(CREATE_MESSAGES.extensions);
// select no extra extensions
await create.pressKey("enter");
// wait for program to finish
await create.waitForFinish();
// the process should exit with code 0
expect(create.getExitCode()).toEqual(0);
// check if the contract was created
expect(await exists("thirdweb-contracts/contracts/Contract.sol")).toEqual(
true,
);
// check that hardhat config exists
expect(await exists("thirdweb-contracts/hardhat.config.js")).toEqual(true);
await create.writeText("cd thirdweb-contracts");
await create.pressKey("enter");
await create.writeText("npm install");
await create.pressKey("enter");
const detect = await spawn(
"node",
"./dist/cli/index.js detect",
"./thirdweb-contracts",
);
// wait for program to finish
await detect.waitForFinish();
const lines = detect.getStdout();
// check that detect excited successfully
expect(detect.getExitCode()).toEqual(0);
// Detected extensions
expect(lines.findIndex((line) => line.includes("ERC721"))).toBeGreaterThan(
-1,
);
expect(
lines.findIndex((line) => line.includes("ERC721Burnable")),
).toBeGreaterThan(-1);
expect(
lines.findIndex((line) => line.includes("ERC721Mintable")),
).toBeGreaterThan(-1);
// Suggested extensions
expect(
lines.findIndex((line) => line.includes("- ERC721Enumerable")),
).toBeGreaterThan(-1);
expect(
lines.findIndex((line) => line.includes("- ERC721LazyMintable")),
).toBeGreaterThan(-1);
expect(
lines.findIndex((line) => line.includes("- ERC721SignatureMint")),
).toBeGreaterThan(-1);
// the process should exit with code 0
expect(detect.getExitCode()).toEqual(0);
await cleanup(); // cleanup after test
});
});
+6
View File
@@ -0,0 +1,6 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract BasicContract {
constructor() {}
}
+38
View File
@@ -0,0 +1,38 @@
import { ERROR_MESSAGES } from "../constants/constants";
import { prepareEnvironment } from "@gmrchk/cli-testing-library";
import { copyFile } from "fs/promises";
import { resolve } from "path";
// this creates an app, can take some time that's fine
jest.setTimeout(120_000);
describe("npx thirdweb release", () => {
it("should return release page url", async () => {
const { spawn, cleanup, exists, path } = await prepareEnvironment();
await copyFile(
resolve("./e2e/files/BasicContract.sol"),
`${path}/BasicContract.sol`,
);
const { waitForText, waitForFinish, getExitCode, writeText, getStderr } =
await spawn("node", "./dist/cli/index.js release");
expect(await exists("BasicContract.sol")).toEqual(true);
await waitForText(ERROR_MESSAGES.noConfiguration);
await writeText("y");
// wait for program to finish
await waitForFinish();
expect(getStderr().at(-1)).toContain(
"https://thirdweb.com/contracts/release/",
);
// the process should exit with code 0
expect(getExitCode()).toEqual(0);
await cleanup(); // cleanup after test
});
});
+5
View File
@@ -0,0 +1,5 @@
/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
};
+20 -25
View File
@@ -1,70 +1,65 @@
{
"name": "thirdweb",
"main": "dist/cli/index.js",
"version": "0.7.3",
"repository": {
"type": "git",
"url": "git+https://github.com:thirdweb-dev/js.git",
"directory": "packages/cli"
},
"version": "0.9.13",
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/cli",
"author": "thirdweb eng <[email protected]>",
"license": "Apache-2.0",
"engines": {
"node": ">=14.0.0 <19"
},
"devDependencies": {
"@types/async-retry": "1.3.0",
"@gmrchk/cli-testing-library": "^0.1.2",
"@types/async-retry": "^1.3.0",
"@types/cross-spawn": "^6.0.2",
"@types/inquirer": "^8.2.1",
"@types/jest": "^29.0.0",
"@types/node": "^18.0.0",
"@types/prompts": "^2.0.14",
"@types/rimraf": "^3.0.0",
"@types/tar": "^6.1.2",
"@types/update-notifier": "^5.1.0",
"@types/update-notifier": "^6.0.1",
"@types/validate-npm-package-name": "^4.0.0",
"hardhat": "^2.9.3",
"prettier": "^2.6.2",
"jest": "^29",
"prettier": "^2.7.1",
"ts-jest": "^29.0.3",
"ts-node": "^10.7.0",
"tsc": "^2.0.4",
"tsup": "^6.2.3",
"typescript": "^4.6.3"
"typescript": "^4.7.4"
},
"dependencies": {
"@thirdweb-dev/sdk": "*",
"async-retry": "^1.3.3",
"cbor": "^8.1.0",
"commander": "^9.1.0",
"cpy": "^9.0.1",
"detect-package-manager": "^2.0.1",
"enquirer": "^2.3.6",
"ethers": "^5.6.9",
"form-data": "^4.0.0",
"ethers": "^5.7.2",
"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",
"solc": "0.8.17",
"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.0.1"
"yaml": "^2.1.1"
},
"bin": {
"thirdweb": "./dist/cli/index.js"
},
"scripts": {
"prettier": "prettier --write 'src/**/*'",
"format": "prettier --write 'src/**/*'",
"lint": "eslint src/",
"fix": "eslint src/ --fix",
"clean": "rm -rf dist/",
"cli": "node -r ts-node/register src/cli/index.ts",
"build": "yarn clean && tsc --noEmit && tsup --env.NODE_ENV production --minify --format cjs --external hardhat --dts-resolve",
"build": "yarn clean && tsup --env.NODE_ENV production --minify --format cjs --external hardhat --dts-resolve",
"build:watch": "yarn run build --watch",
"prepublishOnly": "yarn build",
"generate-thirdweb-package": "node scripts/generate-thirdweb-package.js"
"generate-thirdweb-package": "node scripts/generate-thirdweb-package.js",
"e2e": "jest"
},
"files": [
"src/",
+275 -113
View File
@@ -1,48 +1,87 @@
#!/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 { info, logger, spinner } from "../core/helpers/logger";
import { twCreate } from "../create/command";
import generateDashboardUrl from "../helpers/generate-dashboard-url";
import { deploy } from "../deploy";
import { findPackageInstallation } from "../helpers/detect-local-packages";
import { upload } from "../storage/command";
import { ThirdwebStorage } from "@thirdweb-dev/storage";
import chalk from "chalk";
import { exec, spawn } from "child_process";
import { Command } from "commander";
import open from "open";
import prompts from "prompts";
const main = async () => {
// eslint-disable-next-line turbo/no-undeclared-env-vars
const skipIntro = process.env.THIRDWEB_CLI_SKIP_INTRO === "true";
const program = new Command();
//yes this has to look like this, eliminates whitespace
console.info(`
$$\\ $$\\ $$\\ $$\\ $$\\
$$ | $$ | \\__| $$ | $$ |
$$$$$$\\ $$$$$$$\\ $$\\ $$$$$$\\ $$$$$$$ |$$\\ $$\\ $$\\ $$$$$$\\ $$$$$$$\\
\\_$$ _| $$ __$$\\ $$ |$$ __$$\\ $$ __$$ |$$ | $$ | $$ |$$ __$$\\ $$ __$$\\
$$ | $$ | $$ |$$ |$$ | \\__|$$ / $$ |$$ | $$ | $$ |$$$$$$$$ |$$ | $$ |
$$ |$$\\ $$ | $$ |$$ |$$ | $$ | $$ |$$ | $$ | $$ |$$ ____|$$ | $$ |
\\$$$$ |$$ | $$ |$$ |$$ | \\$$$$$$$ |\\$$$$$\\$$$$ |\\$$$$$$$\\ $$$$$$$ |
\\____/ \\__| \\__|\\__|\\__| \\_______| \\_____\\____/ \\_______|\\_______/ `);
console.info(`\n 💎 thirdweb-cli v${cliVersion} 💎\n`);
import("update-notifier").then(({ default: updateNotifier }) => {
updateNotifier({
pkg,
shouldNotifyInNpmScript: true,
//check every time while we're still building the CLI
updateCheckInterval: 0,
}).notify();
});
// yes this has to look like this, eliminates whitespace
if (!skipIntro) {
console.info(`
$$\\ $$\\ $$\\ $$\\ $$\\
$$ | $$ | \\__| $$ | $$ |
$$$$$$\\ $$$$$$$\\ $$\\ $$$$$$\\ $$$$$$$ |$$\\ $$\\ $$\\ $$$$$$\\ $$$$$$$\\
\\_$$ _| $$ __$$\\ $$ |$$ __$$\\ $$ __$$ |$$ | $$ | $$ |$$ __$$\\ $$ __$$\\
$$ | $$ | $$ |$$ |$$ | \\__|$$ / $$ |$$ | $$ | $$ |$$$$$$$$ |$$ | $$ |
$$ |$$\\ $$ | $$ |$$ |$$ | $$ | $$ |$$ | $$ | $$ |$$ ____|$$ | $$ |
\\$$$$ |$$ | $$ |$$ |$$ | \\$$$$$$$ |\\$$$$$\\$$$$ |\\$$$$$$$\\ $$$$$$$ |
\\____/ \\__| \\__|\\__|\\__| \\_______| \\_____\\____/ \\_______|\\_______/ `);
console.info(`\n 💎 thirdweb-cli v${cliVersion} 💎\n`);
}
program
.name("thirdweb-cli")
.description("Official thirdweb command line interface")
.version(cliVersion, "-v, --version", "output the current version");
.version(cliVersion, "-v, --version");
program
.command("detect")
.command("create [projectType] [projectPath]")
.description(
"Compile contracts and detect implemented thirdweb contract extensions",
"Create a web3 app from any of our official templates: https://github.com/thirdweb-example/",
)
.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.")
.option("--hardhat", "Initialize as a Hardhat project.")
.option("--cra", "Initialize as a Create React App project.")
.option("--next", "Initialize as a Next.js project.")
.option("--vite", "Initialize as a Vite project.")
.option("--reactNative", "Initialize as a React Native project.")
.option("--express", "Initialize as a Express project.")
.option("--node", "Initialize as a Node project.")
.option(
"--use-npm",
"Explicitly tell the CLI to bootstrap the app using npm",
)
.option(
"--use-pnpm",
"Explicitly tell the CLI to bootstrap the app using pnpm",
)
.option("--framework [name]", "The preferred framework.")
.option("--solana", "Initialize as a Solana project.")
.option("--evm", "Initialize as an Ethereum project.")
.option(
"-t, --template [name]",
"A template to start your project from. You can use an template repository name from the official thirdweb-example org.",
)
.option(
"-c, --contract-name [name]",
"Name of the new smart contract to create",
)
.action(async (type, path, options) => {
await twCreate(type, 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")
@@ -50,123 +89,246 @@ $$$$$$\\ $$$$$$$\\ $$\\ $$$$$$\\ $$$$$$$ |$$\\ $$\\ $$\\ $$$$$$\\ $$$$
await detectExtensions(options);
});
program
.command("publish")
.description("[Deprecated] use 'release' instead.")
.option("-p, --path <project-path>", "path to project", ".")
.option("--dry-run", "dry run (skip actually publishing)")
.option("-d, --debug", "show debug logs")
.option("--ci", "Continuous Integration mode")
.action(async (options) => {
logger.warn(
"'publish' is deprecated adn will be removed in a future update. Please use 'release' instead.",
);
const url = await processProject(options, "release");
info(`Open this link to release your contracts:`);
logger.info(chalk.blueBright(url.toString()));
open(url.toString());
});
program
.command("release")
.description(
"Release contracts, making them available for others to deploy and unlocking SDKs, Dashboards and Analytics.",
)
.option("-p, --path <project-path>", "path to project", ".")
.option("--dry-run", "dry run (skip actually publishing)")
.option("-d, --debug", "show debug logs")
.option("--ci", "Continuous Integration mode")
.action(async (options) => {
const url = await processProject(options, "release");
info(`Open this link to release your contracts:`);
logger.info(chalk.blueBright(url.toString()));
open(url.toString());
});
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")
.option("--ci", "Continuous Integration mode")
.option(
"--dist-path [distPath]",
"Path to the dist folder where the HTML based App is",
)
.option(
"-n, --name [name]",
"Name of the pre-built or released contract (such as nft-drop)",
)
.option(
"-f, --file [name]",
"Filter for contract files that contain this file name",
)
.option(
"-cn, --contract-name [name]",
"Filter for contracts that contain this contract name",
)
.option(
"-cv, --contract-version [version]",
"Version of the released contract",
)
.option("--app", "Deploy a web app to decentralized storage")
.option("--contract", "Deploy a smart contract to blockchains")
.action(async (options) => {
if (options.name) {
const url = generateDashboardUrl(options.name, options.contractVersion);
if (!url) {
logger.error(
chalk.red(
`Could not find a contract named ${options.name} ${
options.contractVersion || ""
}`,
),
);
return;
}
info(`Open this link to deploy your contract:`);
logger.info(chalk.blueBright(url));
open(url.toString());
return;
const url = await deploy(options);
if (url) {
open(url);
}
});
const url = await processProject(options, "deploy");
info(`Open this link to deploy your contracts:`);
logger.info(chalk.blueBright(url.toString()));
program
.command("release")
.description(
"Release your protocol so other devs can deploy them and unlock SDKs, Dashboards and Analytics",
)
.option("-p, --path <project-path>", "path to project", ".")
.option(
"-f, --file [name]",
"Filter for contract files that contain the file name",
)
.option(
"-cn, --contract-name [name]",
"Filter for contracts that contain this contract name",
)
.option("--dry-run", "dry run (skip actually publishing)")
.option("-d, --debug", "show debug logs")
.option("--ci", "Continuous Integration mode")
.action(async (options) => {
const url = await processProject(options, "release");
info(
`Open this link to release your contracts: ${chalk.blueBright(
url.toString(),
)}`,
);
open(url.toString());
});
program
.command("create")
.description(
"Create a thirdweb app from any of our official templates. Checkout some examples you can use here: https://github.com/thirdweb-example/",
)
.option("--app", `Create a thirdweb app.`)
.option("--contract", `Create a thirdweb contracts project.`)
.option("--ts, --typescript", `Initialize as a TypeScript project.`)
.option("--js, --javascript", `Initialize as a JavaScript project.`)
.option("--forge", `Initialize as a Forge project.`)
.option("--hardhat", `Initialize as a Hardhat project.`)
.option("--cra", `Initialize as a Create React App project.`)
.option("--next", `Initialize as a Next.js project.`)
.option("--vite", `Initialize as a Vite project.`)
.option(
"--use-npm",
`Explicitly tell the CLI to bootstrap the app using npm`,
)
.option(
"--use-pnpm",
`Explicitly tell the CLI to bootstrap the app using pnpm`,
)
.option("--framework [name]", `The preferred framework.`)
.option(
"-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 (options) => {
await twCreate(options);
.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();
try {
const uri = await upload(storage, path);
info(
`Files stored at the following IPFS URI: ${chalk.blueBright(
uri.toString(),
)}`,
);
const url = storage.resolveScheme(uri);
info(
`Open this link to view your upload: ${chalk.blueBright(
url.toString(),
)}`,
);
} catch (err: any) {
if (
err.toString() === "No files detected in specified directory" ||
err.toString() === "Invalid path provided" ||
err.toString() === "No path provided"
) {
logger.error(
chalk.redBright(
"Please specify a valid file or directory to upload",
),
);
} else {
logger.error(chalk.redBright("Failed to upload files"), err);
}
}
});
program
.command("install-ci")
.command("detect")
.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.",
"(deprecated) Compile contracts and detect implemented 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 installGithubAction(options);
await detectExtensions(options);
});
if (!skipIntro) {
const versionSpinner = spinner("Checking for updates...");
await import("update-notifier").then(
async ({ default: updateNotifier }) => {
const notifier = updateNotifier({
pkg,
shouldNotifyInNpmScript: true,
// check every time while we're still building the CLI
updateCheckInterval: 0,
});
const versionInfo = await notifier.fetchInfo();
versionSpinner.stop();
if (versionInfo.type !== "latest") {
const res = await prompts({
type: "toggle",
name: "upgrade",
message: `A new version of the CLI is available. Would you like to upgrade?`,
initial: true,
active: "yes",
inactive: "no",
});
if (res.upgrade) {
const updateSpinner = spinner(
`Upgrading CLI to version ${versionInfo.latest}...`,
);
const clonedEnvironment = { ...process.env };
clonedEnvironment.THIRDWEB_CLI_SKIP_INTRO = "true";
const installation = await findPackageInstallation();
// If the package isn't installed anywhere, just defer to npx thirdweb@latest
if (!installation) {
updateSpinner.succeed(
`Now using CLI version ${versionInfo.latest}. Continuing execution...`,
);
await new Promise((done, failed) => {
const shell = spawn(
`npx --yes thirdweb@latest ${process.argv
.slice(2)
.join(" ")}`,
[],
{ stdio: "inherit", shell: true, env: clonedEnvironment },
);
shell.on("close", (code) => {
if (code === 0) {
done("");
} else {
failed();
}
});
});
return process.exit(0);
}
// Otherwise, get the correct command based on package manager and local vs. global
let command = "";
switch (installation.packageManager) {
case "npm":
command = installation.isGlobal
? `npm install -g thirdweb`
: `npm install thirdweb`;
break;
case "yarn":
command = installation.isGlobal
? `yarn global add thirdweb`
: `yarn add thirdweb`;
break;
case "pnpm":
command = installation.isGlobal
? `pnpm add -g thirdweb@latest`
: `pnpm add thirdweb@latest`;
break;
default:
console.error(
`Could not detect package manager in use, aborting automatic upgrade.\nIf you want to upgrade the CLI, please do it manually with your package manager.`,
);
process.exit(1);
}
await new Promise((done, failed) => {
exec(command, (err, stdout, stderr) => {
if (err) {
failed(err);
return;
}
done({ stdout, stderr });
});
});
updateSpinner.succeed(
`Successfully upgraded CLI to version ${versionInfo.latest}. Continuing execution...`,
);
// If the package is installed globally with yarn or pnpm, then npx won't recognize it
// So we need to make sure to run the command directly
const executionCommand =
!installation.isGlobal || installation.packageManager === "npm"
? `npx thirdweb`
: `thirdweb`;
await new Promise((done, failed) => {
const shell = spawn(
`${executionCommand} ${process.argv.slice(2).join(" ")}`,
[],
{ stdio: "inherit", shell: true, env: clonedEnvironment },
);
shell.on("close", (code) => {
if (code === 0) {
done("");
} else {
failed();
}
});
});
process.exit(0);
}
}
},
);
}
await program.parseAsync();
};
+3 -3
View File
@@ -1,5 +1,5 @@
import { spinner } from "../core/helpers/logger";
import { IpfsStorage } from "@thirdweb-dev/storage";
import { ThirdwebStorage } from "@thirdweb-dev/storage";
import { existsSync, mkdirSync, writeFileSync } from "fs";
import path, { join } from "path";
@@ -13,10 +13,10 @@ export async function installGithubAction(options: any) {
: path.resolve(`${projectPath}/${options.path}`);
projectPath = resolvedPath;
}
const storage = new IpfsStorage();
const storage = new ThirdwebStorage();
const log = spinner("Installing thirdweb Github Action...");
try {
const ghActionData = await storage.getRaw(ghActionHash);
const ghActionData = await (await storage.download(ghActionHash)).text();
const dir = join(projectPath, ".github/workflows");
if (!existsSync(dir)) {
mkdirSync(dir, { recursive: true });
+28 -9
View File
@@ -1,18 +1,17 @@
import { ALWAYS_SUGGESTED } from "../constants/features";
import build from "../core/builder/build";
import detect from "../core/detection/detect";
import { logger, spinner, warn, info } from "../core/helpers/logger";
import { logger, spinner } from "../core/helpers/logger";
import { createContractsPrompt } from "../core/helpers/selector";
import { ContractFeatures, Feature } from "../core/interfaces/ContractFeatures";
import { ContractPayload } from "../core/interfaces/ContractPayload";
import { detectFeatures } from "@thirdweb-dev/sdk";
import { FeatureWithEnabled } from "@thirdweb-dev/sdk/dist/declarations/src/constants/contract-features";
import { getPkgManager } from "../create/helpers/get-pkg-manager";
import { detectFeatures, FeatureWithEnabled } from "@thirdweb-dev/sdk";
import chalk from "chalk";
import { existsSync, readFileSync } from "fs";
import ora from "ora";
import path from "path";
const { MultiSelect } = require("enquirer");
export async function detectExtensions(options: any) {
logger.setSettings({
minLevel: options.debug ? "debug" : "info",
@@ -44,7 +43,7 @@ export async function detectExtensions(options: any) {
compileLoader.succeed("Compilation successful");
let selectedContracts: ContractPayload[] = [];
if (compiledResult.contracts.length == 1) {
if (compiledResult.contracts.length === 1) {
selectedContracts = [compiledResult.contracts[0]];
} else {
if (options.all) {
@@ -73,13 +72,13 @@ export async function detectExtensions(options: any) {
const enabledFeatures: Feature[] = features.enabledFeatures.map(
(feature) => ({
name: feature.name,
reference: `https://portal.thirdweb.com/contracts/${feature.docLinks.contracts}`,
reference: `https://portal.thirdweb.com/interfaces/${feature.name.toLowerCase()}`,
}),
);
const suggestedFeatures: Feature[] = features.suggestedFeatures.map(
(feature) => ({
name: feature.name,
reference: `https://portal.thirdweb.com/contracts/${feature.docLinks.contracts}`,
reference: `https://portal.thirdweb.com/interfaces/${feature.name.toLowerCase()}`,
}),
);
@@ -104,7 +103,7 @@ export async function detectExtensions(options: any) {
`Detected extension on ${chalk.blueBright(contractWithFeatures.name)}`,
).stopAndPersist({ symbol: "🔎" });
contractWithFeatures.enabledFeatures.map((feature) => {
info(`${chalk.green(feature.name)}`);
logger.info(`✔️ ${chalk.green(feature.name)}`);
});
}
logger.info(``);
@@ -116,6 +115,26 @@ export async function detectExtensions(options: any) {
)} - ${chalk.dim(chalk.gray(feature.reference))}`,
);
});
let deployCmd = `npx thirdweb@latest deploy`;
if (existsSync(projectPath + "/package.json")) {
const packageManager = getPkgManager();
const useYarn = packageManager === "yarn";
const pkgJson = JSON.parse(
readFileSync(projectPath + "/package.json", "utf-8"),
);
if (pkgJson?.scripts?.deploy === deployCmd) {
deployCmd = `${packageManager}${useYarn ? "" : " run"} deploy`;
}
}
logger.info(``);
ora(
`Once you're done writing your contracts, you can run the following command to deploy them:`,
).info();
logger.info(``);
logger.info(` ${chalk.cyan(deployCmd)}`);
logger.info(``);
});
}
+83 -22
View File
@@ -1,13 +1,14 @@
import { THIRDWEB_URL, cliVersion } from "../constants/urls";
import { cliVersion, THIRDWEB_URL } from "../constants/urls";
import build from "../core/builder/build";
import detect from "../core/detection/detect";
import { execute } from "../core/helpers/exec";
import { error, info, logger, spinner, warn } from "../core/helpers/logger";
import { error, info, logger, spinner } from "../core/helpers/logger";
import { createContractsPrompt } from "../core/helpers/selector";
import { ContractPayload } from "../core/interfaces/ContractPayload";
import { IpfsStorage } from "@thirdweb-dev/storage";
import { ThirdwebStorage } from "@thirdweb-dev/storage";
import chalk from "chalk";
import { readFileSync } from "fs";
import ora from "ora";
import path from "path";
export async function processProject(
@@ -15,7 +16,7 @@ export async function processProject(
command: "deploy" | "release",
) {
// TODO: allow overriding the default storage
const storage = new IpfsStorage();
const storage = new ThirdwebStorage();
logger.setSettings({
minLevel: options.debug ? "debug" : "info",
@@ -35,6 +36,27 @@ export async function processProject(
const projectType = await detect(projectPath, options);
if (projectType === "none") {
if (command === "deploy") {
info(
"No contracts detected in this directory. Redirecting to the thirdweb contract deployment page.",
);
return "https://thirdweb.com/contracts";
}
error("No detected contracts in this directory.");
logger.info(``);
ora(
`Detected contract files must end with the '.sol' extension and exist in this directory or the '/contracts' subdirectory`,
).info();
logger.info(``);
ora("To create a new contracts project, run the following command:").info();
logger.info(``);
logger.info(` ${chalk.cyan(`npx thirdweb@latest create --contract`)}`);
logger.info(``);
process.exit(1);
}
if (options.ci) {
logger.info("Installing dependencies...");
try {
@@ -54,7 +76,7 @@ export async function processProject(
}
}
let compiledResult;
let compiledResult: { contracts: ContractPayload[] };
const compileLoader = spinner("Compiling project...");
try {
compiledResult = await build(projectPath, projectType);
@@ -65,7 +87,7 @@ export async function processProject(
}
compileLoader.succeed("Compilation successful");
if (compiledResult.contracts.length == 0) {
if (compiledResult.contracts.length === 0) {
logger.error(
"No deployable contract detected. Run with the '--debug' option to see what contracts were skipped and why.",
);
@@ -73,7 +95,7 @@ export async function processProject(
}
let selectedContracts: ContractPayload[] = [];
if (compiledResult.contracts.length == 1) {
if (compiledResult.contracts.length === 1) {
selectedContracts = [compiledResult.contracts[0]];
info(
`Processing contract: ${chalk.blueBright(
@@ -84,10 +106,47 @@ export async function processProject(
if (options.ci) {
selectedContracts = compiledResult.contracts;
} else {
const choices = compiledResult.contracts.map((c) => ({
name: c.name,
value: c,
}));
const filtered = compiledResult.contracts
.filter((c) => {
if (typeof options.file === "string" && options.file.length > 0) {
return c.fileName.includes(options.file);
}
return true;
})
.filter((c) => {
if (
typeof options.contractName === "string" &&
options.contractName.length > 0
) {
return c.name.includes(options.contractName);
}
return true;
});
if (filtered.length === 0) {
logger.error(`No contracts found matching the specified filters.`);
process.exit(1);
}
const choices = filtered.map((c) => {
if (
compiledResult.contracts.filter(
(other: ContractPayload) => other.name === c.name,
).length > 1
) {
return {
name: `${c.name} - ${chalk.gray(c.fileName)}`,
value: c,
};
}
return {
name: c.name,
value: c,
};
});
const prompt = createContractsPrompt(
choices,
`Choose which contract(s) to ${command}`,
@@ -129,7 +188,9 @@ export async function processProject(
if (file.includes(soliditySDKPackage)) {
usesSoliditySDK = true;
}
return await storage.uploadSingle(file);
return await storage.upload(file, {
uploadWithoutDirectory: true,
});
}),
);
}
@@ -140,14 +201,15 @@ export async function processProject(
const metadataURIs = await Promise.all(
selectedContracts.map(async (c) => {
logger.debug(`Uploading ${c.name}...`);
const hash = await storage.uploadSingle(c.metadata);
return `ipfs://${hash}`;
return await storage.upload(JSON.parse(JSON.stringify(c.metadata)), {
uploadWithoutDirectory: true,
});
}),
);
// Upload batch all bytecodes
const bytecodes = selectedContracts.map((c) => c.bytecode);
const { uris: bytecodeURIs } = await storage.uploadBatch(bytecodes);
const bytecodeURIs = await storage.uploadBatch(bytecodes);
const combinedContents = selectedContracts.map((c, i) => {
// attach analytics blob to metadata
@@ -167,16 +229,15 @@ export async function processProject(
};
});
let combinedURIs: string[] = [];
if (combinedContents.length == 1) {
if (combinedContents.length === 1) {
// use upload single if only one contract to get a clean IPFS hash
const metadataUri = await storage.uploadSingle(
JSON.stringify(combinedContents[0]),
);
const metadataUri = await storage.upload(combinedContents[0], {
uploadWithoutDirectory: true,
});
combinedURIs.push(metadataUri);
} else {
// otherwise upload batch
const { uris } = await storage.uploadMetadataBatch(combinedContents);
combinedURIs = uris;
combinedURIs = await storage.uploadBatch(combinedContents);
}
loader.succeed("Upload successful");
@@ -190,7 +251,7 @@ export async function processProject(
export function getUrl(hashes: string[], command: string) {
let url;
if (hashes.length == 1) {
if (hashes.length === 1) {
url = new URL(
THIRDWEB_URL +
`/contracts/${command}/` +
+294 -294
View File
@@ -1,294 +1,294 @@
const baseContracts = {
ERC721Base: `// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@thirdweb-dev/contracts/base/ERC721Base.sol";
contract Contract is ERC721Base {
constructor(
string memory _name,
string memory _symbol,
address _royaltyRecipient,
uint128 _royaltyBps
)
ERC721Base(
_name,
_symbol,
_royaltyRecipient,
_royaltyBps
)
{}
}`,
ERC721Signature: `// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;
import "@thirdweb-dev/contracts/base/ERC721SignatureMint.sol";
contract Contract is ERC721SignatureMint {
constructor(
string memory _name,
string memory _symbol,
address _royaltyRecipient,
uint128 _royaltyBps,
address _primarySaleRecipient
)
ERC721SignatureMint(
_name,
_symbol,
_royaltyRecipient,
_royaltyBps,
_primarySaleRecipient
)
{}
}`,
ERC721LazyMint: `// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;
import "@thirdweb-dev/contracts/base/ERC721LazyMint.sol";
contract Contract is ERC721LazyMint {
constructor(
string memory _name,
string memory _symbol,
address _royaltyRecipient,
uint128 _royaltyBps
) ERC721LazyMint(_name, _symbol, _royaltyRecipient, _royaltyBps) {}
}`,
ERC721DelayedReveal: `// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;
import "@thirdweb-dev/contracts/base/ERC721DelayedReveal.sol";
contract Contract is ERC721DelayedReveal {
constructor(
string memory _name,
string memory _symbol,
address _royaltyRecipient,
uint128 _royaltyBps
) ERC721DelayedReveal(_name, _symbol, _royaltyRecipient, _royaltyBps) {}
}`,
ERC721Drop: `// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;
import "@thirdweb-dev/contracts/base/ERC721Drop.sol";
contract Contract is ERC721Drop {
constructor(
string memory _name,
string memory _symbol,
address _royaltyRecipient,
uint128 _royaltyBps,
address _primarySaleRecipient
)
ERC721Drop(
_name,
_symbol,
_royaltyRecipient,
_royaltyBps,
_primarySaleRecipient
)
{}
}`,
ERC1155Base: `// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@thirdweb-dev/contracts/base/ERC1155Base.sol";
contract Contract is ERC1155Base {
constructor(
string memory _name,
string memory _symbol,
address _royaltyRecipient,
uint128 _royaltyBps
)
ERC1155Base(
_name,
_symbol,
_royaltyRecipient,
_royaltyBps
)
{}
}`,
ERC1155Signature: `// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;
import "@thirdweb-dev/contracts/base/ERC1155SignatureMint.sol";
contract Contract is ERC1155SignatureMint {
constructor(
string memory _name,
string memory _symbol,
address _royaltyRecipient,
uint128 _royaltyBps,
address _primarySaleRecipient
)
ERC1155SignatureMint(
_name,
_symbol,
_royaltyRecipient,
_royaltyBps,
_primarySaleRecipient
)
{}
}`,
ERC1155LazyMint: `// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;
import "@thirdweb-dev/contracts/base/ERC1155LazyMint.sol";
contract Contract is ERC1155LazyMint {
constructor(
string memory _name,
string memory _symbol,
address _royaltyRecipient,
uint128 _royaltyBps
) ERC1155LazyMint(_name, _symbol, _royaltyRecipient, _royaltyBps) {}
}`,
ERC1155DelayedReveal: `// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;
import "@thirdweb-dev/contracts/base/ERC1155DelayedReveal.sol";
contract Contract is ERC1155DelayedReveal {
constructor(
string memory _name,
string memory _symbol,
address _royaltyRecipient,
uint128 _royaltyBps
) ERC1155DelayedReveal(_name, _symbol, _royaltyRecipient, _royaltyBps) {}
}`,
ERC1155Drop: `// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;
import "@thirdweb-dev/contracts/base/ERC1155Drop.sol";
contract Contract is ERC1155Drop {
constructor(
string memory _name,
string memory _symbol,
address _royaltyRecipient,
uint128 _royaltyBps,
address _primarySaleRecipient
)
ERC1155Drop(
_name,
_symbol,
_royaltyRecipient,
_royaltyBps,
_primarySaleRecipient
)
{}
}`,
ERC20Base: `// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@thirdweb-dev/contracts/base/ERC20Base.sol";
contract Contract is ERC20Base {
constructor(
string memory _name,
string memory _symbol
)
ERC20Base(
_name,
_symbol
)
{}
}`,
ERC20Vote: `// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@thirdweb-dev/contracts/base/ERC20Vote.sol";
contract Contract is ERC20Vote {
constructor(
string memory _name,
string memory _symbol
)
ERC20Vote(
_name,
_symbol
)
{}
}`,
ERC20SignatureMint: `// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@thirdweb-dev/contracts/base/ERC20SignatureMint.sol";
contract Contract is ERC20SignatureMint {
constructor(
string memory _name,
string memory _symbol,
address _primarySaleRecipient
)
ERC20SignatureMint(
_name,
_symbol,
_primarySaleRecipient
)
{}
}`,
ERC20SignatureMintVote: `// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@thirdweb-dev/contracts/base/ERC20SignatureMintVote.sol";
contract Contract is ERC20SignatureMintVote {
constructor(
string memory _name,
string memory _symbol,
address _primarySaleRecipient
)
ERC20SignatureMintVote(
_name,
_symbol,
_primarySaleRecipient
)
{}
}`,
ERC20Drop: `// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@thirdweb-dev/contracts/base/ERC20Drop.sol";
contract Contract is ERC20Drop {
constructor(
string memory _name,
string memory _symbol,
address _primarySaleRecipient
)
ERC20Drop(
_name,
_symbol,
_primarySaleRecipient
)
{}
}`,
ERC20DropVote: `// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@thirdweb-dev/contracts/base/ERC20DropVote.sol";
contract Contract is ERC20DropVote {
constructor(
string memory _name,
string memory _symbol,
address _primarySaleRecipient
)
ERC20DropVote(
_name,
_symbol,
_primarySaleRecipient
)
{}
}`,
};
export default baseContracts;
const baseContracts = {
ERC721Base: `// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@thirdweb-dev/contracts/base/ERC721Base.sol";
contract Contract is ERC721Base {
constructor(
string memory _name,
string memory _symbol,
address _royaltyRecipient,
uint128 _royaltyBps
)
ERC721Base(
_name,
_symbol,
_royaltyRecipient,
_royaltyBps
)
{}
}`,
ERC721SignatureMint: `// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;
import "@thirdweb-dev/contracts/base/ERC721SignatureMint.sol";
contract Contract is ERC721SignatureMint {
constructor(
string memory _name,
string memory _symbol,
address _royaltyRecipient,
uint128 _royaltyBps,
address _primarySaleRecipient
)
ERC721SignatureMint(
_name,
_symbol,
_royaltyRecipient,
_royaltyBps,
_primarySaleRecipient
)
{}
}`,
ERC721LazyMint: `// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;
import "@thirdweb-dev/contracts/base/ERC721LazyMint.sol";
contract Contract is ERC721LazyMint {
constructor(
string memory _name,
string memory _symbol,
address _royaltyRecipient,
uint128 _royaltyBps
) ERC721LazyMint(_name, _symbol, _royaltyRecipient, _royaltyBps) {}
}`,
ERC721DelayedReveal: `// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;
import "@thirdweb-dev/contracts/base/ERC721DelayedReveal.sol";
contract Contract is ERC721DelayedReveal {
constructor(
string memory _name,
string memory _symbol,
address _royaltyRecipient,
uint128 _royaltyBps
) ERC721DelayedReveal(_name, _symbol, _royaltyRecipient, _royaltyBps) {}
}`,
ERC721Drop: `// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;
import "@thirdweb-dev/contracts/base/ERC721Drop.sol";
contract Contract is ERC721Drop {
constructor(
string memory _name,
string memory _symbol,
address _royaltyRecipient,
uint128 _royaltyBps,
address _primarySaleRecipient
)
ERC721Drop(
_name,
_symbol,
_royaltyRecipient,
_royaltyBps,
_primarySaleRecipient
)
{}
}`,
ERC1155Base: `// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@thirdweb-dev/contracts/base/ERC1155Base.sol";
contract Contract is ERC1155Base {
constructor(
string memory _name,
string memory _symbol,
address _royaltyRecipient,
uint128 _royaltyBps
)
ERC1155Base(
_name,
_symbol,
_royaltyRecipient,
_royaltyBps
)
{}
}`,
ERC1155SignatureMint: `// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;
import "@thirdweb-dev/contracts/base/ERC1155SignatureMint.sol";
contract Contract is ERC1155SignatureMint {
constructor(
string memory _name,
string memory _symbol,
address _royaltyRecipient,
uint128 _royaltyBps,
address _primarySaleRecipient
)
ERC1155SignatureMint(
_name,
_symbol,
_royaltyRecipient,
_royaltyBps,
_primarySaleRecipient
)
{}
}`,
ERC1155LazyMint: `// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;
import "@thirdweb-dev/contracts/base/ERC1155LazyMint.sol";
contract Contract is ERC1155LazyMint {
constructor(
string memory _name,
string memory _symbol,
address _royaltyRecipient,
uint128 _royaltyBps
) ERC1155LazyMint(_name, _symbol, _royaltyRecipient, _royaltyBps) {}
}`,
ERC1155DelayedReveal: `// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;
import "@thirdweb-dev/contracts/base/ERC1155DelayedReveal.sol";
contract Contract is ERC1155DelayedReveal {
constructor(
string memory _name,
string memory _symbol,
address _royaltyRecipient,
uint128 _royaltyBps
) ERC1155DelayedReveal(_name, _symbol, _royaltyRecipient, _royaltyBps) {}
}`,
ERC1155Drop: `// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;
import "@thirdweb-dev/contracts/base/ERC1155Drop.sol";
contract Contract is ERC1155Drop {
constructor(
string memory _name,
string memory _symbol,
address _royaltyRecipient,
uint128 _royaltyBps,
address _primarySaleRecipient
)
ERC1155Drop(
_name,
_symbol,
_royaltyRecipient,
_royaltyBps,
_primarySaleRecipient
)
{}
}`,
ERC20Base: `// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@thirdweb-dev/contracts/base/ERC20Base.sol";
contract Contract is ERC20Base {
constructor(
string memory _name,
string memory _symbol
)
ERC20Base(
_name,
_symbol
)
{}
}`,
ERC20Vote: `// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@thirdweb-dev/contracts/base/ERC20Vote.sol";
contract Contract is ERC20Vote {
constructor(
string memory _name,
string memory _symbol
)
ERC20Vote(
_name,
_symbol
)
{}
}`,
ERC20SignatureMint: `// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@thirdweb-dev/contracts/base/ERC20SignatureMint.sol";
contract Contract is ERC20SignatureMint {
constructor(
string memory _name,
string memory _symbol,
address _primarySaleRecipient
)
ERC20SignatureMint(
_name,
_symbol,
_primarySaleRecipient
)
{}
}`,
ERC20SignatureMintVote: `// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@thirdweb-dev/contracts/base/ERC20SignatureMintVote.sol";
contract Contract is ERC20SignatureMintVote {
constructor(
string memory _name,
string memory _symbol,
address _primarySaleRecipient
)
ERC20SignatureMintVote(
_name,
_symbol,
_primarySaleRecipient
)
{}
}`,
ERC20Drop: `// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@thirdweb-dev/contracts/base/ERC20Drop.sol";
contract Contract is ERC20Drop {
constructor(
string memory _name,
string memory _symbol,
address _primarySaleRecipient
)
ERC20Drop(
_name,
_symbol,
_primarySaleRecipient
)
{}
}`,
ERC20DropVote: `// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@thirdweb-dev/contracts/base/ERC20DropVote.sol";
contract Contract is ERC20DropVote {
constructor(
string memory _name,
string memory _symbol,
address _primarySaleRecipient
)
ERC20DropVote(
_name,
_symbol,
_primarySaleRecipient
)
{}
}`,
};
export default baseContracts;
+1 -1
View File
@@ -1,4 +1,4 @@
/**
* Features that are always recommended to implement
*/
export const ALWAYS_SUGGESTED = ["ContractMetadata", "Permissions"];
export const ALWAYS_SUGGESTED = ["ContractMetadata", "Permissions"];
+1
View File
@@ -19,4 +19,5 @@ export const PINATA_IPFS_URL = `https://api.pinata.cloud/pinning/pinFileToIPFS`;
export const THIRDWEB_URL = `https://thirdweb.com`;
export const pkg = require("../../package.json");
export const cliVersion = pkg.version;
+6 -2
View File
@@ -1,6 +1,6 @@
import { findFiles } from "../../common/file-helper";
import { execute } from "../helpers/exec";
import { logger, spinner } from "../helpers/logger";
import { logger } from "../helpers/logger";
import { CompileOptions } from "../interfaces/Builder";
import { ContractPayload } from "../interfaces/ContractPayload";
import { BaseBuilder } from "./builder-base";
@@ -21,7 +21,10 @@ export class BrownieBuilder extends BaseBuilder {
config?.project_structure?.build || "./build",
);
existsSync(buildPath) && rmSync(buildPath, { recursive: true });
if (existsSync(buildPath)) {
rmSync(buildPath, { recursive: true });
}
await execute("brownie compile", options.projectPath);
const contractsPath = join(buildPath, "contracts/");
@@ -52,6 +55,7 @@ export class BrownieBuilder extends BaseBuilder {
metadata: {},
bytecode,
sources: [], // TODO
fileName: "", // TODO
name: contractName,
});
break;
+1 -1
View File
@@ -1,5 +1,5 @@
import { ContractPayload } from "../interfaces/ContractPayload";
import { ProjectType } from "./../types/ProjectType";
import { ProjectType } from "../types/ProjectType";
import { BrownieBuilder } from "./brownie";
import { FoundryBuilder } from "./foundry";
import { HardhatBuilder } from "./hardhat";
@@ -1,9 +1,7 @@
import { extractIPFSHashFromBytecode } from "../helpers/ipfs";
import { logger } from "../helpers/logger";
import { CompileOptions, IBuilder } from "../interfaces/Builder";
import { ContractPayload } from "../interfaces/ContractPayload";
import { existsSync, readdirSync, statSync } from "fs";
import { basename, join } from "path";
import { extractIPFSHashFromBytecode } from "@thirdweb-dev/sdk/evm";
export abstract class BaseBuilder implements IBuilder {
abstract compile(
@@ -20,7 +18,7 @@ export abstract class BaseBuilder implements IBuilder {
bytecode === "" ||
bytecode === "0x" ||
!abi ||
abi.length == 0
abi.length === 0
) {
return false;
}
+22 -1
View File
@@ -1,9 +1,11 @@
import { findFiles } from "../../common/file-helper";
import { hasForge } from "../../create/helpers/has-forge";
import { execute } from "../helpers/exec";
import { logger } from "../helpers/logger";
import { CompileOptions } from "../interfaces/Builder";
import { ContractPayload } from "../interfaces/ContractPayload";
import { BaseBuilder } from "./builder-base";
import chalk from "chalk";
import { existsSync, readFileSync } from "fs";
import { basename, join } from "path";
@@ -11,6 +13,18 @@ export class FoundryBuilder extends BaseBuilder {
public async compile(options: CompileOptions): Promise<{
contracts: ContractPayload[];
}> {
const isInstalled = await hasForge();
if (!isInstalled) {
console.error(
`\n${chalk.redBright(
`error`,
)} You don't have forge installed on this machine!\n\nYou can install forge by following these instructions:\n${chalk.blueBright(
`https://book.getfoundry.sh/getting-started/installation`,
)}\n`,
);
process.exit(1);
}
await execute("forge clean", options.projectPath);
await execute("forge build --extra-output metadata", options.projectPath);
@@ -88,6 +102,11 @@ export class FoundryBuilder extends BaseBuilder {
})
.filter((path) => path !== undefined) as string[];
const fileNames = Object.keys(
parsedMetadata?.settings?.compilationTarget || {},
);
const fileName = fileNames.length > 0 ? fileNames[0] : "";
if (
this.shouldProcessContract(
contractInfo.abi,
@@ -99,6 +118,7 @@ export class FoundryBuilder extends BaseBuilder {
name: contractName,
metadata,
bytecode,
fileName,
sources,
});
}
@@ -111,9 +131,10 @@ export class FoundryBuilder extends BaseBuilder {
}
private sort(object: any) {
if (typeof object != "object" || object instanceof Array)
if (typeof object !== "object" || object instanceof Array) {
// Not to sort the array
return object;
}
var keys = Object.keys(object);
keys.sort();
var newObject: any = {};
+8 -2
View File
@@ -1,6 +1,6 @@
import { findFiles } from "../../common/file-helper";
import { execute } from "../helpers/exec";
import { logger, spinner } from "../helpers/logger";
import { logger } from "../helpers/logger";
import { CompileOptions } from "../interfaces/Builder";
import { ContractPayload } from "../interfaces/ContractPayload";
import { BaseBuilder } from "./builder-base";
@@ -25,7 +25,7 @@ export class HardhatBuilder extends BaseBuilder {
//the hardhat extractor **logs out** the runtime config of hardhat, we take that stdout and parse it
const stringifiedConfig = (
await execute(
`npx hardhat run ${configExtractorScriptPath} --no-compile`,
`npx hardhat run "${configExtractorScriptPath}" --no-compile`,
options.projectPath,
)
).stdout;
@@ -123,11 +123,17 @@ export class HardhatBuilder extends BaseBuilder {
})
.filter((path) => path !== undefined) as string[];
const fileNames = Object.keys(
meta?.settings?.compilationTarget || {},
);
const fileName = fileNames.length > 0 ? fileNames[0] : "";
if (this.shouldProcessContract(abi, deployedBytecode, contractName)) {
contracts.push({
metadata,
bytecode,
name: contractName,
fileName,
sources,
});
}
+9 -3
View File
@@ -1,5 +1,5 @@
import { findFiles } from "../../common/file-helper";
import { logger, spinner } from "../helpers/logger";
import { logger } from "../helpers/logger";
import { CompileOptions } from "../interfaces/Builder";
import { ContractPayload } from "../interfaces/ContractPayload";
import { BaseBuilder } from "./builder-base";
@@ -122,7 +122,7 @@ export class SolcBuilder extends BaseBuilder {
continue;
}
const sources = Object.keys(parsedMetadata.sources)
const _sources = Object.keys(parsedMetadata.sources)
.map((path) => {
const matchingSourcePath = inputPaths.find((p) => p.includes(path));
if (matchingSourcePath && existsSync(matchingSourcePath)) {
@@ -140,12 +140,18 @@ export class SolcBuilder extends BaseBuilder {
})
.filter((path) => path !== undefined) as string[];
const fileNames = Object.keys(
parsedMetadata?.settings?.compilationTarget || {},
);
const fileName = fileNames.length > 0 ? fileNames[0] : "";
if (this.shouldProcessContract(abi, deployedBytecode, contractName)) {
contracts.push({
metadata,
bytecode,
name: contractName,
sources,
fileName,
sources: _sources,
});
}
}
+12 -2
View File
@@ -1,6 +1,6 @@
import { findFiles } from "../../common/file-helper";
import { execute } from "../helpers/exec";
import { logger, spinner } from "../helpers/logger";
import { logger } from "../helpers/logger";
import { CompileOptions } from "../interfaces/Builder";
import { ContractPayload } from "../interfaces/ContractPayload";
import { BaseBuilder } from "./builder-base";
@@ -22,7 +22,10 @@ export class TruffleBuilder extends BaseBuilder {
truffleConfig.contracts_build_directory || "./build/contracts",
);
existsSync(buildPath) && rmSync(buildPath, { recursive: true });
if (existsSync(buildPath)) {
rmSync(buildPath, { recursive: true });
}
await execute("npx --yes truffle compile", options.projectPath);
const contracts: ContractPayload[] = [];
@@ -69,11 +72,18 @@ export class TruffleBuilder extends BaseBuilder {
return undefined;
})
.filter((path) => path !== undefined) as string[];
const fileNames = Object.keys(
parsedMetadata?.settings?.compilationTarget || {},
);
const fileName = fileNames.length > 0 ? fileNames[0] : "";
if (this.shouldProcessContract(abi, deployedBytecode, contractName)) {
contracts.push({
metadata,
bytecode,
name: contractName,
fileName,
sources,
});
}
@@ -1,4 +1,3 @@
import { logger } from "../helpers/logger";
import { ProjectType } from "../types/ProjectType";
import { Detector } from "./detector";
import { existsSync } from "fs";
+42 -10
View File
@@ -1,10 +1,13 @@
import { ERROR_MESSAGES } from "../../../constants/constants";
import { info, logger, warn } from "../helpers/logger";
import { ProjectType } from "../types/ProjectType";
import { Detector } from "./detector";
import FoundryDetector from "./foundry";
import HardhatDetector from "./hardhat";
import TruffleDetector from "./truffle";
import fs, { existsSync } from "fs";
import inquirer from "inquirer";
import { parse } from "path";
const { Confirm } = require("enquirer");
@@ -25,17 +28,25 @@ export default async function detect(
//if there is no project returned at all then just return unknown}
if (!possibleProjectTypes.length) {
warn("Failed to find a supported project configuration file in current directory " + path);
const prompt = new Confirm({
name: "continue",
message: "Do you want to continue and compile this project with solc instead?",
});
const shouldCompile = await prompt.run();
if (!shouldCompile) {
logger.warn("Aborted contract compilation");
process.exit(1);
const canCompile = hasContracts(path);
if (canCompile) {
warn(`${ERROR_MESSAGES.noConfiguration} ${path}`);
const prompt = new Confirm({
name: "continue",
message:
"Do you want to continue and compile this project with solc instead?",
initial: true,
});
const shouldCompile = await prompt.run();
if (!shouldCompile) {
logger.warn("Aborted contract compilation");
process.exit(1);
}
return "solc";
} else {
return "none";
}
return "unknown";
}
//if there is only one possible option just return it
if (possibleProjectTypes.length === 1) {
@@ -62,3 +73,24 @@ export default async function detect(
return answer[question];
}
}
// Check if a directory has any .sol files or a /contracts folder with .sol files
function hasContracts(path: any) {
return (
["", "/contracts"].filter((p) => {
const dirPath = path + p;
if (!existsSync(dirPath)) {
return false;
}
const files = fs.readdirSync(dirPath);
const contracts = files.filter((filePath) => {
const { ext } = parse(filePath);
return ext === ".sol";
});
return contracts.length > 0;
}).length > 0
);
}
@@ -1,4 +1,3 @@
import { logger } from "../helpers/logger";
import { ProjectType } from "../types/ProjectType";
import { Detector } from "./detector";
import { existsSync } from "fs";
@@ -1,4 +1,3 @@
import { logger } from "../helpers/logger";
import { ProjectType } from "../types/ProjectType";
import { Detector } from "./detector";
import { existsSync } from "fs";
+11
View File
@@ -0,0 +1,11 @@
import { ProjectType } from "../types/ProjectType";
import { Detector } from "./detector";
import { existsSync } from "fs";
export default class NPMDetector implements Detector {
public projectType: ProjectType = "npm";
public matches(path: string): boolean {
return existsSync(path + "/package-lock.json");
}
}
@@ -1,4 +1,3 @@
import { logger } from "../helpers/logger";
import { ProjectType } from "../types/ProjectType";
import { Detector } from "./detector";
import { existsSync } from "fs";
+14
View File
@@ -0,0 +1,14 @@
import { ProjectType } from "../types/ProjectType";
import { Detector } from "./detector";
import { existsSync } from "fs";
export default class ViteDetector implements Detector {
public projectType: ProjectType = "vite";
public matches(path: string): boolean {
return (
existsSync(path + "/vite.config.js") ||
existsSync(path + "/vite.config.ts")
);
}
}
+11
View File
@@ -0,0 +1,11 @@
import { ProjectType } from "../types/ProjectType";
import { Detector } from "./detector";
import { existsSync } from "fs";
export default class YarnDetector implements Detector {
public projectType: ProjectType = "yarn";
public matches(path: string): boolean {
return existsSync(path + "/yarn.lock");
}
}
-66
View File
@@ -1,66 +0,0 @@
import { logger } from "./logger";
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())
);
}
+2 -2
View File
@@ -5,7 +5,7 @@ const { MultiSelect } = require("enquirer");
export function createContractsPrompt(
choices: { name: string; value: ContractPayload }[],
message: string
message: string,
) {
return new MultiSelect({
name: "value",
@@ -33,4 +33,4 @@ export function createContractsPrompt(
},
},
});
}
}
@@ -1,4 +1,3 @@
export interface Feature {
/**
* The name of the contract (with the .sol extension)
@@ -20,7 +19,6 @@ export interface ContractFeatures {
*/
name: string;
/**
* The features detected on the contract
*/
@@ -30,4 +28,4 @@ export interface ContractFeatures {
* The features suggested to implement on the contract
*/
suggestedFeatures: Feature[];
}
}
@@ -17,6 +17,11 @@ export interface ContractPayload {
*/
metadata: any;
/**
* The file name of the original compiled file
*/
fileName: string;
/**
* The source file paths
*/
+8 -1
View File
@@ -3,4 +3,11 @@ export type ProjectType =
| "foundry"
| "hardhat"
| "truffle"
| "unknown";
| "solc"
| "vite"
| "next"
| "cra"
| "spa-webapp"
| "yarn"
| "npm"
| "none";
+335 -155
View File
@@ -1,60 +1,95 @@
#!/usr/bin/env node
/* eslint-disable import/no-extraneous-dependencies */
import { CREATE_MESSAGES } from "../../constants/constants";
import detect from "../core/detection/detect";
import { DownloadError, createApp } from "./helpers/create-app";
import { createContract } from "./helpers/create-contract";
import { createContractProject } from "./helpers/create-contract";
import { getPkgManager } from "./helpers/get-pkg-manager";
import { validateNpmName } from "./helpers/validate-pkg";
import chalk from "chalk";
import fs from "fs";
import path from "path";
import prompts from "prompts";
let projectType: string = "";
let contractName: string = "";
let projectPath: string = "";
let projectType: string = "";
let framework: string = "";
let language: string = "";
let baseContract: string = "";
let chain: string = "";
/* let createType: string = "app"; */
export async function twCreate(options: any) {
if (typeof projectPath === "string") {
projectPath = projectPath.trim();
export async function twCreate(
pType: string,
pPath: string = "",
options: any,
) {
if (typeof pPath === "string") {
projectPath = pPath;
}
if (options.app) {
if (typeof options.contractName === "string") {
contractName = options.contractName;
}
if (pType === "app" || options.app) {
projectType = "app";
}
if (options.contract) {
} else if (pType === "contract" || options.contract) {
projectType = "contract";
}
if (options.typescript) {
language = "typescript";
if (projectType === "app") {
if (options.typescript) {
language = "typescript";
}
if (options.javascript) {
language = "javascript";
}
if (options.next) {
framework = "next";
}
if (options.cra) {
framework = "cra";
}
if (options.vite) {
framework = "vite";
}
if (options.node) {
framework = "node";
}
if (options.express) {
framework = "express";
}
if (options.reactNative) {
framework = "react-native";
}
if (options.solana) {
chain = "solana";
}
if (options.evm) {
chain = "evm";
}
}
if (options.javascript) {
language = "javascript";
}
if (projectType === "contract") {
if (options.forge) {
framework = "forge";
}
if (options.next) {
framework = "next";
}
if (options.cra) {
framework = "cra";
}
if (options.vite) {
framework = "vite";
}
if (options.forge) {
framework = "forge";
}
if (options.hardhat) {
framework = "hardhat";
if (options.hardhat) {
framework = "hardhat";
}
}
if (options.framework) {
@@ -65,7 +100,7 @@ export async function twCreate(options: any) {
const res = await prompts({
type: "select",
name: "projectType",
message: "What type of project do you want to create?",
message: CREATE_MESSAGES.typeOfProject,
choices: [
{ title: "App", value: "app" },
{ title: "Contract", value: "contract" },
@@ -75,151 +110,293 @@ export async function twCreate(options: any) {
if (typeof res.projectType === "string") {
projectType = res.projectType.trim();
}
} else if (!projectType && options.template) {
} else if (!projectType || options.template) {
// If no project type is specified, but a template is, we assume the user wants to create an app.
// We do this so old users can still use the --template flag to create an app.
projectType = "app";
}
if (!projectPath) {
const defaultName =
projectType === "contract" ? "thirdweb-contracts" : "thirdweb-app";
const res = await prompts({
type: "text",
name: "path",
message: "What is your project named?",
initial: options.template || defaultName,
validate: (name) => {
const validation = validateNpmName(path.basename(path.resolve(name)));
if (validation.valid) {
return true;
}
return "Invalid project name: " + validation.problems![0];
},
});
if (typeof res.path === "string") {
projectPath = res.path.trim();
}
}
if (!projectPath) {
console.log(
"\nPlease specify the project directory:\n" +
` ${chalk.cyan("npx thirdweb create")} ${chalk.green(
"<project-directory>",
)}\n` +
"For example:\n" +
` ${chalk.cyan("npx thirdweb create")} ${chalk.green(
"my-thirdweb-app",
)}\n\n` +
`Run ${chalk.cyan("npx thirdweb --help")} to see all options.`,
);
process.exit(1);
}
if (!options.template) {
if (projectType === "app" && !framework) {
const res = await prompts({
type: "select",
name: "framework",
message: "What framework do you want to use?",
choices: [
{ title: "Next.js", value: "next" },
{ title: "Create React App", value: "cra" },
{ title: "Vite", value: "vite" },
],
});
if (typeof res.framework === "string") {
framework = res.framework.trim();
}
}
if (projectType === "app" && !language) {
const res = await prompts({
type: "select",
name: "language",
message: "What language do you want to use?",
choices: [
{ title: "JavaScript", value: "javascript" },
{ title: "TypeScript", value: "typescript" },
],
});
if (typeof res.language === "string") {
language = res.language.trim();
}
}
// Whether to only create a new contract without the project
let onlyContract = false;
if (projectType === "contract") {
const resolvedProjectPath = path.resolve(projectPath);
const contractProjectType = await detect(resolvedProjectPath, {});
if (
projectType === "contract" &&
framework !== "forge" &&
framework !== "hardhat"
contractProjectType === "foundry" &&
fs.existsSync(path.join(resolvedProjectPath, "src"))
) {
onlyContract = true;
projectPath = path.join(projectPath, "src");
} else if (contractProjectType === "hardhat") {
if (fs.existsSync(path.join(resolvedProjectPath, "contracts"))) {
onlyContract = true;
projectPath = path.join(projectPath, "contracts");
} else {
// If there's no expected contracts folder, check for a directory that has .sol files
const directories = fs
.readdirSync(resolvedProjectPath)
.filter((file) =>
fs.lstatSync(path.join(resolvedProjectPath, file)).isDirectory(),
);
for (const directory of directories) {
const files = fs.readdirSync(path.join(projectPath, directory));
if (files.some((file) => file.endsWith(".sol"))) {
onlyContract = true;
projectPath = path.join(projectPath, directory);
break;
}
}
}
}
}
if (!onlyContract) {
if (!projectPath) {
const defaultName =
projectType === "contract" ? "thirdweb-contracts" : "thirdweb-app";
const res = await prompts({
type: "select",
name: "framework",
message: "What framework do you want to use?",
choices: [
{ title: "Hardhat", value: "hardhat" },
{ title: "Forge", value: "forge" },
],
type: "text",
name: "path",
message: CREATE_MESSAGES.projectName,
initial: options.template || defaultName,
format: (name: string) => name.toLowerCase(),
validate: (name: string) => {
const validation = validateNpmName(
path.basename(path.resolve(name.toLowerCase())),
);
if (validation.valid) {
return true;
}
return "Invalid project name: " + validation.problems?.[0];
},
});
if (typeof res.framework === "string") {
framework = res.framework.trim();
if (typeof res.path === "string") {
projectPath = path.join(projectPath, res.path.trim());
}
}
if (!projectPath) {
console.log(
"\nPlease specify the project directory:\n" +
` ${chalk.cyan("npx thirdweb create")} ${chalk.green(
"<project-directory>",
)}\n` +
"For example:\n" +
` ${chalk.cyan("npx thirdweb create")} ${chalk.green(
"my-thirdweb-app",
)}\n\n` +
`Run ${chalk.cyan("npx thirdweb --help")} to see all options.`,
);
process.exit(1);
}
if (!options.template) {
if (projectType === "app" && !chain) {
const res = await prompts({
type: "select",
name: "chain",
message: CREATE_MESSAGES.chain,
choices: [
{ title: "EVM", value: "evm" },
{ title: "Solana", value: "solana" },
],
});
if (res.chain === "solana") {
chain = "solana";
} else {
chain = "evm";
}
}
if (projectType === "app" && !framework) {
const res = await prompts({
type: "select",
name: "framework",
message: CREATE_MESSAGES.framework,
choices:
chain === "solana"
? [
{ title: "Next.js", value: "next" },
{ title: "Create React App", value: "cra" },
{ title: "Node.js", value: "node" },
{ title: "Express", value: "express" },
// Solana doesn't support Vite just yet:
// { title: "Vite", value: "vite" },
]
: [
{ title: "Next.js", value: "next" },
{ title: "Create React App", value: "cra" },
{ title: "Vite", value: "vite" },
{ title: "React Native", value: "react-native" },
{ title: "Node.js", value: "node" },
{ title: "Express", value: "express" },
],
});
if (typeof res.framework === "string") {
framework = res.framework.trim();
}
}
if (projectType === "app" && !language) {
const res = await prompts({
type: "select",
name: "language",
message: CREATE_MESSAGES.language,
choices: [
{ title: "JavaScript", value: "javascript" },
{ title: "TypeScript", value: "typescript" },
],
});
if (typeof res.language === "string") {
language = res.language.trim();
}
}
if (
projectType === "contract" &&
framework !== "forge" &&
framework !== "hardhat"
) {
const res = await prompts({
type: "select",
name: "framework",
message: CREATE_MESSAGES.framework,
choices: [
{ title: "Hardhat", value: "hardhat" },
{ title: "Forge", value: "forge" },
],
});
if (typeof res.framework === "string") {
framework = res.framework.trim();
}
}
if (!framework) {
console.log("Please specify a framework");
process.exit(1);
}
if (!language) {
// Default = JavaScript
language = "javascript";
}
}
}
if (projectType === "contract") {
// Select contract name
if (!contractName) {
const defaultName = "MyContract";
const res = await prompts({
type: "text",
name: "path",
message: CREATE_MESSAGES.contractName,
initial: defaultName,
validate: (name: string) => {
const isValid = /(^[a-z0-9A-Z]+$)|(^[a-z0-9A-Z]+\.sol$)/.test(name);
if (isValid) {
return true;
}
return `Invalid contract name 'contractName' (only alphanumeric characters allowed)`;
},
});
if (typeof res.path === "string") {
contractName = res.path.trim();
}
if (!contractName) {
console.log(
"\nPlease specify the contract name:\n" +
` ${chalk.cyan(
"npx thirdweb create --contract --name",
)} ${chalk.green("<contract-name>")}\n` +
"For example:\n" +
` ${chalk.cyan(
"npx thirdweb create --contract --name",
)} ${chalk.green("MyContract")}\n\n` +
`Run ${chalk.cyan("npx thirdweb --help")} to see all options.`,
);
process.exit(1);
}
}
// Select base contract
if (projectType === "contract" && !baseContract) {
const res = await prompts({
let standard = "none";
const standardPrompt = await prompts({
type: "select",
name: "baseContract",
message: "What contract do you want to start from?",
name: "standard",
message: CREATE_MESSAGES.contract,
choices: [
{ title: "Empty Contract", value: "" },
{ title: "ERC721 Base", value: "ERC721Base" },
{ title: "ERC721 + Signature Mint", value: "ERC721Signature" },
{ title: "ERC721 + Lazy Mint", value: "ERC721LazyMint" },
{ title: "ERC721 + Delayed Reveal", value: "ERC721DelayedReveal" },
{ title: "ERC721 + Drop", value: "ERC721Drop" },
{ title: "ERC1155 Base", value: "ERC1155Base" },
{ title: "ERC1155 + Signature Mint", value: "ERC1155Signature" },
{ title: "ERC1155 + Lazy Mint", value: "ERC1155LazyMint" },
{ title: "ERC1155 + Delayed Reveal", value: "ERC1155DelayedReveal" },
{ title: "ERC1155 + Drop", value: "ERC1155Drop" },
{ title: "ERC20 Base", value: "ERC20Base" },
{ title: "ERC20 + Vote", value: "ERC20Vote" },
{ title: "ERC20 + Signature Mint", value: "ERC20SignatureMint" },
{
title: "ERC20 + Vote + Signature Mint",
value: "ERC20SignatureMintVote",
},
{ title: "ERC20 + Drop", value: "ERC20Drop" },
{ title: "ERC20 + Vote + Drop", value: "ERC20DropVote" },
...(!onlyContract
? [{ title: "Empty Contract", value: "none" }]
: []),
{ title: "ERC721", value: "erc721" },
{ title: "ERC20", value: "erc20" },
{ title: "ERC1155", value: "erc1155" },
],
});
if (typeof res.baseContract === "string") {
baseContract = res.baseContract.trim();
if (typeof standardPrompt.standard === "string") {
standard = standardPrompt.standard.trim();
}
}
if (!framework) {
console.log("Please specify a framework");
process.exit(1);
}
if (standard === "none") {
baseContract = "";
} else {
let choices: prompts.Choice[] = [];
if (standard === "erc721") {
choices = [
{ title: "None", value: "ERC721Base" },
{ title: "Signature Mint", value: "ERC721SignatureMint" },
{ title: "Lazy Mint", value: "ERC721LazyMint" },
{ title: "Delayed Reveal", value: "ERC721DelayedReveal" },
{ title: "Drop", value: "ERC721Drop" },
];
} else if (standard === "erc20") {
choices = [
{ title: "None", value: "ERC20Base" },
{ title: "Vote", value: "ERC20Vote" },
{ title: "Signature Mint", value: "ERC20SignatureMint" },
{ title: "Vote + Signature Mint", value: "ERC20SignatureMintVote" },
{ title: "Drop", value: "ERC20Drop" },
{ title: "Vote + Drop", value: "ERC20DropVote" },
];
} else if (standard === "erc1155") {
choices = [
{ title: "None", value: "ERC1155Base" },
{ title: "Signature Mint", value: "ERC1155SignatureMint" },
{ title: "Lazy Mint", value: "ERC1155LazyMint" },
{ title: "Delayed Reveal", value: "ERC1155DelayedReveal" },
{ title: "Drop", value: "ERC1155Drop" },
];
}
if (!language) {
// Default = JavaScript
language = "javascript";
const res = await prompts({
type: "select",
name: "baseContract",
message: CREATE_MESSAGES.extensions,
choices: choices,
});
if (typeof res.baseContract === "string") {
baseContract = res.baseContract.trim();
}
}
}
}
const resolvedProjectPath = path.resolve(projectPath);
const projectName = path.basename(resolvedProjectPath);
// Resolve project path
projectPath = path.resolve(projectPath);
const projectName = path.basename(projectPath);
const { valid, problems } = validateNpmName(projectName);
if (!valid) {
@@ -229,7 +406,7 @@ export async function twCreate(options: any) {
)} because of npm naming restrictions:`,
);
problems!.forEach((p) => console.error(` ${chalk.red.bold("*")} ${p}`));
problems?.forEach((p) => console.error(` ${chalk.red.bold("*")} ${p}`));
process.exit(1);
}
@@ -251,18 +428,21 @@ export async function twCreate(options: any) {
try {
if (projectType === "app") {
await createApp({
appPath: resolvedProjectPath,
appPath: projectPath,
packageManager,
framework,
language,
template,
chain,
});
} else {
await createContract({
contractPath: resolvedProjectPath,
await createContractProject({
contractPath: projectPath,
packageManager,
framework,
contractName,
baseContract,
onlyContract,
});
}
} catch (reason) {
@@ -1,10 +1,10 @@
// Import the base contracts'
import baseContracts from "../../constants/base-contracts";
export function hasBaseContract(name: string): boolean {
return baseContracts[name as keyof typeof baseContracts] !== undefined;
}
export function readBaseContract(name: string): string {
return baseContracts[name as keyof typeof baseContracts];
}
// Import the base contracts'
import baseContracts from "../../constants/base-contracts";
export function hasBaseContract(name: string): boolean {
return baseContracts[name as keyof typeof baseContracts] !== undefined;
}
export function readBaseContract(name: string): string {
return baseContracts[name as keyof typeof baseContracts];
}
+38 -3
View File
@@ -10,6 +10,8 @@ import { makeDir } from "./make-dir";
import { downloadAndExtractRepo, hasTemplate } from "./templates";
import retry from "async-retry";
import chalk from "chalk";
import fs from "fs";
import os from "os";
import path from "path";
export class DownloadError extends Error {}
@@ -20,12 +22,14 @@ export async function createApp({
framework,
language,
template,
chain,
}: {
appPath: string;
packageManager: PackageManager;
framework?: string;
language?: string;
template?: string;
chain?: string;
}): Promise<void> {
let frameworkPath = "";
@@ -41,7 +45,9 @@ export async function createApp({
process.exit(1);
}
} else if (framework) {
frameworkPath = `${framework}-${language || "javascript"}-starter`;
frameworkPath = `${framework}-${language || "javascript"}-${
chain === "solana" ? "solana-" : ""
}starter`;
const found = await hasTemplate(frameworkPath);
if (!found) {
@@ -109,10 +115,24 @@ export async function createApp({
);
} catch (reason) {
throw new DownloadError(
isErrorLike(reason) ? reason.message : reason + "",
isErrorLike(reason) ? reason.message : String(reason),
);
}
const jsonFile = path.join(root, "package.json");
const packageString = fs.readFileSync(jsonFile);
try {
const packageJson = JSON.parse(packageString.toString());
if (packageJson) {
packageJson.name = appName;
}
fs.writeFileSync(jsonFile, JSON.stringify(packageJson, null, 2) + os.EOL);
} catch (e) {
console.log("Failed to re-name package.json, continuing...");
console.log();
}
console.log("Installing packages. This might take a couple of minutes.");
console.log();
@@ -137,10 +157,25 @@ export async function createApp({
);
} catch (reason) {
throw new DownloadError(
isErrorLike(reason) ? reason.message : reason + "",
isErrorLike(reason) ? reason.message : String(reason),
);
}
const jsonFile = path.join(root, "package.json");
const packageString = fs.readFileSync(jsonFile);
try {
const packageJson = JSON.parse(packageString.toString());
if (packageJson) {
packageJson.name = appName;
}
fs.writeFileSync(jsonFile, JSON.stringify(packageJson, null, 2) + os.EOL);
} catch (e) {
console.log("Failed to re-name package.json, continuing...");
console.log();
}
console.log("Installing packages. This might take a couple of minutes.");
console.log();
@@ -2,31 +2,38 @@ import { hasBaseContract, readBaseContract } from "./base-contracts";
import { DownloadError } from "./create-app";
import { PackageManager } from "./get-pkg-manager";
import { tryGitInit } from "./git";
import { hasForge } from "./has-forge";
import { install } from "./install";
import { isFolderEmpty } from "./is-folder-empty";
import { getOnline } from "./is-online";
import { isWriteable } from "./is-writeable";
import { makeDir } from "./make-dir";
import { downloadAndExtractRepo, hasTemplate } from "./templates";
import { submodules } from "./submodules";
import { downloadAndExtractRepo } from "./templates";
import retry from "async-retry";
import chalk from "chalk";
import fs from "fs";
import { writeFile } from "fs/promises";
import path from "path";
import { submodules } from "./submodules";
interface ICreateContract {
interface ICreateContractProject {
contractPath: string;
packageManager: PackageManager;
contractName: string;
framework?: string;
baseContract?: string;
onlyContract: boolean;
}
export async function createContract({
export async function createContractProject({
contractPath,
packageManager,
framework,
contractName,
baseContract,
}: ICreateContract) {
onlyContract,
}: ICreateContractProject) {
// Check that the selected base contract is valid
if (baseContract) {
const found = hasBaseContract(baseContract);
@@ -40,8 +47,8 @@ export async function createContract({
}
}
// Check that we have write permission for the specified directory
const root = path.resolve(contractPath);
if (!(await isWriteable(path.dirname(root)))) {
console.error(
"The application path is not writable, please check folder permissions and try again.",
@@ -52,109 +59,179 @@ export async function createContract({
process.exit(1);
}
const projectName = path.basename(root);
await makeDir(root);
if (!isFolderEmpty(root, projectName)) {
// Check that the contract name is valid
if (!/(^[a-z0-9A-Z]+$)|(^[a-z0-9A-Z]+\.sol$)/.test(contractName)) {
console.error(
`Contract name ${chalk.red(
`"${contractName}"`,
)} is not valid (only alphanumeric characters are allowed).`,
);
process.exit(1);
}
// Clean the contract name
contractName = contractName.replace(/\.sol$/, "") + ".sol";
const useYarn = packageManager === "yarn";
const isOnline = !useYarn || (await getOnline());
const originalDirectory = process.cwd();
console.log(
`Creating a new thirdweb contracts project in ${chalk.green(root)}.`,
);
console.log();
process.chdir(root);
function isErrorLike(err: unknown): err is { message: string } {
return (
typeof err === "object" &&
err !== null &&
typeof (err as { message?: unknown }).message === "string"
);
}
try {
console.log(`Downloading files. This might take a moment.`);
let starter = "";
if (framework === "hardhat") {
starter = "hardhat-javascript-starter";
} else if (framework === "forge") {
starter = "forge-starter";
} else {
console.error("Please provide a valid contracts framework.");
process.exit(1)
if (onlyContract) {
// If we are already in a contracts project, just create a new contract
if (fs.existsSync(path.join(root, contractName))) {
console.error(
`A contract with the name ${chalk.red(
`"${contractName}"`,
)} already exists in specified directory.`,
);
process.exit(1);
}
await retry(
() => downloadAndExtractRepo(root, { name: starter, filePath: "" }),
{
retries: 3,
},
);
// Modify the /contracts/MyContract.sol contents to match the base contract
if (baseContract && baseContract.length > 0) {
const baseContractText = readBaseContract(baseContract);
// Set the contents of the Contract.sol file to the base contract
let contractFile = "";
if (framework === "hardhat") {
contractFile = path.join(root, "contracts", "Contract.sol");
}
if (framework === "forge") {
contractFile = path.join(root, "src", "Contract.sol");
}
contractFile = path.join(root, contractName);
// Write the base contract to the MyContract.sol file
await writeFile(contractFile, baseContractText);
console.log(
`${chalk.green("Success!")} Created ${contractName} at ${contractPath}`,
);
}
} catch (reason) {
throw new DownloadError(isErrorLike(reason) ? reason.message : reason + "");
}
console.log("Installing packages. This might take a couple of minutes.");
console.log();
await install(root, null, { packageManager, isOnline });
console.log();
if (tryGitInit(root)) {
console.log("Initialized a git repository.");
console.log();
}
if (framework === "forge") {
await submodules();
}
let cdpath: string;
if (path.join(originalDirectory, projectName) === contractPath) {
cdpath = projectName;
} else {
cdpath = contractPath;
}
// Otherwise, create a new contracts project
const projectName = path.basename(root);
console.log(
`${chalk.green("Success!")} Created ${projectName} at ${contractPath}`,
);
console.log("Inside that directory, you can run several commands:");
console.log();
console.log(chalk.cyan(` ${packageManager} ${useYarn ? "" : "run"} deploy`));
console.log(" Deploys your contracts with the thirdweb deploy flow.");
console.log();
console.log(
chalk.cyan(` ${packageManager} ${useYarn ? "" : "run"} release`),
);
console.log(" Releases your contracts with the thirdweb release flow..");
console.log();
console.log("We suggest that you begin by typing:");
console.log();
console.log(chalk.cyan(" cd"), cdpath);
console.log();
await makeDir(root);
if (!isFolderEmpty(root, projectName)) {
process.exit(1);
}
const isOnline = !useYarn || (await getOnline());
const originalDirectory = process.cwd();
console.log(
`Creating a new thirdweb contracts project in ${chalk.green(root)}.`,
);
console.log();
process.chdir(root);
function isErrorLike(err: unknown): err is { message: string } {
return (
typeof err === "object" &&
err !== null &&
typeof (err as { message?: unknown }).message === "string"
);
}
try {
console.log(`Downloading files. This might take a moment.`);
let starter = "";
if (framework === "hardhat") {
starter = "hardhat-javascript-starter";
} else if (framework === "forge") {
starter = "forge-starter";
} else {
console.error("Please provide a valid contracts framework.");
process.exit(1);
}
await retry(
() => downloadAndExtractRepo(root, { name: starter, filePath: "" }),
{
retries: 3,
},
);
// Add in a new contracts file with specific base contract
if (baseContract && baseContract.length > 0) {
const baseContractText = readBaseContract(baseContract);
// Set the filename of the new file and delete the dummy Contract.sol file
let contractFile = "";
if (framework === "hardhat") {
fs.unlinkSync(path.join(root, "contracts", "Contract.sol"));
contractFile = path.join(root, "contracts", contractName);
}
if (framework === "forge") {
fs.unlinkSync(path.join(root, "src", "Contract.sol"));
contractFile = path.join(root, "src", contractName);
}
// Write the base contract to the new file
await writeFile(contractFile, baseContractText);
}
} catch (reason) {
throw new DownloadError(
isErrorLike(reason) ? reason.message : String(reason),
);
}
console.log("Installing packages. This might take a couple of minutes.");
console.log();
await install(root, null, { packageManager, isOnline });
console.log();
if (tryGitInit(root)) {
console.log("Initialized a git repository.");
console.log();
}
if (framework === "forge") {
await submodules();
}
let cdpath: string;
if (path.join(originalDirectory, projectName) === contractPath) {
cdpath = projectName;
} else {
cdpath = contractPath;
}
console.log(
`${chalk.green("Success!")} Created ${projectName} at ${contractPath}`,
);
console.log();
console.log("Inside that directory, you can run several commands:");
console.log();
console.log(
chalk.cyan(` ${packageManager}${useYarn ? "" : " run"} build`),
);
console.log(
" Compiles your contracts and detects thirdweb extensions implemented on them.",
);
console.log();
console.log(
chalk.cyan(` ${packageManager}${useYarn ? "" : " run"} deploy`),
);
console.log(" Deploys your contracts with the thirdweb deploy flow.");
console.log();
console.log(
chalk.cyan(` ${packageManager}${useYarn ? "" : " run"} release`),
);
console.log(" Releases your contracts with the thirdweb release flow.");
console.log();
console.log("We suggest that you begin by typing:");
console.log();
console.log(chalk.cyan(" cd"), cdpath);
console.log();
if (framework === "forge") {
const isInstalled = await hasForge();
if (!isInstalled) {
console.log(
`\n${chalk.yellowBright(
`warning`,
)} You don't have forge installed on this machine, and will need it to run this project! \n\nYou can install forge by following these instructions:\n${chalk.blueBright(
`https://book.getfoundry.sh/getting-started/installation`,
)}\n`,
);
}
}
}
}
@@ -4,6 +4,7 @@ export type PackageManager = "npm" | "pnpm" | "yarn";
export function getPkgManager(): PackageManager {
try {
// eslint-disable-next-line turbo/no-undeclared-env-vars
const userAgent = process.env.npm_config_user_agent;
if (userAgent) {
if (userAgent.startsWith("yarn")) {
+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;
@@ -0,0 +1,14 @@
import { exec } from "child_process";
export async function hasForge() {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
return await new Promise((resolve, reject) => {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
exec("forge --version", (error, stdout, stderr) => {
if (error) {
resolve(false);
}
resolve(true);
});
});
}
+7 -3
View File
@@ -1,7 +1,7 @@
/* eslint-disable import/no-extraneous-dependencies */
import type { PackageManager } from "./get-pkg-manager";
import chalk from "chalk";
import spawn from "cross-spawn";
import type { PackageManager } from "./get-pkg-manager";
interface InstallArgs {
/**
@@ -53,9 +53,13 @@ export function install(
* Call `yarn add --exact (--offline)? (-D)? ...`.
*/
args = ["add", "--exact"];
if (!isOnline) args.push("--offline");
if (!isOnline) {
args.push("--offline");
}
args.push("--cwd", root);
if (devDependencies) args.push("--dev");
if (devDependencies) {
args.push("--dev");
}
args.push(...dependencies);
} else {
/**
+3 -1
View File
@@ -3,7 +3,9 @@ import dns from "dns";
import url from "url";
function getProxy(): string | undefined {
// eslint-disable-next-line turbo/no-undeclared-env-vars
if (process.env.https_proxy) {
// eslint-disable-next-line turbo/no-undeclared-env-vars
return process.env.https_proxy;
}
@@ -33,7 +35,7 @@ export function getOnline(): Promise<boolean> {
}
dns.lookup(hostname, (proxyErr) => {
resolve(proxyErr == null);
resolve(proxyErr === null);
});
});
});

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