Compare commits

...
Author SHA1 Message Date
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
211 changed files with 16437 additions and 4342 deletions
+1 -1
View File
@@ -7,7 +7,7 @@
"access": "public",
"baseBranch": "main",
"updateInternalDependencies": "patch",
"ignore": [],
"ignore": ["e2e-cra-5", "e2e-next-12", "e2e-vite-3"],
"___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH": {
"updateInternalDependents": "always",
"onlyUpdatePeerDependentsWhenOutOfRange": true
+2 -2
View File
@@ -9,10 +9,10 @@ runs:
with:
node-version: 16
registry-url: "https://registry.npmjs.org"
# cache: "yarn"
cache: "yarn"
- name: Install dependencies
shell: bash
run: yarn install --frozen-lockfile
run: yarn install
env:
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: "1"
+1 -2
View File
@@ -14,8 +14,7 @@ jobs:
build:
name: Build and Test
# timeout-minutes: 15
# try out buildjet to speed up build and test
runs-on: buildjet-32vcpu-ubuntu-2204
runs-on: ubuntu-latest-32
# To use Remote Caching, uncomment the next lines and follow the steps below.
env:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
+1 -8
View File
@@ -13,8 +13,7 @@ concurrency:
jobs:
integration-tests:
timeout-minutes: 60
# try out buildjet to speed up build and test
runs-on: buildjet-32vcpu-ubuntu-2204
runs-on: ubuntu-latest-32
steps:
- uses: actions/checkout@v3
@@ -28,9 +27,3 @@ jobs:
run: yarn e2e
env:
NODE_OPTIONS: "--max_old_space_size=4096"
- uses: actions/upload-artifact@v2
if: always()
with:
name: playwright-report
path: packages/react/playwright-report/
retention-days: 30
+1 -2
View File
@@ -40,8 +40,7 @@ jobs:
comment_body: ${{ github.event.comment.body }}
deploy:
# try out buildjet to speed up build and test
runs-on: buildjet-32vcpu-ubuntu-2204
runs-on: ubuntu-latest-32
needs: deploy-check
if: needs.deploy-check.outputs.triggered == 'true'
steps:
+1 -2
View File
@@ -14,8 +14,7 @@ concurrency:
jobs:
release:
name: Release
# try out buildjet to speed up build and test
runs-on: buildjet-32vcpu-ubuntu-2204
runs-on: ubuntu-latest-32
steps:
- name: Checkout branch
uses: actions/checkout@v3
+3 -2
View File
@@ -4,8 +4,7 @@ on: [pull_request]
jobs:
build:
# try out buildjet to speed up build and test
runs-on: buildjet-32vcpu-ubuntu-2204
runs-on: ubuntu-latest-32
steps:
- uses: actions/checkout@v3
@@ -16,3 +15,5 @@ jobs:
repo-token: "${{ secrets.GITHUB_TOKEN }}"
strip-hash: "\\-(\\w{8})(?:\\.esm|\\.cjs)"
minimum-change-threshold: 100
# exclude source maps, node modules and any test related files
exclude: "{**/*.map,**/node_modules/**,**/test/**}"
+6 -1
View File
@@ -12,4 +12,9 @@ server/dist
public/dist
.turbo
./packages/contracts-js/abis
.idea/
.idea/
.yalc/
yalc.lock
build/
playwright-report/
.vscode/
-1
View File
@@ -1,4 +1,3 @@
lib
node_modules
dist
contracts-js
+5 -9
View File
@@ -8,14 +8,15 @@
"private": true,
"workspaces": {
"packages": [
"packages/*"
"packages/*",
"test/**/*"
]
},
"scripts": {
"test": "turbo run test test:evm test:solana",
"e2e": "turbo run e2e",
"build": "turbo run build",
"build:release": "turbo run build --force",
"build": "turbo run build --filter=./packages/*",
"build:release": "turbo run build --force --filter=./packages/*",
"dev": "turbo run dev --no-cache --parallel --continue",
"fix:repo": "manypkg fix",
"lint": "turbo run lint",
@@ -27,12 +28,7 @@
"release": "node ./scripts/pre-release.mjs && changeset publish",
"version-packages:nightly": "changeset version --snapshot nightly",
"release:nightly": "node ./scripts/pre-release.mjs && changeset publish --tag nightly",
"push": "turbo run push"
},
"preconstruct": {
"packages": [
"packages/*"
]
"push": "turbo run push --filter=./packages/*"
},
"packageManager": "[email protected]",
"dependencies": {
+32
View File
@@ -1,5 +1,37 @@
# @thirdweb-dev/auth
## 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
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/auth",
"version": "2.0.19",
"version": "2.0.31",
"exports": {
"./next": {
"module": "./next/dist/thirdweb-dev-auth-next.esm.js",
+7 -1
View File
@@ -70,7 +70,13 @@ export function ThirdwebAuth(cfg: ThirdwebAuthConfig) {
if (token) {
try {
const address = await sdk.auth.authenticate(domain, token);
user = { address };
let data = {};
if (ctx.callbacks?.user) {
data = await ctx.callbacks.user(address);
}
user = { ...data, address };
} catch {
// No-op
}
+7 -1
View File
@@ -70,7 +70,13 @@ export function ThirdwebAuth(cfg: ThirdwebAuthConfig) {
if (token) {
try {
const address = await sdk.auth.authenticate(domain, token);
user = { address };
let data = {};
if (ctx.callbacks?.user) {
data = await ctx.callbacks.user(address);
}
user = { ...data, address };
} catch {
// No-op
}
+86
View File
@@ -1,5 +1,91 @@
# thirdweb
## 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
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "thirdweb",
"main": "dist/cli/index.js",
"version": "0.8.5",
"version": "0.8.17",
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/cli",
"author": "thirdweb eng <[email protected]>",
"license": "Apache-2.0",
+3 -3
View File
@@ -40,7 +40,7 @@ $$$$$$\\ $$$$$$$\\ $$\\ $$$$$$\\ $$$$$$$ |$$\\ $$\\ $$\\ $$$$$$\\ $$$$
.version(cliVersion, "-v, --version");
program
.command("create")
.command("create [projectPath]")
.description(
"Create a web3 app from any of our official templates: https://github.com/thirdweb-example/",
)
@@ -66,8 +66,8 @@ $$$$$$\\ $$$$$$$\\ $$\\ $$$$$$\\ $$$$$$$ |$$\\ $$\\ $$\\ $$$$$$\\ $$$$
"-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);
.action(async (path, options) => {
await twCreate(path, options);
});
program
+3 -3
View File
@@ -17,9 +17,9 @@ let language: string = "";
let baseContract: string = "";
/* let createType: string = "app"; */
export async function twCreate(options: any) {
if (typeof projectPath === "string") {
projectPath = projectPath.trim();
export async function twCreate(pPath: string = "", options: any) {
if (typeof pPath === "string") {
projectPath = pPath;
}
if (options.app) {
+14
View File
@@ -1,5 +1,19 @@
# @thirdweb-dev/contracts-js
## 1.2.1
### Patch Changes
- [#376](https://github.com/thirdweb-dev/js/pull/376) [`4af785c`](https://github.com/thirdweb-dev/js/commit/4af785c37c831ecd7087c1c9eb9095922cac0855) Thanks [@adam-maj](https://github.com/adam-maj)! - Update tiered-drop with tokens claimed
- [#373](https://github.com/thirdweb-dev/js/pull/373) [`6f818f3`](https://github.com/thirdweb-dev/js/commit/6f818f393eb9023b53a3358d62d60ec23a9246bd) Thanks [@adam-maj](https://github.com/adam-maj)! - Update tiered-drop contract version
## 1.2.0
### Minor Changes
- [#333](https://github.com/thirdweb-dev/js/pull/333) [`aa6d9ed`](https://github.com/thirdweb-dev/js/commit/aa6d9ed75d126d7a0cca9fb8fc389ff94e9d1e14) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Update to contracts package v3.2.0
## 1.1.9
### Patch Changes
@@ -0,0 +1,4 @@
{
"main": "dist/thirdweb-dev-contracts-js-factories-TWMultichainRegistry__factory.cjs.js",
"module": "dist/thirdweb-dev-contracts-js-factories-TWMultichainRegistry__factory.esm.js"
}
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "@thirdweb-dev/contracts-js",
"version": "1.1.9",
"version": "1.2.1",
"main": "dist/thirdweb-dev-contracts-js.cjs.js",
"module": "dist/thirdweb-dev-contracts-js.esm.js",
"license": "Apache-2.0",
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/contracts-js",
"dependencies": {
"@thirdweb-dev/contracts": "3.1.8-0"
"@thirdweb-dev/contracts": "^3.2.2"
},
"devDependencies": {
"@babel/preset-env": "^7.18.10",
+105
View File
@@ -1,5 +1,110 @@
# @thirdweb-dev/react
## 3.6.1
### Patch Changes
- [#392](https://github.com/thirdweb-dev/js/pull/392) [`846022e`](https://github.com/thirdweb-dev/js/commit/846022e2cfb12b846aae029b85899fb72fd2c9ad) Thanks [@nachoiacovino](https://github.com/nachoiacovino)! - Fix unlimited price for useActiveClaimConditionForWallet
- [#388](https://github.com/thirdweb-dev/js/pull/388) [`7c0744a`](https://github.com/thirdweb-dev/js/commit/7c0744a006e78987ad8b271b2c64f4bac7759510) Thanks [@adam-maj](https://github.com/adam-maj)! - Add support for total supply and pagination on get all
## 3.6.0
### Patch Changes
- [#386](https://github.com/thirdweb-dev/js/pull/386) [`612bcdd`](https://github.com/thirdweb-dev/js/commit/612bcdd16fc242d8b04bc70d609e44b0df47ed3f) Thanks [@jnsdls](https://github.com/jnsdls)! - [Gnosis Connector] - switch to safe.global hostname & add support for bsc and optimism chains
- [#364](https://github.com/thirdweb-dev/js/pull/364) [`7cb8e59`](https://github.com/thirdweb-dev/js/commit/7cb8e59be48dc923316eb36ab43a1bef7364f6b1) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Add experimental `useActiveClaimConditionForWallet()` hook
## 3.5.2
### Patch Changes
- [#369](https://github.com/thirdweb-dev/js/pull/369) [`2d3fca5`](https://github.com/thirdweb-dev/js/commit/2d3fca52373046c88392139922e9c9da79f9a2d4) Thanks [@nachoiacovino](https://github.com/nachoiacovino)! - Add useClaimerProofs hook
- [#378](https://github.com/thirdweb-dev/js/pull/378) [`32d91ef`](https://github.com/thirdweb-dev/js/commit/32d91ef709c8fb92a903e52c65b27538a80ea4eb) Thanks [@mykcryptodev](https://github.com/mykcryptodev)! - round borders for buttons and menus on safari
- [#377](https://github.com/thirdweb-dev/js/pull/377) [`b79dc18`](https://github.com/thirdweb-dev/js/commit/b79dc18f7ab5155bbf4af02dc1a953546160bad0) Thanks [@adam-maj](https://github.com/adam-maj)! - Add event filters
## 3.5.1
## 3.5.0
## 3.4.5
## 3.4.4
### Patch Changes
- [#356](https://github.com/thirdweb-dev/js/pull/356) [`cc4613b`](https://github.com/thirdweb-dev/js/commit/cc4613b5fd69840f4f9cfd0ac1c4e6743e62fe52) Thanks [@nachoiacovino](https://github.com/nachoiacovino)! - Add Pack and Multiwrap as NFTContract types
## 3.4.3
### Patch Changes
- [#353](https://github.com/thirdweb-dev/js/pull/353) [`1c24c3c`](https://github.com/thirdweb-dev/js/commit/1c24c3c3c48476a824f817e09e7bf0fbe67c1db5) Thanks [@nachoiacovino](https://github.com/nachoiacovino)! - Add useUpdateCreators hook, fix CreatorInput schema
## 3.4.2
### Patch Changes
- [#348](https://github.com/thirdweb-dev/js/pull/348) [`7d9a4c6`](https://github.com/thirdweb-dev/js/commit/7d9a4c6abcad1b4f43e431ce7b0b38db9016ea9a) Thanks [@nachoiacovino](https://github.com/nachoiacovino)! - [SOL] - add `useUpdateRoyaltySettings()` hook
[SOL] - change `useRoyalty()` hook -> `useRoyaltySettings()` for consistency
- [#352](https://github.com/thirdweb-dev/js/pull/352) [`3522917`](https://github.com/thirdweb-dev/js/commit/352291791ee900e1500f84f095290497934d2f60) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - expose useExecuteAuctionSale and useAcceptDirectListingOffer
- [#351](https://github.com/thirdweb-dev/js/pull/351) [`aabbb14`](https://github.com/thirdweb-dev/js/commit/aabbb149e81d6824015a4797b2b6c929ca359545) Thanks [@jnsdls](https://github.com/jnsdls)! - [EVM] - fix wallet connection during dev (hot reload)
## 3.4.1
### Patch Changes
- [#343](https://github.com/thirdweb-dev/js/pull/343) [`72227b2`](https://github.com/thirdweb-dev/js/commit/72227b2e166a3a68bbb41cf2b389322f5b7547a2) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Expose new useOffers and useMakeOffer hooks
## 3.4.0
### Minor Changes
- [#327](https://github.com/thirdweb-dev/js/pull/327) [`ef27aad`](https://github.com/thirdweb-dev/js/commit/ef27aad0aafc4577e85f44dc77dfbe880bd239b5) Thanks [@jnsdls](https://github.com/jnsdls)! - Gnosis Safe and Magic Link connectors are no longer included in the default export. They are now available as named exports instead.
## Gnosis Safe
### Connector
```diff
- import { GnosisSafeConnector } from "@thirdweb-dev/react";
+ import { GnosisSafeConnector } from "@thirdweb-dev/react/evm/connectors/gnosis-safe";
```
### Hook
```diff
- import { useGnosis } from "@thirdweb-dev/react";
+ import { useGnosis } from "@thirdweb-dev/react/evm/connectors/gnosis-safe";
```
## Magic Link
### Connector
```diff
- import { MagicLink } from "@thirdweb-dev/react";
+ import { MagicConnector } from "@thirdweb-dev/react/evm/connectors/magic";
```
### Hook
```diff
- import { useMagic } from "@thirdweb-dev/react";
+ import { useMagic } from "@thirdweb-dev/react/evm/connectors/magic";
```
### Patch Changes
- [#339](https://github.com/thirdweb-dev/js/pull/339) [`b03a902`](https://github.com/thirdweb-dev/js/commit/b03a9021451b79f802f682f66e5ae8e9355d7e6f) Thanks [@nachoiacovino](https://github.com/nachoiacovino)! - Fix docs for some functions
## 3.3.1
## 3.3.0
## 3.2.6
+2 -2
View File
@@ -26,11 +26,11 @@ The `useContractCall` hook to call a smart contract function has been renamed to
### Prebuilt Contract Hooks
Hooks to connect to prebuilt contracts such as `useNFTDrop` and `useMarketplace` are now deprecated in favour of using the generic `useContract` hook:
Hooks to connect to prebuilt contracts such as `useNFTDrop` and `useMarketplace` are now deprecated in favour of using the generic `useContract` hook followed by the contract type:
```diff
- const contract = useNFTDrop("0x...")
+ const { contract } = useContract("0x...")
+ const { contract } = useContract("0x...", "nft-drop")
```
You can currently still continue to use the prebuilt hooks, however they provide a warning in the console informing you of their deprecation. We will likely remove these hooks in a future version of the SDK.
+1 -1
View File
@@ -191,7 +191,7 @@ export default function Home() {
<div>
{nfts.map((nft) => (
<ThirdwebNftMedia
key={nft.tokenId}
key={nft.metadata.id}
metadata={nft.metadata}
height={200}
/>
@@ -0,0 +1,4 @@
{
"main": "dist/thirdweb-dev-react-evm-connectors-gnosis-safe.cjs.js",
"module": "dist/thirdweb-dev-react-evm-connectors-gnosis-safe.esm.js"
}
@@ -0,0 +1,4 @@
{
"main": "dist/thirdweb-dev-react-evm-connectors-magic.cjs.js",
"module": "dist/thirdweb-dev-react-evm-connectors-magic.esm.js"
}
+19 -18
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/react",
"version": "3.3.0",
"version": "3.6.1",
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/react",
"author": "thirdweb eng <[email protected]>",
"license": "Apache-2.0",
@@ -20,6 +20,14 @@
"module": "./solana/dist/thirdweb-dev-react-solana.esm.js",
"default": "./solana/dist/thirdweb-dev-react-solana.cjs.js"
},
"./evm/connectors/magic": {
"module": "./evm/connectors/magic/dist/thirdweb-dev-react-evm-connectors-magic.esm.js",
"default": "./evm/connectors/magic/dist/thirdweb-dev-react-evm-connectors-magic.cjs.js"
},
"./evm/connectors/gnosis-safe": {
"module": "./evm/connectors/gnosis-safe/dist/thirdweb-dev-react-evm-connectors-gnosis-safe.esm.js",
"default": "./evm/connectors/gnosis-safe/dist/thirdweb-dev-react-evm-connectors-gnosis-safe.cjs.js"
},
"./package.json": "./package.json"
},
"files": [
@@ -31,7 +39,8 @@
"entrypoints": [
"index.ts",
"solana/index.ts",
"evm/index.ts"
"evm/index.ts",
"evm/connectors/**.ts"
],
"___experimentalFlags_WILL_CHANGE_IN_PATCH": {
"exports": true
@@ -49,8 +58,6 @@
"generate-docs:solana": "api-extractor run --local --config ./config/api-extractor-solana.json && api-documenter markdown -i ./temp -o ./docs/solana && rm -rf ./temp-solana && mv ./temp ./temp-solana",
"generate-snippets": "node ./scripts/generate-snippets-evm.mjs && node ./scripts/generate-snippets-solana.mjs && node ./scripts/generate-feature-snippets-evm.mjs",
"build": "tsc && preconstruct build",
"e2e": "yarn test-ct",
"test-ct": "playwright test -c playwright-ct.config.ts",
"push": "yalc push"
},
"sideEffects:": false,
@@ -63,15 +70,11 @@
"@microsoft/api-documenter": "^7.19.4",
"@microsoft/api-extractor": "^7.29.2",
"@microsoft/tsdoc": "^0.14.1",
"@playwright/experimental-ct-react": "1.25.1",
"@playwright/test": "1.25.1",
"@preconstruct/cli": "^2.2.1",
"@solana/wallet-adapter-base": "^0.9.17",
"@solana/wallet-adapter-react": "^0.15.19",
"@solana/wallet-adapter-wallets": "^0.19.0",
"@solana/web3.js": "^1.63.0",
"@solana/web3.js": "^1.62.0",
"@thirdweb-dev/sdk": "*",
"@thirdweb-dev/solana": "*",
"@types/color": "^3.0.3",
"@types/mime": "^3.0.1",
"@types/react": "^18.0.5",
@@ -93,13 +96,14 @@
"dependencies": {
"@emotion/react": "^11.10.0",
"@emotion/styled": "^11.10.0",
"@gnosis.pm/safe-core-sdk": "2.1.0",
"@gnosis.pm/safe-ethers-adapters": "0.1.0-alpha.9",
"@gnosis.pm/safe-ethers-lib": "1.1.0",
"@gnosis.pm/safe-core-sdk": "^3.1.1",
"@gnosis.pm/safe-ethers-adapters": "0.1.0-alpha.11",
"@gnosis.pm/safe-ethers-lib": "^1.6.1",
"@react-icons/all-files": "^4.1.0",
"@solana/wallet-adapter-phantom": "^0.9.17",
"@tanstack/react-query": "^4.0.10",
"@zag-js/menu": "0.1.14",
"@zag-js/react": "0.1.16",
"@zag-js/menu": "^0.1.16",
"@zag-js/react": "^0.1.18",
"buffer": "^6.0.3",
"color": "^4.2.3",
"copy-to-clipboard": "^3.3.2",
@@ -114,7 +118,7 @@
"@solana/wallet-adapter-base": "^0.9.17",
"@solana/wallet-adapter-react": "^0.15.19",
"@solana/wallet-adapter-wallets": "^0.19.0",
"@solana/web3.js": "^1.63.0",
"@solana/web3.js": "^1.62.0",
"@thirdweb-dev/sdk": "*",
"ethers": ">=5.5.1",
"react": ">=18.0.0",
@@ -136,9 +140,6 @@
},
"@solana/web3.js": {
"optional": true
},
"@thirdweb-dev/solana": {
"optional": true
}
}
}
-57
View File
@@ -1,57 +0,0 @@
import type { PlaywrightTestConfig } from "@playwright/experimental-ct-react";
import { devices } from "@playwright/experimental-ct-react";
/**
* See https://playwright.dev/docs/test-configuration.
*/
const config: PlaywrightTestConfig = {
testDir: "./",
/* The base directory, relative to the config file, for snapshot files created with toMatchSnapshot and toHaveScreenshot. */
snapshotDir: "./__snapshots__",
/* Maximum time one test can run for. */
timeout: 10 * 1000,
/* Run tests in files in parallel */
fullyParallel: true,
/* Fail the build on CI if you accidentally left test.only in the source code. */
forbidOnly: !!process.env.CI,
/* Retry on CI only */
retries: process.env.CI ? 2 : 0,
/* Opt out of parallel tests on CI. */
// workers: process.env.CI ? 1 : undefined,
// we can use parallel tests because we have more cores now
workers: undefined,
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
reporter: "html",
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
use: {
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
trace: "on-first-retry",
/* Port to use for Playwright component endpoint. */
ctPort: 3100,
},
/* Configure projects for major browsers */
projects: [
{
name: "chromium",
use: {
...devices["Desktop Chrome"],
},
},
{
name: "firefox",
use: {
...devices["Desktop Firefox"],
},
},
{
name: "webkit",
use: {
...devices["Desktop Safari"],
},
},
],
};
export default config;
-4
View File
@@ -1,4 +0,0 @@
// Import styles, initialize component theme here.
// import '../src/common.css';
export {};
@@ -1,15 +0,0 @@
import { ConnectWallet } from ".";
import { ThirdwebProvider } from "../../providers/full";
import { test, expect } from "@playwright/experimental-ct-react";
test.use({ viewport: { width: 500, height: 500 } });
test("should render the connect wallet button", async ({ mount }) => {
const component = await mount(
<ThirdwebProvider desiredChainId={1}>
<ConnectWallet />
</ThirdwebProvider>,
);
await expect(component).toContainText("Connect Wallet");
});
@@ -144,7 +144,7 @@ export const ConnectWallet: React.FC<ConnectWalletProps> = ({
},
connect,
] = useConnect();
const disconnect = useDisconnect({ reconnectAfterGnosis: false });
const disconnect = useDisconnect({ reconnectPrevious: false });
const supportedConnectors = connectors.filter((c) =>
SUPPORTED_CONNECTORS.includes(c.id as typeof SUPPORTED_CONNECTORS[number]),
@@ -13,12 +13,14 @@ export interface ThirdwebNftMediaProps extends SharedMediaProps {
}
/**
*
* This component can be used to render NFTs from the thirdweb SDK.
* Props: {@link ThirdwebNftMediaProps}
*
* @example
* ```jsx
* import { ThirdwebNftMedia, useNFTCollection, useNFT } from "@thirdweb-dev/react";
* import { ThirdwebNftMedia, useContract, useNFT } from "@thirdweb-dev/react";
* export default function NFTCollectionRender() {
* const contract = useNFTCollection(<your-contract-address>);
* const { contract } = useContract(<your-contract-address>);
* const { data: nft, isLoading } = useNFT(contract, 0);
*
* return (
@@ -32,11 +34,6 @@ export interface ThirdwebNftMediaProps extends SharedMediaProps {
* );
* }
* ```
*
* Use this to get the primary sales recipient of your {@link SmartContract}
* @param contract - an instance of a {@link SmartContract}
* @returns the wallet address of the primary sales recipient
* @beta
*/
export const ThirdwebNftMedia = React.forwardRef<
HTMLMediaElement,
@@ -1,16 +0,0 @@
import { Web3Button } from ".";
import { ThirdwebProvider } from "../../providers/full";
import { test, expect } from "@playwright/experimental-ct-react";
test.use({ viewport: { width: 500, height: 500 } });
test("should render the connect wallet button", async ({ mount }) => {
const component = await mount(
<ThirdwebProvider desiredChainId={1}>
<Web3Button contractAddress="0x123" action={() => undefined}>
Do the thing
</Web3Button>
</ThirdwebProvider>,
);
await expect(component).toContainText("Connect Wallet");
});
@@ -37,8 +37,19 @@ const BaseButton = styled.button<BaseButtonProps>`
cursor: not-allowed;
}
&:focus {
outline: 2px solid ${(props) => (props.theme as TwUiTheme).colors.accent};
outline-offset: 1px;
position: relative;
border-radius: 0.5em;
outline: 0;
}
&:focus:after {
content: "";
position: absolute;
top: -5px;
right: -5px;
bottom: -5px;
left: -5px;
border: 2px solid ${(props) => (props.theme as TwUiTheme).colors.accent};
border-radius: 11px;
}
`;
@@ -8,10 +8,23 @@ export const Menu = styled.ul<MenuProps>`
margin: 0;
background: ${(props) => (props.theme as TwUiTheme).colors.background};
color: ${(props) => (props.theme as TwUiTheme).colors.text};
outline-color: ${(props) => (props.theme as TwUiTheme).colors.accent};
outline-style: solid;
outline-offset: 0px;
list-style: none;
border-radius: 0.5em;
overflow: hidden;
position: relative;
pointer-events: auto;
&:focus {
outline: 0;
}
&:after {
content: "";
position: absolute;
border: 1px solid ${(props) => (props.theme as TwUiTheme).colors.accent};
border-radius: 0.5em;
pointer-events: none;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
`;
@@ -1,14 +1,29 @@
import { ChainId } from "@thirdweb-dev/sdk";
import { useConnect } from "../hooks/wagmi-required/useConnect";
import { Signer, ethers, utils } from "ethers";
import invariant from "tiny-invariant";
import { Chain, Connector, ConnectorData, normalizeChainId } from "wagmi";
import {
Chain,
Connector,
ConnectorData,
normalizeChainId,
useContext as useWagmiContext,
} from "wagmi";
// exerpt from https://docs.gnosis-safe.io/backend/available-services
const CHAIN_ID_TO_GNOSIS_SERVER_URL = {
[ChainId.Mainnet]: "https://safe-transaction.mainnet.gnosis.io",
[ChainId.Avalanche]: "https://safe-transaction.avalanche.gnosis.io",
[ChainId.Polygon]: "https://safe-transaction.polygon.gnosis.io",
[ChainId.Goerli]: "https://safe-transaction.goerli.gnosis.io",
};
// mainnet
1: "https://safe-transaction-mainnet.safe.global",
// avalanche
43114: "https://safe-transaction-avalanche.safe.global",
// polygon
137: "https://safe-transaction-polygon.safe.global",
// goerli
5: "https://safe-transaction-goerli.safe.global",
// bsc
56: "https://safe-transaction-bsc.safe.global",
// optimism
10: "https://safe-transaction-optimism.safe.global",
} as const;
export interface GnosisConnectorArguments {
safeAddress: string;
@@ -18,6 +33,8 @@ export interface GnosisConnectorArguments {
const __IS_SERVER__ = typeof window === "undefined";
export class GnosisSafeConnector extends Connector {
static supportedChains = Object.keys(CHAIN_ID_TO_GNOSIS_SERVER_URL);
public supportedChains = GnosisSafeConnector.supportedChains;
id = "gnosis";
ready = __IS_SERVER__;
name = "Gnosis Safe";
@@ -50,6 +67,11 @@ export class GnosisSafeConnector extends Connector {
};
}
public isChainSupported(chainId: string | number) {
const id = normalizeChainId(chainId);
return !this.isChainUnsupported(id);
}
private async createSafeSigner() {
const signer = await this.previousConnector?.getSigner();
const safeAddress = this.config?.safeAddress;
@@ -163,3 +185,67 @@ export class GnosisSafeConnector extends Connector {
this.config = config;
}
}
/**
* Hook for connecting to a Gnosis Safe. This enables multisig wallets to connect to your application and sing transactions.
*
* ```javascript
* import { useGnosis } from "@thirdweb-dev/react"
* ```
*
*
* @example
* ```javascript
* import { useGnosis } from "@thirdweb-dev/react"
*
* const App = () => {
* const connectWithGnosis = useGnosis()
*
* return (
* <button onClick={() => connectWithGnosis({ safeAddress: "0x...", safeChainId: 1 })}>
* Connect Gnosis Safe
* </button>
* )
* }
* ```
*
* @public
*/
export function useGnosis() {
const wagmiContext = useWagmiContext();
invariant(
wagmiContext,
`useGnosis() can only be used inside <ThirdwebProvider />. If you are using <ThirdwebSDKProvider /> you will have to use your own wallet-connection logic.`,
);
const [connectors, connect] = useConnect();
if (connectors.loading) {
return () => Promise.reject("Gnosis connector not ready to be used, yet");
}
const connector = connectors.data.connectors.find((c) => c.id === "gnosis");
invariant(
connector,
"Gnosis connector not found, please make sure it is provided to your <ThirdwebProvider />",
);
return async (config: GnosisConnectorArguments) => {
const previousConnector = connectors.data.connector;
const previousConnectorChain = await previousConnector?.getChainId();
invariant(
!!previousConnector,
"Cannot connect to Gnosis Safe without first being connected to a personal wallet.",
);
invariant(
previousConnectorChain === config.safeChainId,
"Gnosis safe chain id must match personal wallet chain id.",
);
invariant(
utils.isAddress(config.safeAddress),
"Gnosis safe address must be a valid address.",
);
(connector as GnosisSafeConnector).setConfiguration(
previousConnector,
config,
);
return connect(connector);
};
}
+93 -1
View File
@@ -1,3 +1,4 @@
import { useConnect } from "../hooks/wagmi-required/useConnect";
import { providers, utils } from "ethers";
import type {
LoginWithMagicLinkConfiguration,
@@ -5,7 +6,12 @@ import type {
MagicSDKAdditionalConfiguration,
} from "magic-sdk";
import invariant from "tiny-invariant";
import { Chain, Connector, normalizeChainId } from "wagmi";
import {
Chain,
Connector,
normalizeChainId,
useContext as useWagmiContext,
} from "wagmi";
export interface MagicConnectorArguments
extends MagicSDKAdditionalConfiguration {
@@ -186,3 +192,89 @@ export class MagicConnector extends Connector {
}
}
}
/**
* Hook for connecting to an email wallet using magic link.
* This enables users without their own wallets to connect to your application and sign transactions securely using their email.
*
* ```javascript
* import { useMagic } from "@thirdweb-dev/react"
* ```
*
*
* @example
* Before using this hook, you first need to set up the magic configuration in your `ThirdwebProvider`, including your magic API key.
*
* ```javascript
* // Add the magic configuration object to your wallet connectors
* const connectors = [
* "metamask",
* "walletConnect",
* "walletLink",
* {
* name: "magic",
* options: {
* apiKey: "your-magic-api-key",
* }
* }
* ]
*
* // Add the above to the walletConnectors prop of your <ThirdwebProvider />
* const Provider = ({ children }) => (
* return (
* <ThirdwebProvider
* walletConnectors={connectors}
* // Specify remaining parameters
* ...
* >
* {children}
* </ThirdwebProvider>
* )
* }
* ```
*
* In order to use the hook to connect users with magic link, you just need to provide the users email to the connect function.
*
* You can setup the hook with the following configuration:
* ```javascript
* import { useMagic } from "@thirdweb-dev/react"
* import { useState } from "react"
*
* const LoginWithMagicLink = () => {
* const connectWithMagic = useMagic()
* const [email, setEmail] = useState()
*
* return (
* <div>
* <input value={email} onChange={(e) => setEmail(e.target.value)} />
* <button onClick={() => connectWithMagic({ email })}>Login</button>
* </div>
* )
* }
* ```
*
* @public
*/
export function useMagic() {
const wagmiContext = useWagmiContext();
invariant(
wagmiContext,
`useMagic() can only be used inside <ThirdwebProvider />. If you are using <ThirdwebSDKProvider /> you will have to use your own wallet-connection logic.`,
);
const [connectors, connect] = useConnect();
if (connectors.loading) {
return () => Promise.reject("Magic connector not ready to be used, yet");
}
const connector = connectors.data.connectors.find((c) => c.id === "magic");
invariant(
connector,
"Magic connector not found, please make sure it is provided to your <ThirdwebProvider />",
);
return (configuration: LoginWithMagicLinkConfiguration) => {
(connector as MagicConnector).setConfiguration(configuration);
return connect(connector);
};
}
@@ -33,7 +33,17 @@ export const ThirdwebConnectedWalletProvider: React.FC<
const { rpcUrlMap } = useThirdwebConfigContext();
const [contextValue, setContextValue] =
useState<ThirdwebConnectedWalletContext>(INITIAL_CONTEXT_VALUE);
useState<ThirdwebConnectedWalletContext>({
...INITIAL_CONTEXT_VALUE,
signer: signer ? signer : undefined,
});
useEffect(() => {
setContextValue((val) => ({
...val,
signer: signer ? signer : undefined,
}));
}, [signer]);
useEffect(() => {
let s = signer;
@@ -2,7 +2,7 @@ import {
RequiredParam,
requiredParamInvariant,
} from "../../../core/query-utils/required-param";
import { useSDKChainId } from "../../providers/base";
import { useSDK, useSDKChainId } from "../../providers/base";
import { getErcs, DropContract, WalletAddress } from "../../types";
import {
cacheKeys,
@@ -14,8 +14,12 @@ import {
ClaimCondition,
ClaimConditionFetchOptions,
ClaimConditionInput,
SnapshotEntryWithProof,
fetchCurrencyValue,
convertToReadableQuantity,
fetchCurrencyMetadata,
} from "@thirdweb-dev/sdk";
import { BigNumberish } from "ethers";
import { BigNumberish, constants, utils } from "ethers";
import invariant from "tiny-invariant";
/**
@@ -45,7 +49,7 @@ export type SetClaimConditionsParams = {
/** **********************/
/**
* Use this to get the active claim conditon for ERC20, ERC721 or ERC1155 based contracts. They need to extend the `claimCondition` extension for this hook to work.
* Use this to get the active claim condition for ERC20, ERC721 or ERC1155 based contracts. They need to extend the `claimCondition` extension for this hook to work.
*
* @example
* ```javascript
@@ -60,7 +64,7 @@ export type SetClaimConditionsParams = {
* const { data: activeClaimCondition, isLoading, error } = useActiveClaimCondition(<YourERC1155ContractInstance>, <tokenId>);
* ```
*
* @param contract - an instance of a contract that extends the ERC721 or ERC1155 spec and implements the `claimConditions` extension.
* @param contract - an instance of a contract that extends the ERC721, ERC1155 or ERC20 spec and implements the `claimConditions` extension.
* @param tokenId - the id of the token to fetch the claim conditions for (if the contract is an ERC1155 contract)
* @returns a response object with the currently active claim condition
* @twfeature ERC721ClaimableWithConditions | ERC1155ClaimableWithConditions | ERC20ClaimableWithConditions
@@ -106,7 +110,80 @@ export function useActiveClaimCondition(
}
/**
* Use this to get all claim conditons for ERC20, ERC721 or ERC1155 based contracts. They need to extend the `claimCondition` extension for this hook to work.
* Use this to get the claimer proofs for an adddress for ERC20, ERC721 or ERC1155 based contracts. They need to extend the `claimCondition` extension for this hook to work.
*
* @example
* ```javascript
* const { data: claimerProofs, isLoading, error } = useClaimerProofs(<YourERC20ContractInstance>);
* ```
* @example
* ```javascript
* const { data: claimerProofs, isLoading, error } = useClaimerProofs(<YourERC721ContractInstance>);
* ```
* @example
* ```javascript
* const { data: claimerProofs, isLoading, error } = useClaimerProofs(<YourERC1155ContractInstance>, <tokenId>);
* ```
*
* @param contract - an instance of a contract that extends the ERC721, ERC1155 or ERC20 spec and implements the `claimConditions` extension.
* @param claimerAddress - the address of the claimer to fetch the claimer proofs for
* @param tokenId - the id of the token to fetch the claimer proofs for (if the contract is an ERC1155 contract)
* @param claimConditionId - optional the claim condition id to get the proofs for
* @returns a response object with the snapshot for the provided address
* @twfeature ERC721ClaimableWithConditions | ERC1155ClaimableWithConditions | ERC20ClaimableWithConditions
* @beta
*/
export function useClaimerProofs(
contract: RequiredParam<DropContract>,
claimerAddress: string,
tokenId?: BigNumberish,
claimConditionId?: BigNumberish,
) {
const contractAddress = contract?.getAddress();
const { erc1155, erc721, erc20 } = getErcs(contract);
return useQueryWithNetwork(
cacheKeys.extensions.claimConditions.getClaimerProofs(
contractAddress,
tokenId,
),
() => {
if (erc1155) {
requiredParamInvariant(
tokenId,
"tokenId is required for ERC1155 claim conditions",
);
return erc1155.claimConditions.getClaimerProofs(
tokenId,
claimerAddress,
claimConditionId,
);
}
if (erc721) {
return erc721.claimConditions.getClaimerProofs(
claimerAddress,
claimConditionId,
);
}
if (erc20) {
return erc20.claimConditions.getClaimerProofs(
claimerAddress,
claimConditionId,
);
}
throw new Error("Contract must be ERC721, ERC1155 or ERC20");
},
{
// Checks that happen here:
// 1. if the contract is based on ERC1155 contract => tokenId cannot be `undefined`
// 2. if the contract is NOT based on ERC1155 => we have to have either an ERC721 or ERC20 contract
enabled: erc1155 ? tokenId !== undefined : !!erc721 || !!erc20,
},
);
}
/**
* Use this to get all claim conditions for ERC20, ERC721 or ERC1155 based contracts. They need to extend the `claimCondition` extension for this hook to work.
*
* @example
* ```javascript
@@ -121,7 +198,7 @@ export function useActiveClaimCondition(
* const { data: claimConditions, isLoading, error } = useClaimConditions(<YourERC1155ContractInstance>, <tokenId>);
* ```
*
* @param contract - an instance of a contract that extends the ERC721 or ERC1155 spec and implements the `claimConditions` extension.
* @param contract - an instance of a contract that extends the ERC721, ERC1155 or ERC20 spec and implements the `claimConditions` extension.
* @param tokenId - the id of the token to fetch the claim conditions for (if the contract is an ERC1155 contract)
* @returns a response object with the list of claim conditions
* @twfeature ERC721ClaimableWithConditions | ERC1155ClaimableWithConditions | ERC20ClaimableWithConditions
@@ -242,6 +319,136 @@ export function useClaimIneligibilityReasons(
);
}
/**
* Use this to check the claim condition for a given wallet address (including checking for overrides that may exist for that given wallet address).
*
* @param contract - an instance of a contract that extends the ERC20, ERC721 or ERC1155 spec and implements the `claimConditions` extension.
* @param walletAddress - the wallet address to check the active claim condition for
* @param tokenId - the id of the token to fetch the claim conditions for (if the contract is an ERC1155 contract)
* @returns the active claim conditon for the wallet address or null if there is no active claim condition
*
* @beta
*/
export function useActiveClaimConditionForWallet(
contract: RequiredParam<DropContract>,
walletAddress: RequiredParam<WalletAddress>,
tokenId?: BigNumberish,
) {
const sdk = useSDK();
const contractAddress = contract?.getAddress();
const { erc1155, erc721, erc20 } = getErcs(contract);
return useQueryWithNetwork<ClaimCondition | null>(
cacheKeys.extensions.claimConditions.useActiveClaimConditionForWallet(
contractAddress,
walletAddress || "_NO_WALLET_",
tokenId,
),
async () => {
// if we do not have a walletAddress just do the same logic as basic useClaimCondition
if (!walletAddress) {
if (erc1155) {
requiredParamInvariant(
tokenId,
"tokenId is required for ERC1155 claim conditions",
);
return erc1155.claimConditions.getActive(tokenId);
}
if (erc721) {
return erc721.claimConditions.getActive();
}
if (erc20) {
return erc20.claimConditions.getActive();
}
throw new Error("Contract must be ERC721, ERC1155 or ERC20");
}
invariant(sdk, "sdk is required");
let activeGeneralClaimCondition: ClaimCondition | null = null;
let claimerProofForWallet: SnapshotEntryWithProof | null = null;
if (erc1155) {
requiredParamInvariant(tokenId, "tokenId is required for ERC1155");
const [cc, cp] = await Promise.all([
erc1155.claimConditions.getActive(tokenId),
erc1155.claimConditions.getClaimerProofs(tokenId, walletAddress),
]);
activeGeneralClaimCondition = cc;
claimerProofForWallet = cp;
}
if (erc721) {
const [cc, cp] = await Promise.all([
erc721.claimConditions.getActive(),
erc721.claimConditions.getClaimerProofs(walletAddress),
]);
activeGeneralClaimCondition = cc;
claimerProofForWallet = cp;
}
if (erc20) {
const [cc, cp] = await Promise.all([
erc20.claimConditions.getActive(),
erc20.claimConditions.getClaimerProofs(walletAddress),
]);
activeGeneralClaimCondition = cc;
claimerProofForWallet = cp;
}
// if there is no active claim condition nothing matters, return null
if (!activeGeneralClaimCondition) {
return null;
}
// if there is no claimer proof then just fall back to the active general claim condition
if (!claimerProofForWallet) {
return activeGeneralClaimCondition;
}
const { maxClaimable, currencyAddress, price } = claimerProofForWallet;
const currencyWithOverride =
currencyAddress || activeGeneralClaimCondition.currencyAddress;
const currencyMetadata = await fetchCurrencyMetadata(
sdk.getProvider(),
currencyWithOverride,
);
const normalizedPrize = price
? price === "unlimited"
? constants.MaxUint256
: utils.parseUnits(price, currencyMetadata.decimals)
: null;
const priceWithOverride =
normalizedPrize || activeGeneralClaimCondition.price;
const maxClaimableWithOverride =
// have to transform this the same way that claim conditions do it in SDK
convertToReadableQuantity(maxClaimable, currencyMetadata.decimals) ||
activeGeneralClaimCondition.maxClaimablePerWallet;
const currencyValueWithOverride = await fetchCurrencyValue(
sdk.getProvider(),
currencyWithOverride,
priceWithOverride,
);
return {
// inherit the entire claim condition
...activeGeneralClaimCondition,
// overwrite all keys that could be changed based on overwrites
maxClaimablePerWallet: maxClaimableWithOverride,
price: priceWithOverride,
currency: currencyWithOverride,
currencyAddress: currencyWithOverride,
currencyMetadata: currencyValueWithOverride,
};
},
{
// Checks that happen here:
// 1. if the contract is based on ERC1155 contract => tokenId cannot be `undefined`
// 2. if the contract is NOT based on ERC1155 => we have to have either an ERC721 or ERC20 contract
enabled: erc1155 ? tokenId !== undefined : !!erc721 || !!erc20,
},
);
}
/** **********************/
/** WRITE HOOKS **/
/** **********************/
@@ -368,8 +575,6 @@ export function useResetClaimConditions(
return conditions.map((c) => ({
...c,
price: c.currencyMetadata.displayValue,
maxQuantity: c.maxQuantity.toString(),
quantityLimitPerTransaction: c.quantityLimitPerTransaction.toString(),
}));
};
@@ -23,7 +23,7 @@ import {
ContractEvent,
ContractForPrebuiltContractType,
ContractType,
EventQueryFilter,
EventQueryOptions,
PrebuiltContractType,
SUPPORTED_CHAIN_ID,
ThirdwebSDK,
@@ -346,7 +346,7 @@ export function useContractMetadataUpdate(
export function useContractEvents(
contract: RequiredParam<ValidContractInstance>,
eventName?: string,
options: { queryFilter?: EventQueryFilter; subscribe?: boolean } = {
options: { queryFilter?: EventQueryOptions; subscribe?: boolean } = {
subscribe: true,
},
) {
@@ -3,13 +3,20 @@ import {
requiredParamInvariant,
} from "../../../core/query-utils/required-param";
import { useSDKChainId } from "../../providers/base";
import { BuyNowParams, MakeBidParams } from "../../types";
import {
AcceptDirectOffer,
BuyNowParams,
ExecuteAuctionSale,
MakeBidParams,
MakeOfferParams,
} from "../../types";
import {
cacheKeys,
invalidateContractAndBalances,
} from "../../utils/cache-keys";
import { useQueryWithNetwork } from "../query-utils/useQueryWithNetwork";
import { useAddress } from "../wallet";
import { useContractEvents } from "./contracts";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import type {
AuctionListing,
@@ -514,6 +521,203 @@ export function useMakeBid(contract: RequiredParam<Marketplace>) {
);
}
/**
* Use this to make an offer on direct or auction listing from your marketplace contract.
*
* @example
* ```jsx
* const Component = () => {
* const {
* mutate: makeOffer,
* isLoading,
* error,
* } = useMakeOffer(">>YourMarketplaceContractInstance<<");
*
* if (error) {
* console.error("failed to make a bid", error);
* }
*
* return (
* <button
* disabled={isLoading}
* onClick={() => makeOffer({ listingId: 1, pricePerToken: 0.5, quantity: 1 })}
* >
* Bid!
* </button>
* );
* };
* ```
*
* @param contract - an instance of a Marketplace contract
* @returns a mutation object that can be used to make a bid on an auction listing
* @beta
*/
export function useMakeOffer(contract: RequiredParam<Marketplace>) {
const activeChainId = useSDKChainId();
const contractAddress = contract?.getAddress();
const queryClient = useQueryClient();
const walletAddress = useAddress();
return useMutation(
async (data: MakeOfferParams) => {
invariant(walletAddress, "no wallet connected, cannot make bid");
requiredParamInvariant(contract, "No Contract instance provided");
return await contract.makeOffer(
data.listingId,
data.pricePerToken,
data.quantity,
);
},
{
onSettled: () =>
invalidateContractAndBalances(
queryClient,
contractAddress,
activeChainId,
),
},
);
}
/**
* Accept an offer on a direct listing from an offeror, will accept the latest offer by the given offeror.
*
* @example
* ```jsx
* const Component = () => {
* const {
* mutate: acceptOffer,
* isLoading,
* error,
* } = useAcceptDirectListingOffer(">>YourMarketplaceContractInstance<<");
*
* if (error) {
* console.error("failed to accept offer", error);
* }
*
* return (
* <button
* disabled={isLoading}
* onClick={() => acceptOffer({ listingId: 1, addressOfOfferor: "0x..." })}
* >
* Accept offer
* </button>
* );
* };
* ```
*
* @param contract - an instance of a Marketplace contract
* @returns a mutation object that can be used to accept an offer on a direct listing
* @beta
*/
export function useAcceptDirectListingOffer(
contract: RequiredParam<Marketplace>,
) {
const activeChainId = useSDKChainId();
const contractAddress = contract?.getAddress();
const queryClient = useQueryClient();
const walletAddress = useAddress();
return useMutation(
async (data: AcceptDirectOffer) => {
invariant(walletAddress, "no wallet connected, cannot make bid");
requiredParamInvariant(contract?.direct, "No Direct instance provided");
return await contract.direct.acceptOffer(
data.listingId,
data.addressOfOfferor,
);
},
{
onSettled: () =>
invalidateContractAndBalances(
queryClient,
contractAddress,
activeChainId,
),
},
);
}
/**
* Execute an auction sale. Can only be executed once the auction has ended and the auction has a winning bid.
*
* @example
* ```jsx
* const Component = () => {
* const {
* mutate: executeAuctionSale,
* isLoading,
* error,
* } = useExecuteAuctionSale(">>YourMarketplaceContractInstance<<");
*
* if (error) {
* console.error("failed to execute sale", error);
* }
*
* return (
* <button
* disabled={isLoading}
* onClick={() => executeAuctionSale({ listingId: 1 })}
* >
* Execute sale
* </button>
* );
* };
* ```
*
* @param contract - an instance of a Marketplace contract
* @returns a mutation object that can be used to accept an offer on a direct listing
* @beta
*/
export function useExecuteAuctionSale(contract: RequiredParam<Marketplace>) {
const activeChainId = useSDKChainId();
const contractAddress = contract?.getAddress();
const queryClient = useQueryClient();
const walletAddress = useAddress();
return useMutation(
async (data: ExecuteAuctionSale) => {
invariant(walletAddress, "no wallet connected, cannot make bid");
requiredParamInvariant(
contract?.auction,
"No Auction marketplace instance provided",
);
return await contract.auction.executeSale(data.listingId);
},
{
onSettled: () =>
invalidateContractAndBalances(
queryClient,
contractAddress,
activeChainId,
),
},
);
}
/**
* Get all the offers for a listing
*
* @remarks Fetch all the offers for a specified direct or auction listing.
* @example
* ```javascript
* const { data: offers, isLoading, error } = useOffers(<YourMarketplaceContractInstance>, <listingId>);
* ```
*
* @param contract - an instance of a Marketplace contract
* @param listingId - the id of the listing to fetch offers for
* @beta
*/
export function useOffers(
contract: RequiredParam<Marketplace>,
listingId: RequiredParam<BigNumberish>,
) {
const result = useContractEvents(contract, "NewOffer");
return {
...result,
data: result.data
?.filter((ev) => ev.data.listingId.eq(listingId))
?.map((ev) => ev.data),
};
}
/**
* Use this to buy out an auction listing from your marketplace contract.
*
+1 -1
View File
@@ -542,7 +542,7 @@ export function useTransferNFT<TContract extends NFTContract>(
* @example
* ```jsx
* const Component = () => {
* const editionDrop = useEditionDrop(<ContractAddress>);
* const editionDrop = useContract(<ContractAddress>, "edition-drop");
* const {
* mutate: airdropNFT,
* isLoading,
+3 -5
View File
@@ -232,11 +232,9 @@ export function useClaimToken(contract: RequiredParam<TokenContract>) {
invariant(data.to, 'No "to" address provided');
if (erc20) {
invariant(erc20?.claimTo, "contract does not support claimTo");
return await erc20.claimTo(
data.to,
data.amount,
data.checkERC20Allowance,
);
return await erc20.claimTo(data.to, data.amount, {
checkERC20Allowance: data.checkERC20Allowance,
});
}
invariant(false, "Smart contract is not a valid erc20 contract");
},
@@ -1,72 +0,0 @@
import {
GnosisConnectorArguments,
GnosisSafeConnector,
} from "../../connectors/gnosis-safe";
import { useConnect } from "../wagmi-required/useConnect";
import { utils } from "ethers";
import invariant from "tiny-invariant";
import { useContext as useWagmiContext } from "wagmi";
/**
* Hook for connecting to a Gnosis Safe. This enables multisig wallets to connect to your application and sing transactions.
*
* ```javascript
* import { useGnosis } from "@thirdweb-dev/react"
* ```
*
*
* @example
* ```javascript
* import { useGnosis } from "@thirdweb-dev/react"
*
* const App = () => {
* const connectWithGnosis = useGnosis()
*
* return (
* <button onClick={() => connectWithGnosis({ safeAddress: "0x...", safeChainId: 1 })}>
* Connect Gnosis Safe
* </button>
* )
* }
* ```
*
* @public
*/
export function useGnosis() {
const wagmiContext = useWagmiContext();
invariant(
wagmiContext,
`useGnosis() can only be used inside <ThirdwebProvider />. If you are using <ThirdwebSDKProvider /> you will have to use your own wallet-connection logic.`,
);
const [connectors, connect] = useConnect();
if (connectors.loading) {
return () => Promise.reject("Gnosis connector not ready to be used, yet");
}
const connector = connectors.data.connectors.find((c) => c.id === "gnosis");
invariant(
connector,
"Gnosis connector not found, please make sure it is provided to your <ThirdwebProvider />",
);
return async (config: GnosisConnectorArguments) => {
const previousConnector = connectors.data.connector;
const previousConnectorChain = await previousConnector?.getChainId();
invariant(
!!previousConnector,
"Cannot connect to Gnosis Safe without first being connected to a personal wallet.",
);
invariant(
previousConnectorChain === config.safeChainId,
"Gnosis safe chain id must match personal wallet chain id.",
);
invariant(
utils.isAddress(config.safeAddress),
"Gnosis safe address must be a valid address.",
);
(connector as GnosisSafeConnector).setConfiguration(
previousConnector,
config,
);
return connect(connector);
};
}
@@ -1,91 +0,0 @@
import { MagicConnector } from "../../connectors/magic";
import { useConnect } from "../wagmi-required/useConnect";
import { LoginWithMagicLinkConfiguration } from "magic-sdk";
import invariant from "tiny-invariant";
import { useContext as useWagmiContext } from "wagmi";
/**
* Hook for connecting to an email wallet using magic link.
* This enables users without their own wallets to connect to your application and sign transactions securely using their email.
*
* ```javascript
* import { useMagic } from "@thirdweb-dev/react"
* ```
*
*
* @example
* Before using this hook, you first need to set up the magic configuration in your `ThirdwebProvider`, including your magic API key.
*
* ```javascript
* // Add the magic configuration object to your wallet connectors
* const connectors = [
* "metamask",
* "walletConnect",
* "walletLink",
* {
* name: "magic",
* options: {
* apiKey: "your-magic-api-key",
* }
* }
* ]
*
* // Add the above to the walletConnectors prop of your <ThirdwebProvider />
* const Provider = ({ children }) => (
* return (
* <ThirdwebProvider
* walletConnectors={connectors}
* // Specify remaining parameters
* ...
* >
* {children}
* </ThirdwebProvider>
* )
* }
* ```
*
* In order to use the hook to connect users with magic link, you just need to provide the users email to the connect function.
*
* You can setup the hook with the following configuration:
* ```javascript
* import { useMagic } from "@thirdweb-dev/react"
* import { useState } from "react"
*
* const LoginWithMagicLink = () => {
* const connectWithMagic = useMagic()
* const [email, setEmail] = useState()
*
* return (
* <div>
* <input value={email} onChange={(e) => setEmail(e.target.value)} />
* <button onClick={() => connectWithMagic({ email })}>Login</button>
* </div>
* )
* }
* ```
*
* @public
*/
export function useMagic() {
const wagmiContext = useWagmiContext();
invariant(
wagmiContext,
`useMagic() can only be used inside <ThirdwebProvider />. If you are using <ThirdwebSDKProvider /> you will have to use your own wallet-connection logic.`,
);
const [connectors, connect] = useConnect();
if (connectors.loading) {
return () => Promise.reject("Magic connector not ready to be used, yet");
}
const connector = connectors.data.connectors.find((c) => c.id === "magic");
invariant(
connector,
"Magic connector not found, please make sure it is provided to your <ThirdwebProvider />",
);
return (configuration: LoginWithMagicLinkConfiguration) => {
(connector as MagicConnector).setConfiguration(configuration);
return connect(connector);
};
}
@@ -1,10 +1,10 @@
import { useConnect } from "../wagmi-required/useConnect";
import { Buffer } from "buffer";
import invariant from "tiny-invariant";
import { useContext as useWagmiContext } from "wagmi";
if (!globalThis.Buffer) {
globalThis.Buffer = require("buffer/").Buffer;
}
globalThis.Buffer = Buffer;
/**
* Hook for connecting to a mobile wallet with Wallet Connect
*
@@ -1,10 +1,10 @@
import { useConnect } from "../wagmi-required/useConnect";
import { Buffer } from "buffer";
import invariant from "tiny-invariant";
import { useContext as useWagmiContext } from "wagmi";
if (!globalThis.Buffer) {
globalThis.Buffer = require("buffer/").Buffer;
}
globalThis.Buffer = Buffer;
/**
* Hook for connecting to a Coinbase wallet.
*
@@ -28,8 +28,8 @@ import { useContract } from "../async/contracts";
* @deprecated
* This hook is deprecated and will be removed in a future major version. You should use {@link useContract} instead.
* ```diff
* - const edition = await sdk.useEdition("0x1234...");
* + const edition = await sdk.useContract("0x1234...", "edition").contract;
* - const edition = useEdition("0x1234...");
* + const edition = useContract("0x1234...", "edition").contract;
* ```
*/
export function useEdition(contractAddress: RequiredParam<string>) {
@@ -27,8 +27,8 @@ import { useContract } from "../async/contracts";
* @deprecated
* This hook is deprecated and will be removed in a future major version. You should use {@link useContract} instead.
* ```diff
* - const editionDrop = await sdk.useEditionDrop("0x1234...");
* + const editionDrop = await sdk.useContract("0x1234...", "edition-drop").contract;
* - const editionDrop = useEditionDrop("0x1234...");
* + const editionDrop = useContract("0x1234...", "edition-drop").contract;
* ```
*/
export function useEditionDrop(contractAddress: RequiredParam<string>) {
@@ -28,8 +28,8 @@ import { useContract } from "../async/contracts";
* @deprecated
* This hook is deprecated and will be removed in a future major version. You should use {@link useContract} instead.
* ```diff
* - const marketplace = await sdk.useMarketplace("0x1234...");
* + const marketplace = await sdk.useContract("0x1234...", "marketplace").contract;
* - const marketplace = useMarketplace("0x1234...");
* + const marketplace = useContract("0x1234...", "marketplace").contract;
* ```
*/
export function useMarketplace(contractAddress: RequiredParam<string>) {
@@ -27,8 +27,8 @@ import { useContract } from "../async/contracts";
* @deprecated
* This hook is deprecated and will be removed in a future major version. You should use {@link useContract} instead.
* ```diff
* - const multiwrap = await sdk.useMultiwrap("0x1234...");
* + const multiwrap = await sdk.useContract("0x1234...", "multiwrap").contract;
* - const multiwrap = useMultiwrap("0x1234...");
* + const multiwrap = useContract("0x1234...", "multiwrap").contract;
* ```
*/
export function useMultiwrap(contractAddress: RequiredParam<string>) {
@@ -11,7 +11,7 @@ import { useContract } from "../async/contracts";
* import { useContract } from '@thirdweb-dev/react'
*
* export default function Component() {
* const { contract } = useNFTCollection("<YOUR-CONTRACT-ADDRESS>", "nft-collection")
* const { contract, isLoading, error } = useContract("<YOUR-CONTRACT-ADDRESS>", "nft-collection")
*
* // Now you can use the nftCollection contract in the rest of the component
*
@@ -28,8 +28,8 @@ import { useContract } from "../async/contracts";
* @deprecated
* This hook is deprecated and will be removed in a future major version. You should use {@link useContract} instead.
* ```diff
* - const nftCollection = await sdk.useNFTCollection("0x1234...");
* + const nftCollection = await sdk.useContract("0x1234...", "nft-collection").contract;
* - const nftCollection = useNFTCollection("0x1234...");
* + const nftCollection = useContract("0x1234...", "nft-collection").contract;
* ```
*/
export function useNFTCollection(contractAddress: RequiredParam<string>) {
@@ -27,8 +27,8 @@ import { useContract } from "../async/contracts";
* @deprecated
* This hook is deprecated and will be removed in a future major version. You should use {@link useContract} instead.
* ```diff
* - const nftDrop = await sdk.useNFTDrop("0x1234...");
* + const nftDrop = await sdk.useContract("0x1234...", "nft-drop").contract;
* - const nftDrop = useNFTDrop("0x1234...");
* + const nftDrop = useContract("0x1234...", "nft-drop").contract;
* ```
*/
export function useNFTDrop(contractAddress: RequiredParam<string>) {
@@ -28,8 +28,8 @@ import { useContract } from "../async/contracts";
* @deprecated
* This hook is deprecated and will be removed in a future major version. You should use {@link useContract} instead.
* ```diff
* - const pack = await sdk.usePack("0x1234...");
* + const pack = await sdk.useContract("0x1234...", "pack").contract;
* - const pack = usePack("0x1234...");
* + const pack = useContract("0x1234...", "pack").contract;
* ```
*/
export function usePack(contractAddress: RequiredParam<string>) {
@@ -27,8 +27,8 @@ import { useContract } from "../async/contracts";
* @deprecated
* This hook is deprecated and will be removed in a future major version. You should use {@link useContract} instead.
* ```diff
* - const signatureDrop = await sdk.useSignatureDrop("0x1234...");
* + const signatureDrop = await sdk.useContract("0x1234...", "signature-drop").contract;
* - const signatureDrop = useSignatureDrop("0x1234...");
* + const signatureDrop = useContract("0x1234...", "signature-drop").contract;
* ```
*/
export function useSignatureDrop(contractAddress?: RequiredParam<string>) {
@@ -28,8 +28,8 @@ import { useContract } from "../async/contracts";
* @deprecated
* This hook is deprecated and will be removed in a future major version. You should use {@link useContract} instead.
* ```diff
* - const split = await sdk.useSplit("0x1234...");
* + const split = await sdk.useContract("0x1234...", "split").contract;
* - const split = useSplit("0x1234...");
* + const split = useContract("0x1234...", "split").contract;
* ```
*/
export function useSplit(contractAddress: RequiredParam<string>) {
@@ -28,8 +28,8 @@ import { useContract } from "../async/contracts";
* @deprecated
* This hook is deprecated and will be removed in a future major version. You should use {@link useContract} instead.
* ```diff
* - const token = await sdk.useToken("0x1234...");
* + const token = await sdk.useContract("0x1234...", "token").contract;
* - const token = useToken("0x1234...");
* + const token = useContract("0x1234...", "token").contract;
* ```
*/
export function useToken(contractAddress: RequiredParam<string>) {
@@ -28,8 +28,8 @@ import { useContract } from "../async/contracts";
* @deprecated
* This hook is deprecated and will be removed in a future major version. You should use {@link useContract} instead.
* ```diff
* - const token = await sdk.useTokenDrop("0x1234...");
* + const token = await sdk.useContract("0x1234...", "token-drop").contract;
* - const token = useTokenDrop("0x1234...");
* + const token = useContract("0x1234...", "token-drop").contract;
* ```
*/
export function useTokenDrop(contractAddress: RequiredParam<string>) {
@@ -28,8 +28,8 @@ import { useContract } from "../async/contracts";
* @deprecated
* This hook is deprecated and will be removed in a future major version. You should use {@link useContract} instead.
* ```diff
* - const vote = await sdk.useVote("0x1234...");
* + const vote = await sdk.useContract("0x1234...", "vote").contract;
* - const vote = useVote("0x1234...");
* + const vote = useContract("0x1234...", "vote").contract;
* ```
*/
export function useVote(contractAddress: RequiredParam<string>) {
@@ -1,7 +1,6 @@
import { GnosisSafeConnector } from "../../connectors/gnosis-safe";
import { useConnect } from "./useConnect";
import invariant from "tiny-invariant";
import { useAccount, useContext as useWagmiContext } from "wagmi";
import { Connector, useAccount, useContext as useWagmiContext } from "wagmi";
/**
* Hook for disconnecting the currently connected wallet
@@ -31,23 +30,22 @@ import { useAccount, useContext as useWagmiContext } from "wagmi";
*
* @public
*/
export function useDisconnect(options?: { reconnectAfterGnosis?: boolean }) {
export function useDisconnect(options?: { reconnectPrevious?: boolean }) {
const wagmiContext = useWagmiContext();
invariant(
wagmiContext,
`useDisconnect() can only be used inside <ThirdwebProvider />. If you are using <ThirdwebSDKProvider /> you will have to use your own connection logic.`,
);
const optsWithDefaults = { ...{ reconnectAfterGnosis: true }, ...options };
const optsWithDefaults = { ...{ reconnectPrevious: true }, ...options };
const [, connect] = useConnect();
const [data, disconnect] = useAccount();
return async () => {
const previousConnector =
(data.data?.connector instanceof GnosisSafeConnector &&
data.data.connector.previousConnector) ||
undefined;
// if there is a previous connector get it
const previousConnector: Connector | undefined =
(data?.data?.connector as any)?.previousConnector || undefined;
// if it's gnosis, just connect the previous connector
if (optsWithDefaults.reconnectAfterGnosis && previousConnector) {
if (optsWithDefaults.reconnectPrevious && previousConnector) {
try {
return await connect(previousConnector);
} catch (err) {
-2
View File
@@ -31,8 +31,6 @@ export * from "./hooks/wagmi-required/useConnect";
export * from "./hooks/connectors/useMetamask";
export * from "./hooks/connectors/useWalletConnect";
export * from "./hooks/connectors/useWalletLink";
export * from "./hooks/connectors/useGnosis";
export * from "./hooks/connectors/useMagic";
// re-exports
export { ChainId } from "@thirdweb-dev/sdk";
+76 -73
View File
@@ -1,90 +1,93 @@
import { useState, useCallback, useLayoutEffect, useEffect, useRef, PropsWithChildren } from "react";
import { createPortal } from "react-dom";
import {
useState,
useCallback,
useLayoutEffect,
useEffect,
useRef,
PropsWithChildren,
} from "react";
import { createPortal } from "react-dom";
function canUseDOM() {
function canUseDOM() {
return !!(
typeof window !== "undefined" &&
window.document &&
window.document.createElement
);
typeof window !== "undefined" &&
window.document &&
window.document.createElement
);
}
/**
* Forces a re-render, similar to `forceUpdate` in class components.
*/
function useForceUpdate() {
const [, dispatch] = useState<{}>(Object.create(null));
return useCallback(() => {
dispatch(Object.create(null));
}, []);
const [, dispatch] = useState<{}>(Object.create(null));
return useCallback(() => {
dispatch(Object.create(null));
}, []);
}
const useIsomorphicLayoutEffect = canUseDOM() ? useLayoutEffect : useEffect;
/**
* Portal from `@reach/portal`
*
* @see Docs https://reach.tech/portal#portal
*/
const PortalImpl: React.FC<PortalProps> = ({
children,
type = "reach-portal",
containerRef,
}) => {
const mountNode = useRef<HTMLDivElement | null>(null);
const portalNode = useRef<HTMLElement | null>(null);
const forceUpdate = useForceUpdate();
/**
* Portal from `@reach/portal`
*
* @see Docs https://reach.tech/portal#portal
*/
const PortalImpl: React.FC<PortalProps> = ({
children,
type = "reach-portal",
containerRef,
}) => {
const mountNode = useRef<HTMLDivElement | null>(null);
const portalNode = useRef<HTMLElement | null>(null);
const forceUpdate = useForceUpdate();
useIsomorphicLayoutEffect(() => {
// This ref may be null when a hot-loader replaces components on the page
if (!mountNode.current) {
return;
}
// It's possible that the content of the portal has, itself, been portaled.
// In that case, it's important to append to the correct document element.
const ownerDocument = mountNode.current.ownerDocument;
const body = containerRef?.current || ownerDocument.body;
portalNode.current = ownerDocument?.createElement(type);
body.appendChild(portalNode.current);
forceUpdate();
return () => {
if (portalNode.current && body) {
body.removeChild(portalNode.current);
}
};
}, [type, forceUpdate, containerRef]);
return portalNode.current ? (
createPortal(children, portalNode.current)
) : (
<span ref={mountNode} />
);
};
useIsomorphicLayoutEffect(() => {
// This ref may be null when a hot-loader replaces components on the page
if (!mountNode.current) {return;}
// It's possible that the content of the portal has, itself, been portaled.
// In that case, it's important to append to the correct document element.
const ownerDocument = mountNode.current.ownerDocument;
const body = containerRef?.current || ownerDocument.body;
portalNode.current = ownerDocument?.createElement(type);
body.appendChild(portalNode.current);
forceUpdate();
return () => {
if (portalNode.current && body) {
body.removeChild(portalNode.current);
}
};
}, [type, forceUpdate, containerRef]);
export const Portal: React.FC<PortalProps> = ({
unstable_skipInitialRender,
...props
}) => {
const [hydrated, setHydrated] = useState(false);
useEffect(() => {
if (unstable_skipInitialRender) {
setHydrated(true);
}
}, [unstable_skipInitialRender]);
if (unstable_skipInitialRender && !hydrated) {
return null;
}
return <PortalImpl {...props} />;
};
return portalNode.current ? (
createPortal(children, portalNode.current)
) : (
<span ref={mountNode} />
);
};
export const Portal: React.FC<PortalProps> = ({
unstable_skipInitialRender,
...props
}) => {
const [hydrated, setHydrated] = useState(false);
useEffect(() => {
if (unstable_skipInitialRender) {
setHydrated(true);
}
}, [unstable_skipInitialRender]);
if (unstable_skipInitialRender && !hydrated) {
return null;
}
return <PortalImpl {...props} />;
};
export type PortalProps = PropsWithChildren<{
type?: string;
containerRef?: React.RefObject<Node>;
unstable_skipInitialRender?: boolean;
}>;
Portal.displayName = "Portal";
export type PortalProps = PropsWithChildren<{
type?: string;
containerRef?: React.RefObject<Node>;
unstable_skipInitialRender?: boolean;
}>;
Portal.displayName = "Portal";
+21 -6
View File
@@ -21,7 +21,7 @@ import {
} from "@thirdweb-dev/sdk";
import { ThirdwebStorage } from "@thirdweb-dev/storage";
import { Signer } from "ethers";
import { createContext, useContext, useEffect, useMemo } from "react";
import { createContext, useContext, useEffect, useMemo, useState } from "react";
import invariant from "tiny-invariant";
interface TWSDKContext {
@@ -60,21 +60,36 @@ export const WrappedThirdwebSDKProvider: ComponentWithChildren<
}) => {
const { signer } = useThirdwebConnectedWalletContext();
const sdk = useMemo(() => {
const [sdk, setSDK] = useState<ThirdwebSDK>();
useEffect(() => {
if (!desiredChainId || typeof window === "undefined") {
return undefined;
}
const _sdk = new ThirdwebSDK(provider, sdkOptions, storageInterface);
// if we already have a signer from the wallet context, use it immediately
if (signer) {
_sdk.updateSignerOrProvider(signer);
}
(_sdk as any)._constructedAt = Date.now();
(_sdk as any)._chainId = desiredChainId;
return _sdk;
setSDK(_sdk);
// explicitly *not* passing the signer, if we have it we use it if we don't we don't
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [provider, sdkOptions, storageInterface, desiredChainId]);
useEffect(() => {
if (signer && sdk && (sdk as any)._chainId === desiredChainId) {
sdk.updateSignerOrProvider(signer);
if (sdk && (sdk as any)._chainId === desiredChainId) {
if (signer) {
sdk.updateSignerOrProvider(signer);
} else {
sdk.updateSignerOrProvider(provider);
}
}
}, [signer, sdk, desiredChainId]);
}, [signer, sdk, desiredChainId, provider]);
const ctxValue = useMemo(
() => ({
+9 -46
View File
@@ -1,8 +1,5 @@
import {
GnosisConnectorArguments,
GnosisSafeConnector,
} from "../connectors/gnosis-safe";
import { MagicConnector, MagicConnectorArguments } from "../connectors/magic";
import type { GnosisSafeConnector } from "../connectors/gnosis-safe";
import type { MagicConnector } from "../connectors/magic";
import {
Chain,
SupportedChain,
@@ -25,6 +22,7 @@ import {
ProviderProps as WagmiproviderProps,
useProvider,
useSigner,
Connector,
} from "wagmi";
import { CoinbaseWalletConnector } from "wagmi/connectors/coinbaseWallet";
import { InjectedConnector } from "wagmi/connectors/injected";
@@ -56,26 +54,6 @@ export type WalletLinkConnectorType =
options: CoinbaseWalletConnector["options"];
};
/**
* @internal
*/
export type MagicConnectorType =
| "magic"
| {
name: "magic";
options: Omit<MagicConnectorArguments, "network">;
};
/**
* @internal
*/
export type GnosisConnectorType =
| "gnosis"
| {
name: "gnosis";
options: GnosisConnectorArguments;
};
/**
* @internal
*/
@@ -83,8 +61,8 @@ export type WalletConnector =
| InjectedConnectorType
| WalletConnectConnectorType
| WalletLinkConnectorType
| MagicConnectorType
| GnosisConnectorType;
| GnosisSafeConnector
| MagicConnector;
/**
* @internal
@@ -280,6 +258,9 @@ export const ThirdwebProvider = <
connectors: ({ chainId }: { chainId?: number }) => {
return walletConnectors
.map((connector) => {
if (connector instanceof Connector) {
return connector;
}
// injected connector
if (
(typeof connector === "string" &&
@@ -344,25 +325,7 @@ export const ThirdwebProvider = <
},
});
}
if (typeof connector === "object" && connector.name === "magic") {
const jsonRpcUrl = _rpcUrlMap[chainId || desiredChainId || 1];
return new MagicConnector({
chains: _supporrtedChains,
options: {
...connector.options,
network: { rpcUrl: jsonRpcUrl, chainId: desiredChainId || 1 },
rpcUrls: _rpcUrlMap,
},
});
}
if (
(typeof connector === "string" && connector === "gnosis") ||
(typeof connector === "object" && connector.name === "gnosis")
) {
return new GnosisSafeConnector({
chains: _supporrtedChains,
});
}
return null;
})
.filter((c) => c !== null);
+16
View File
@@ -14,6 +14,8 @@ import type {
ClaimOptions,
NFTMetadataOrUri,
Token,
Multiwrap,
Pack,
} from "@thirdweb-dev/sdk";
import type { Edition } from "@thirdweb-dev/sdk/dist/declarations/src/evm/contracts/prebuilt-implementations/edition";
import type { EditionDrop } from "@thirdweb-dev/sdk/dist/declarations/src/evm/contracts/prebuilt-implementations/edition-drop";
@@ -68,6 +70,8 @@ export type TokenBurnParams = {
export type NFTContract =
| NFTCollection
| Edition
| Pack
| Multiwrap
| Exclude<DropContract, "TokenDrop">;
/**
@@ -212,6 +216,18 @@ export type ClaimNFTReturnType =
// MARKETPLACE //
export type MakeBidParams = { listingId: BigNumberish; bid: Price };
export type MakeOfferParams = {
listingId: BigNumberish;
pricePerToken: Price;
quantity?: Amount;
};
export type AcceptDirectOffer = {
listingId: BigNumberish;
addressOfOfferor: string;
};
export type ExecuteAuctionSale = {
listingId: BigNumberish;
};
export type BuyNowParams<TListingType = ListingType> =
TListingType extends ListingType.Direct
@@ -287,6 +287,16 @@ export const cacheKeys = {
? ["claimConditions", "getAll", { tokenId }, options]
: ["claimConditions", "getAll", options],
),
getClaimerProofs: (
contractAddress: RequiredParam<ContractAddress>,
tokenId?: BigNumberish,
) =>
createContractCacheKey(
contractAddress,
tokenId
? ["claimConditions", "getClaimerProofs", { tokenId }]
: ["claimConditions", "getClaimerProofs"],
),
getClaimIneligibilityReasons: (
contractAddress: RequiredParam<ContractAddress>,
params: { walletAddress?: WalletAddress; quantity: string | number },
@@ -303,6 +313,23 @@ export const cacheKeys = {
]
: ["claimConditions", "getIneligibilityReasons", params],
),
// combinations of queries cache keys
useActiveClaimConditionForWallet: (
contractAddress: RequiredParam<ContractAddress>,
walletAddress: WalletAddress,
tokenId?: BigNumberish,
) =>
createContractCacheKey(
contractAddress,
tokenId
? [
"claimConditions",
"useActiveClaimConditionForWallet",
{ tokenId, walletAddress },
,
]
: ["claimConditions", "getIneligibilityReasons", { walletAddress }],
),
},
// primary sale contracts
+1 -1
View File
@@ -1,4 +1,4 @@
// at the moment we'll re-export everything from the ethereum package (only!)
// at the moment we'll re-export everything from the ethereum package
export * from "./evm";
// core exports
+12 -4
View File
@@ -4,17 +4,22 @@ import {
RequiredParam,
} from "../../../core/query-utils/required-param";
import { useQuery } from "@tanstack/react-query";
import { QueryAllParams } from "@thirdweb-dev/sdk";
import type { NFTCollection, NFTDrop } from "@thirdweb-dev/sdk/solana";
export function nftGetAllQuery(
program: RequiredParam<NFTCollection | NFTDrop>,
queryParams?: QueryAllParams,
) {
return {
queryKey: createSOLProgramQueryKey(program, ["getAll"] as const),
queryKey: createSOLProgramQueryKey(program, [
"getAll",
queryParams,
] as const),
queryFn: async () => {
requiredParamInvariant(program, "program is required");
return await program.getAll();
return program.getAll(queryParams);
},
enabled: !!program,
};
@@ -40,6 +45,9 @@ export function nftGetAllQuery(
*
* @public
*/
export function useNFTs(program: RequiredParam<NFTCollection | NFTDrop>) {
return useQuery(nftGetAllQuery(program));
export function useNFTs(
program: RequiredParam<NFTCollection | NFTDrop>,
queryParams?: QueryAllParams,
) {
return useQuery(nftGetAllQuery(program, queryParams));
}
@@ -30,7 +30,7 @@ export function nftRoyaltyQuery(
*
* export default function Component() {
* const { program } = useProgram("{{program_address}}");
* const { data: royalty, isLoading } = useRoyalty(program);
* const { data: royalty, isLoading } = useRoyaltySettings(program);
*
* return (
* <p>{royalty}</p>
@@ -40,6 +40,6 @@ export function nftRoyaltyQuery(
*
* @public
*/
export function useRoyalty(program: RequiredParam<NFTCollection | NFTDrop>) {
export function useRoyaltySettings(program: RequiredParam<NFTCollection | NFTDrop>) {
return useQuery(nftRoyaltyQuery(program));
}
@@ -0,0 +1,47 @@
import { createSOLProgramQueryKey } from "../../../core/query-utils/query-key";
import {
requiredParamInvariant,
RequiredParam,
} from "../../../core/query-utils/required-param";
import { useQuery } from "@tanstack/react-query";
import type { NFTCollection, NFTDrop } from "@thirdweb-dev/sdk/solana";
export function nftTotalSupplyQuery(
program: RequiredParam<NFTCollection | NFTDrop>,
) {
return {
queryKey: createSOLProgramQueryKey(program, ["supply"] as const),
queryFn: async () => {
requiredParamInvariant(program, "program is required");
return program.totalSupply();
},
enabled: !!program,
};
}
/**
* Get the totaly supply of NFTs on the program
* @param program - The NFT program to get the total supply of
*
* @example
* ```jsx
* import { useProgram, useTotalSupply } from "@thirdweb-dev/react/solana";
*
* export default function Component() {
* const { program } = useProgram("{{program_address}}");
* const { data: supply, isLoading } = useTotalSupply(program);
*
* return (
* <pre>{JSON.stringify(supply)}</pre>
* )
* }
* ```
*
* @public
*/
export function useTotalSupply(
program: RequiredParam<NFTCollection | NFTDrop>,
) {
return useQuery(nftTotalSupplyQuery(program));
}
@@ -0,0 +1,47 @@
import { createSOLProgramQueryKey } from "../../../core/query-utils/query-key";
import {
requiredParamInvariant,
RequiredParam,
} from "../../../core/query-utils/required-param";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import type { CreatorInput, NFTCollection, NFTDrop } from "@thirdweb-dev/sdk/solana";
/**
* Update the creators for an NFT program
* @param program - The NFT program instance to update the creators for
*
* @example
* ```jsx
* import { useProgram, useUpdateCreators } from "@thirdweb-dev/react/solana";
*
* export default function Component() {
* const { program } = useProgram("{{program_address}}");
* const { mutateAsync: updateCreators, isLoading, error } = useUpdateCreators(program);
*
* return (
* <button
* onClick={() => updateCreators([{ address: "0x...", share: 10 }])}
* >
* Update Creators
* </button>
* )
* }
* ```
*
* @public
*/
export function useUpdateCreators(
program: RequiredParam<NFTCollection | NFTDrop>,
) {
const queryClient = useQueryClient();
return useMutation(
async (creators: CreatorInput[]) => {
requiredParamInvariant(program, "program is required");
return await program.updateCreators(creators);
},
{
onSettled: () =>
queryClient.invalidateQueries(createSOLProgramQueryKey(program)),
},
);
}
@@ -0,0 +1,47 @@
import { createSOLProgramQueryKey } from "../../../core/query-utils/query-key";
import {
requiredParamInvariant,
RequiredParam,
} from "../../../core/query-utils/required-param";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import type { NFTCollection, NFTDrop } from "@thirdweb-dev/sdk/solana";
/**
* Update the royalty for an NFT program
* @param program - The NFT program instance to update the royalty for
*
* @example
* ```jsx
* import { useProgram, useUpdateRoyaltySettings } from "@thirdweb-dev/react/solana";
*
* export default function Component() {
* const { program } = useProgram("{{program_address}}");
* const { mutateAsync: updateRoyalties, isLoading, error } = useUpdateRoyaltySettings(program);
*
* return (
* <button
* onClick={() => updateRoyalties(300)}
* >
* Update Royalties
* </button>
* )
* }
* ```
*
* @public
*/
export function useUpdateRoyaltySettings(
program: RequiredParam<NFTCollection | NFTDrop>,
) {
const queryClient = useQueryClient();
return useMutation(
async (sellerFeeBasisPoints: number) => {
requiredParamInvariant(program, "program is required");
return await program.updateRoyalty(sellerFeeBasisPoints);
},
{
onSettled: () =>
queryClient.invalidateQueries(createSOLProgramQueryKey(program)),
},
);
}
@@ -4,7 +4,8 @@ import {
RequiredParam,
} from "../../../core/query-utils/required-param";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import type { Amount, Token } from "@thirdweb-dev/sdk/solana";
import type { Amount } from "@thirdweb-dev/sdk";
import type { Token } from "@thirdweb-dev/sdk/solana";
export type TransferTokenMutationParams = {
amount: Amount;
+5
View File
@@ -14,6 +14,11 @@ export * from "./hooks/program/useProgramAccountType";
export * from "./hooks/nft/useNFTs";
export * from "./hooks/nft/useTransferNFT";
export * from "./hooks/nft/useBurnNFT";
export * from "./hooks/nft/useRoyaltySettings";
export * from "./hooks/nft/useUpdateRoyaltySettings";
export * from "./hooks/nft/useCreators";
export * from "./hooks/nft/useUpdateCreators";
export * from "./hooks/nft/useTotalSupply";
// NFT Collection
export * from "./hooks/nft/collection/useMintNFT";
+1 -1
View File
@@ -1,12 +1,12 @@
import { ThirdwebAuthConfig } from "../contexts/thirdweb-auth";
import { ThirdwebSDKProvider } from "./base";
import type { WalletAdapter } from "@solana/wallet-adapter-base";
import { PhantomWalletAdapter } from "@solana/wallet-adapter-phantom";
import {
ConnectionProvider,
useWallet,
WalletProvider,
} from "@solana/wallet-adapter-react";
import { PhantomWalletAdapter } from "@solana/wallet-adapter-wallets";
import { getUrlForNetwork, Network } from "@thirdweb-dev/sdk/solana";
import { PropsWithChildren } from "react";
+128
View File
@@ -1,5 +1,133 @@
# @thirdweb-dev/sdk
## 3.6.1
### Patch Changes
- [#398](https://github.com/thirdweb-dev/js/pull/398) [`15514f1`](https://github.com/thirdweb-dev/js/commit/15514f190a844d809ae5b1edb580aab46a682485) Thanks [@jnsdls](https://github.com/jnsdls)! - [EVM] - add `defaultValue` to constructorParams schema for releases
- [#388](https://github.com/thirdweb-dev/js/pull/388) [`7c0744a`](https://github.com/thirdweb-dev/js/commit/7c0744a006e78987ad8b271b2c64f4bac7759510) Thanks [@adam-maj](https://github.com/adam-maj)! - Add support for total supply and pagination on get all
- [#395](https://github.com/thirdweb-dev/js/pull/395) [`f8bceec`](https://github.com/thirdweb-dev/js/commit/f8bceec10c60b30dc3d6f45926e2a9e018202578) Thanks [@jnsdls](https://github.com/jnsdls)! - [Solana] - fix claim condition reset to native token from custom
- [#399](https://github.com/thirdweb-dev/js/pull/399) [`887af24`](https://github.com/thirdweb-dev/js/commit/887af24450e316c2ea390cd49c0563cebe793c0f) Thanks [@jnsdls](https://github.com/jnsdls)! - [EVM] - fix custom error handling for null-ish error values
- [#390](https://github.com/thirdweb-dev/js/pull/390) [`96bd92e`](https://github.com/thirdweb-dev/js/commit/96bd92e724ec7d880d23348071b26ab8c21dd868) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - [SDK] Expose `getAllDetectedFeatures(ABI)` helper function
## 3.6.0
### Minor Changes
- [#364](https://github.com/thirdweb-dev/js/pull/364) [`7cb8e59`](https://github.com/thirdweb-dev/js/commit/7cb8e59be48dc923316eb36ab43a1bef7364f6b1) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Enable new Drop contracts
### Patch Changes
- [#382](https://github.com/thirdweb-dev/js/pull/382) [`0ed77d2`](https://github.com/thirdweb-dev/js/commit/0ed77d2c9e7526444ef660aab82a950865234aa6) Thanks [@jnsdls](https://github.com/jnsdls)! - Add `constructorParams` to `ExtraPublishMetadataSchemaInput` schema to support better deploys from releases.
- [#385](https://github.com/thirdweb-dev/js/pull/385) [`04a47e8`](https://github.com/thirdweb-dev/js/commit/04a47e8971d28da2532ebd9db824eeb459c17bd4) Thanks [@adam-maj](https://github.com/adam-maj)! - Add retroactive royalty and creator setting
## 3.5.2
### Patch Changes
- [#380](https://github.com/thirdweb-dev/js/pull/380) [`3eba69a`](https://github.com/thirdweb-dev/js/commit/3eba69afd78b7aa472a9c00c9d4220bb69ad1d3e) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Expose sdk.wallet.getChainId()
- [#376](https://github.com/thirdweb-dev/js/pull/376) [`4af785c`](https://github.com/thirdweb-dev/js/commit/4af785c37c831ecd7087c1c9eb9095922cac0855) Thanks [@adam-maj](https://github.com/adam-maj)! - Update tiered-drop with tokens claimed
- [#373](https://github.com/thirdweb-dev/js/pull/373) [`6f818f3`](https://github.com/thirdweb-dev/js/commit/6f818f393eb9023b53a3358d62d60ec23a9246bd) Thanks [@adam-maj](https://github.com/adam-maj)! - Update tiered-drop contract version
- [#377](https://github.com/thirdweb-dev/js/pull/377) [`b79dc18`](https://github.com/thirdweb-dev/js/commit/b79dc18f7ab5155bbf4af02dc1a953546160bad0) Thanks [@adam-maj](https://github.com/adam-maj)! - Add event filters
- Updated dependencies [[`4af785c`](https://github.com/thirdweb-dev/js/commit/4af785c37c831ecd7087c1c9eb9095922cac0855), [`6f818f3`](https://github.com/thirdweb-dev/js/commit/6f818f393eb9023b53a3358d62d60ec23a9246bd)]:
- @thirdweb-dev/contracts-js@1.2.1
## 3.5.1
### Patch Changes
- [#368](https://github.com/thirdweb-dev/js/pull/368) [`4e7b293`](https://github.com/thirdweb-dev/js/commit/4e7b293c543244de79c150ca8c37d941221fc9a3) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Fix handling of old allowlist proof format
- [#365](https://github.com/thirdweb-dev/js/pull/365) [`c9c0e4f`](https://github.com/thirdweb-dev/js/commit/c9c0e4ff39b054af746f3aec75fcbd0a3ef7239d) Thanks [@jnsdls](https://github.com/jnsdls)! - [EVM] - now React Native compatible
## 3.5.0
### Minor Changes
- [#329](https://github.com/thirdweb-dev/js/pull/329) [`5200d5d`](https://github.com/thirdweb-dev/js/commit/5200d5d7e730da58bd4d3e7c222e3c661265f913) Thanks [@adam-maj](https://github.com/adam-maj)! - ### Breaking changes:
1. claim conditions had some changes to support the new drop contracts:
- `maxClaimablePerTransaction` is now named `maxClaimablePerWallet`
- `maxQuantity` is now named `maxClaimable`
2. signature minting now requires a `to` address to be set for security purposees
### Main Changes:
- Support for new optimized Drop contracts
- Support for new claim conditions with overrides
- Don't allow zero address recipient on signature minting
### Patch Changes
- [#362](https://github.com/thirdweb-dev/js/pull/362) [`42a6065`](https://github.com/thirdweb-dev/js/commit/42a606588dfed03c67c57357dd0ee1ad19fad6ea) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - [SDK] Fix fetching NFTs with numbers as ids
- Updated dependencies [[`aa6d9ed`](https://github.com/thirdweb-dev/js/commit/aa6d9ed75d126d7a0cca9fb8fc389ff94e9d1e14)]:
- @thirdweb-dev/contracts-js@1.2.0
## 3.4.5
### Patch Changes
- [#359](https://github.com/thirdweb-dev/js/pull/359) [`e731c1a`](https://github.com/thirdweb-dev/js/commit/e731c1a7c341e6f2b9cc42c88d5c1b036dcf4a7c) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - [SDK] Allow fetching claimerProofs from any claim condition
## 3.4.4
## 3.4.3
### Patch Changes
- [#353](https://github.com/thirdweb-dev/js/pull/353) [`1c24c3c`](https://github.com/thirdweb-dev/js/commit/1c24c3c3c48476a824f817e09e7bf0fbe67c1db5) Thanks [@nachoiacovino](https://github.com/nachoiacovino)! - Add useUpdateCreators hook, fix CreatorInput schema
- [#357](https://github.com/thirdweb-dev/js/pull/357) [`65fc7ba`](https://github.com/thirdweb-dev/js/commit/65fc7ba6fff093a971b53cb0979c39768e090ba1) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Expose claimConditions.getClaimerProofs(address) to get allowlist information for a given address
## 3.4.2
### Patch Changes
- [#344](https://github.com/thirdweb-dev/js/pull/344) [`21c21c1`](https://github.com/thirdweb-dev/js/commit/21c21c1adfd09b60ad563bfd4c308597140de33c) Thanks [@nachoiacovino](https://github.com/nachoiacovino)! - Update internal function resolveContractUriFromAddress -> resolveContractUriFromAddress
- [#352](https://github.com/thirdweb-dev/js/pull/352) [`3522917`](https://github.com/thirdweb-dev/js/commit/352291791ee900e1500f84f095290497934d2f60) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Expose marketplace.auction.executeSale(listingId)
## 3.4.1
### Patch Changes
- [#343](https://github.com/thirdweb-dev/js/pull/343) [`72227b2`](https://github.com/thirdweb-dev/js/commit/72227b2e166a3a68bbb41cf2b389322f5b7547a2) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Expose general marketplace.makeOffer() function
## 3.4.0
### Patch Changes
- [#337](https://github.com/thirdweb-dev/js/pull/337) [`340605b`](https://github.com/thirdweb-dev/js/commit/340605b507f384fbd2999b9c16542af3c53e84a9) Thanks [@adam-maj](https://github.com/adam-maj)! - Clean native token address in listing
- [#342](https://github.com/thirdweb-dev/js/pull/342) [`26116a6`](https://github.com/thirdweb-dev/js/commit/26116a6f637ee845f7bd17f20ffe17caf184658e) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Add new univeral RPC urls
- [#335](https://github.com/thirdweb-dev/js/pull/335) [`15f8006`](https://github.com/thirdweb-dev/js/commit/15f8006e1fb22333b7ee239b45e7b1b12d6dccc8) Thanks [@adam-maj](https://github.com/adam-maj)! - Export smart contract
- [#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 [[`ef27aad`](https://github.com/thirdweb-dev/js/commit/ef27aad0aafc4577e85f44dc77dfbe880bd239b5)]:
- @thirdweb-dev/storage@1.0.6
## 3.3.1
### Patch Changes
- [#332](https://github.com/thirdweb-dev/js/pull/332) [`ad851f7`](https://github.com/thirdweb-dev/js/commit/ad851f7b4d6abb5ab9364ef3dec229f913e18ce5) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Fix for finding addresses in legacy merkle tree format
- [#330](https://github.com/thirdweb-dev/js/pull/330) [`6218c88`](https://github.com/thirdweb-dev/js/commit/6218c885842e6b4e44ec38ec92a9211e56c49bf4) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Fix deploying contracts with non-32 bytes parameters like bytes4
## 3.3.0
### Minor Changes
+8 -9
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/sdk",
"version": "3.3.0",
"version": "3.6.1",
"description": "The main thirdweb SDK.",
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/sdk",
"license": "Apache-2.0",
@@ -65,8 +65,8 @@
"push": "yalc push"
},
"devDependencies": {
"@metaplex-foundation/amman": "^0.10.0",
"@metaplex-foundation/amman-client": "^0.2.1",
"@metaplex-foundation/amman": "0.10.0",
"@metaplex-foundation/amman-client": "0.2.1",
"@microsoft/api-documenter": "^7.19.4",
"@microsoft/api-extractor": "^7.29.2",
"@microsoft/tsdoc": "^0.14.1",
@@ -99,16 +99,15 @@
"ethers": "^5"
},
"dependencies": {
"@metaplex-foundation/js": "^0.15.0",
"@metaplex-foundation/mpl-token-metadata": "^2.2.2",
"@project-serum/anchor": "^0.25.0",
"@solana/spl-token": "^0.2.0",
"@solana/web3.js": "^1.50.1",
"@metaplex-foundation/js": "0.15.0",
"@metaplex-foundation/mpl-token-metadata": "2.2.2",
"@project-serum/anchor": "0.25.0",
"@solana/spl-token": "0.2.0",
"@solana/web3.js": "1.62.0",
"@thirdweb-dev/contracts-js": "*",
"@thirdweb-dev/storage": "*",
"bn.js": "^5.2.1",
"bs58": "^5.0.0",
"cbor-x": "^1.4.1",
"cross-fetch": "^3.1.5",
"eventemitter3": "^4.0.7",
"fast-deep-equal": "^3.1.3",
+6
View File
@@ -0,0 +1,6 @@
export const DEFAULT_API_KEY =
"c6634ad2d97b74baf15ff556016830c251050e6c36b9da508ce3ec80095d3dc1";
export function getRpcUrl(network: string) {
return `https://${network}.rpc.thirdweb.com/${DEFAULT_API_KEY}`;
}
+16 -10
View File
@@ -1,19 +1,25 @@
import { z } from "zod";
/**
* @internal
*/
export const DEFAULT_QUERY_ALL_COUNT = 100;
export const QueryAllParamsSchema = z
.object({
start: z.number().default(0),
count: z.number().default(DEFAULT_QUERY_ALL_COUNT),
})
.default({
start: 0,
count: DEFAULT_QUERY_ALL_COUNT,
});
/**
* Pagination Parameters
* @public
*/
export interface QueryAllParams {
/**
* the index to start from (default: 0)
*/
start?: number;
/**
* how many items to return (default: 100)
*/
count?: number;
}
export type QueryAllParams = Exclude<
z.input<typeof QueryAllParamsSchema>,
undefined
>;
+17 -4
View File
@@ -2,8 +2,14 @@ import BN from "bn.js";
import { BigNumber } from "ethers";
import { z } from "zod";
const isBrowser = () => typeof window !== "undefined";
const FileOrBufferUnionSchema = isBrowser()
/**
* @internal
*/
export const isBrowser = () => typeof window !== "undefined";
/**
* @internal
*/
export const FileOrBufferUnionSchema = isBrowser()
? (z.instanceof(File) as z.ZodType<InstanceType<typeof File>>)
: (z.instanceof(Buffer) as z.ZodTypeAny); // @fixme, this is a hack to make browser happy for now
@@ -72,12 +78,12 @@ export const BigNumberTransformSchema = z
export const BasisPointsSchema = z
.number()
.max(MAX_BPS, "Cannot exeed 100%")
.max(MAX_BPS, "Cannot exceed 100%")
.min(0, "Cannot be below 0%");
export const PercentSchema = z
.number()
.max(100, "Cannot exeed 100%")
.max(100, "Cannot exceed 100%")
.min(0, "Cannot be below 0%");
export const HexColor = z.union([
@@ -96,6 +102,11 @@ export const AmountSchema = z
])
.transform((arg) => (typeof arg === "number" ? arg.toString() : arg));
/**
* @internal
*/
export type Amount = z.input<typeof AmountSchema>;
/**
* @internal
*/
@@ -103,6 +114,8 @@ export const QuantitySchema = z
.union([AmountSchema, z.literal("unlimited")])
.default("unlimited");
export type Quantity = z.output<typeof QuantitySchema>;
export const RawDateSchema = z.date().transform((i) => {
return BigNumber.from(Math.floor(i.getTime() / 1000));
});
+249 -97
View File
@@ -1,6 +1,8 @@
import { Quantity } from "../../core/schema/shared";
import { NATIVE_TOKEN_ADDRESS } from "../constants";
import { ContractWrapper } from "../core/classes/contract-wrapper";
import {
AbstractClaimConditionContractStruct,
ClaimConditionInputArray,
ClaimConditionInputSchema,
ClaimConditionOutputSchema,
@@ -8,7 +10,6 @@ import {
import {
SnapshotEntry,
SnapshotEntryWithProof,
SnapshotInputSchema,
SnapshotSchema,
} from "../schema/contracts/common/snapshots";
import {
@@ -25,9 +26,13 @@ import {
isNativeToken,
normalizePriceValue,
} from "./currency";
import { ShardedMerkleTree } from "./sharded-merkle-tree";
import {
ShardedMerkleTree,
SnapshotFormatVersion,
} from "./sharded-merkle-tree";
import { createSnapshot } from "./snapshots";
import { IDropClaimCondition } from "@thirdweb-dev/contracts-js/dist/declarations/src/DropERC20";
import { IClaimCondition } from "@thirdweb-dev/contracts-js/dist/declarations/src/Drop";
import { IDropClaimCondition_V2 } from "@thirdweb-dev/contracts-js/dist/declarations/src/IDropERC20_V2";
import { ThirdwebStorage } from "@thirdweb-dev/storage";
import {
BigNumber,
@@ -45,6 +50,7 @@ import {
* @returns - `overrides` and `proofs` as an object.
*/
export async function prepareClaim(
addressToClaim: string,
quantity: BigNumberish,
activeClaimCondition: ClaimCondition,
merkleMetadataFetcher: () => Promise<Record<string, string>>,
@@ -52,27 +58,57 @@ export async function prepareClaim(
contractWrapper: ContractWrapper<any>,
storage: ThirdwebStorage,
checkERC20Allowance: boolean,
snapshotFormatVersion: SnapshotFormatVersion,
): Promise<ClaimVerification> {
const addressToClaim = await contractWrapper.getSignerAddress();
let maxClaimable = BigNumber.from(0);
let maxClaimable = convertQuantityToBigNumber(
activeClaimCondition.maxClaimablePerWallet,
tokenDecimals,
);
let proofs = [utils.hexZeroPad([0], 32)];
let priceInProof = activeClaimCondition.price; // the price to send to the contract in claim proofs
let currencyAddressInProof = activeClaimCondition.currencyAddress;
try {
if (
!activeClaimCondition.merkleRootHash
.toString()
.startsWith(constants.AddressZero)
) {
const claim = await fetchSnapshotEntryForAddress(
const snapshotEntry = await fetchSnapshotEntryForAddress(
addressToClaim,
activeClaimCondition.merkleRootHash.toString(),
await merkleMetadataFetcher(),
contractWrapper.getProvider(),
storage,
snapshotFormatVersion,
);
if (!claim) {
throw new Error("No claim found for this address");
if (snapshotEntry) {
proofs = snapshotEntry.proof;
// override only if not default values (unlimited for quantity, zero addr for currency)
maxClaimable =
snapshotEntry.maxClaimable === "unlimited"
? ethers.constants.MaxUint256
: ethers.utils.parseUnits(
snapshotEntry.maxClaimable,
tokenDecimals,
);
priceInProof =
snapshotEntry.price === undefined ||
snapshotEntry.price === "unlimited"
? ethers.constants.MaxUint256
: await normalizePriceValue(
contractWrapper.getProvider(),
snapshotEntry.price,
snapshotEntry.currencyAddress || ethers.constants.AddressZero,
);
currencyAddressInProof =
snapshotEntry.currencyAddress || ethers.constants.AddressZero;
} else {
// if no snapshot entry, and it's a v1 format (exclusive allowlist) then address can't claim
if (snapshotFormatVersion === SnapshotFormatVersion.V1) {
throw new Error("No claim found for this address");
}
// but if its snapshot v2 (override list behavior) then address can still claim with default settings
}
proofs = claim.proof;
maxClaimable = ethers.utils.parseUnits(claim.maxClaimable, tokenDecimals);
}
} catch (e) {
// have to handle the valid error case that we *do* want to throw on
@@ -87,18 +123,28 @@ export async function prepareClaim(
}
const overrides = (await contractWrapper.getCallOverrides()) || {};
const price = activeClaimCondition.price;
const currencyAddress = activeClaimCondition.currencyAddress;
if (price.gt(0)) {
// the actual price to check allowance against
// if proof price is unlimited, then we use the price from the claim condition
// this mimics the contract behavior
const pricePerToken =
priceInProof.toString() !== ethers.constants.MaxUint256.toString()
? priceInProof
: activeClaimCondition.price;
// same for currency address
const currencyAddress =
currencyAddressInProof !== ethers.constants.AddressZero
? currencyAddressInProof
: activeClaimCondition.currencyAddress;
if (pricePerToken.gt(0)) {
if (isNativeToken(currencyAddress)) {
overrides["value"] = BigNumber.from(price)
overrides["value"] = BigNumber.from(pricePerToken)
.mul(quantity)
.div(ethers.utils.parseUnits("1", tokenDecimals));
} else if (checkERC20Allowance) {
await approveErc20Allowance(
contractWrapper,
currencyAddress,
price,
pricePerToken,
quantity,
tokenDecimals,
);
@@ -107,9 +153,11 @@ export async function prepareClaim(
return {
overrides,
proofs,
maxQuantityPerTransaction: maxClaimable,
price,
currencyAddress,
maxClaimable,
price: pricePerToken,
currencyAddress: currencyAddress,
priceInProof,
currencyAddressInProof,
};
}
@@ -139,6 +187,8 @@ export async function fetchSnapshot(
return snapshotData.claims.map((claim) => ({
address: claim.address,
maxClaimable: claim.maxClaimable,
price: claim.price,
currencyAddress: claim.currencyAddress,
}));
}
}
@@ -150,7 +200,9 @@ export async function fetchSnapshotEntryForAddress(
address: string,
merkleRoot: string,
merkleMetadata: Record<string, string> | undefined,
provider: ethers.providers.Provider,
storage: ThirdwebStorage,
snapshotFormatVersion: SnapshotFormatVersion,
): Promise<SnapshotEntryWithProof | null> {
if (!merkleMetadata) {
return null;
@@ -163,12 +215,20 @@ export async function fetchSnapshotEntryForAddress(
raw,
storage,
);
return await merkleTree.getProof(address);
return await merkleTree.getProof(
address,
provider,
snapshotFormatVersion,
);
}
// legacy non-sharded, just fetch it all and filter out
const snapshotData = SnapshotSchema.parse(raw);
if (merkleRoot === snapshotData.merkleRoot) {
return snapshotData.claims.find((c) => c.address === address) || null;
return (
snapshotData.claims.find(
(c) => c.address.toLowerCase() === address.toLowerCase(),
) || null
);
}
}
return null;
@@ -227,53 +287,20 @@ export async function updateExistingClaimConditions(
}
/**
* Fetches the proof for the current signer for a particular wallet.
*
* @param addressToClaim
* @param merkleRoot - The merkle root of the condition to check.
* @param tokenDecimals
* @param merkleMetadata
* @param storage
* @returns - The proof for the current signer for the specified condition.
*/
export async function getClaimerProofs(
addressToClaim: string,
merkleRoot: string,
tokenDecimals: number,
merkleMetadata: Record<string, string>,
storage: ThirdwebStorage,
): Promise<{ maxClaimable: BigNumber; proof: string[] }> {
const claim = await fetchSnapshotEntryForAddress(
addressToClaim,
merkleRoot,
merkleMetadata,
storage,
);
if (!claim) {
return {
proof: [],
maxClaimable: BigNumber.from(0),
};
}
return {
proof: claim.proof,
maxClaimable: ethers.utils.parseUnits(claim.maxClaimable, tokenDecimals),
};
}
/**
* Create and uploads snapshots + converts claim conditions to contract format
* @internal
* Decorates claim conditions with merkle roots from snapshots if present
* @param claimConditionInputs
* @param tokenDecimals
* @param provider
* @param storage
* @internal
* @param snapshotFormatVersion
*/
export async function processClaimConditionInputs(
async function processSnapshotData(
claimConditionInputs: ClaimConditionInput[],
tokenDecimals: number,
provider: providers.Provider,
provider: ethers.providers.Provider,
storage: ThirdwebStorage,
snapshotFormatVersion: SnapshotFormatVersion,
) {
const snapshotInfos: SnapshotInfo[] = [];
const inputsWithSnapshots = await Promise.all(
@@ -281,9 +308,11 @@ export async function processClaimConditionInputs(
// check snapshots and upload if provided
if (conditionInput.snapshot && conditionInput.snapshot.length > 0) {
const snapshotInfo = await createSnapshot(
SnapshotInputSchema.parse(conditionInput.snapshot),
conditionInput.snapshot,
tokenDecimals,
provider,
storage,
snapshotFormatVersion,
);
snapshotInfos.push(snapshotInfo);
conditionInput.merkleRootHash = snapshotInfo.merkleRoot;
@@ -295,26 +324,54 @@ export async function processClaimConditionInputs(
return conditionInput;
}),
);
return { inputsWithSnapshots, snapshotInfos };
}
function compare(a: BigNumberish, b: BigNumberish) {
const left = BigNumber.from(a);
const right = BigNumber.from(b);
if (left.eq(right)) {
return 0;
} else if (left.gt(right)) {
return 1;
} else {
return -1;
}
}
/**
* Create and uploads snapshots + converts claim conditions to contract format
* @param claimConditionInputs
* @param tokenDecimals
* @param provider
* @param storage
* @param snapshotFormatVersion
* @internal
*/
export async function processClaimConditionInputs(
claimConditionInputs: ClaimConditionInput[],
tokenDecimals: number,
provider: providers.Provider,
storage: ThirdwebStorage,
snapshotFormatVersion: SnapshotFormatVersion,
) {
const { inputsWithSnapshots, snapshotInfos } = await processSnapshotData(
claimConditionInputs,
tokenDecimals,
provider,
storage,
snapshotFormatVersion,
);
const parsedInputs = ClaimConditionInputArray.parse(inputsWithSnapshots);
// Convert processed inputs to the format the contract expects, and sort by timestamp
const sortedConditions: IDropClaimCondition.ClaimConditionStruct[] = (
const sortedConditions: AbstractClaimConditionContractStruct[] = (
await Promise.all(
parsedInputs.map((c) =>
convertToContractModel(c, tokenDecimals, provider),
convertToContractModel(c, tokenDecimals, provider, storage),
),
)
).sort((a, b) => {
const left = BigNumber.from(a.startTimestamp);
const right = BigNumber.from(b.startTimestamp);
if (left.eq(right)) {
return 0;
} else if (left.gt(right)) {
return 1;
} else {
return -1;
}
return compare(a.startTimestamp, b.startTimestamp);
});
return { snapshotInfos, sortedConditions };
}
@@ -324,41 +381,106 @@ export async function processClaimConditionInputs(
* @param c
* @param tokenDecimals
* @param provider
* @param storage
* @internal
*/
async function convertToContractModel(
c: FilledConditionInput,
tokenDecimals: number,
provider: providers.Provider,
): Promise<IDropClaimCondition.ClaimConditionStruct> {
storage: ThirdwebStorage,
): Promise<AbstractClaimConditionContractStruct> {
const currency =
c.currencyAddress === constants.AddressZero
? NATIVE_TOKEN_ADDRESS
: c.currencyAddress;
let maxClaimableSupply;
let quantityLimitPerTransaction;
if (c.maxQuantity === "unlimited") {
maxClaimableSupply = ethers.constants.MaxUint256.toString();
} else {
maxClaimableSupply = ethers.utils.parseUnits(c.maxQuantity, tokenDecimals);
}
if (c.quantityLimitPerTransaction === "unlimited") {
quantityLimitPerTransaction = ethers.constants.MaxUint256.toString();
} else {
quantityLimitPerTransaction = ethers.utils.parseUnits(
c.quantityLimitPerTransaction,
tokenDecimals,
);
const maxClaimableSupply = convertQuantityToBigNumber(
c.maxClaimableSupply,
tokenDecimals,
);
const maxClaimablePerWallet = convertQuantityToBigNumber(
c.maxClaimablePerWallet,
tokenDecimals,
);
let metadataOrUri;
if (c.metadata) {
if (typeof c.metadata === "string") {
metadataOrUri = c.metadata;
} else {
metadataOrUri = await storage.upload(c.metadata);
}
}
return {
startTimestamp: c.startTime,
maxClaimableSupply,
supplyClaimed: 0,
quantityLimitPerTransaction,
waitTimeInSecondsBetweenClaims: c.waitInSeconds,
maxClaimablePerWallet,
pricePerToken: await normalizePriceValue(provider, c.price, currency),
currency,
merkleRoot: c.merkleRootHash,
merkleRoot: c.merkleRootHash.toString(),
waitTimeInSecondsBetweenClaims: c.waitInSeconds || 0,
metadata: metadataOrUri,
};
}
export function abstractContractModelToLegacy(
model: AbstractClaimConditionContractStruct,
): IDropClaimCondition_V2.ClaimConditionStruct {
return {
startTimestamp: model.startTimestamp,
maxClaimableSupply: model.maxClaimableSupply,
supplyClaimed: model.supplyClaimed,
merkleRoot: model.merkleRoot,
pricePerToken: model.pricePerToken,
currency: model.currency,
quantityLimitPerTransaction: model.maxClaimablePerWallet,
waitTimeInSecondsBetweenClaims: model.waitTimeInSecondsBetweenClaims || 0,
};
}
export function abstractContractModelToNew(
model: AbstractClaimConditionContractStruct,
): IClaimCondition.ClaimConditionStruct {
return {
startTimestamp: model.startTimestamp,
maxClaimableSupply: model.maxClaimableSupply,
supplyClaimed: model.supplyClaimed,
merkleRoot: model.merkleRoot,
pricePerToken: model.pricePerToken,
currency: model.currency,
quantityLimitPerWallet: model.maxClaimablePerWallet,
metadata: model.metadata || "",
};
}
export function legacyContractModelToAbstract(
model: IDropClaimCondition_V2.ClaimConditionStruct,
): AbstractClaimConditionContractStruct {
return {
startTimestamp: model.startTimestamp,
maxClaimableSupply: model.maxClaimableSupply,
supplyClaimed: model.supplyClaimed,
merkleRoot: model.merkleRoot.toString(),
pricePerToken: model.pricePerToken,
currency: model.currency,
maxClaimablePerWallet: model.quantityLimitPerTransaction,
waitTimeInSecondsBetweenClaims: model.waitTimeInSecondsBetweenClaims,
};
}
export function newContractModelToAbstract(
model: IClaimCondition.ClaimConditionStruct,
): AbstractClaimConditionContractStruct {
return {
startTimestamp: model.startTimestamp,
maxClaimableSupply: model.maxClaimableSupply,
supplyClaimed: model.supplyClaimed,
merkleRoot: model.merkleRoot.toString(),
pricePerToken: model.pricePerToken,
currency: model.currency,
maxClaimablePerWallet: model.quantityLimitPerWallet,
waitTimeInSecondsBetweenClaims: 0,
metadata: model.metadata,
};
}
@@ -369,10 +491,11 @@ async function convertToContractModel(
* @param provider
* @param merkleMetadata
* @param storage
* @param shouldDownloadSnapshot
* @internal
*/
export async function transformResultToClaimCondition(
pm: IDropClaimCondition.ClaimConditionStructOutput,
pm: AbstractClaimConditionContractStruct,
tokenDecimals: number,
provider: providers.Provider,
merkleMetadata: Record<string, string> | undefined,
@@ -385,8 +508,8 @@ export async function transformResultToClaimCondition(
pm.maxClaimableSupply,
tokenDecimals,
);
const quantityLimitPerTransaction = convertToReadableQuantity(
pm.quantityLimitPerTransaction,
const maxClaimablePerWallet = convertToReadableQuantity(
pm.maxClaimablePerWallet,
tokenDecimals,
);
const availableSupply = convertToReadableQuantity(
@@ -397,13 +520,17 @@ export async function transformResultToClaimCondition(
pm.supplyClaimed,
tokenDecimals,
);
let resolvedMetadata;
if (pm.metadata) {
resolvedMetadata = await storage.downloadJSON(pm.metadata);
}
return ClaimConditionOutputSchema.parse({
startTime: pm.startTimestamp,
maxQuantity: maxClaimableSupply,
maxClaimableSupply,
maxClaimablePerWallet,
currentMintSupply,
availableSupply,
quantityLimitPerTransaction,
waitInSeconds: pm.waitTimeInSecondsBetweenClaims.toString(),
waitInSeconds: pm.waitTimeInSecondsBetweenClaims?.toString(),
price: BigNumber.from(pm.pricePerToken),
currency: pm.currency,
currencyAddress: pm.currency,
@@ -412,10 +539,19 @@ export async function transformResultToClaimCondition(
snapshot: shouldDownloadSnapshot
? await fetchSnapshot(pm.merkleRoot, merkleMetadata, storage)
: undefined,
metadata: resolvedMetadata,
});
}
function convertToReadableQuantity(bn: BigNumber, tokenDecimals: number) {
/**
* @internal
* @param bn
* @param tokenDecimals
*/
export function convertToReadableQuantity(
bn: BigNumberish,
tokenDecimals: number,
) {
if (bn.toString() === ethers.constants.MaxUint256.toString()) {
return "unlimited";
} else {
@@ -423,6 +559,22 @@ function convertToReadableQuantity(bn: BigNumber, tokenDecimals: number) {
}
}
/**
* @internal
* @param quantity
* @param tokenDecimals
*/
export function convertQuantityToBigNumber(
quantity: Quantity,
tokenDecimals: number,
) {
if (quantity === "unlimited") {
return ethers.constants.MaxUint256;
} else {
return ethers.utils.parseUnits(quantity, tokenDecimals);
}
}
export async function calculateClaimCost(
contractWrapper: ContractWrapper<any>,
pricePerToken: Price,
+31 -1
View File
@@ -1,9 +1,9 @@
import { AmountSchema } from "../../core/schema/shared";
import {
getNativeTokenByChainId,
NATIVE_TOKEN_ADDRESS,
} from "../constants/currency";
import { ContractWrapper } from "../core/classes/contract-wrapper";
import { AmountSchema } from "../schema/shared";
import { Amount, Currency, CurrencyValue, Price } from "../types/currency";
import { BaseERC20 } from "../types/eips";
import type { IERC20, IERC20Metadata } from "@thirdweb-dev/contracts-js";
@@ -26,6 +26,21 @@ export function isNativeToken(tokenAddress: string): boolean {
);
}
export function cleanCurrencyAddress(currencyAddress: string): string {
if (isNativeToken(currencyAddress)) {
return NATIVE_TOKEN_ADDRESS;
}
return currencyAddress;
}
/**
*
* @param provider
* @param inputPrice
* @param currencyAddress
* @returns
* @internal
*/
export async function normalizePriceValue(
provider: providers.Provider,
inputPrice: Price,
@@ -35,6 +50,13 @@ export async function normalizePriceValue(
return utils.parseUnits(AmountSchema.parse(inputPrice), metadata.decimals);
}
/**
*
* @param provider
* @param asset
* @returns
* @internal
*/
export async function fetchCurrencyMetadata(
provider: providers.Provider,
asset: string,
@@ -66,6 +88,14 @@ export async function fetchCurrencyMetadata(
}
}
/**
*
* @param providerOrSigner
* @param asset
* @param price
* @returns
* @internal
*/
export async function fetchCurrencyValue(
providerOrSigner: providers.Provider,
asset: string,
+5 -2
View File
@@ -459,9 +459,12 @@ function parseMessageParts(regex: RegExp, raw: string): string {
* @param message
*/
export function includesErrorMessage(err: any, message: string): boolean {
if (!err) {
return false;
}
return (
(err && err.toString().includes(message)) ||
(err.message && err.message.toString().includes(message)) ||
(err.error && err.error.toString().includes(message))
(err && err.message && err.message.toString().includes(message)) ||
(err && err.error && err.error.toString().includes(message))
);
}
+104 -23
View File
@@ -6,7 +6,9 @@ import {
} from "../constants/contract-features";
import { ContractWrapper } from "../core/classes/contract-wrapper";
import { DetectableFeature } from "../core/interfaces/DetectableFeature";
import { decode } from "../lib/cbor-decode.js";
import {
Abi,
AbiEvent,
AbiFunction,
AbiSchema,
@@ -23,7 +25,6 @@ import {
import { ExtensionNotImplementedError } from "./error";
import { ThirdwebStorage } from "@thirdweb-dev/storage";
import bs58 from "bs58";
import { decode } from "cbor-x";
import { BaseContract, BigNumber, ethers } from "ethers";
import { z } from "zod";
@@ -32,26 +33,57 @@ import { z } from "zod";
* @param abi
* @param feature
*/
function matchesAbiInterface(
abi: z.input<typeof AbiSchema>,
feature: Feature,
): boolean {
function matchesAbiInterface(abi: Abi, feature: Feature): boolean {
// returns true if all the functions in `interfaceToMatch` are found in `contract` (removing any duplicates)
const contractFn = [
...new Set(extractFunctionsFromAbi(abi).map((f) => f.name)),
];
const interfaceFn = [
...new Set(
feature.abis
.flatMap((i: any) => extractFunctionsFromAbi(i))
.map((f: AbiFunction) => f.name),
),
];
return hasMatchingAbi(abi, feature.abis);
}
return (
contractFn.filter((k) => interfaceFn.includes(k)).length ===
interfaceFn.length
/**
* @internal
* @param contractWrapper
* @param abi
* @returns
*/
export function matchesPrebuiltAbi<T extends BaseContract>(
contractWrapper: ContractWrapper<BaseContract>,
abi: Abi,
): contractWrapper is ContractWrapper<T> {
return hasMatchingAbi(contractWrapper.abi as Abi, [abi]);
}
/**
* @internal
* @param contractAbi
* @param featureAbis
* @returns
*/
export function hasMatchingAbi(contractAbi: Abi, featureAbis: readonly Abi[]) {
const contractFn = extractFunctionsFromAbi(contractAbi);
const interfaceFn = featureAbis.flatMap((i: any) =>
extractFunctionsFromAbi(i),
);
// match every function and their arguments
const intersection = contractFn.filter((fn) => {
const match = interfaceFn.find(
(iFn) =>
iFn.name === fn.name &&
iFn.inputs.length === fn.inputs.length &&
iFn.inputs.every((i, index) => {
if (i.type === "tuple" || i.type === "tuple[]") {
// check that all properties in the tuple are the same type
return (
i.type === fn.inputs[index].type &&
i.components?.every((c, cIndex) => {
return c.type === fn.inputs[index].components?.[cIndex]?.type;
})
);
}
return i.type === fn.inputs[index].type;
}),
);
return match !== undefined;
});
return intersection.length === interfaceFn.length;
}
/**
@@ -145,7 +177,7 @@ export function extractFunctionParamsFromAbi(
* @param metadata
*/
export function extractFunctionsFromAbi(
abi: z.input<typeof AbiSchema>,
abi: Abi,
metadata?: Record<string, any>,
): AbiFunction[] {
const functions = (abi || []).filter((el) => el.type === "function");
@@ -178,7 +210,7 @@ export function extractFunctionsFromAbi(
* @param metadata
*/
export function extractEventsFromAbi(
abi: z.input<typeof AbiSchema>,
abi: Abi,
metadata?: Record<string, any>,
): AbiEvent[] {
const events = (abi || []).filter((el) => el.type === "event");
@@ -405,7 +437,9 @@ export async function fetchSourceFilesFromMetadata(
Object.entries(publishedMetadata.metadata.sources).map(
async ([path, info]) => {
const urls = (info as any).urls as string[];
const ipfsLink = urls.find((url) => url.includes("ipfs"));
const ipfsLink = urls
? urls.find((url) => url.includes("ipfs"))
: undefined;
if (ipfsLink) {
const ipfsHash = ipfsLink.split("ipfs/")[1];
// 5 sec timeout for sources that haven't been uploaded to ipfs
@@ -423,7 +457,9 @@ export async function fetchSourceFilesFromMetadata(
} else {
return {
filename: path,
source: "Could not find source for this contract",
source:
(info as any).content ||
"Could not find source for this contract",
};
}
},
@@ -489,7 +525,7 @@ export async function fetchExtendedReleaseMetadata(
* @returns the nested struct of all features and whether they're detected in the abi
*/
export function detectFeatures(
abi: z.input<typeof AbiSchema>,
abi: Abi,
features: Record<string, Feature> = SUPPORTED_FEATURES,
): Record<string, FeatureWithEnabled> {
const results: Record<string, FeatureWithEnabled> = {};
@@ -506,6 +542,51 @@ export function detectFeatures(
return results;
}
function extractFeatures(
input: Record<string, FeatureWithEnabled>,
enabledExtensions: FeatureWithEnabled[],
) {
if (!input) {
return;
}
for (const extensionKey in input) {
const extension = input[extensionKey];
// if extension is enabled, then add it to enabledFeatures
if (extension.enabled) {
enabledExtensions.push(extension);
}
// recurse
extractFeatures(
extension.features,
enabledExtensions,
);
}
}
/**
* Return all the detected features in the abi
* @param abi - parsed array of abi entries
* @returns array of all detected extensions with full information on each feature
* @public
*/
export function getAllDetectedFeatures(abi: Abi): FeatureWithEnabled[] {
const features: FeatureWithEnabled[] = [];
extractFeatures(detectFeatures(abi), features);
return features;
}
/**
* Return all the detected features names in the abi
* @param abi - parsed array of abi entries
* @returns array of all detected features names
* @public
*/
export function getAllDetectedFeatureNames(abi: Abi): string[] {
const features: FeatureWithEnabled[] = [];
extractFeatures(detectFeatures(abi), features);
return features.map((f) => f.name);
}
/**
* Checks whether the given ABI supports a given feature
* @internal
+7
View File
@@ -3,3 +3,10 @@ export * from "./snapshots";
export * from "./role";
export * from "./feature-detection";
export * from "./version-checker";
// export helpful util
export {
fetchCurrencyValue,
fetchCurrencyMetadata,
normalizePriceValue,
} from "./currency";
export { convertToReadableQuantity } from "./claim-conditions";
+27
View File
@@ -0,0 +1,27 @@
import IThirdwebContractABI from "@thirdweb-dev/contracts-js/dist/abis/IThirdwebContract.json";
import { ethers } from "ethers";
export async function getPrebuiltInfo(
address: string,
provider: ethers.providers.Provider,
): Promise<{ type: string; version: number } | undefined> {
try {
const contract = new ethers.Contract(
address,
IThirdwebContractABI,
provider,
);
const [type, version] = await Promise.all([
ethers.utils
.toUtf8String(await contract.contractType()) // eslint-disable-next-line no-control-regex
.replace(/\x00/g, ""),
await contract.contractVersion(),
]);
return {
type,
version,
};
} catch (e) {
return undefined;
}
}
+1 -1
View File
@@ -1,10 +1,10 @@
import { MAX_BPS } from "../../core/schema/shared";
import {
InterfaceId_IERC1155,
InterfaceId_IERC721,
} from "../constants/contract";
import { SignerOrProvider } from "../core";
import { ContractWrapper } from "../core/classes/contract-wrapper";
import { MAX_BPS } from "../schema/shared";
import {
NewAuctionListing,
NewDirectListing,
+1 -1
View File
@@ -67,9 +67,9 @@ export async function fetchTokenMetadata(
}
return CommonNFTOutput.parse({
...jsonMetadata,
id: BigNumber.from(tokenId).toString(),
uri: tokenUri,
...jsonMetadata,
});
}
@@ -4,12 +4,14 @@ import {
ShardedSnapshot,
SnapshotEntry,
SnapshotEntryWithProof,
SnapshotEntryWithProofSchema,
SnapshotInputSchema,
} from "../schema";
} from "../schema/contracts/common/snapshots";
import { SnapshotInput } from "../types";
import { hashLeafNode } from "./snapshots";
import { convertQuantityToBigNumber } from "./claim-conditions";
import { fetchCurrencyMetadata } from "./currency";
import { ThirdwebStorage } from "@thirdweb-dev/storage";
import { utils } from "ethers";
import { ethers, utils } from "ethers";
import { MerkleTree } from "merkletreejs";
// shard using the first 2 hex character of the address
@@ -17,6 +19,11 @@ import { MerkleTree } from "merkletreejs";
// shard files will be 00.json, 01.json, 02.json, ..., ff.json
const SHARD_NYBBLES = 2;
export enum SnapshotFormatVersion {
V1 = 1, // address and maxClaimable
V2 = 2, // address, maxClaimable, price, currencyAddress
}
export class ShardedMerkleTree {
private shardNybbles: number;
private shards: Record<string, ShardData>;
@@ -73,17 +80,64 @@ export class ShardedMerkleTree {
);
}
static hashEntry(entry: SnapshotEntry, tokenDecimals: number): string {
return hashLeafNode(
entry.address,
utils.parseUnits(entry.maxClaimable, tokenDecimals),
);
static hashEntry(
entry: SnapshotEntry,
tokenDecimals: number,
currencyDecimals: number,
snapshotFormatVersion: SnapshotFormatVersion,
): string {
switch (snapshotFormatVersion) {
case SnapshotFormatVersion.V1:
return utils.solidityKeccak256(
["address", "uint256"],
[
entry.address,
convertQuantityToBigNumber(entry.maxClaimable, tokenDecimals),
],
);
case SnapshotFormatVersion.V2:
return utils.solidityKeccak256(
["address", "uint256", "uint256", "address"],
[
entry.address,
convertQuantityToBigNumber(entry.maxClaimable, tokenDecimals),
convertQuantityToBigNumber(
entry.price || "unlimited",
currencyDecimals,
),
entry.currencyAddress || ethers.constants.AddressZero,
],
);
}
}
static async fetchAndCacheDecimals(
cache: Record<string, number>,
provider: ethers.providers.Provider,
currencyAddress?: string,
): Promise<number> {
if (!currencyAddress) {
return 18;
}
// cache decimals for each currency to avoid refetching for every address
let currencyDecimals = cache[currencyAddress];
if (currencyDecimals === undefined) {
const currencyMetadata = await fetchCurrencyMetadata(
provider,
currencyAddress,
);
currencyDecimals = currencyMetadata.decimals;
cache[currencyAddress] = currencyDecimals;
}
return currencyDecimals;
}
static async buildAndUpload(
snapshotInput: SnapshotInput,
tokenDecimals: number,
provider: ethers.providers.Provider,
storage: ThirdwebStorage,
snapshotFormatVersion: SnapshotFormatVersion,
shardNybbles = SHARD_NYBBLES,
): Promise<ShardedSnapshot> {
const inputs = SnapshotInputSchema.parse(snapshotInput);
@@ -98,13 +152,28 @@ export class ShardedMerkleTree {
}
shards[shard].push(snapshotEntry);
}
const currencyDecimalMap: Record<string, number> = {};
// create shard => subtree root map
const roots = Object.fromEntries(
Object.entries(shards).map(([shard, entries]) => [
const subTrees = await Promise.all(
Object.entries(shards).map(async ([shard, entries]) => [
shard,
new MerkleTree(
entries.map((entry) =>
ShardedMerkleTree.hashEntry(entry, tokenDecimals),
await Promise.all(
entries.map(async (entry) => {
// cache decimals for each currency to avoid refetching for every address
const currencyDecimals =
await ShardedMerkleTree.fetchAndCacheDecimals(
currencyDecimalMap,
provider,
entry.currencyAddress,
);
return ShardedMerkleTree.hashEntry(
entry,
tokenDecimals,
currencyDecimals,
snapshotFormatVersion,
);
}),
),
utils.keccak256,
{
@@ -113,6 +182,7 @@ export class ShardedMerkleTree {
).getHexRoot(),
]),
);
const roots = Object.fromEntries(subTrees);
// create master tree from shard => subtree root map
const tree = new MerkleTree(Object.values(roots), utils.keccak256, {
sort: true,
@@ -155,22 +225,38 @@ export class ShardedMerkleTree {
public async getProof(
address: string,
provider: ethers.providers.Provider,
snapshotFormatVersion: SnapshotFormatVersion,
): Promise<SnapshotEntryWithProof | null> {
const shardId = address.slice(2, 2 + this.shardNybbles).toLowerCase();
let shard = this.shards[shardId];
const currencyDecimalMap: Record<string, number> = {};
if (shard === undefined) {
try {
shard = this.shards[shardId] =
await this.storage.downloadJSON<ShardData>(
`${this.baseUri}/${shardId}.json`,
);
this.trees[shardId] = new MerkleTree(
shard.entries.map((entry) =>
ShardedMerkleTree.hashEntry(entry, this.tokenDecimals),
),
utils.keccak256,
{ sort: true },
const hashedEntries = await Promise.all(
shard.entries.map(async (entry) => {
// cache decimals for each currency to avoid refetching for every address
const currencyDecimals =
await ShardedMerkleTree.fetchAndCacheDecimals(
currencyDecimalMap,
provider,
entry.currencyAddress,
);
return ShardedMerkleTree.hashEntry(
entry,
this.tokenDecimals,
currencyDecimals,
snapshotFormatVersion,
);
}),
);
this.trees[shardId] = new MerkleTree(hashedEntries, utils.keccak256, {
sort: true,
});
} catch (e) {
console.warn("No merkle entry found for address", address);
return null;
@@ -182,35 +268,34 @@ export class ShardedMerkleTree {
if (!entry) {
return null;
}
const leaf = ShardedMerkleTree.hashEntry(entry, this.tokenDecimals);
const currencyDecimals = await ShardedMerkleTree.fetchAndCacheDecimals(
currencyDecimalMap,
provider,
entry.currencyAddress,
);
const leaf = ShardedMerkleTree.hashEntry(
entry,
this.tokenDecimals,
currencyDecimals,
snapshotFormatVersion,
);
const proof = this.trees[shardId]
.getProof(leaf)
.map((i) => "0x" + i.data.toString("hex"));
return {
address,
return SnapshotEntryWithProofSchema.parse({
...entry,
proof: proof.concat(shard.proofs),
maxClaimable: entry.maxClaimable,
};
});
}
public async getAllEntries(): Promise<SnapshotEntry[]> {
try {
const entries = await this.storage.downloadJSON<SnapshotEntry[]>(
return await this.storage.downloadJSON<SnapshotEntry[]>(
this.originalEntriesUri,
);
return entries;
} catch (e) {
console.warn("Could not fetch original snapshot entries", e);
return [];
}
}
private computeAllShardIds(shardNybbles: number) {
const max = parseInt(`0x${"f".repeat(shardNybbles)}`, 16);
const allShards = [];
for (let i = 0; i <= max; i++) {
allShards.push(i.toString(16).padStart(shardNybbles, "0"));
}
return allShards;
}
}
+12 -19
View File
@@ -1,25 +1,32 @@
import { SnapshotInputSchema } from "../schema/contracts/common/snapshots";
import { SnapshotInputSchema } from "../schema";
import {
SnapshotInfo,
SnapshotInput,
} from "../types/claim-conditions/claim-conditions";
import { DuplicateLeafsError } from "./error";
import { ShardedMerkleTree } from "./sharded-merkle-tree";
import {
ShardedMerkleTree,
SnapshotFormatVersion,
} from "./sharded-merkle-tree";
import { ThirdwebStorage } from "@thirdweb-dev/storage";
import { BigNumber, BigNumberish, utils } from "ethers";
import { ethers } from "ethers";
/**
* Create a snapshot (merkle tree) from a list of addresses and uploads it to IPFS
* @param snapshotInput - the list of addresses to hash
* @param tokenDecimals - the token decimals
* @param provider
* @param storage - the storage to upload to
* @param snapshotFormatVersion
* @returns the generated snapshot and URI
* @internal
*/
export async function createSnapshot(
snapshotInput: SnapshotInput,
tokenDecimals: number,
provider: ethers.providers.Provider,
storage: ThirdwebStorage,
snapshotFormatVersion: SnapshotFormatVersion,
): Promise<SnapshotInfo> {
const input = SnapshotInputSchema.parse(snapshotInput);
const addresses = input.map((i) => i.address);
@@ -30,26 +37,12 @@ export async function createSnapshot(
const tree = await ShardedMerkleTree.buildAndUpload(
input,
tokenDecimals,
provider,
storage,
snapshotFormatVersion,
);
return {
merkleRoot: tree.shardedMerkleInfo.merkleRoot,
snapshotUri: tree.uri,
};
}
/**
* Hash an address and the corresponding claimable amount
* @internal
* @param address - the address
* @param maxClaimableAmount - the amount
*/
export function hashLeafNode(
address: string,
maxClaimableAmount: BigNumberish,
): string {
return utils.solidityKeccak256(
["address", "uint256"],
[address, BigNumber.from(maxClaimableAmount)],
);
}
+113
View File
@@ -1,3 +1,4 @@
import { PrebuiltContractType } from "../core/types";
import { ChainId, SUPPORTED_CHAIN_ID } from "./chains";
import { constants } from "ethers";
@@ -142,6 +143,118 @@ export const CONTRACT_ADDRESSES: Record<
},
};
type DropContract = Extract<
PrebuiltContractType,
"nft-drop" | "token-drop" | "edition-drop" | "signature-drop"
>;
export const APPROVED_IMPLEMENTATIONS: Record<
SUPPORTED_CHAIN_ID,
Record<DropContract, string>
> = {
[ChainId.Mainnet]: {
"nft-drop": "0x60fF9952e0084A6DEac44203838cDC91ABeC8736",
"edition-drop": "0x74af262d0671F378F97a1EDC3d0970Dbe8A1C550",
"token-drop": "0xE1eE43D23f247b6A9aF81fcE2766E76709482728",
"signature-drop": "0x6fD690EB509BdE4C50028C5D9C0dE3750C2Fad6A",
},
[ChainId.Polygon]: {
"nft-drop": "0xB96508050Ba0925256184103560EBADA912Fcc69",
"edition-drop": "0x74af262d0671F378F97a1EDC3d0970Dbe8A1C550",
"token-drop": "0x5A8eA4Adad8289746D073947BA06D69A62499aaf",
"signature-drop": "0xBE2fDc35410E268e41Bec62DBb01AEb43245c7d5",
},
[ChainId.Fantom]: {
"nft-drop": "0x2A396b2D90BAcEF19cDa973586B2633d22710fC2",
"edition-drop": "0x06395FCF9AC6ED827f9dD6e776809cEF1Be0d21B",
"token-drop": "0x0148b28a38efaaC31b6aa0a6D9FEb70FE7C91FFa",
"signature-drop": "0xe135Ef65C2B2213C3fD56d0Bd6500A2cA147aC10",
},
[ChainId.Avalanche]: {
"nft-drop": "0x9cF91118C8ee2913F0588e0F10e36B3d63F68bF6",
"edition-drop": "0x135fC9D26E5eC51260ece1DF4ED424E2f55c7766",
"token-drop": "0xca0B071899E575BA86495D46c5066971b6f3A901",
"signature-drop": "0x1d47526C3292B0130ef0afD5F02c1DA052A017B3",
},
[ChainId.Optimism]: {
"nft-drop": "0xFBd7D24d80ee005671E731a7287DEB6073264dD1",
"edition-drop": "0xe135Ef65C2B2213C3fD56d0Bd6500A2cA147aC10",
"token-drop": "0x902Dd246e66d8C3CE652375a723F2a52b43b9AAE",
"signature-drop": "0x8a4cd3549e548bbEEb38C16E041FFf040a5acabD",
},
[ChainId.Arbitrum]: {
"nft-drop": "0xC4903c1Ff5367b9ac2c349B63DC2409421AaEE2a",
"edition-drop": "0xCcddcec1831646Beff2753249f1B9C580327E89F",
"token-drop": "0x1b5947e1a2d5a29D0df20931DeAB0B87818209B9",
"signature-drop": "0x2dF9851af45dd41C8584ac55D983C604da985Bc7",
},
[ChainId.BinanceSmartChainMainnet]: {
"nft-drop": "0x902Dd246e66d8C3CE652375a723F2a52b43b9AAE",
"edition-drop": "0x2A396b2D90BAcEF19cDa973586B2633d22710fC2",
"token-drop": "0xe135Ef65C2B2213C3fD56d0Bd6500A2cA147aC10",
"signature-drop": "0xFBd7D24d80ee005671E731a7287DEB6073264dD1",
},
[ChainId.Goerli]: {
"nft-drop": "0xD11c97DD5F5546B5bBd630D7D1d7327481B0b92C",
"edition-drop": "0x5A8eA4Adad8289746D073947BA06D69A62499aaf",
"token-drop": "0x5680933221B752EB443654a014f88B101F868d50",
"signature-drop": "0x1b5947e1a2d5a29D0df20931DeAB0B87818209B9",
},
[ChainId.Mumbai]: {
"nft-drop": "0xC4903c1Ff5367b9ac2c349B63DC2409421AaEE2a",
"edition-drop": "0xCcddcec1831646Beff2753249f1B9C580327E89F",
"token-drop": "0x1b5947e1a2d5a29D0df20931DeAB0B87818209B9",
"signature-drop": "0x2dF9851af45dd41C8584ac55D983C604da985Bc7",
},
[ChainId.FantomTestnet]: {
"nft-drop": "0x8a4cd3549e548bbEEb38C16E041FFf040a5acabD",
"edition-drop": "0x902Dd246e66d8C3CE652375a723F2a52b43b9AAE",
"token-drop": "0xFBd7D24d80ee005671E731a7287DEB6073264dD1",
"signature-drop": "0x5A8eA4Adad8289746D073947BA06D69A62499aaf",
},
[ChainId.AvalancheFujiTestnet]: {
"nft-drop": "0xD11c97DD5F5546B5bBd630D7D1d7327481B0b92C",
"edition-drop": "0xE1eE43D23f247b6A9aF81fcE2766E76709482728",
"token-drop": "0x6fD690EB509BdE4C50028C5D9C0dE3750C2Fad6A",
"signature-drop": "0xCcddcec1831646Beff2753249f1B9C580327E89F",
},
[ChainId.OptimismGoerli]: {
"nft-drop": "0xCcddcec1831646Beff2753249f1B9C580327E89F",
"edition-drop": "0x6fD690EB509BdE4C50028C5D9C0dE3750C2Fad6A",
"token-drop": "0xD11c97DD5F5546B5bBd630D7D1d7327481B0b92C",
"signature-drop": "0x1b5947e1a2d5a29D0df20931DeAB0B87818209B9",
},
[ChainId.ArbitrumGoerli]: {
"nft-drop": "0x9CfE807a5b124b962064Fa8F7FD823Cc701255b6",
"edition-drop": "0x9cF91118C8ee2913F0588e0F10e36B3d63F68bF6",
"token-drop": "0x1d47526C3292B0130ef0afD5F02c1DA052A017B3",
"signature-drop": "0xE1eE43D23f247b6A9aF81fcE2766E76709482728",
},
[ChainId.BinanceSmartChainTestnet]: {
"nft-drop": "",
"edition-drop": "",
"token-drop": "",
"signature-drop": "", // TODO
},
};
/**
* @internal
* @param chainId
* @param contractType
*/
export function getApprovedImplementation(
chainId: SUPPORTED_CHAIN_ID, // TODO use SupportedChainId once we deploy to all chains
contractType: PrebuiltContractType,
): string | null {
if (chainId in APPROVED_IMPLEMENTATIONS) {
const approvedImpls = APPROVED_IMPLEMENTATIONS[chainId];
if (contractType in approvedImpls) {
return approvedImpls[contractType as keyof typeof approvedImpls];
}
}
return null;
}
/**
* @internal
*/
-1
View File
@@ -6,7 +6,6 @@ export enum ChainId {
Goerli = 5,
Polygon = 137,
Mumbai = 80001,
Harmony = 1666600000,
Localhost = 1337,
Hardhat = 31337,
Fantom = 250,
@@ -3,33 +3,45 @@ import {
FEATURE_TOKEN_BATCH_MINTABLE,
FEATURE_TOKEN_BURNABLE,
FEATURE_TOKEN_MINTABLE,
FEATURE_TOKEN_CLAIMABLE_WITH_CONDITIONS,
FEATURE_TOKEN_SIGNATURE_MINTABLE,
FEATURE_TOKEN_CLAIM_CONDITIONS_V1,
FEATURE_TOKEN_CLAIM_CONDITIONS_V2,
FEATURE_TOKEN_CLAIM_PHASES_V2,
FEATURE_TOKEN_CLAIM_PHASES_V1,
} from "./erc20-features";
import {
FEATURE_NFT,
FEATURE_NFT_BATCH_MINTABLE,
FEATURE_NFT_BURNABLE,
FEATURE_NFT_CLAIMABLE,
FEATURE_NFT_CLAIM_CUSTOM,
FEATURE_NFT_LAZY_MINTABLE,
FEATURE_NFT_ENUMERABLE,
FEATURE_NFT_MINTABLE,
FEATURE_NFT_REVEALABLE,
FEATURE_NFT_SIGNATURE_MINTABLE,
FEATURE_NFT_SUPPLY,
FEATURE_NFT_CLAIMABLE_WITH_CONDITIONS,
FEATURE_NFT_CLAIM_CONDITIONS_V1,
FEATURE_NFT_CLAIM_CONDITIONS_V2,
FEATURE_NFT_CLAIM_PHASES_V2,
FEATURE_NFT_TIERED_DROP,
FEATURE_NFT_CLAIM_PHASES_V1,
FEATURE_NFT_SIGNATURE_MINTABLE_V1,
FEATURE_NFT_SIGNATURE_MINTABLE_V2,
} from "./erc721-features";
import {
FEATURE_EDITION,
FEATURE_EDITION_BATCH_MINTABLE,
FEATURE_EDITION_BURNABLE,
FEATURE_EDITION_CLAIMABLE,
FEATURE_EDITION_CLAIMABLE_WITH_CONDITIONS,
FEATURE_EDITION_CLAIM_CUSTOM,
FEATURE_EDITION_CLAIM_CONDITIONS_V1,
FEATURE_EDITION_CLAIM_CONDITIONS_V2,
FEATURE_EDITION_CLAIM_PHASES_V2,
FEATURE_EDITION_ENUMERABLE,
FEATURE_EDITION_LAZY_MINTABLE,
FEATURE_EDITION_LAZY_MINTABLE_V1,
FEATURE_EDITION_LAZY_MINTABLE_V2,
FEATURE_EDITION_MINTABLE,
FEATURE_EDITION_REVEALABLE,
FEATURE_EDITION_SIGNATURE_MINTABLE,
FEATURE_EDITION_CLAIM_PHASES_V1,
} from "./erc1155-features";
import {
FEATURE_METADATA,
@@ -50,7 +62,10 @@ export type Feature =
| typeof FEATURE_TOKEN_MINTABLE
| typeof FEATURE_TOKEN_BATCH_MINTABLE
| typeof FEATURE_TOKEN_BURNABLE
| typeof FEATURE_TOKEN_CLAIMABLE_WITH_CONDITIONS
| typeof FEATURE_TOKEN_CLAIM_CONDITIONS_V1
| typeof FEATURE_TOKEN_CLAIM_CONDITIONS_V2
| typeof FEATURE_TOKEN_CLAIM_PHASES_V1
| typeof FEATURE_TOKEN_CLAIM_PHASES_V2
| typeof FEATURE_TOKEN_SIGNATURE_MINTABLE
| typeof FEATURE_NFT
| typeof FEATURE_NFT_SUPPLY
@@ -58,17 +73,26 @@ export type Feature =
| typeof FEATURE_NFT_MINTABLE
| typeof FEATURE_NFT_BATCH_MINTABLE
| typeof FEATURE_NFT_LAZY_MINTABLE
| typeof FEATURE_NFT_CLAIMABLE
| typeof FEATURE_NFT_CLAIMABLE_WITH_CONDITIONS
| typeof FEATURE_NFT_CLAIM_CUSTOM
| typeof FEATURE_NFT_CLAIM_CONDITIONS_V1
| typeof FEATURE_NFT_CLAIM_CONDITIONS_V2
| typeof FEATURE_NFT_CLAIM_PHASES_V1
| typeof FEATURE_NFT_CLAIM_PHASES_V2
| typeof FEATURE_NFT_REVEALABLE
| typeof FEATURE_NFT_SIGNATURE_MINTABLE
| typeof FEATURE_NFT_SIGNATURE_MINTABLE_V1
| typeof FEATURE_NFT_SIGNATURE_MINTABLE_V2
| typeof FEATURE_NFT_BURNABLE
| typeof FEATURE_NFT_TIERED_DROP
| typeof FEATURE_EDITION
| typeof FEATURE_EDITION_ENUMERABLE
| typeof FEATURE_EDITION_MINTABLE
| typeof FEATURE_EDITION_CLAIMABLE
| typeof FEATURE_EDITION_CLAIMABLE_WITH_CONDITIONS
| typeof FEATURE_EDITION_LAZY_MINTABLE
| typeof FEATURE_EDITION_CLAIM_CUSTOM
| typeof FEATURE_EDITION_CLAIM_CONDITIONS_V1
| typeof FEATURE_EDITION_CLAIM_CONDITIONS_V2
| typeof FEATURE_EDITION_CLAIM_PHASES_V1
| typeof FEATURE_EDITION_CLAIM_PHASES_V2
| typeof FEATURE_EDITION_LAZY_MINTABLE_V1
| typeof FEATURE_EDITION_LAZY_MINTABLE_V2
| typeof FEATURE_EDITION_REVEALABLE
| typeof FEATURE_EDITION_BATCH_MINTABLE
| typeof FEATURE_EDITION_BURNABLE
+1 -2
View File
@@ -24,13 +24,12 @@ export const NATIVE_TOKENS: Record<
symbol: "WETH",
},
},
[ChainId.Goerli]: {
name: "Görli Ether",
symbol: "GOR",
decimals: 18,
wrapped: {
address: "0x0bb7509324ce409f7bbc4b701f932eaca9736ab7",
address: "0xb4fbf271143f4fbf7b91a5ded31805e42b2208d6",
name: "Wrapped Ether",
symbol: "WETH",
},
@@ -1,7 +1,10 @@
import DelayedRevealAbi from "@thirdweb-dev/contracts-js/dist/abis/DelayedReveal.json";
import DropSinglePhase1155 from "@thirdweb-dev/contracts-js/dist/abis/DropSinglePhase1155.json";
import DropERC1155_V2Abi from "@thirdweb-dev/contracts-js/dist/abis/DropERC1155_V2.json";
import IBurnableERC1155Abi from "@thirdweb-dev/contracts-js/dist/abis/IBurnableERC1155.json";
import IClaimableERC1155 from "@thirdweb-dev/contracts-js/dist/abis/IClaimableERC1155.json";
import DelayedRevealAbi from "@thirdweb-dev/contracts-js/dist/abis/IDelayedReveal.json";
import IDropMultiPhase1155 from "@thirdweb-dev/contracts-js/dist/abis/IDrop1155.json";
import IDropSinglePhase1155 from "@thirdweb-dev/contracts-js/dist/abis/IDropSinglePhase1155.json";
import IDropSinglePhase1155_V1 from "@thirdweb-dev/contracts-js/dist/abis/IDropSinglePhase1155_V1.json";
import Erc1155Abi from "@thirdweb-dev/contracts-js/dist/abis/IERC1155.json";
import Erc1155EnumerableAbi from "@thirdweb-dev/contracts-js/dist/abis/IERC1155Enumerable.json";
import ILazyMintAbi from "@thirdweb-dev/contracts-js/dist/abis/ILazyMint.json";
@@ -20,19 +23,52 @@ export const FEATURE_EDITION_BURNABLE = {
features: {},
} as const;
export const FEATURE_EDITION_CLAIMABLE_WITH_CONDITIONS = {
name: "ERC1155ClaimableWithConditions",
export const FEATURE_EDITION_CLAIM_CONDITIONS_V1 = {
name: "ERC1155ClaimConditionsV1",
namespace: "edition.drop.claim",
docLinks: {
sdk: "sdk.erc1155claimable",
contracts: "DropSinglePhase1155",
},
abis: [Erc1155Abi, ILazyMintAbi, DropSinglePhase1155],
abis: [Erc1155Abi, ILazyMintAbi, IDropSinglePhase1155_V1],
features: {},
} as const;
export const FEATURE_EDITION_CLAIMABLE = {
name: "ERC1155Claimable",
export const FEATURE_EDITION_CLAIM_CONDITIONS_V2 = {
name: "ERC1155ClaimConditionsV2",
namespace: "edition.drop.claim",
docLinks: {
sdk: "sdk.erc1155claimable",
contracts: "DropSinglePhase1155",
},
abis: [Erc1155Abi, ILazyMintAbi, IDropSinglePhase1155],
features: {},
} as const;
export const FEATURE_EDITION_CLAIM_PHASES_V2 = {
name: "ERC1155ClaimPhasesV2",
namespace: "edition.drop.claim",
docLinks: {
sdk: "sdk.erc1155claimable",
contracts: "Drop1155",
},
abis: [Erc1155Abi, ILazyMintAbi, IDropMultiPhase1155],
features: {},
} as const;
export const FEATURE_EDITION_CLAIM_PHASES_V1 = {
name: "ERC1155ClaimPhasesV1",
namespace: "edition.drop.claim",
docLinks: {
sdk: "sdk.erc1155claimable",
contracts: "Drop1155",
},
abis: [DropERC1155_V2Abi],
features: {},
} as const;
export const FEATURE_EDITION_CLAIM_CUSTOM = {
name: "ERC1155ClaimCustom",
namespace: "edition.drop.claim",
docLinks: {
sdk: "sdk.erc1155claimable",
@@ -42,21 +78,6 @@ export const FEATURE_EDITION_CLAIMABLE = {
features: {},
} as const;
export const FEATURE_EDITION_LAZY_MINTABLE = {
name: "ERC1155LazyMintable",
namespace: "edition.drop",
docLinks: {
sdk: "sdk.erc1155droppable",
contracts: "LazyMint",
},
abis: [Erc1155Abi, ILazyMintAbi],
features: {
[FEATURE_EDITION_CLAIMABLE.name]: FEATURE_EDITION_CLAIMABLE,
[FEATURE_EDITION_CLAIMABLE_WITH_CONDITIONS.name]:
FEATURE_EDITION_CLAIMABLE_WITH_CONDITIONS,
},
} as const;
export const FEATURE_EDITION_REVEALABLE = {
name: "ERC1155Revealable",
namespace: "edition.drop.revealer",
@@ -68,6 +89,38 @@ export const FEATURE_EDITION_REVEALABLE = {
features: {},
} as const;
export const FEATURE_EDITION_LAZY_MINTABLE_V2 = {
name: "ERC1155LazyMintableV2",
namespace: "edition.drop",
docLinks: {
sdk: "sdk.erc1155droppable",
contracts: "LazyMint",
},
abis: [Erc1155Abi, ILazyMintAbi],
features: {
[FEATURE_EDITION_REVEALABLE.name]: FEATURE_EDITION_REVEALABLE,
[FEATURE_EDITION_CLAIM_CUSTOM.name]: FEATURE_EDITION_CLAIM_CUSTOM,
[FEATURE_EDITION_CLAIM_CONDITIONS_V1.name]:
FEATURE_EDITION_CLAIM_CONDITIONS_V1,
[FEATURE_EDITION_CLAIM_CONDITIONS_V2.name]:
FEATURE_EDITION_CLAIM_CONDITIONS_V2,
[FEATURE_EDITION_CLAIM_PHASES_V2.name]: FEATURE_EDITION_CLAIM_PHASES_V2,
},
} as const;
export const FEATURE_EDITION_LAZY_MINTABLE_V1 = {
name: "ERC1155LazyMintableV1",
namespace: "edition.drop",
docLinks: {
sdk: "sdk.erc1155droppable",
contracts: "LazyMint",
},
abis: [DropERC1155_V2Abi],
features: {
[FEATURE_EDITION_CLAIM_PHASES_V1.name]: FEATURE_EDITION_CLAIM_PHASES_V1,
},
} as const;
export const FEATURE_EDITION_SIGNATURE_MINTABLE = {
name: "ERC1155SignatureMintable",
namespace: "edition.signature",
@@ -126,7 +179,8 @@ export const FEATURE_EDITION = {
[FEATURE_EDITION_BURNABLE.name]: FEATURE_EDITION_BURNABLE,
[FEATURE_EDITION_ENUMERABLE.name]: FEATURE_EDITION_ENUMERABLE,
[FEATURE_EDITION_MINTABLE.name]: FEATURE_EDITION_MINTABLE,
[FEATURE_EDITION_LAZY_MINTABLE.name]: FEATURE_EDITION_LAZY_MINTABLE,
[FEATURE_EDITION_LAZY_MINTABLE_V1.name]: FEATURE_EDITION_LAZY_MINTABLE_V1,
[FEATURE_EDITION_LAZY_MINTABLE_V2.name]: FEATURE_EDITION_LAZY_MINTABLE_V2,
[FEATURE_EDITION_REVEALABLE.name]: FEATURE_EDITION_REVEALABLE,
[FEATURE_EDITION_SIGNATURE_MINTABLE.name]:
FEATURE_EDITION_SIGNATURE_MINTABLE,
@@ -1,12 +1,26 @@
import DropERC20_V2Abi from "@thirdweb-dev/contracts-js/dist/abis/DropERC20_V2.json";
import IBurnableERC20Abi from "@thirdweb-dev/contracts-js/dist/abis/IBurnableERC20.json";
import IDrop from "@thirdweb-dev/contracts-js/dist/abis/IDrop.json";
import IDropSinglePhase from "@thirdweb-dev/contracts-js/dist/abis/IDropSinglePhase.json";
import IDropSinglePhaseV1 from "@thirdweb-dev/contracts-js/dist/abis/IDropSinglePhase_V1.json";
import ERC20Abi from "@thirdweb-dev/contracts-js/dist/abis/IERC20.json";
import IMintableERC20Abi from "@thirdweb-dev/contracts-js/dist/abis/IMintableERC20.json";
import MulticallAbi from "@thirdweb-dev/contracts-js/dist/abis/IMulticall.json";
import ISignatureMintERC20Abi from "@thirdweb-dev/contracts-js/dist/abis/ISignatureMintERC20.json";
export const FEATURE_TOKEN_CLAIMABLE_WITH_CONDITIONS = {
name: "ERC20ClaimableWithConditions",
export const FEATURE_TOKEN_CLAIM_CONDITIONS_V1 = {
name: "ERC20ClaimConditionsV1",
namespace: "token.drop.claim",
docLinks: {
sdk: "sdk.erc20dclaimable",
contracts: "DropSinglePhase_V1",
},
abis: [ERC20Abi, IDropSinglePhaseV1],
features: {},
} as const;
export const FEATURE_TOKEN_CLAIM_CONDITIONS_V2 = {
name: "ERC20ClaimConditionsV2",
namespace: "token.drop.claim",
docLinks: {
sdk: "sdk.erc20dclaimable",
@@ -16,6 +30,28 @@ export const FEATURE_TOKEN_CLAIMABLE_WITH_CONDITIONS = {
features: {},
} as const;
export const FEATURE_TOKEN_CLAIM_PHASES_V2 = {
name: "ERC20ClaimPhasesV2",
namespace: "token.drop.claim",
docLinks: {
sdk: "sdk.erc20dclaimable",
contracts: "Drop",
},
abis: [ERC20Abi, IDrop],
features: {},
} as const;
export const FEATURE_TOKEN_CLAIM_PHASES_V1 = {
name: "ERC20ClaimPhasesV1",
namespace: "token.drop.claim",
docLinks: {
sdk: "sdk.erc20dclaimable",
contracts: "Drop",
},
abis: [DropERC20_V2Abi],
features: {},
} as const;
export const FEATURE_TOKEN_BURNABLE = {
name: "ERC20Burnable",
namespace: "token.burn",
@@ -73,8 +109,10 @@ export const FEATURE_TOKEN = {
features: {
[FEATURE_TOKEN_BURNABLE.name]: FEATURE_TOKEN_BURNABLE,
[FEATURE_TOKEN_MINTABLE.name]: FEATURE_TOKEN_MINTABLE,
[FEATURE_TOKEN_CLAIMABLE_WITH_CONDITIONS.name]:
FEATURE_TOKEN_CLAIMABLE_WITH_CONDITIONS,
[FEATURE_TOKEN_CLAIM_CONDITIONS_V1.name]: FEATURE_TOKEN_CLAIM_CONDITIONS_V1,
[FEATURE_TOKEN_CLAIM_CONDITIONS_V2.name]: FEATURE_TOKEN_CLAIM_CONDITIONS_V2,
[FEATURE_TOKEN_CLAIM_PHASES_V1.name]: FEATURE_TOKEN_CLAIM_PHASES_V1,
[FEATURE_TOKEN_CLAIM_PHASES_V2.name]: FEATURE_TOKEN_CLAIM_PHASES_V2,
[FEATURE_TOKEN_SIGNATURE_MINTABLE.name]: FEATURE_TOKEN_SIGNATURE_MINTABLE,
},
} as const;
@@ -1,14 +1,19 @@
import DropERC721_V3Abi from "@thirdweb-dev/contracts-js/dist/abis/DropERC721_V3.json";
import IBurnableERC721Abi from "@thirdweb-dev/contracts-js/dist/abis/IBurnableERC721.json";
import IClaimableERC721 from "@thirdweb-dev/contracts-js/dist/abis/IClaimableERC721.json";
import DelayedRevealAbi from "@thirdweb-dev/contracts-js/dist/abis/IDelayedReveal.json";
import IDrop from "@thirdweb-dev/contracts-js/dist/abis/IDrop.json";
import IDropSinglePhase from "@thirdweb-dev/contracts-js/dist/abis/IDropSinglePhase.json";
import IDropSinglePhaseV1 from "@thirdweb-dev/contracts-js/dist/abis/IDropSinglePhase_V1.json";
import Erc721Abi from "@thirdweb-dev/contracts-js/dist/abis/IERC721.json";
import Erc721EnumerableAbi from "@thirdweb-dev/contracts-js/dist/abis/IERC721Enumerable.json";
import Erc721SupplyAbi from "@thirdweb-dev/contracts-js/dist/abis/IERC721Supply.json";
import ILazyMintAbi from "@thirdweb-dev/contracts-js/dist/abis/ILazyMint.json";
import IMintableERC721Abi from "@thirdweb-dev/contracts-js/dist/abis/IMintableERC721.json";
import MulticallAbi from "@thirdweb-dev/contracts-js/dist/abis/IMulticall.json";
import SignatureMintERC721Abi from "@thirdweb-dev/contracts-js/dist/abis/SignatureMintERC721.json";
import SignatureMintERC721Abi from "@thirdweb-dev/contracts-js/dist/abis/ISignatureMintERC721.json";
import TieredDropAbi from "@thirdweb-dev/contracts-js/dist/abis/LazyMintWithTier.json";
import TokenERC721Abi from "@thirdweb-dev/contracts-js/dist/abis/TokenERC721.json";
export const FEATURE_NFT_BURNABLE = {
name: "ERC721Burnable",
@@ -25,15 +30,37 @@ export const FEATURE_NFT_REVEALABLE = {
name: "ERC721Revealable",
namespace: "nft.drop.revealer",
docLinks: {
sdk: "sdk.drop.delayedreveal",
sdk: "sdk.delayedreveal",
contracts: "DelayedReveal",
},
abis: [Erc721Abi, ILazyMintAbi, DelayedRevealAbi],
features: {},
} as const;
export const FEATURE_NFT_CLAIMABLE_WITH_CONDITIONS = {
name: "ERC721ClaimableWithConditions",
export const FEATURE_NFT_TIERED_DROP = {
name: "ERC721TieredDrop",
namespace: "nft.tieredDrop",
docLinks: {
sdk: "sdk.erc721tiereddrop",
contracts: "TieredDrop",
},
abis: [Erc721Abi, TieredDropAbi],
features: {},
} as const;
export const FEATURE_NFT_CLAIM_CONDITIONS_V1 = {
name: "ERC721ClaimConditionsV1",
namespace: "nft.drop.claim",
docLinks: {
sdk: "sdk.erc721claimable",
contracts: "DropSinglePhase_V1",
},
abis: [Erc721Abi, ILazyMintAbi, IDropSinglePhaseV1],
features: {},
} as const;
export const FEATURE_NFT_CLAIM_CONDITIONS_V2 = {
name: "ERC721ClaimConditionsV2",
namespace: "nft.drop.claim",
docLinks: {
sdk: "sdk.erc721claimable",
@@ -43,8 +70,30 @@ export const FEATURE_NFT_CLAIMABLE_WITH_CONDITIONS = {
features: {},
} as const;
export const FEATURE_NFT_CLAIMABLE = {
name: "ERC721Claimable",
export const FEATURE_NFT_CLAIM_PHASES_V1 = {
name: "ERC721ClaimPhasesV1",
namespace: "nft.drop.claim",
docLinks: {
sdk: "sdk.erc721claimable",
contracts: "Drop",
},
abis: [DropERC721_V3Abi],
features: {},
} as const;
export const FEATURE_NFT_CLAIM_PHASES_V2 = {
name: "ERC721ClaimPhasesV2",
namespace: "nft.drop.claim",
docLinks: {
sdk: "sdk.erc721claimable",
contracts: "Drop",
},
abis: [Erc721Abi, ILazyMintAbi, IDrop],
features: {},
} as const;
export const FEATURE_NFT_CLAIM_CUSTOM = {
name: "ERC721ClaimCustom",
namespace: "nft.drop.claim",
docLinks: {
sdk: "sdk.erc721claimable",
@@ -64,9 +113,11 @@ export const FEATURE_NFT_LAZY_MINTABLE = {
abis: [Erc721Abi, ILazyMintAbi],
features: {
[FEATURE_NFT_REVEALABLE.name]: FEATURE_NFT_REVEALABLE,
[FEATURE_NFT_CLAIMABLE.name]: FEATURE_NFT_CLAIMABLE,
[FEATURE_NFT_CLAIMABLE_WITH_CONDITIONS.name]:
FEATURE_NFT_CLAIMABLE_WITH_CONDITIONS,
[FEATURE_NFT_CLAIM_CUSTOM.name]: FEATURE_NFT_CLAIM_CUSTOM,
[FEATURE_NFT_CLAIM_CONDITIONS_V1.name]: FEATURE_NFT_CLAIM_CONDITIONS_V1,
[FEATURE_NFT_CLAIM_CONDITIONS_V2.name]: FEATURE_NFT_CLAIM_CONDITIONS_V2,
[FEATURE_NFT_CLAIM_PHASES_V1.name]: FEATURE_NFT_CLAIM_PHASES_V1,
[FEATURE_NFT_CLAIM_PHASES_V2.name]: FEATURE_NFT_CLAIM_PHASES_V2,
},
} as const;
@@ -94,8 +145,8 @@ export const FEATURE_NFT_MINTABLE = {
},
} as const;
export const FEATURE_NFT_SIGNATURE_MINTABLE = {
name: "ERC721SignatureMint",
export const FEATURE_NFT_SIGNATURE_MINTABLE_V2 = {
name: "ERC721SignatureMintV2",
namespace: "nft.signature",
docLinks: {
sdk: "sdk.erc721signaturemint",
@@ -105,6 +156,17 @@ export const FEATURE_NFT_SIGNATURE_MINTABLE = {
features: {},
} as const;
export const FEATURE_NFT_SIGNATURE_MINTABLE_V1 = {
name: "ERC721SignatureMintV1",
namespace: "nft.signature",
docLinks: {
sdk: "sdk.erc721signaturemint",
contracts: "ISignatureMintERC721",
},
abis: [TokenERC721Abi],
features: {},
} as const;
export const FEATURE_NFT_ENUMERABLE = {
name: "ERC721Enumerable",
namespace: "nft.query.owned",
@@ -142,6 +204,8 @@ export const FEATURE_NFT = {
[FEATURE_NFT_SUPPLY.name]: FEATURE_NFT_SUPPLY,
[FEATURE_NFT_MINTABLE.name]: FEATURE_NFT_MINTABLE,
[FEATURE_NFT_LAZY_MINTABLE.name]: FEATURE_NFT_LAZY_MINTABLE,
[FEATURE_NFT_SIGNATURE_MINTABLE.name]: FEATURE_NFT_SIGNATURE_MINTABLE,
[FEATURE_NFT_SIGNATURE_MINTABLE_V1.name]: FEATURE_NFT_SIGNATURE_MINTABLE_V1,
[FEATURE_NFT_SIGNATURE_MINTABLE_V2.name]: FEATURE_NFT_SIGNATURE_MINTABLE_V2,
[FEATURE_NFT_TIERED_DROP.name]: FEATURE_NFT_TIERED_DROP,
},
} as const;
+24 -58
View File
@@ -1,3 +1,4 @@
import { getRpcUrl } from "../../core/constants/urls";
import { SignerOrProvider } from "../core/types";
import { StaticJsonRpcBatchProvider } from "../lib/static-batch-rpc";
import { ChainId, SUPPORTED_CHAIN_ID, SUPPORTED_CHAIN_IDS } from "./chains";
@@ -7,39 +8,16 @@ import { ethers, providers } from "ethers";
* @internal
*/
export const DEFAULT_IPFS_GATEWAY = "https://gateway.ipfscdn.io/ipfs/";
/**
* @internal
*/
export const PUBLIC_GATEWAYS = [
"https://gateway.ipfscdn.io/ipfs/",
"https://cloudflare-ipfs.com/ipfs/",
"https://ipfs.io/ipfs/",
];
/**
* @internal
*/
export const TW_IPFS_SERVER_URL = "https://upload.nftlabs.co";
/**
* @internal
*/
export const PINATA_IPFS_URL = `https://api.pinata.cloud/pinning/pinFileToIPFS`;
/**
* @internal
* This is a community API key that is subject to rate limiting. Please use your own key.
*/
const DEFAULT_API_KEY = "_gg7wSSi0KMBsdKnGVfHDueq6xMB9EkC";
type ChainNames =
| "mumbai"
| "polygon"
// common alias for `polygon`
| "matic"
| "goerli"
| "mainnet"
// common alias for `mainnet`
| "ethereum"
| "goerli"
| "polygon"
// common alias for `polygon`
| "matic"
| "mumbai"
| "fantom"
| "fantom-testnet"
| "avalanche"
@@ -105,49 +83,37 @@ export function getProviderForNetwork(network: ChainOrRpc | SignerOrProvider) {
return network;
}
switch (network) {
case "mumbai":
return `https://polygon-mumbai.g.alchemy.com/v2/${DEFAULT_API_KEY}`;
case "rinkeby":
return `https://eth-rinkeby.g.alchemy.com/v2/${DEFAULT_API_KEY}`;
case "goerli":
return `https://eth-goerli.g.alchemy.com/v2/${DEFAULT_API_KEY}`;
case "polygon":
case "matic":
return `https://polygon-mainnet.g.alchemy.com/v2/${DEFAULT_API_KEY}`;
case "mainnet":
case "ethereum":
return `https://eth-mainnet.g.alchemy.com/v2/${DEFAULT_API_KEY}`;
return getRpcUrl("ethereum");
case "goerli":
return getRpcUrl("goerli");
case "polygon":
case "matic":
return getRpcUrl("polygon");
case "mumbai":
return getRpcUrl("mumbai");
case "optimism":
// TODO test this RPC
return `https://opt-mainnet.g.alchemy.com/v2/${DEFAULT_API_KEY}`;
case "optimism-kovan":
// alchemy optimism kovan rpc doesn't link to the testnet sequencer...
return "https://kovan.optimism.io";
return getRpcUrl("optimism");
case "optimism-goerli":
// TODO test this RPC
return `https://opt-goerli.g.alchemy.com/v2/${DEFAULT_API_KEY}`;
return getRpcUrl("optimism-goerli");
case "arbitrum":
// TODO test this RPC
return `https://arb-mainnet.g.alchemy.com/v2/${DEFAULT_API_KEY}`;
case "arbitrum-rinkeby":
// TODO test this RPC
return `https://arb-rinkeby.g.alchemy.com/v2/${DEFAULT_API_KEY}`;
return getRpcUrl("arbitrum");
case "arbitrum-goerli":
// TODO test this RPC
return `https://arb-goerli.g.alchemy.com/v2/${DEFAULT_API_KEY}`;
return getRpcUrl("arbitrum-goerli");
case "fantom":
return "https://rpc.ftm.tools";
return getRpcUrl("fantom");
case "fantom-testnet":
return "https://rpc.testnet.fantom.network/";
return getRpcUrl("fantom-testnet");
case "avalanche":
return "https://api.avax.network/ext/bc/C/rpc";
return getRpcUrl("avalanche");
case "avalanche-testnet":
case "avalanche-fuji":
return "https://api.avax-test.network/ext/bc/C/rpc";
return getRpcUrl("avalanche-fuji");
case "binance":
return "https://bsc-dataseed1.binance.org";
return getRpcUrl("binance");
case "binance-testnet":
return "https://data-seed-prebsc-1-s1.binance.org:8545";
return getRpcUrl("binance-testnet");
default:
if (network.startsWith("http") || network.startsWith("ws")) {
return network;
+143 -47
View File
@@ -1,6 +1,11 @@
import { getPrebuiltInfo } from "../common/legacy";
import { ALL_ROLES } from "../common/role";
import { getSignerAndProvider } from "../core/classes/rpc-connection-handler";
import type { ContractType, NetworkOrSignerOrProvider } from "../core/types";
import type {
ContractType,
NetworkOrSignerOrProvider,
PrebuiltContractType,
} from "../core/types";
import {
DropErc1155ContractSchema,
DropErc721ContractSchema,
@@ -17,6 +22,7 @@ import { CustomContractSchema } from "../schema/contracts/custom";
import { DropErc20ContractSchema } from "../schema/contracts/drop-erc20";
import { MultiwrapContractSchema } from "../schema/contracts/multiwrap";
import type { ThirdwebStorage } from "@thirdweb-dev/storage";
import { ethers } from "ethers";
type InitalizeParams = [
network: NetworkOrSignerOrProvider,
@@ -35,7 +41,7 @@ export const EditionDropInitializer = {
) => {
const [, provider] = getSignerAndProvider(network, options);
const [abi, contract, _network] = await Promise.all([
EditionDropInitializer.getAbi(),
await EditionDropInitializer.getAbi(address, provider),
import("./prebuilt-implementations/edition-drop"),
provider.getNetwork(),
]);
@@ -49,9 +55,21 @@ export const EditionDropInitializer = {
_network.chainId,
);
},
getAbi: async () =>
(await import("@thirdweb-dev/contracts-js/dist/abis/DropERC1155.json"))
.default,
getAbi: async (address: string, provider: ethers.providers.Provider) => {
const contractInfo = await assertContractType(
address,
provider,
"edition-drop",
);
return contractInfo.version > 2
? (await import("@thirdweb-dev/contracts-js/dist/abis/DropERC1155.json"))
.default
: (
await import(
"@thirdweb-dev/contracts-js/dist/abis/DropERC1155_V2.json"
)
).default;
},
};
export const EditionInitializer = {
@@ -64,7 +82,7 @@ export const EditionInitializer = {
) => {
const [, provider] = getSignerAndProvider(network, options);
const [abi, contract, _network] = await Promise.all([
EditionInitializer.getAbi(),
EditionInitializer.getAbi(address, provider),
import("./prebuilt-implementations/edition"),
provider.getNetwork(),
]);
@@ -78,9 +96,12 @@ export const EditionInitializer = {
_network.chainId,
);
},
getAbi: async () =>
(await import("@thirdweb-dev/contracts-js/dist/abis/TokenERC1155.json"))
.default,
getAbi: async (address: string, provider: ethers.providers.Provider) => {
await assertContractType(address, provider, "edition");
return (
await import("@thirdweb-dev/contracts-js/dist/abis/TokenERC1155.json")
).default;
},
};
export const MarketplaceInitializer = {
@@ -93,7 +114,7 @@ export const MarketplaceInitializer = {
) => {
const [, provider] = getSignerAndProvider(network, options);
const [abi, contract, _network] = await Promise.all([
MarketplaceInitializer.getAbi(),
MarketplaceInitializer.getAbi(address, provider),
import("./prebuilt-implementations/marketplace"),
provider.getNetwork(),
]);
@@ -107,9 +128,12 @@ export const MarketplaceInitializer = {
_network.chainId,
);
},
getAbi: async () =>
(await import("@thirdweb-dev/contracts-js/dist/abis/Marketplace.json"))
.default,
getAbi: async (address: string, provider: ethers.providers.Provider) => {
await assertContractType(address, provider, "marketplace");
return (
await import("@thirdweb-dev/contracts-js/dist/abis/Marketplace.json")
).default;
},
};
export const MultiwrapInitializer = {
@@ -122,7 +146,7 @@ export const MultiwrapInitializer = {
) => {
const [, provider] = getSignerAndProvider(network, options);
const [abi, contract, _network] = await Promise.all([
MultiwrapInitializer.getAbi(),
MultiwrapInitializer.getAbi(address, provider),
import("./prebuilt-implementations/multiwrap"),
provider.getNetwork(),
]);
@@ -136,9 +160,11 @@ export const MultiwrapInitializer = {
_network.chainId,
);
},
getAbi: async () =>
(await import("@thirdweb-dev/contracts-js/dist/abis/Multiwrap.json"))
.default,
getAbi: async (address: string, provider: ethers.providers.Provider) => {
await assertContractType(address, provider, "multiwrap");
return (await import("@thirdweb-dev/contracts-js/dist/abis/Multiwrap.json"))
.default;
},
};
export const NFTCollectionInitializer = {
@@ -152,7 +178,7 @@ export const NFTCollectionInitializer = {
) => {
const [, provider] = getSignerAndProvider(network, options);
const [abi, contract, _network] = await Promise.all([
NFTCollectionInitializer.getAbi(),
NFTCollectionInitializer.getAbi(address, provider),
import("./prebuilt-implementations/nft-collection"),
provider.getNetwork(),
]);
@@ -166,9 +192,12 @@ export const NFTCollectionInitializer = {
_network.chainId,
);
},
getAbi: async () =>
(await import("@thirdweb-dev/contracts-js/dist/abis/TokenERC721.json"))
.default,
getAbi: async (address: string, provider: ethers.providers.Provider) => {
await assertContractType(address, provider, "nft-collection");
return (
await import("@thirdweb-dev/contracts-js/dist/abis/TokenERC721.json")
).default;
},
};
export const NFTDropInitializer = {
@@ -181,7 +210,7 @@ export const NFTDropInitializer = {
) => {
const [, provider] = getSignerAndProvider(network, options);
const [abi, contract, _network] = await Promise.all([
NFTDropInitializer.getAbi(),
NFTDropInitializer.getAbi(address, provider),
import("./prebuilt-implementations/nft-drop"),
provider.getNetwork(),
]);
@@ -195,9 +224,21 @@ export const NFTDropInitializer = {
_network.chainId,
);
},
getAbi: async () =>
(await import("@thirdweb-dev/contracts-js/dist/abis/DropERC721.json"))
.default,
getAbi: async (address: string, provider: ethers.providers.Provider) => {
const contractInfo = await assertContractType(
address,
provider,
"nft-drop",
);
return contractInfo.version > 3
? (await import("@thirdweb-dev/contracts-js/dist/abis/DropERC721.json"))
.default
: (
await import(
"@thirdweb-dev/contracts-js/dist/abis/DropERC721_V3.json"
)
).default;
},
};
export const PackInitializer = {
@@ -211,7 +252,7 @@ export const PackInitializer = {
) => {
const [, provider] = getSignerAndProvider(network, options);
const [abi, contract, _network] = await Promise.all([
PackInitializer.getAbi(),
PackInitializer.getAbi(address, provider),
import("./prebuilt-implementations/pack"),
provider.getNetwork(),
]);
@@ -225,8 +266,11 @@ export const PackInitializer = {
_network.chainId,
);
},
getAbi: async () =>
(await import("@thirdweb-dev/contracts-js/dist/abis/Pack.json")).default,
getAbi: async (address: string, provider: ethers.providers.Provider) => {
await assertContractType(address, provider, "pack");
return (await import("@thirdweb-dev/contracts-js/dist/abis/Pack.json"))
.default;
},
};
export const SignatureDropInitializer = {
@@ -240,7 +284,7 @@ export const SignatureDropInitializer = {
) => {
const [, provider] = getSignerAndProvider(network, options);
const [abi, contract, _network] = await Promise.all([
SignatureDropInitializer.getAbi(),
SignatureDropInitializer.getAbi(address, provider),
import("./prebuilt-implementations/signature-drop"),
provider.getNetwork(),
]);
@@ -254,9 +298,24 @@ export const SignatureDropInitializer = {
_network.chainId,
);
},
getAbi: async () =>
(await import("@thirdweb-dev/contracts-js/dist/abis/SignatureDrop.json"))
.default,
getAbi: async (address: string, provider: ethers.providers.Provider) => {
const contractInfo = await assertContractType(
address,
provider,
"signature-drop",
);
return contractInfo.version > 4
? (
await import(
"@thirdweb-dev/contracts-js/dist/abis/SignatureDrop.json"
)
).default
: (
await import(
"@thirdweb-dev/contracts-js/dist/abis/SignatureDrop_V4.json"
)
).default;
},
};
export const SplitInitializer = {
@@ -270,7 +329,7 @@ export const SplitInitializer = {
) => {
const [, provider] = getSignerAndProvider(network, options);
const [abi, contract, _network] = await Promise.all([
SplitInitializer.getAbi(),
SplitInitializer.getAbi(address, provider),
import("./prebuilt-implementations/split"),
provider.getNetwork(),
]);
@@ -284,8 +343,11 @@ export const SplitInitializer = {
_network.chainId,
);
},
getAbi: async () =>
(await import("@thirdweb-dev/contracts-js/dist/abis/Split.json")).default,
getAbi: async (address: string, provider: ethers.providers.Provider) => {
await assertContractType(address, provider, "split");
return (await import("@thirdweb-dev/contracts-js/dist/abis/Split.json"))
.default;
},
};
export const TokenDropInitializer = {
@@ -299,7 +361,7 @@ export const TokenDropInitializer = {
) => {
const [, provider] = getSignerAndProvider(network, options);
const [abi, contract, _network] = await Promise.all([
TokenDropInitializer.getAbi(),
TokenDropInitializer.getAbi(address, provider),
import("./prebuilt-implementations/token-drop"),
provider.getNetwork(),
]);
@@ -313,9 +375,18 @@ export const TokenDropInitializer = {
_network.chainId,
);
},
getAbi: async () =>
(await import("@thirdweb-dev/contracts-js/dist/abis/DropERC20.json"))
.default,
getAbi: async (address: string, provider: ethers.providers.Provider) => {
const contractInfo = await assertContractType(
address,
provider,
"token-drop",
);
return contractInfo.version > 2
? (await import("@thirdweb-dev/contracts-js/dist/abis/DropERC20.json"))
.default
: (await import("@thirdweb-dev/contracts-js/dist/abis/DropERC20_V2.json"))
.default;
},
};
export const TokenInitializer = {
@@ -328,7 +399,7 @@ export const TokenInitializer = {
) => {
const [, provider] = getSignerAndProvider(network, options);
const [abi, contract, _network] = await Promise.all([
TokenInitializer.getAbi(),
TokenInitializer.getAbi(address, provider),
import("./prebuilt-implementations/token"),
provider.getNetwork(),
]);
@@ -342,9 +413,12 @@ export const TokenInitializer = {
_network.chainId,
);
},
getAbi: async () =>
(await import("@thirdweb-dev/contracts-js/dist/abis/TokenERC20.json"))
.default,
getAbi: async (address: string, provider: ethers.providers.Provider) => {
await assertContractType(address, provider, "token");
return (
await import("@thirdweb-dev/contracts-js/dist/abis/TokenERC20.json")
).default;
},
};
export const VoteInitializer = {
@@ -358,7 +432,7 @@ export const VoteInitializer = {
) => {
const [, provider] = getSignerAndProvider(network, options);
const [abi, contract, _network] = await Promise.all([
VoteInitializer.getAbi(),
VoteInitializer.getAbi(address, provider),
import("./prebuilt-implementations/vote"),
provider.getNetwork(),
]);
@@ -372,11 +446,25 @@ export const VoteInitializer = {
_network.chainId,
);
},
getAbi: async () =>
(await import("@thirdweb-dev/contracts-js/dist/abis/VoteERC20.json"))
.default,
getAbi: async (address: string, provider: ethers.providers.Provider) => {
await assertContractType(address, provider, "vote");
return (await import("@thirdweb-dev/contracts-js/dist/abis/VoteERC20.json"))
.default;
},
};
async function assertContractType(
address: string,
provider: ethers.providers.Provider,
type: PrebuiltContractType,
) {
const contractInfo = await getPrebuiltInfo(address, provider);
if (!contractInfo || contractInfo.type !== getContractName(type)) {
throw new Error(`Contract is not a ${type}`);
}
return contractInfo;
}
/**
* a map from contractType -> contract metadata
* @internal
@@ -417,3 +505,11 @@ export function getContractTypeForRemoteName(name: string): ContractType {
?.contractType || "custom"
);
}
export function getContractName(
type: PrebuiltContractType,
): string | undefined {
return Object.values(CONTRACTS_MAP).find(
(contract) => contract.contractType === type,
)?.name;
}
@@ -22,10 +22,10 @@ import {
TransactionResult,
TransactionResultWithId,
} from "../../core/types";
import { PaperCheckout } from "../../integrations/paper-xyz";
import { PaperCheckout } from "../../integrations/thirdweb-checkout";
import { DropErc1155ContractSchema } from "../../schema/contracts/drop-erc1155";
import { SDKOptions } from "../../schema/sdk-options";
import type { DropERC1155 } from "@thirdweb-dev/contracts-js";
import { PrebuiltEditionDrop } from "../../types/eips";
import type ABI from "@thirdweb-dev/contracts-js/dist/abis/DropERC1155.json";
import { ThirdwebStorage, UploadProgressEvent } from "@thirdweb-dev/storage";
import { BigNumber, BigNumberish, CallOverrides, constants } from "ethers";
@@ -39,26 +39,26 @@ import { BigNumber, BigNumberish, CallOverrides, constants } from "ethers";
* import { ThirdwebSDK } from "@thirdweb-dev/sdk";
*
* const sdk = new ThirdwebSDK("{{chainName}}");
* const contract = sdk.getContract("{{contract_address}}", "edition-drop");
* const contract = await sdk.getContract("{{contract_address}}", "edition-drop");
* ```
*
* @public
*/
export class EditionDrop extends StandardErc1155<DropERC1155> {
export class EditionDrop extends StandardErc1155<PrebuiltEditionDrop> {
private static contractRoles = ["admin", "minter", "transfer"] as const;
public abi: typeof ABI;
public sales: ContractPrimarySale<DropERC1155>;
public platformFees: ContractPlatformFee<DropERC1155>;
public encoder: ContractEncoder<DropERC1155>;
public estimator: GasCostEstimator<DropERC1155>;
public events: ContractEvents<DropERC1155>;
public sales: ContractPrimarySale<PrebuiltEditionDrop>;
public platformFees: ContractPlatformFee<PrebuiltEditionDrop>;
public encoder: ContractEncoder<PrebuiltEditionDrop>;
public estimator: GasCostEstimator<PrebuiltEditionDrop>;
public events: ContractEvents<PrebuiltEditionDrop>;
public metadata: ContractMetadata<
DropERC1155,
PrebuiltEditionDrop,
typeof DropErc1155ContractSchema
>;
public roles: ContractRoles<
DropERC1155,
PrebuiltEditionDrop,
typeof EditionDrop.contractRoles[number]
>;
/**
@@ -79,7 +79,7 @@ export class EditionDrop extends StandardErc1155<DropERC1155> {
* ```
*/
public royalties: ContractRoyalty<
DropERC1155,
PrebuiltEditionDrop,
typeof DropErc1155ContractSchema
>;
/**
@@ -106,18 +106,18 @@ export class EditionDrop extends StandardErc1155<DropERC1155> {
* await contract.claimConditions.set(tokenId, claimConditions);
* ```
*/
public claimConditions: DropErc1155ClaimConditions<DropERC1155>;
public claimConditions: DropErc1155ClaimConditions<PrebuiltEditionDrop>;
/**
* Checkout
* @remarks Create a FIAT currency checkout for your NFT drop.
*/
public checkout: PaperCheckout<DropERC1155>;
public checkout: PaperCheckout<PrebuiltEditionDrop>;
public history: DropErc1155History;
public interceptor: ContractInterceptor<DropERC1155>;
public erc1155: Erc1155<DropERC1155>;
public owner: ContractOwner<DropERC1155>;
public interceptor: ContractInterceptor<PrebuiltEditionDrop>;
public erc1155: Erc1155<PrebuiltEditionDrop>;
public owner: ContractOwner<PrebuiltEditionDrop>;
constructor(
network: NetworkOrSignerOrProvider,
@@ -126,7 +126,7 @@ export class EditionDrop extends StandardErc1155<DropERC1155> {
options: SDKOptions = {},
abi: typeof ABI,
chainId: number,
contractWrapper = new ContractWrapper<DropERC1155>(
contractWrapper = new ContractWrapper<PrebuiltEditionDrop>(
network,
address,
abi,

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