Compare commits
53
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b4e005be7f | ||
|
|
ccb7db4873 | ||
|
|
9151aa7dc8 | ||
|
|
5a01c537e3 | ||
|
|
f09c153ff7 | ||
|
|
15431621fd | ||
|
|
887af24450 | ||
|
|
15514f190a | ||
|
|
72589ec234 | ||
|
|
f8bceec10c | ||
|
|
7c0744a006 | ||
|
|
846022e2cf | ||
|
|
96bd92e724 | ||
|
|
c2b6d977ed | ||
|
|
7cb8e59be4 | ||
|
|
04a47e8971 | ||
|
|
8a6a97a3e9 | ||
|
|
612bcdd16f | ||
|
|
0ed77d2c9e | ||
|
|
7129aef6e1 | ||
|
|
f50b74819d | ||
|
|
3eba69afd7 | ||
|
|
98c2253e8f | ||
|
|
32d91ef709 | ||
|
|
b79dc18f7a | ||
|
|
4af785c37c | ||
|
|
6f818f393e | ||
|
|
97ed4779a9 | ||
|
|
2d3fca5237 | ||
|
|
7016019f2a | ||
|
|
4e7b293c54 | ||
|
|
c9c0e4ff39 | ||
|
|
06c24c153a | ||
|
|
aa6d9ed75d | ||
|
|
42a606588d | ||
|
|
2820761003 | ||
|
|
5200d5d7e7 | ||
|
|
e731c1a7c3 | ||
|
|
2e8d3fd6e7 | ||
|
|
cc4613b5fd | ||
|
|
78566babaa | ||
|
|
65fc7ba6ff | ||
|
|
1c24c3c3c4 | ||
|
|
408c5a81fa | ||
|
|
352291791e | ||
|
|
aabbb149e8 | ||
|
|
d59450f814 | ||
|
|
7d9a4c6abc | ||
|
|
99806e595d | ||
|
|
21c21c1adf | ||
|
|
4b10719047 | ||
|
|
989cf7347c | ||
|
|
72227b2e16 |
@@ -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
|
||||
|
||||
@@ -15,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/**}"
|
||||
|
||||
+2
-1
@@ -16,4 +16,5 @@ public/dist
|
||||
.yalc/
|
||||
yalc.lock
|
||||
build/
|
||||
playwright-report/
|
||||
playwright-report/
|
||||
.vscode/
|
||||
+3
-3
@@ -15,8 +15,8 @@
|
||||
"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",
|
||||
@@ -28,7 +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"
|
||||
"push": "turbo run push --filter=./packages/*"
|
||||
},
|
||||
"packageManager": "[email protected]",
|
||||
"dependencies": {
|
||||
|
||||
@@ -1,5 +1,31 @@
|
||||
# @thirdweb-dev/auth
|
||||
|
||||
## 2.0.32
|
||||
|
||||
## 2.0.31
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#397](https://github.com/thirdweb-dev/js/pull/397) [`72589ec`](https://github.com/thirdweb-dev/js/commit/72589ec234d86939b67b0ca8b3b789f2e9c54cc2) Thanks [@adam-maj](https://github.com/adam-maj)! - Call user callback in getUser
|
||||
|
||||
## 2.0.30
|
||||
|
||||
## 2.0.29
|
||||
|
||||
## 2.0.28
|
||||
|
||||
## 2.0.27
|
||||
|
||||
## 2.0.26
|
||||
|
||||
## 2.0.25
|
||||
|
||||
## 2.0.24
|
||||
|
||||
## 2.0.23
|
||||
|
||||
## 2.0.22
|
||||
|
||||
## 2.0.21
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@thirdweb-dev/auth",
|
||||
"version": "2.0.21",
|
||||
"version": "2.0.32",
|
||||
"exports": {
|
||||
"./next": {
|
||||
"module": "./next/dist/thirdweb-dev-auth-next.esm.js",
|
||||
@@ -73,7 +73,7 @@
|
||||
"eslint-plugin-inclusive-language": "^2.2.0",
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
"eslint-plugin-tsdoc": "^0.2.16",
|
||||
"ethers": "^5.7.1",
|
||||
"ethers": "^5.7.2",
|
||||
"express": "^4.18.1",
|
||||
"next": "^12.2.0",
|
||||
"next-auth": "^4.10.3",
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -1,5 +1,82 @@
|
||||
# thirdweb
|
||||
|
||||
## 0.8.18
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`ccb7db4`](https://github.com/thirdweb-dev/js/commit/ccb7db48739b8dddcb2c032b3b6e3e5200485715), [`9151aa7`](https://github.com/thirdweb-dev/js/commit/9151aa7dc80754a7aa4fd3f6ce0207bccbb6451f), [`5a01c53`](https://github.com/thirdweb-dev/js/commit/5a01c537e30bd7ef562aad1012413990de6faedf)]:
|
||||
- @thirdweb-dev/sdk@3.6.2
|
||||
|
||||
## 0.8.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`15514f1`](https://github.com/thirdweb-dev/js/commit/15514f190a844d809ae5b1edb580aab46a682485), [`7c0744a`](https://github.com/thirdweb-dev/js/commit/7c0744a006e78987ad8b271b2c64f4bac7759510), [`f8bceec`](https://github.com/thirdweb-dev/js/commit/f8bceec10c60b30dc3d6f45926e2a9e018202578), [`887af24`](https://github.com/thirdweb-dev/js/commit/887af24450e316c2ea390cd49c0563cebe793c0f), [`96bd92e`](https://github.com/thirdweb-dev/js/commit/96bd92e724ec7d880d23348071b26ab8c21dd868)]:
|
||||
- @thirdweb-dev/sdk@3.6.1
|
||||
|
||||
## 0.8.16
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`7cb8e59`](https://github.com/thirdweb-dev/js/commit/7cb8e59be48dc923316eb36ab43a1bef7364f6b1), [`0ed77d2`](https://github.com/thirdweb-dev/js/commit/0ed77d2c9e7526444ef660aab82a950865234aa6), [`04a47e8`](https://github.com/thirdweb-dev/js/commit/04a47e8971d28da2532ebd9db824eeb459c17bd4)]:
|
||||
- @thirdweb-dev/sdk@3.6.0
|
||||
|
||||
## 0.8.15
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`3eba69a`](https://github.com/thirdweb-dev/js/commit/3eba69afd78b7aa472a9c00c9d4220bb69ad1d3e), [`4af785c`](https://github.com/thirdweb-dev/js/commit/4af785c37c831ecd7087c1c9eb9095922cac0855), [`6f818f3`](https://github.com/thirdweb-dev/js/commit/6f818f393eb9023b53a3358d62d60ec23a9246bd), [`b79dc18`](https://github.com/thirdweb-dev/js/commit/b79dc18f7ab5155bbf4af02dc1a953546160bad0)]:
|
||||
- @thirdweb-dev/sdk@3.5.2
|
||||
|
||||
## 0.8.14
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`4e7b293`](https://github.com/thirdweb-dev/js/commit/4e7b293c543244de79c150ca8c37d941221fc9a3), [`c9c0e4f`](https://github.com/thirdweb-dev/js/commit/c9c0e4ff39b054af746f3aec75fcbd0a3ef7239d)]:
|
||||
- @thirdweb-dev/sdk@3.5.1
|
||||
|
||||
## 0.8.13
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`5200d5d`](https://github.com/thirdweb-dev/js/commit/5200d5d7e730da58bd4d3e7c222e3c661265f913), [`42a6065`](https://github.com/thirdweb-dev/js/commit/42a606588dfed03c67c57357dd0ee1ad19fad6ea)]:
|
||||
- @thirdweb-dev/sdk@3.5.0
|
||||
|
||||
## 0.8.12
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`e731c1a`](https://github.com/thirdweb-dev/js/commit/e731c1a7c341e6f2b9cc42c88d5c1b036dcf4a7c)]:
|
||||
- @thirdweb-dev/sdk@3.4.5
|
||||
|
||||
## 0.8.11
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies []:
|
||||
- @thirdweb-dev/sdk@3.4.4
|
||||
|
||||
## 0.8.10
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`1c24c3c`](https://github.com/thirdweb-dev/js/commit/1c24c3c3c48476a824f817e09e7bf0fbe67c1db5), [`65fc7ba`](https://github.com/thirdweb-dev/js/commit/65fc7ba6fff093a971b53cb0979c39768e090ba1)]:
|
||||
- @thirdweb-dev/sdk@3.4.3
|
||||
|
||||
## 0.8.9
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`21c21c1`](https://github.com/thirdweb-dev/js/commit/21c21c1adfd09b60ad563bfd4c308597140de33c), [`3522917`](https://github.com/thirdweb-dev/js/commit/352291791ee900e1500f84f095290497934d2f60)]:
|
||||
- @thirdweb-dev/sdk@3.4.2
|
||||
|
||||
## 0.8.8
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`72227b2`](https://github.com/thirdweb-dev/js/commit/72227b2e166a3a68bbb41cf2b389322f5b7547a2)]:
|
||||
- @thirdweb-dev/sdk@3.4.1
|
||||
|
||||
## 0.8.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "thirdweb",
|
||||
"main": "dist/cli/index.js",
|
||||
"version": "0.8.7",
|
||||
"version": "0.8.18",
|
||||
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/cli",
|
||||
"author": "thirdweb eng <[email protected]>",
|
||||
"license": "Apache-2.0",
|
||||
@@ -10,7 +10,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@gmrchk/cli-testing-library": "^0.1.2",
|
||||
"@types/async-retry": "1.3.0",
|
||||
"@types/async-retry": "^1.3.0",
|
||||
"@types/cross-spawn": "^6.0.2",
|
||||
"@types/inquirer": "^8.2.1",
|
||||
"@types/jest": "^29.0.0",
|
||||
@@ -33,13 +33,13 @@
|
||||
"async-retry": "^1.3.3",
|
||||
"commander": "^9.1.0",
|
||||
"enquirer": "^2.3.6",
|
||||
"ethers": "^5.7.0",
|
||||
"ethers": "^5.7.2",
|
||||
"got": "11.8.5",
|
||||
"inquirer": "^8.2.3",
|
||||
"open": "^8.4.0",
|
||||
"prompts": "^2.4.2",
|
||||
"rimraf": "^3.0.2",
|
||||
"solc": "0.8.13",
|
||||
"solc": "0.8.17",
|
||||
"tar": "^6.1.11",
|
||||
"tslog": "^3.3.3",
|
||||
"update-notifier": "^6.0.2",
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
@@ -1,19 +1,19 @@
|
||||
{
|
||||
"name": "@thirdweb-dev/contracts-js",
|
||||
"version": "1.1.9",
|
||||
"version": "1.2.1",
|
||||
"main": "dist/thirdweb-dev-contracts-js.cjs.js",
|
||||
"module": "dist/thirdweb-dev-contracts-js.esm.js",
|
||||
"license": "Apache-2.0",
|
||||
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/contracts-js",
|
||||
"dependencies": {
|
||||
"@thirdweb-dev/contracts": "3.1.8-0"
|
||||
"@thirdweb-dev/contracts": "^3.2.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/preset-env": "^7.18.10",
|
||||
"@babel/preset-typescript": "^7.18.6",
|
||||
"@preconstruct/cli": "^2.2.1",
|
||||
"@typechain/ethers-v5": "10.0.0",
|
||||
"ethers": "^5.7.0",
|
||||
"ethers": "^5.7.2",
|
||||
"typechain": "^8.1.0",
|
||||
"typescript": "^4.7.4"
|
||||
},
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"dependencies": {
|
||||
"@typescript-eslint/eslint-plugin": "^5.33.0",
|
||||
"@typescript-eslint/parser": "^5.33.0",
|
||||
"eslint-config-next": "^12.0.8",
|
||||
"eslint-config-next": "12.3.1",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-config-turbo": "latest",
|
||||
"eslint-plugin-react": "^7.28.0"
|
||||
|
||||
@@ -1,5 +1,72 @@
|
||||
# @thirdweb-dev/react
|
||||
|
||||
## 3.6.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#405](https://github.com/thirdweb-dev/js/pull/405) [`ccb7db4`](https://github.com/thirdweb-dev/js/commit/ccb7db48739b8dddcb2c032b3b6e3e5200485715) Thanks [@nachoiacovino](https://github.com/nachoiacovino)! - Add updateAll param to creators and royalty update hooks for Solana
|
||||
|
||||
## 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@thirdweb-dev/react",
|
||||
"version": "3.4.0",
|
||||
"version": "3.6.2",
|
||||
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/react",
|
||||
"author": "thirdweb eng <[email protected]>",
|
||||
"license": "Apache-2.0",
|
||||
@@ -62,7 +62,7 @@
|
||||
},
|
||||
"sideEffects:": false,
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.18.10",
|
||||
"@babel/core": "^7.19.3",
|
||||
"@babel/preset-env": "^7.18.10",
|
||||
"@babel/preset-react": "^7.18.6",
|
||||
"@babel/preset-typescript": "^7.18.6",
|
||||
@@ -73,6 +73,7 @@
|
||||
"@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.62.0",
|
||||
"@thirdweb-dev/sdk": "*",
|
||||
"@types/color": "^3.0.3",
|
||||
@@ -87,7 +88,7 @@
|
||||
"eslint-plugin-inclusive-language": "^2.2.0",
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
"eslint-plugin-tsdoc": "^0.2.16",
|
||||
"ethers": "^5.7.1",
|
||||
"ethers": "^5.7.2",
|
||||
"prettier": "^2.7.1",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -9,16 +9,21 @@ import {
|
||||
useContext as useWagmiContext,
|
||||
} from "wagmi";
|
||||
|
||||
// exerpt from https://docs.gnosis-safe.io/backend/available-services
|
||||
const CHAIN_ID_TO_GNOSIS_SERVER_URL = {
|
||||
// mainnet
|
||||
1: "https://safe-transaction.mainnet.gnosis.io",
|
||||
1: "https://safe-transaction-mainnet.safe.global",
|
||||
// avalanche
|
||||
43114: "https://safe-transaction.avalanche.gnosis.io",
|
||||
43114: "https://safe-transaction-avalanche.safe.global",
|
||||
// polygon
|
||||
137: "https://safe-transaction.polygon.gnosis.io",
|
||||
137: "https://safe-transaction-polygon.safe.global",
|
||||
// goerli
|
||||
5: "https://safe-transaction.goerli.gnosis.io",
|
||||
};
|
||||
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;
|
||||
@@ -28,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";
|
||||
@@ -60,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;
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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");
|
||||
},
|
||||
|
||||
@@ -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
|
||||
*
|
||||
|
||||
@@ -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(
|
||||
() => ({
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
+2
-2
@@ -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 { NFTCollection, NFTDrop, UpdateCreatorInput } 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, updateAll }: UpdateCreatorInput) => {
|
||||
requiredParamInvariant(program, "program is required");
|
||||
return await program.updateCreators(creators, updateAll);
|
||||
},
|
||||
{
|
||||
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, UpdateRoyaltySettingsInput } 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, updateAll }: UpdateRoyaltySettingsInput) => {
|
||||
requiredParamInvariant(program, "program is required");
|
||||
return await program.updateRoyalty(sellerFeeBasisPoints, updateAll);
|
||||
},
|
||||
{
|
||||
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;
|
||||
|
||||
@@ -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";
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,5 +1,120 @@
|
||||
# @thirdweb-dev/sdk
|
||||
|
||||
## 3.6.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#405](https://github.com/thirdweb-dev/js/pull/405) [`ccb7db4`](https://github.com/thirdweb-dev/js/commit/ccb7db48739b8dddcb2c032b3b6e3e5200485715) Thanks [@nachoiacovino](https://github.com/nachoiacovino)! - Add updateAll param to creators and royalty update hooks for Solana
|
||||
|
||||
- [#404](https://github.com/thirdweb-dev/js/pull/404) [`9151aa7`](https://github.com/thirdweb-dev/js/commit/9151aa7dc80754a7aa4fd3f6ce0207bccbb6451f) Thanks [@jnsdls](https://github.com/jnsdls)! - [EVM] - expose `getDefaultTrustedForwarders` helper function
|
||||
|
||||
- [#305](https://github.com/thirdweb-dev/js/pull/305) [`5a01c53`](https://github.com/thirdweb-dev/js/commit/5a01c537e30bd7ef562aad1012413990de6faedf) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Update to Metaplex 0.17
|
||||
|
||||
## 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
|
||||
|
||||
+12
-13
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@thirdweb-dev/sdk",
|
||||
"version": "3.4.0",
|
||||
"version": "3.6.2",
|
||||
"description": "The main thirdweb SDK.",
|
||||
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/sdk",
|
||||
"license": "Apache-2.0",
|
||||
@@ -57,7 +57,7 @@
|
||||
"test:evm:all": "SWC_NODE_PROJECT=./tsconfig.test.json mocha --config './test/evm/.mocharc.json' --timeout 30000 --parallel './test/evm/**/*.test.ts'",
|
||||
"test:evm": "make test-evm",
|
||||
"test:evm:single": "SWC_NODE_PROJECT=./tsconfig.test.json mocha --config './test/evm/.mocharc.json' --timeout 30000",
|
||||
"node:solana:start": "DEBUG='amman:(info|error|debug)' amman start",
|
||||
"node:solana:start": "DEBUG='amman:(info|error|debug)' amman start --forceClone",
|
||||
"node:solana:stop": "amman stop",
|
||||
"test:solana:all": "SWC_NODE_PROJECT=./tsconfig.test.json mocha --config './test/solana/.mocharc.json' --timeout 30000 --parallel './test/solana/**/*.test.ts'",
|
||||
"test:solana": "make test-sol",
|
||||
@@ -65,18 +65,18 @@
|
||||
"push": "yalc push"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@metaplex-foundation/amman": "0.10.0",
|
||||
"@metaplex-foundation/amman-client": "0.2.1",
|
||||
"@metaplex-foundation/amman": "^0.12.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",
|
||||
"@nomiclabs/hardhat-ethers": "2.0.5",
|
||||
"@nomiclabs/hardhat-ethers": "^2.0.5",
|
||||
"@preconstruct/cli": "^2.2.1",
|
||||
"@swc-node/register": "^1.5.1",
|
||||
"@swc/core": "^1.2.177",
|
||||
"@types/chai": "^4.3.3",
|
||||
"@types/deep-equal-in-any-order": "^1.0.1",
|
||||
"@types/mocha": "^9.1.1",
|
||||
"@types/mocha": "^10.0.0",
|
||||
"@types/node": "^18.0.0",
|
||||
"@types/uuid": "^8.3.3",
|
||||
"@typescript-eslint/eslint-plugin": "^5.33.0",
|
||||
@@ -89,7 +89,7 @@
|
||||
"eslint-plugin-inclusive-language": "^2.2.0",
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
"eslint-plugin-tsdoc": "^0.2.16",
|
||||
"ethers": "^5.7.0",
|
||||
"ethers": "^5.7.2",
|
||||
"hardhat": "^2.9.3",
|
||||
"mocha": "^10.0.0",
|
||||
"prettier": "^2.7.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.62.0",
|
||||
"@metaplex-foundation/js": "^0.17.6",
|
||||
"@metaplex-foundation/mpl-token-metadata": "^2.3.3",
|
||||
"@project-serum/anchor": "^0.25.0",
|
||||
"@solana/spl-token": "^0.3.5",
|
||||
"@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",
|
||||
|
||||
@@ -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
|
||||
>;
|
||||
|
||||
@@ -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));
|
||||
});
|
||||
|
||||
@@ -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,7 +215,11 @@ 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);
|
||||
@@ -231,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(
|
||||
@@ -285,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;
|
||||
@@ -299,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 };
|
||||
}
|
||||
@@ -328,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,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -373,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,
|
||||
@@ -389,8 +508,8 @@ export async function transformResultToClaimCondition(
|
||||
pm.maxClaimableSupply,
|
||||
tokenDecimals,
|
||||
);
|
||||
const quantityLimitPerTransaction = convertToReadableQuantity(
|
||||
pm.quantityLimitPerTransaction,
|
||||
const maxClaimablePerWallet = convertToReadableQuantity(
|
||||
pm.maxClaimablePerWallet,
|
||||
tokenDecimals,
|
||||
);
|
||||
const availableSupply = convertToReadableQuantity(
|
||||
@@ -401,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,
|
||||
@@ -416,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 {
|
||||
@@ -427,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,
|
||||
|
||||
@@ -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";
|
||||
@@ -33,6 +33,14 @@ export function cleanCurrencyAddress(currencyAddress: string): string {
|
||||
return currencyAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param provider
|
||||
* @param inputPrice
|
||||
* @param currencyAddress
|
||||
* @returns
|
||||
* @internal
|
||||
*/
|
||||
export async function normalizePriceValue(
|
||||
provider: providers.Provider,
|
||||
inputPrice: Price,
|
||||
@@ -42,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,
|
||||
@@ -73,6 +88,14 @@ export async function fetchCurrencyMetadata(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param providerOrSigner
|
||||
* @param asset
|
||||
* @param price
|
||||
* @returns
|
||||
* @internal
|
||||
*/
|
||||
export async function fetchCurrencyValue(
|
||||
providerOrSigner: providers.Provider,
|
||||
asset: string,
|
||||
|
||||
@@ -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))
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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,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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)],
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { ChainId, SUPPORTED_CHAIN_ID } from "./chains";
|
||||
import { PrebuiltContractType } from "../core/types";
|
||||
import { ChainId, SUPPORTED_CHAIN_ID, SUPPORTED_CHAIN_IDS } 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
|
||||
*/
|
||||
@@ -177,3 +290,24 @@ export function getContractPublisherAddress() {
|
||||
return ContractPublisher_address;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param chainId - chain id
|
||||
* @returns the array of trusted forwarders for the given chain id
|
||||
* @internal
|
||||
*/
|
||||
export function getDefaultTrustedForwarders(
|
||||
chainId: SUPPORTED_CHAIN_ID,
|
||||
): string[] {
|
||||
const chainEnum = SUPPORTED_CHAIN_IDS.find((c) => c === chainId);
|
||||
const biconomyForwarder = chainEnum
|
||||
? CONTRACT_ADDRESSES[chainEnum].biconomyForwarder
|
||||
: constants.AddressZero;
|
||||
const openzeppelinForwarder = chainEnum
|
||||
? CONTRACT_ADDRESSES[chainEnum].openzeppelinForwarder
|
||||
: constants.AddressZero;
|
||||
return biconomyForwarder !== constants.AddressZero
|
||||
? [openzeppelinForwarder, biconomyForwarder]
|
||||
: [openzeppelinForwarder];
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -37,7 +37,7 @@ 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");
|
||||
* const contract = await sdk.getContract("{{contract_address}}", "edition");
|
||||
* ```
|
||||
*
|
||||
* @public
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import { DEFAULT_QUERY_ALL_COUNT } from "../../../core/schema/QueryParams";
|
||||
import { ListingNotFoundError } from "../../common";
|
||||
import { isNativeToken } from "../../common/currency";
|
||||
import { mapOffer } from "../../common/marketplace";
|
||||
import { getRoleHash } from "../../common/role";
|
||||
import { NATIVE_TOKENS, SUPPORTED_CHAIN_ID } from "../../constants";
|
||||
import { ContractEncoder } from "../../core/classes/contract-encoder";
|
||||
import { ContractEvents } from "../../core/classes/contract-events";
|
||||
import { ContractInterceptor } from "../../core/classes/contract-interceptor";
|
||||
@@ -17,6 +19,7 @@ import { NetworkOrSignerOrProvider, TransactionResult } from "../../core/types";
|
||||
import { ListingType } from "../../enums";
|
||||
import { MarketplaceContractSchema } from "../../schema/contracts/marketplace";
|
||||
import { SDKOptions } from "../../schema/sdk-options";
|
||||
import { Price } from "../../types/currency";
|
||||
import { AuctionListing, DirectListing, Offer } from "../../types/marketplace";
|
||||
import { MarketplaceFilter } from "../../types/marketplace/MarketPlaceFilter";
|
||||
import { UnmappedOffer } from "../../types/marketplace/UnmappedOffer";
|
||||
@@ -36,7 +39,7 @@ import invariant from "tiny-invariant";
|
||||
* import { ThirdwebSDK } from "@thirdweb-dev/sdk";
|
||||
*
|
||||
* const sdk = new ThirdwebSDK("{{chainName}}");
|
||||
* const contract = sdk.getContract("{{contract_address}}", "marketplace");
|
||||
* const contract = await sdk.getContract("{{contract_address}}", "marketplace");
|
||||
* ```
|
||||
*
|
||||
* @public
|
||||
@@ -320,26 +323,32 @@ export class Marketplace implements UpdateableNetwork {
|
||||
*/
|
||||
public async getOffers(listingId: BigNumberish): Promise<Offer[]> {
|
||||
// get all new offer events from this contract
|
||||
const events = await this.events.getEvents<NewOfferEventObject>("NewOffer");
|
||||
// get only the events for this listing id
|
||||
const listingEvents = events.filter((e) => e.data.listingId.eq(listingId));
|
||||
const listingEvents = await this.events.getEvents<NewOfferEventObject>(
|
||||
"NewOffer",
|
||||
{
|
||||
order: "desc",
|
||||
filters: {
|
||||
listingId,
|
||||
},
|
||||
},
|
||||
);
|
||||
// derive the offers from the events
|
||||
const offers = await Promise.all(
|
||||
return await Promise.all(
|
||||
listingEvents.map(async (e): Promise<Offer> => {
|
||||
const offer = await mapOffer(
|
||||
return await mapOffer(
|
||||
this.contractWrapper.getProvider(),
|
||||
BigNumber.from(listingId),
|
||||
{
|
||||
quantityWanted: e.data.quantityWanted,
|
||||
pricePerToken: e.data.totalOfferAmount.div(e.data.quantityWanted),
|
||||
pricePerToken: e.data.quantityWanted.gt(0)
|
||||
? e.data.totalOfferAmount.div(e.data.quantityWanted)
|
||||
: e.data.totalOfferAmount,
|
||||
currency: e.data.currency,
|
||||
offeror: e.data.offeror,
|
||||
} as UnmappedOffer,
|
||||
);
|
||||
return offer;
|
||||
}),
|
||||
);
|
||||
return offers;
|
||||
}
|
||||
|
||||
/** ******************************
|
||||
@@ -391,6 +400,60 @@ export class Marketplace implements UpdateableNetwork {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Make an offer for a Direct or Auction Listing
|
||||
*
|
||||
* @remarks Make an offer on a direct or auction listing
|
||||
*
|
||||
* @example
|
||||
* ```javascript
|
||||
* // The listing ID of the asset you want to offer on
|
||||
* const listingId = 0;
|
||||
* // The price you are willing to offer per token
|
||||
* const pricePerToken = 0.5;
|
||||
* // The quantity of tokens you want to receive for this offer
|
||||
* const quantity = 1;
|
||||
*
|
||||
* await contract.makeOffer(
|
||||
* listingId,
|
||||
* pricePerToken,
|
||||
* quantity,
|
||||
* );
|
||||
* ```
|
||||
*/
|
||||
public async makeOffer(
|
||||
listingId: BigNumberish,
|
||||
pricePerToken: Price,
|
||||
quantity?: BigNumberish,
|
||||
): Promise<TransactionResult> {
|
||||
const listing = await this.contractWrapper.readContract.listings(listingId);
|
||||
if (listing.listingId.toString() !== listingId.toString()) {
|
||||
throw new ListingNotFoundError(this.getAddress(), listingId.toString());
|
||||
}
|
||||
const chainId = await this.contractWrapper.getChainID();
|
||||
switch (listing.listingType) {
|
||||
case ListingType.Direct: {
|
||||
invariant(
|
||||
quantity,
|
||||
"quantity is required when making an offer on a direct listing",
|
||||
);
|
||||
return await this.direct.makeOffer(
|
||||
listingId,
|
||||
quantity,
|
||||
isNativeToken(listing.currency)
|
||||
? NATIVE_TOKENS[chainId as SUPPORTED_CHAIN_ID].wrapped.address
|
||||
: listing.currency,
|
||||
pricePerToken,
|
||||
);
|
||||
}
|
||||
case ListingType.Auction: {
|
||||
return await this.auction.makeBid(listingId, pricePerToken);
|
||||
}
|
||||
default:
|
||||
throw Error(`Unknown listing type: ${listing.listingType}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Auction bid buffer
|
||||
* @remarks A percentage (e.g. 5%) in basis points (5% = 500, 100% = 10000). A new bid is considered to be a winning bid only if its bid amount is at least the bid buffer (e.g. 5%) greater than the previous winning bid. This prevents buyers from making very slightly higher bids to win the auctioned items.
|
||||
|
||||
@@ -47,7 +47,7 @@ import { BigNumberish, CallOverrides, ethers } from "ethers";
|
||||
* import { ThirdwebSDK } from "@thirdweb-dev/sdk";
|
||||
*
|
||||
* const sdk = new ThirdwebSDK("{{chainName}}");
|
||||
* const contract = sdk.getContract("{{contract_address}}", "multiwrap");
|
||||
* const contract = await sdk.getContract("{{contract_address}}", "multiwrap");
|
||||
* ```
|
||||
*
|
||||
* @beta
|
||||
|
||||
@@ -35,7 +35,7 @@ import { BigNumberish, CallOverrides, constants } from "ethers";
|
||||
* import { ThirdwebSDK } from "@thirdweb-dev/sdk";
|
||||
*
|
||||
* const sdk = new ThirdwebSDK("{{chainName}}");
|
||||
* const contract = sdk.getContract("{{contract_address}}", "nft-collection");
|
||||
* const contract = await sdk.getContract("{{contract_address}}", "nft-collection");
|
||||
* ```
|
||||
*
|
||||
* @public
|
||||
|
||||
@@ -4,7 +4,6 @@ import {
|
||||
} from "../../../core/schema/QueryParams";
|
||||
import { NFT, NFTMetadata, NFTMetadataOrUri } from "../../../core/schema/nft";
|
||||
import { getRoleHash } from "../../common";
|
||||
import { uploadOrExtractURIs } from "../../common/nft";
|
||||
import { FEATURE_NFT_REVEALABLE } from "../../constants/erc721-features";
|
||||
import { TransactionTask } from "../../core/classes/TransactionTask";
|
||||
import { ContractEncoder } from "../../core/classes/contract-encoder";
|
||||
@@ -27,21 +26,14 @@ import {
|
||||
TransactionResult,
|
||||
TransactionResultWithId,
|
||||
} from "../../core/types";
|
||||
import { PaperCheckout } from "../../integrations/paper-xyz";
|
||||
import { PaperCheckout } from "../../integrations/thirdweb-checkout";
|
||||
import { DropErc721ContractSchema } from "../../schema/contracts/drop-erc721";
|
||||
import { SDKOptions } from "../../schema/sdk-options";
|
||||
import { PrebuiltNFTDrop } from "../../types/eips";
|
||||
import { UploadProgressEvent } from "../../types/events";
|
||||
import type { DropERC721 } from "@thirdweb-dev/contracts-js";
|
||||
import type ABI from "@thirdweb-dev/contracts-js/dist/abis/DropERC721.json";
|
||||
import { TokensLazyMintedEvent } from "@thirdweb-dev/contracts-js/dist/declarations/src/DropERC721";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
import {
|
||||
BigNumber,
|
||||
BigNumberish,
|
||||
CallOverrides,
|
||||
constants,
|
||||
ethers,
|
||||
} from "ethers";
|
||||
import { BigNumber, BigNumberish, CallOverrides, constants } from "ethers";
|
||||
|
||||
/**
|
||||
* Setup a collection of one-of-one NFTs that are minted as users claim them.
|
||||
@@ -52,29 +44,32 @@ import {
|
||||
* import { ThirdwebSDK } from "@thirdweb-dev/sdk";
|
||||
*
|
||||
* const sdk = new ThirdwebSDK("{{chainName}}");
|
||||
* const contract = sdk.getContract("{{contract_address}}", "nft-drop");
|
||||
* const contract = await sdk.getContract("{{contract_address}}", "nft-drop");
|
||||
* ```
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export class NFTDrop extends StandardErc721<DropERC721> {
|
||||
export class NFTDrop extends StandardErc721<PrebuiltNFTDrop> {
|
||||
static contractRoles = ["admin", "minter", "transfer"] as const;
|
||||
|
||||
public abi: typeof ABI;
|
||||
public encoder: ContractEncoder<DropERC721>;
|
||||
public estimator: GasCostEstimator<DropERC721>;
|
||||
public encoder: ContractEncoder<PrebuiltNFTDrop>;
|
||||
public estimator: GasCostEstimator<PrebuiltNFTDrop>;
|
||||
public metadata: ContractMetadata<
|
||||
DropERC721,
|
||||
PrebuiltNFTDrop,
|
||||
typeof DropErc721ContractSchema
|
||||
>;
|
||||
public sales: ContractPrimarySale<DropERC721>;
|
||||
public platformFees: ContractPlatformFee<DropERC721>;
|
||||
public events: ContractEvents<DropERC721>;
|
||||
public roles: ContractRoles<DropERC721, typeof NFTDrop.contractRoles[number]>;
|
||||
public sales: ContractPrimarySale<PrebuiltNFTDrop>;
|
||||
public platformFees: ContractPlatformFee<PrebuiltNFTDrop>;
|
||||
public events: ContractEvents<PrebuiltNFTDrop>;
|
||||
public roles: ContractRoles<
|
||||
PrebuiltNFTDrop,
|
||||
typeof NFTDrop.contractRoles[number]
|
||||
>;
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public interceptor: ContractInterceptor<DropERC721>;
|
||||
public interceptor: ContractInterceptor<PrebuiltNFTDrop>;
|
||||
/**
|
||||
* Configure royalties
|
||||
* @remarks Set your own royalties for the entire contract or per token
|
||||
@@ -93,7 +88,7 @@ export class NFTDrop extends StandardErc721<DropERC721> {
|
||||
* ```
|
||||
*/
|
||||
public royalties: ContractRoyalty<
|
||||
DropERC721,
|
||||
PrebuiltNFTDrop,
|
||||
typeof DropErc721ContractSchema
|
||||
>;
|
||||
/**
|
||||
@@ -118,7 +113,7 @@ export class NFTDrop extends StandardErc721<DropERC721> {
|
||||
* await contract.claimConditions.set(claimConditions);
|
||||
* ```
|
||||
*/
|
||||
public claimConditions: DropClaimConditions<DropERC721>;
|
||||
public claimConditions: DropClaimConditions<PrebuiltNFTDrop>;
|
||||
/**
|
||||
* Delayed reveal
|
||||
* @remarks Create a batch of encrypted NFTs that can be revealed at a later time.
|
||||
@@ -150,16 +145,16 @@ export class NFTDrop extends StandardErc721<DropERC721> {
|
||||
* await contract.revealer.reveal(batchId, "my secret password");
|
||||
* ```
|
||||
*/
|
||||
public revealer: DelayedReveal<DropERC721>;
|
||||
public revealer: DelayedReveal<PrebuiltNFTDrop>;
|
||||
|
||||
/**
|
||||
* Checkout
|
||||
* @remarks Create a FIAT currency checkout for your NFT drop.
|
||||
*/
|
||||
public checkout: PaperCheckout<DropERC721>;
|
||||
public checkout: PaperCheckout<PrebuiltNFTDrop>;
|
||||
|
||||
public erc721: Erc721<DropERC721>;
|
||||
public owner: ContractOwner<DropERC721>;
|
||||
public erc721: Erc721<PrebuiltNFTDrop>;
|
||||
public owner: ContractOwner<PrebuiltNFTDrop>;
|
||||
|
||||
constructor(
|
||||
network: NetworkOrSignerOrProvider,
|
||||
@@ -168,7 +163,7 @@ export class NFTDrop extends StandardErc721<DropERC721> {
|
||||
options: SDKOptions = {},
|
||||
abi: typeof ABI,
|
||||
chainId: number,
|
||||
contractWrapper = new ContractWrapper<DropERC721>(
|
||||
contractWrapper = new ContractWrapper<PrebuiltNFTDrop>(
|
||||
network,
|
||||
address,
|
||||
abi,
|
||||
@@ -195,7 +190,7 @@ export class NFTDrop extends StandardErc721<DropERC721> {
|
||||
this.events = new ContractEvents(this.contractWrapper);
|
||||
this.platformFees = new ContractPlatformFee(this.contractWrapper);
|
||||
this.erc721 = new Erc721(this.contractWrapper, this.storage, chainId);
|
||||
this.revealer = new DelayedReveal<DropERC721>(
|
||||
this.revealer = new DelayedReveal(
|
||||
this.contractWrapper,
|
||||
this.storage,
|
||||
FEATURE_NFT_REVEALABLE.name,
|
||||
@@ -383,44 +378,7 @@ export class NFTDrop extends StandardErc721<DropERC721> {
|
||||
onProgress: (event: UploadProgressEvent) => void;
|
||||
},
|
||||
): Promise<TransactionResultWithId<NFTMetadata>[]> {
|
||||
const startFileNumber =
|
||||
await this.contractWrapper.readContract.nextTokenIdToMint();
|
||||
const batch = await uploadOrExtractURIs(
|
||||
metadatas,
|
||||
this.storage,
|
||||
startFileNumber.toNumber(),
|
||||
options,
|
||||
);
|
||||
// ensure baseUri is the same for the entire batch
|
||||
const baseUri = batch[0].substring(0, batch[0].lastIndexOf("/"));
|
||||
for (let i = 0; i < batch.length; i++) {
|
||||
const uri = batch[i].substring(0, batch[i].lastIndexOf("/"));
|
||||
if (baseUri !== uri) {
|
||||
throw new Error(
|
||||
`Can only create batches with the same base URI for every entry in the batch. Expected '${baseUri}' but got '${uri}'`,
|
||||
);
|
||||
}
|
||||
}
|
||||
const receipt = await this.contractWrapper.sendTransaction("lazyMint", [
|
||||
batch.length,
|
||||
baseUri.endsWith("/") ? baseUri : `${baseUri}/`,
|
||||
ethers.utils.toUtf8Bytes(""),
|
||||
]);
|
||||
const event = this.contractWrapper.parseLogs<TokensLazyMintedEvent>(
|
||||
"TokensLazyMinted",
|
||||
receipt?.logs,
|
||||
);
|
||||
const startingIndex = event[0].args.startTokenId;
|
||||
const endingIndex = event[0].args.endTokenId;
|
||||
const results: TransactionResultWithId<NFTMetadata>[] = [];
|
||||
for (let id = startingIndex; id.lte(endingIndex); id = id.add(1)) {
|
||||
results.push({
|
||||
id,
|
||||
receipt,
|
||||
data: () => this.erc721.getTokenMetadata(id),
|
||||
});
|
||||
}
|
||||
return results;
|
||||
return this.erc721.lazyMint(metadatas, options);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -451,9 +409,9 @@ export class NFTDrop extends StandardErc721<DropERC721> {
|
||||
* const quantity = 1; // how many unique NFTs you want to claim
|
||||
*
|
||||
* const tx = await contract.claimTo(address, quantity);
|
||||
* const receipt = tx.receipt; // the transaction receipt
|
||||
* const claimedTokenId = tx.id; // the id of the NFT claimed
|
||||
* const claimedNFT = await tx.data(); // (optional) get the claimed NFT metadata
|
||||
* const receipt = tx[0].receipt; // the transaction receipt
|
||||
* const claimedTokenId = tx[0].id; // the id of the NFT claimed
|
||||
* const claimedNFT = await tx[0].data(); // (optional) get the claimed NFT metadata
|
||||
* ```
|
||||
*
|
||||
* @param destinationAddress - Address you want to send the token to
|
||||
|
||||
@@ -52,7 +52,7 @@ import { BigNumber, BigNumberish, CallOverrides, ethers } from "ethers";
|
||||
* import { ThirdwebSDK } from "@thirdweb-dev/sdk";
|
||||
*
|
||||
* const sdk = new ThirdwebSDK("{{chainName}}");
|
||||
* const contract = sdk.getContract("{{contract_address}}", "pack");
|
||||
* const contract = await sdk.getContract("{{contract_address}}", "pack");
|
||||
* ```
|
||||
*
|
||||
* @public
|
||||
|
||||
@@ -27,7 +27,7 @@ import {
|
||||
TransactionResult,
|
||||
TransactionResultWithId,
|
||||
} from "../../core/types";
|
||||
import { PaperCheckout } from "../../integrations/paper-xyz";
|
||||
import { PaperCheckout } from "../../integrations/thirdweb-checkout";
|
||||
import { DropErc721ContractSchema } from "../../schema/contracts/drop-erc721";
|
||||
import { SDKOptions } from "../../schema/sdk-options";
|
||||
import { ClaimOptions, UploadProgressEvent } from "../../types";
|
||||
@@ -47,7 +47,7 @@ import { BigNumber, BigNumberish, CallOverrides, constants } from "ethers";
|
||||
* import { ThirdwebSDK } from "@thirdweb-dev/sdk";
|
||||
*
|
||||
* const sdk = new ThirdwebSDK("{{chainName}}");
|
||||
* const contract = sdk.getContract("{{contract_address}}", "signature-drop");
|
||||
* const contract = await sdk.getContract("{{contract_address}}", "signature-drop");
|
||||
* ```
|
||||
*
|
||||
* @public
|
||||
|
||||
@@ -30,7 +30,7 @@ import { BigNumber, CallOverrides, Contract } from "ethers";
|
||||
* import { ThirdwebSDK } from "@thirdweb-dev/sdk";
|
||||
*
|
||||
* const sdk = new ThirdwebSDK("{{chainName}}");
|
||||
* const contract = sdk.getContract("{{contract_address}}", "split");
|
||||
* const contract = await sdk.getContract("{{contract_address}}", "split");
|
||||
* ```
|
||||
*
|
||||
* @public
|
||||
|
||||
@@ -14,7 +14,7 @@ import { NetworkOrSignerOrProvider, TransactionResult } from "../../core/types";
|
||||
import { DropErc20ContractSchema } from "../../schema/contracts/drop-erc20";
|
||||
import { SDKOptions } from "../../schema/sdk-options";
|
||||
import { Amount, CurrencyValue } from "../../types";
|
||||
import type { DropERC20 } from "@thirdweb-dev/contracts-js";
|
||||
import { PrebuiltTokenDrop } from "../../types/eips";
|
||||
import type ABI from "@thirdweb-dev/contracts-js/dist/abis/DropERC20.json";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
import { CallOverrides, constants } from "ethers";
|
||||
@@ -28,24 +28,27 @@ import { CallOverrides, constants } from "ethers";
|
||||
* import { ThirdwebSDK } from "@thirdweb-dev/sdk";
|
||||
*
|
||||
* const sdk = new ThirdwebSDK("{{chainName}}");
|
||||
* const contract = sdk.getContract("{{contract_address}}", "token-drop");
|
||||
* const contract = await sdk.getContract("{{contract_address}}", "token-drop");
|
||||
* ```
|
||||
*
|
||||
*/
|
||||
export class TokenDrop extends StandardErc20<DropERC20> {
|
||||
export class TokenDrop extends StandardErc20<PrebuiltTokenDrop> {
|
||||
static contractRoles = ["admin", "transfer"] as const;
|
||||
|
||||
public abi: typeof ABI;
|
||||
public metadata: ContractMetadata<DropERC20, typeof DropErc20ContractSchema>;
|
||||
public metadata: ContractMetadata<
|
||||
PrebuiltTokenDrop,
|
||||
typeof DropErc20ContractSchema
|
||||
>;
|
||||
public roles: ContractRoles<
|
||||
DropERC20,
|
||||
PrebuiltTokenDrop,
|
||||
typeof TokenDrop.contractRoles[number]
|
||||
>;
|
||||
public encoder: ContractEncoder<DropERC20>;
|
||||
public estimator: GasCostEstimator<DropERC20>;
|
||||
public sales: ContractPrimarySale<DropERC20>;
|
||||
public platformFees: ContractPlatformFee<DropERC20>;
|
||||
public events: ContractEvents<DropERC20>;
|
||||
public encoder: ContractEncoder<PrebuiltTokenDrop>;
|
||||
public estimator: GasCostEstimator<PrebuiltTokenDrop>;
|
||||
public sales: ContractPrimarySale<PrebuiltTokenDrop>;
|
||||
public platformFees: ContractPlatformFee<PrebuiltTokenDrop>;
|
||||
public events: ContractEvents<PrebuiltTokenDrop>;
|
||||
/**
|
||||
* Configure claim conditions
|
||||
* @remarks Define who can claim Tokens, when and how many.
|
||||
@@ -68,11 +71,11 @@ export class TokenDrop extends StandardErc20<DropERC20> {
|
||||
* await contract.claimConditions.set(claimConditions);
|
||||
* ```
|
||||
*/
|
||||
public claimConditions: DropClaimConditions<DropERC20>;
|
||||
public claimConditions: DropClaimConditions<PrebuiltTokenDrop>;
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public interceptor: ContractInterceptor<DropERC20>;
|
||||
public interceptor: ContractInterceptor<PrebuiltTokenDrop>;
|
||||
|
||||
constructor(
|
||||
network: NetworkOrSignerOrProvider,
|
||||
@@ -81,7 +84,7 @@ export class TokenDrop extends StandardErc20<DropERC20> {
|
||||
options: SDKOptions = {},
|
||||
abi: typeof ABI,
|
||||
chainId: number,
|
||||
contractWrapper = new ContractWrapper<DropERC20>(
|
||||
contractWrapper = new ContractWrapper<PrebuiltTokenDrop>(
|
||||
network,
|
||||
address,
|
||||
abi,
|
||||
@@ -105,7 +108,7 @@ export class TokenDrop extends StandardErc20<DropERC20> {
|
||||
this.sales = new ContractPrimarySale(this.contractWrapper);
|
||||
this.platformFees = new ContractPlatformFee(this.contractWrapper);
|
||||
this.interceptor = new ContractInterceptor(this.contractWrapper);
|
||||
this.claimConditions = new DropClaimConditions<DropERC20>(
|
||||
this.claimConditions = new DropClaimConditions<PrebuiltTokenDrop>(
|
||||
this.contractWrapper,
|
||||
this.metadata,
|
||||
this.storage,
|
||||
@@ -210,7 +213,9 @@ export class TokenDrop extends StandardErc20<DropERC20> {
|
||||
amount: Amount,
|
||||
checkERC20Allowance = true,
|
||||
): Promise<TransactionResult> {
|
||||
return this.erc20.claimTo(destinationAddress, amount, checkERC20Allowance);
|
||||
return this.erc20.claimTo(destinationAddress, amount, {
|
||||
checkERC20Allowance,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -30,7 +30,7 @@ import { CallOverrides, constants } from "ethers";
|
||||
* import { ThirdwebSDK } from "@thirdweb-dev/sdk";
|
||||
*
|
||||
* const sdk = new ThirdwebSDK("{{chainName}}");
|
||||
* const contract = sdk.getContract("{{contract_address}}", "token");
|
||||
* const contract = await sdk.getContract("{{contract_address}}", "token");
|
||||
* ```
|
||||
*
|
||||
* @public
|
||||
|
||||
@@ -46,7 +46,7 @@ import {
|
||||
* import { ThirdwebSDK } from "@thirdweb-dev/sdk";
|
||||
*
|
||||
* const sdk = new ThirdwebSDK("{{chainName}}");
|
||||
* const contract = sdk.getContract("{{contract_address}}", "vote");
|
||||
* const contract = await sdk.getContract("{{contract_address}}", "vote");
|
||||
* ```
|
||||
*
|
||||
* @public
|
||||
@@ -319,7 +319,7 @@ export class Vote implements UpdateableNetwork {
|
||||
this.contractWrapper.readContract.votingDelay(),
|
||||
this.contractWrapper.readContract.votingPeriod(),
|
||||
this.contractWrapper.readContract.token(),
|
||||
this.contractWrapper.readContract.quorumNumerator(),
|
||||
this.contractWrapper.readContract["quorumNumerator()"](),
|
||||
this.contractWrapper.readContract.proposalThreshold(),
|
||||
]);
|
||||
const votingTokenMetadata = await fetchCurrencyMetadata(
|
||||
|
||||
@@ -4,10 +4,12 @@ import {
|
||||
fetchExtendedReleaseMetadata,
|
||||
fetchPreDeployMetadata,
|
||||
} from "../../common/index";
|
||||
import { ChainId } from "../../constants";
|
||||
import { getContractAddressByChainId } from "../../constants/addresses";
|
||||
import {
|
||||
EditionDropInitializer,
|
||||
EditionInitializer,
|
||||
getContractName,
|
||||
MarketplaceInitializer,
|
||||
MultiwrapInitializer,
|
||||
NFTCollectionInitializer,
|
||||
@@ -387,6 +389,7 @@ export class ContractDeployer extends RPCConnectionHandler {
|
||||
* @internal
|
||||
* @param contractType - the type of contract to deploy
|
||||
* @param contractMetadata - the metadata to deploy the contract with
|
||||
* @param version
|
||||
* @returns a promise of the address of the newly deployed contract
|
||||
*/
|
||||
public async deployBuiltInContract<
|
||||
@@ -396,13 +399,72 @@ export class ContractDeployer extends RPCConnectionHandler {
|
||||
contractMetadata: z.input<
|
||||
DeploySchemaForPrebuiltContractType<TContractType>
|
||||
>,
|
||||
version: string = "latest",
|
||||
): Promise<string> {
|
||||
const parsed =
|
||||
const activeChainId = (await this.getProvider().getNetwork()).chainId;
|
||||
|
||||
const parsedMetadata =
|
||||
PREBUILT_CONTRACTS_MAP[contractType].schema.deploy.parse(
|
||||
contractMetadata,
|
||||
);
|
||||
const factory = await this.getFactory();
|
||||
return await factory.deploy(contractType, parsed);
|
||||
|
||||
if (
|
||||
activeChainId === ChainId.Hardhat ||
|
||||
activeChainId === ChainId.Localhost
|
||||
) {
|
||||
//
|
||||
// old behavior for hardhat and localhost chains
|
||||
//
|
||||
|
||||
// parse version into the first number of the version string (or undefined if unparseable)
|
||||
let parsedVersion: number | undefined = undefined;
|
||||
try {
|
||||
parsedVersion = parseInt(version);
|
||||
if (isNaN(parsedVersion)) {
|
||||
parsedVersion = undefined;
|
||||
}
|
||||
} catch (e) {
|
||||
parsedVersion = undefined;
|
||||
}
|
||||
return await factory.deploy(contractType, parsedMetadata, parsedVersion);
|
||||
}
|
||||
|
||||
//
|
||||
// new behavior for all other chains
|
||||
//
|
||||
|
||||
// resolve contract name from type
|
||||
const contractName = getContractName(contractType);
|
||||
invariant(contractName, "contract name not found");
|
||||
// get deploy arugments for the contractType
|
||||
// first upload the contractmetadata
|
||||
const contractURI = await this.storage.upload(parsedMetadata);
|
||||
// the get the deploy arguments
|
||||
const constructorParams = await factory.getDeployArguments(
|
||||
contractType,
|
||||
parsedMetadata,
|
||||
contractURI,
|
||||
);
|
||||
|
||||
return this.deployReleasedContract(
|
||||
// 0xdd99b75f095d0c4d5112aCe938e4e6ed962fb024 === deployer.thirdweb.eth
|
||||
"0xdd99b75f095d0c4d5112aCe938e4e6ed962fb024",
|
||||
contractName,
|
||||
constructorParams,
|
||||
version,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @param contractType
|
||||
*/
|
||||
public async getLatestBuiltInContractVersion<
|
||||
TContractType extends PrebuiltContractType,
|
||||
>(contractType: TContractType) {
|
||||
const factory = await this.getFactory();
|
||||
return await factory.getLatestVersion(contractType);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -415,13 +477,14 @@ export class ContractDeployer extends RPCConnectionHandler {
|
||||
releaserAddress: string,
|
||||
contractName: string,
|
||||
constructorParams: any[],
|
||||
version = "latest",
|
||||
): Promise<string> {
|
||||
const release = await new ThirdwebSDK("polygon")
|
||||
.getPublisher()
|
||||
.getLatest(releaserAddress, contractName);
|
||||
.getVersion(releaserAddress, contractName, version);
|
||||
if (!release) {
|
||||
throw new Error(
|
||||
`No release found for '${contractName}' by ${releaserAddress}`,
|
||||
`No release found for '${contractName}' at version '${version}' by '${releaserAddress}'`,
|
||||
);
|
||||
}
|
||||
return await this.deployContractFromUri(
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { EventType } from "../../constants";
|
||||
import { ContractEvent, EventQueryFilter } from "../../types/index";
|
||||
import { ContractEvent, EventQueryOptions } from "../../types/index";
|
||||
import { ContractWrapper } from "./contract-wrapper";
|
||||
import { EventFragment } from "@ethersproject/abi";
|
||||
import { BaseContract, Event, providers } from "ethers";
|
||||
@@ -198,7 +198,7 @@ export class ContractEvents<TContract extends BaseContract> {
|
||||
* @returns The event objects of the events emitted with event names and data for each event
|
||||
*/
|
||||
public async getAllEvents<TEvent extends Record<string, any>>(
|
||||
filters: EventQueryFilter = {
|
||||
filters: Omit<EventQueryOptions, "filters"> = {
|
||||
fromBlock: 0,
|
||||
toBlock: "latest",
|
||||
order: "desc",
|
||||
@@ -226,23 +226,34 @@ export class ContractEvents<TContract extends BaseContract> {
|
||||
* ```javascript
|
||||
* // The name of the event to get logs for
|
||||
* const eventName = "Transfer";
|
||||
* // Optionally pass in filters to limit the blocks from which events are retrieved
|
||||
* const filters = {
|
||||
*
|
||||
* // Optionally pass in options to limit the blocks from which events are retrieved
|
||||
* const options = {
|
||||
* fromBlock: 0,
|
||||
* toBlock: 1000000,
|
||||
* }
|
||||
* const events = await contract.events.getEvents(eventName, filters);
|
||||
* toBlock: 1000000, // can also pass "latest"
|
||||
* order: "desc",
|
||||
* // Configure event filters (filter on indexed event parameters)
|
||||
* filters: {
|
||||
* from: "0x...",
|
||||
* to: "0x..."
|
||||
* }
|
||||
* };
|
||||
*
|
||||
* const events = await contract.events.getEvents(eventName, options);
|
||||
* console.log(events[0].eventName);
|
||||
* console.log(events[0].data);
|
||||
* ```
|
||||
*
|
||||
* @param eventName - The name of the event to get logs for
|
||||
* @param filters - Specify the from and to block numbers to get events for, defaults to all blocks. @see EventQueryFilter
|
||||
* @param options - Specify the from and to block numbers to get events for, defaults to all blocks. @see EventQueryOptions
|
||||
* @returns The requested event objects with event data
|
||||
*/
|
||||
public async getEvents<TEvent extends Record<string, any>>(
|
||||
public async getEvents<
|
||||
TEvent extends Record<string, any> = Record<string, any>,
|
||||
TFilter extends Record<string, any> = Record<string, any>,
|
||||
>(
|
||||
eventName: string,
|
||||
filters: EventQueryFilter = {
|
||||
options: EventQueryOptions<TFilter> = {
|
||||
fromBlock: 0,
|
||||
toBlock: "latest",
|
||||
order: "desc",
|
||||
@@ -251,24 +262,32 @@ export class ContractEvents<TContract extends BaseContract> {
|
||||
const event = this.contractWrapper.readContract.interface.getEvent(
|
||||
eventName as string,
|
||||
);
|
||||
const filter = this.contractWrapper.readContract.filters[event.name];
|
||||
|
||||
const eventInterface =
|
||||
this.contractWrapper.readContract.interface.getEvent(eventName);
|
||||
const args = options.filters
|
||||
? eventInterface.inputs.map((e) => (options.filters as TFilter)[e.name])
|
||||
: [];
|
||||
const filter = this.contractWrapper.readContract.filters[event.name](
|
||||
...args,
|
||||
);
|
||||
|
||||
const events = await this.contractWrapper.readContract.queryFilter(
|
||||
filter(),
|
||||
filters.fromBlock,
|
||||
filters.toBlock,
|
||||
filter,
|
||||
options.fromBlock,
|
||||
options.toBlock,
|
||||
);
|
||||
|
||||
const orderedEvents = events.sort((a, b) => {
|
||||
return filters.order === "desc"
|
||||
return options.order === "desc"
|
||||
? b.blockNumber - a.blockNumber
|
||||
: a.blockNumber - b.blockNumber;
|
||||
});
|
||||
|
||||
return this.parseEvents(orderedEvents);
|
||||
return this.parseEvents<TEvent>(orderedEvents);
|
||||
}
|
||||
|
||||
private parseEvents<TEvent extends Record<string, any>>(
|
||||
private parseEvents<TEvent = Record<string, any>>(
|
||||
events: Event[],
|
||||
): ContractEvent<TEvent>[] {
|
||||
return events.map((e) => {
|
||||
|
||||
@@ -195,13 +195,13 @@ export class ContractPublisher extends RPCConnectionHandler {
|
||||
* @internal
|
||||
* TODO clean this up (see method above, too)
|
||||
*/
|
||||
public async resolveReleasesFromAddress(address: string) {
|
||||
public async resolveContractUriFromAddress(address: string) {
|
||||
const contractUri = await resolveContractUriFromAddress(
|
||||
address,
|
||||
this.getProvider(),
|
||||
);
|
||||
invariant(contractUri, "Could not resolve contract URI from address");
|
||||
return await this.resolvePublishMetadataFromCompilerMetadata(contractUri);
|
||||
return contractUri;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -293,6 +293,30 @@ export class ContractPublisher extends RPCConnectionHandler {
|
||||
return contractStructs.map((d) => this.toPublishedContract(d));
|
||||
}
|
||||
|
||||
public async getVersion(
|
||||
publisherAddress: string,
|
||||
contractId: string,
|
||||
version = "latest",
|
||||
): Promise<PublishedContract | undefined> {
|
||||
if (version === "latest") {
|
||||
return this.getLatest(publisherAddress, contractId);
|
||||
}
|
||||
const allVersions = await this.getAllVersions(publisherAddress, contractId);
|
||||
// get the metadata for each version
|
||||
const versionMetadata = await Promise.all(
|
||||
allVersions.map((contract) => this.fetchPublishedContractInfo(contract)),
|
||||
);
|
||||
// find the version that matches the version string
|
||||
const versionMatch = versionMetadata.find(
|
||||
(metadata) => metadata.publishedMetadata.version === version,
|
||||
);
|
||||
invariant(versionMatch, "Contract version not found");
|
||||
// match the version back to the contract based on the release timestamp
|
||||
return allVersions.find(
|
||||
(contract) => contract.timestamp === versionMatch.publishedTimestamp,
|
||||
);
|
||||
}
|
||||
|
||||
public async getLatest(
|
||||
publisherAddress: string,
|
||||
contractId: string,
|
||||
|
||||
@@ -18,7 +18,7 @@ import { UploadProgressEvent } from "../../types/events";
|
||||
import { TransactionResult, TransactionResultWithId } from "../index";
|
||||
import { ContractWrapper } from "./contract-wrapper";
|
||||
import type {
|
||||
DropERC721,
|
||||
DropERC721_V3,
|
||||
IThirdwebContract,
|
||||
SignatureDrop,
|
||||
} from "@thirdweb-dev/contracts-js";
|
||||
@@ -33,7 +33,7 @@ import { BigNumber, BigNumberish, ethers } from "ethers";
|
||||
*/
|
||||
export class DelayedReveal<
|
||||
T extends
|
||||
| DropERC721
|
||||
| DropERC721_V3
|
||||
| BaseDelayedRevealERC721
|
||||
| SignatureDrop
|
||||
| BaseDelayedRevealERC1155,
|
||||
@@ -246,7 +246,9 @@ export class DelayedReveal<
|
||||
return this.contractWrapper.readContract.getBatchIdAtIndex(i);
|
||||
}
|
||||
|
||||
if (hasFunction<DropERC721>("baseURIIndices", this.contractWrapper)) {
|
||||
if (
|
||||
hasFunction<DropERC721_V3>("baseURIIndices", this.contractWrapper)
|
||||
) {
|
||||
return this.contractWrapper.readContract.baseURIIndices(i);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
import { AmountSchema } from "../../../core/schema/shared";
|
||||
import { includesErrorMessage } from "../../common";
|
||||
import {
|
||||
getClaimerProofs,
|
||||
abstractContractModelToLegacy,
|
||||
abstractContractModelToNew,
|
||||
convertQuantityToBigNumber,
|
||||
fetchSnapshotEntryForAddress,
|
||||
legacyContractModelToAbstract,
|
||||
newContractModelToAbstract,
|
||||
prepareClaim,
|
||||
processClaimConditionInputs,
|
||||
transformResultToClaimCondition,
|
||||
@@ -11,32 +17,46 @@ import {
|
||||
detectContractFeature,
|
||||
hasFunction,
|
||||
} from "../../common/feature-detection";
|
||||
import { SnapshotFormatVersion } from "../../common/sharded-merkle-tree";
|
||||
import { isNode } from "../../common/utils";
|
||||
import { NATIVE_TOKEN_ADDRESS } from "../../constants/index";
|
||||
import { ClaimEligibility } from "../../enums";
|
||||
import { AmountSchema } from "../../schema";
|
||||
import {
|
||||
AbstractClaimConditionContractStruct,
|
||||
SnapshotEntryWithProof,
|
||||
} from "../../schema";
|
||||
import {
|
||||
Amount,
|
||||
ClaimCondition,
|
||||
ClaimConditionFetchOptions,
|
||||
ClaimConditionInput,
|
||||
ClaimOptions,
|
||||
ClaimVerification,
|
||||
} from "../../types";
|
||||
import { BaseClaimConditionERC721, BaseDropERC20 } from "../../types/eips";
|
||||
import {
|
||||
BaseClaimConditionERC721,
|
||||
BaseDropERC20,
|
||||
PrebuiltNFTDrop,
|
||||
PrebuiltTokenDrop,
|
||||
} from "../../types/eips";
|
||||
import { TransactionResult } from "../types";
|
||||
import { TransactionTask } from "./TransactionTask";
|
||||
import { ContractMetadata } from "./contract-metadata";
|
||||
import { ContractWrapper } from "./contract-wrapper";
|
||||
import type {
|
||||
ContractMetadata as ContractMetadataContract,
|
||||
DropERC20,
|
||||
DropERC721,
|
||||
Drop,
|
||||
DropERC20_V2,
|
||||
DropERC721_V3,
|
||||
DropSinglePhase,
|
||||
DropSinglePhase_V1,
|
||||
IDropSinglePhase_V1,
|
||||
IERC20,
|
||||
IERC20Metadata,
|
||||
SignatureDrop,
|
||||
} from "@thirdweb-dev/contracts-js";
|
||||
import ERC20Abi from "@thirdweb-dev/contracts-js/dist/abis/IERC20.json";
|
||||
import { IDropClaimCondition } from "@thirdweb-dev/contracts-js/dist/declarations/src/DropERC20";
|
||||
import type { IDropClaimCondition_V2 } from "@thirdweb-dev/contracts-js/dist/declarations/src/IDropERC20_V2";
|
||||
import type { IDropSinglePhase } from "@thirdweb-dev/contracts-js/src/DropSinglePhase";
|
||||
import type { IClaimCondition } from "@thirdweb-dev/contracts-js/src/IDrop";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
import { BigNumber, BigNumberish, constants, ethers, utils } from "ethers";
|
||||
import deepEqual from "fast-deep-equal";
|
||||
@@ -47,11 +67,10 @@ import deepEqual from "fast-deep-equal";
|
||||
*/
|
||||
export class DropClaimConditions<
|
||||
TContract extends
|
||||
| DropERC721
|
||||
| DropERC20
|
||||
| PrebuiltNFTDrop
|
||||
| PrebuiltTokenDrop
|
||||
| BaseClaimConditionERC721
|
||||
| BaseDropERC20
|
||||
| SignatureDrop,
|
||||
| BaseDropERC20,
|
||||
> {
|
||||
private contractWrapper;
|
||||
private metadata;
|
||||
@@ -91,13 +110,37 @@ export class DropClaimConditions<
|
||||
);
|
||||
}
|
||||
|
||||
private async get(): Promise<IDropClaimCondition.ClaimConditionStructOutput> {
|
||||
if (this.isSinglePhaseDropContract(this.contractWrapper)) {
|
||||
return (await this.contractWrapper.readContract.claimCondition()) as IDropClaimCondition.ClaimConditionStructOutput;
|
||||
} else if (this.isMultiPhaseDropContract(this.contractWrapper)) {
|
||||
private async get(
|
||||
conditionId?: BigNumberish,
|
||||
): Promise<AbstractClaimConditionContractStruct> {
|
||||
if (this.isLegacySinglePhaseDrop(this.contractWrapper)) {
|
||||
const contractModel =
|
||||
(await this.contractWrapper.readContract.claimCondition()) as IDropClaimCondition_V2.ClaimConditionStructOutput;
|
||||
return legacyContractModelToAbstract(contractModel);
|
||||
} else if (this.isLegacyMultiPhaseDrop(this.contractWrapper)) {
|
||||
const id =
|
||||
await this.contractWrapper.readContract.getActiveClaimConditionId();
|
||||
return await this.contractWrapper.readContract.getClaimConditionById(id);
|
||||
conditionId !== undefined
|
||||
? conditionId
|
||||
: await this.contractWrapper.readContract.getActiveClaimConditionId();
|
||||
const contractModel =
|
||||
(await this.contractWrapper.readContract.getClaimConditionById(
|
||||
id,
|
||||
)) as IDropClaimCondition_V2.ClaimConditionStructOutput;
|
||||
return legacyContractModelToAbstract(contractModel);
|
||||
} else if (this.isNewSinglePhaseDrop(this.contractWrapper)) {
|
||||
const contractModel =
|
||||
(await this.contractWrapper.readContract.claimCondition()) as IClaimCondition.ClaimConditionStructOutput;
|
||||
return newContractModelToAbstract(contractModel);
|
||||
} else if (this.isNewMultiphaseDrop(this.contractWrapper)) {
|
||||
const id =
|
||||
conditionId !== undefined
|
||||
? conditionId
|
||||
: await this.contractWrapper.readContract.getActiveClaimConditionId();
|
||||
const contractModel =
|
||||
(await this.contractWrapper.readContract.getClaimConditionById(
|
||||
id,
|
||||
)) as IClaimCondition.ClaimConditionStruct;
|
||||
return newContractModelToAbstract(contractModel);
|
||||
} else {
|
||||
throw new Error("Contract does not support claim conditions");
|
||||
}
|
||||
@@ -111,7 +154,10 @@ export class DropClaimConditions<
|
||||
public async getAll(
|
||||
options?: ClaimConditionFetchOptions,
|
||||
): Promise<ClaimCondition[]> {
|
||||
if (this.isMultiPhaseDropContract(this.contractWrapper)) {
|
||||
if (
|
||||
this.isLegacyMultiPhaseDrop(this.contractWrapper) ||
|
||||
this.isNewMultiphaseDrop(this.contractWrapper)
|
||||
) {
|
||||
const claimCondition =
|
||||
(await this.contractWrapper.readContract.claimCondition()) as {
|
||||
currentStartId: BigNumber;
|
||||
@@ -119,11 +165,9 @@ export class DropClaimConditions<
|
||||
};
|
||||
const startId = claimCondition.currentStartId.toNumber();
|
||||
const count = claimCondition.count.toNumber();
|
||||
const conditions: IDropClaimCondition.ClaimConditionStructOutput[] = [];
|
||||
const conditions: AbstractClaimConditionContractStruct[] = [];
|
||||
for (let i = startId; i < startId + count; i++) {
|
||||
conditions.push(
|
||||
await this.contractWrapper.readContract.getClaimConditionById(i),
|
||||
);
|
||||
conditions.push(await this.get(i));
|
||||
}
|
||||
const metadata = await this.metadata.get();
|
||||
const decimals = await this.getTokenDecimals();
|
||||
@@ -233,82 +277,176 @@ export class DropClaimConditions<
|
||||
const merkleRootArray = ethers.utils.stripZeros(
|
||||
claimCondition.merkleRootHash,
|
||||
);
|
||||
if (merkleRootArray.length > 0) {
|
||||
const merkleLower = claimCondition.merkleRootHash.toString();
|
||||
const metadata = await this.metadata.get();
|
||||
const proofs = await getClaimerProofs(
|
||||
addressToCheck,
|
||||
merkleLower,
|
||||
await this.getTokenDecimals(),
|
||||
metadata.merkle,
|
||||
this.storage,
|
||||
);
|
||||
|
||||
try {
|
||||
let validMerkleProof;
|
||||
if (this.isMultiPhaseDropContract(this.contractWrapper)) {
|
||||
activeConditionIndex =
|
||||
await this.contractWrapper.readContract.getActiveClaimConditionId();
|
||||
// legacy verifyClaimerMerkleProofs function
|
||||
[validMerkleProof] =
|
||||
await this.contractWrapper.readContract.verifyClaimMerkleProof(
|
||||
activeConditionIndex,
|
||||
addressToCheck,
|
||||
quantity,
|
||||
proofs.proof,
|
||||
proofs.maxClaimable,
|
||||
);
|
||||
} else if (this.isSinglePhaseDropContract(this.contractWrapper)) {
|
||||
[validMerkleProof] =
|
||||
await this.contractWrapper.readContract.verifyClaimMerkleProof(
|
||||
addressToCheck,
|
||||
quantity,
|
||||
{
|
||||
proof: proofs.proof,
|
||||
maxQuantityInAllowlist: proofs.maxClaimable,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
if (!validMerkleProof) {
|
||||
reasons.push(ClaimEligibility.AddressNotAllowed);
|
||||
return reasons;
|
||||
}
|
||||
} catch (e) {
|
||||
const hasAllowList = merkleRootArray.length > 0;
|
||||
let allowListEntry: SnapshotEntryWithProof | null = null;
|
||||
if (hasAllowList) {
|
||||
allowListEntry = await this.getClaimerProofs(addressToCheck);
|
||||
if (
|
||||
!allowListEntry &&
|
||||
(this.isLegacySinglePhaseDrop(this.contractWrapper) ||
|
||||
this.isLegacyMultiPhaseDrop(this.contractWrapper))
|
||||
) {
|
||||
// exclusive allowlist behavior
|
||||
reasons.push(ClaimEligibility.AddressNotAllowed);
|
||||
return reasons;
|
||||
}
|
||||
|
||||
if (allowListEntry) {
|
||||
try {
|
||||
const claimVerification = await this.prepareClaim(
|
||||
quantity,
|
||||
false,
|
||||
decimals,
|
||||
addressToCheck,
|
||||
);
|
||||
|
||||
let validMerkleProof;
|
||||
if (this.isLegacyMultiPhaseDrop(this.contractWrapper)) {
|
||||
activeConditionIndex =
|
||||
await this.contractWrapper.readContract.getActiveClaimConditionId();
|
||||
// legacy verifyClaimerMerkleProofs function
|
||||
[validMerkleProof] =
|
||||
await this.contractWrapper.readContract.verifyClaimMerkleProof(
|
||||
activeConditionIndex,
|
||||
addressToCheck,
|
||||
quantity,
|
||||
claimVerification.proofs,
|
||||
claimVerification.maxClaimable,
|
||||
);
|
||||
if (!validMerkleProof) {
|
||||
reasons.push(ClaimEligibility.AddressNotAllowed);
|
||||
return reasons;
|
||||
}
|
||||
} else if (this.isLegacySinglePhaseDrop(this.contractWrapper)) {
|
||||
[validMerkleProof] =
|
||||
await this.contractWrapper.readContract.verifyClaimMerkleProof(
|
||||
addressToCheck,
|
||||
quantity,
|
||||
{
|
||||
proof: claimVerification.proofs,
|
||||
maxQuantityInAllowlist: claimVerification.maxClaimable,
|
||||
},
|
||||
);
|
||||
if (!validMerkleProof) {
|
||||
reasons.push(ClaimEligibility.AddressNotAllowed);
|
||||
return reasons;
|
||||
}
|
||||
} else if (this.isNewSinglePhaseDrop(this.contractWrapper)) {
|
||||
await this.contractWrapper.readContract.verifyClaim(
|
||||
addressToCheck,
|
||||
quantity,
|
||||
claimVerification.currencyAddress,
|
||||
claimVerification.price,
|
||||
{
|
||||
proof: claimVerification.proofs,
|
||||
quantityLimitPerWallet: claimVerification.maxClaimable,
|
||||
currency: claimVerification.currencyAddressInProof,
|
||||
pricePerToken: claimVerification.priceInProof,
|
||||
} as IDropSinglePhase.AllowlistProofStruct,
|
||||
);
|
||||
if (
|
||||
(convertQuantityToBigNumber(
|
||||
claimCondition.maxClaimablePerWallet,
|
||||
decimals,
|
||||
).eq(0) &&
|
||||
claimVerification.maxClaimable ===
|
||||
ethers.constants.MaxUint256) ||
|
||||
claimVerification.maxClaimable === BigNumber.from(0)
|
||||
) {
|
||||
reasons.push(ClaimEligibility.AddressNotAllowed);
|
||||
return reasons;
|
||||
}
|
||||
} else if (this.isNewMultiphaseDrop(this.contractWrapper)) {
|
||||
activeConditionIndex =
|
||||
await this.contractWrapper.readContract.getActiveClaimConditionId();
|
||||
await this.contractWrapper.readContract.verifyClaim(
|
||||
activeConditionIndex,
|
||||
addressToCheck,
|
||||
quantity,
|
||||
claimVerification.currencyAddress,
|
||||
claimVerification.price,
|
||||
{
|
||||
proof: claimVerification.proofs,
|
||||
quantityLimitPerWallet: claimVerification.maxClaimable,
|
||||
currency: claimVerification.currencyAddressInProof,
|
||||
pricePerToken: claimVerification.priceInProof,
|
||||
} as IDropSinglePhase.AllowlistProofStruct,
|
||||
);
|
||||
if (
|
||||
(convertQuantityToBigNumber(
|
||||
claimCondition.maxClaimablePerWallet,
|
||||
decimals,
|
||||
).eq(0) &&
|
||||
claimVerification.maxClaimable ===
|
||||
ethers.constants.MaxUint256) ||
|
||||
claimVerification.maxClaimable === BigNumber.from(0)
|
||||
) {
|
||||
reasons.push(ClaimEligibility.AddressNotAllowed);
|
||||
return reasons;
|
||||
}
|
||||
}
|
||||
} catch (e: any) {
|
||||
console.warn(
|
||||
"Merkle proof verification failed:",
|
||||
"reason" in e ? e.reason : e,
|
||||
);
|
||||
reasons.push(ClaimEligibility.AddressNotAllowed);
|
||||
return reasons;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check for claim timestamp between claims
|
||||
let [lastClaimedTimestamp, timestampForNextClaim] = [
|
||||
BigNumber.from(0),
|
||||
BigNumber.from(0),
|
||||
];
|
||||
if (this.isMultiPhaseDropContract(this.contractWrapper)) {
|
||||
activeConditionIndex =
|
||||
await this.contractWrapper.readContract.getActiveClaimConditionId();
|
||||
[lastClaimedTimestamp, timestampForNextClaim] =
|
||||
await this.contractWrapper.readContract.getClaimTimestamp(
|
||||
activeConditionIndex,
|
||||
addressToCheck,
|
||||
);
|
||||
} else if (this.isSinglePhaseDropContract(this.contractWrapper)) {
|
||||
// check for claim timestamp between claims
|
||||
[lastClaimedTimestamp, timestampForNextClaim] =
|
||||
await this.contractWrapper.readContract.getClaimTimestamp(
|
||||
addressToCheck,
|
||||
);
|
||||
// TODO (cc) check for max claimable per wallet and how much each wallet has claimed
|
||||
if (
|
||||
this.isNewSinglePhaseDrop(this.contractWrapper) ||
|
||||
this.isNewMultiphaseDrop(this.contractWrapper)
|
||||
) {
|
||||
if (!hasAllowList || (hasAllowList && !allowListEntry)) {
|
||||
if (
|
||||
convertQuantityToBigNumber(
|
||||
claimCondition.maxClaimablePerWallet,
|
||||
decimals,
|
||||
).eq(0)
|
||||
) {
|
||||
reasons.push(ClaimEligibility.AddressNotAllowed);
|
||||
return reasons;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const now = BigNumber.from(Date.now()).div(1000);
|
||||
// check for claim timestamp between claims (ONLY FOR LEGACY)
|
||||
if (
|
||||
this.isLegacySinglePhaseDrop(this.contractWrapper) ||
|
||||
this.isLegacyMultiPhaseDrop(this.contractWrapper)
|
||||
) {
|
||||
let [lastClaimedTimestamp, timestampForNextClaim] = [
|
||||
BigNumber.from(0),
|
||||
BigNumber.from(0),
|
||||
];
|
||||
if (this.isLegacyMultiPhaseDrop(this.contractWrapper)) {
|
||||
activeConditionIndex =
|
||||
await this.contractWrapper.readContract.getActiveClaimConditionId();
|
||||
[lastClaimedTimestamp, timestampForNextClaim] =
|
||||
await this.contractWrapper.readContract.getClaimTimestamp(
|
||||
activeConditionIndex,
|
||||
addressToCheck,
|
||||
);
|
||||
} else if (this.isLegacySinglePhaseDrop(this.contractWrapper)) {
|
||||
// check for claim timestamp between claims
|
||||
[lastClaimedTimestamp, timestampForNextClaim] =
|
||||
await this.contractWrapper.readContract.getClaimTimestamp(
|
||||
addressToCheck,
|
||||
);
|
||||
}
|
||||
|
||||
if (lastClaimedTimestamp.gt(0) && now.lt(timestampForNextClaim)) {
|
||||
// contract will return MaxUint256 if user has already claimed and cannot claim again
|
||||
if (timestampForNextClaim.eq(constants.MaxUint256)) {
|
||||
reasons.push(ClaimEligibility.AlreadyClaimed);
|
||||
} else {
|
||||
reasons.push(ClaimEligibility.WaitBeforeNextClaimTransaction);
|
||||
const now = BigNumber.from(Date.now()).div(1000);
|
||||
if (lastClaimedTimestamp.gt(0) && now.lt(timestampForNextClaim)) {
|
||||
// contract will return MaxUint256 if user has already claimed and cannot claim again
|
||||
if (timestampForNextClaim.eq(constants.MaxUint256)) {
|
||||
reasons.push(ClaimEligibility.AlreadyClaimed);
|
||||
} else {
|
||||
reasons.push(ClaimEligibility.WaitBeforeNextClaimTransaction);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -339,6 +477,33 @@ export class DropClaimConditions<
|
||||
return reasons;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns allow list information and merkle proofs for the given address.
|
||||
* @param claimerAddress - the claimer address
|
||||
* @param claimConditionId - optional the claim condition id to get the proofs for
|
||||
*/
|
||||
public async getClaimerProofs(
|
||||
claimerAddress: string,
|
||||
claimConditionId?: BigNumberish,
|
||||
): Promise<SnapshotEntryWithProof | null> {
|
||||
const claimCondition = await this.get(claimConditionId);
|
||||
const merkleRoot = claimCondition.merkleRoot;
|
||||
const merkleRootArray = ethers.utils.stripZeros(merkleRoot);
|
||||
if (merkleRootArray.length > 0) {
|
||||
const metadata = await this.metadata.get();
|
||||
return await fetchSnapshotEntryForAddress(
|
||||
claimerAddress,
|
||||
merkleRoot.toString(),
|
||||
metadata.merkle,
|
||||
this.contractWrapper.getProvider(),
|
||||
this.storage,
|
||||
this.getSnapshotFormatVersion(),
|
||||
);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/** ***************************************
|
||||
* WRITE FUNCTIONS
|
||||
*****************************************/
|
||||
@@ -383,16 +548,19 @@ export class DropClaimConditions<
|
||||
resetClaimEligibilityForAll = false,
|
||||
): Promise<TransactionResult> {
|
||||
let claimConditionsProcessed = claimConditionInputs;
|
||||
if (this.isSinglePhaseDropContract(this.contractWrapper)) {
|
||||
if (
|
||||
this.isLegacySinglePhaseDrop(this.contractWrapper) ||
|
||||
this.isNewSinglePhaseDrop(this.contractWrapper)
|
||||
) {
|
||||
resetClaimEligibilityForAll = true;
|
||||
if (claimConditionInputs.length === 0) {
|
||||
claimConditionsProcessed = [
|
||||
{
|
||||
startTime: new Date(0),
|
||||
currencyAddress: NATIVE_TOKEN_ADDRESS,
|
||||
currencyAddress: ethers.constants.AddressZero,
|
||||
price: 0,
|
||||
maxQuantity: 0,
|
||||
quantityLimitPerTransaction: 0,
|
||||
maxClaimableSupply: 0,
|
||||
maxClaimablePerWallet: 0,
|
||||
waitInSeconds: 0,
|
||||
merkleRootHash: utils.hexZeroPad([0], 32),
|
||||
snapshot: [],
|
||||
@@ -404,6 +572,48 @@ export class DropClaimConditions<
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// if using new snapshot format, make sure that maxClaimablePerWallet is set if allowlist is set as well
|
||||
if (
|
||||
this.isNewSinglePhaseDrop(this.contractWrapper) ||
|
||||
this.isNewMultiphaseDrop(this.contractWrapper)
|
||||
) {
|
||||
claimConditionsProcessed.forEach((cc) => {
|
||||
if (
|
||||
cc.snapshot &&
|
||||
cc.snapshot.length > 0 &&
|
||||
(cc.maxClaimablePerWallet === undefined ||
|
||||
cc.maxClaimablePerWallet === "unlimited")
|
||||
) {
|
||||
throw new Error(
|
||||
"maxClaimablePerWallet must be set to a specific value when an allowlist is set.\n" +
|
||||
"Example: Set it to 0 to only allow addresses in the allowlist to claim the amount specified in the allowlist.\n" +
|
||||
"contract.claimConditions.set([{ snapshot: [{ address: '0x...', maxClaimable: 1 }], maxClaimablePerWallet: 0 }])",
|
||||
);
|
||||
}
|
||||
if (
|
||||
cc.snapshot &&
|
||||
cc.snapshot.length > 0 &&
|
||||
cc.maxClaimablePerWallet?.toString() === "0" &&
|
||||
cc.snapshot
|
||||
.map((s) => {
|
||||
if (typeof s === "string") {
|
||||
return 0;
|
||||
} else {
|
||||
return Number(s.maxClaimable?.toString() || 0);
|
||||
}
|
||||
})
|
||||
.reduce((acc, current) => {
|
||||
return acc + current;
|
||||
}, 0) === 0
|
||||
) {
|
||||
throw new Error(
|
||||
"maxClaimablePerWallet is set to 0, and all addresses in the allowlist have max claimable 0. This means that no one can claim.",
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// process inputs
|
||||
const { snapshotInfos, sortedConditions } =
|
||||
await processClaimConditionInputs(
|
||||
@@ -411,6 +621,7 @@ export class DropClaimConditions<
|
||||
await this.getTokenDecimals(),
|
||||
this.contractWrapper.getProvider(),
|
||||
this.storage,
|
||||
this.getSnapshotFormatVersion(),
|
||||
);
|
||||
|
||||
const merkleInfo: { [key: string]: string } = {};
|
||||
@@ -431,6 +642,8 @@ export class DropClaimConditions<
|
||||
mergedMetadata,
|
||||
);
|
||||
|
||||
// TODO (cc) we could write the merkle tree info on the claim condition metadata instead
|
||||
// TODO (cc) but we still need to maintain the behavior here for older contracts
|
||||
if (
|
||||
hasFunction<ContractMetadataContract>(
|
||||
"setContractURI",
|
||||
@@ -451,17 +664,31 @@ export class DropClaimConditions<
|
||||
}
|
||||
|
||||
const cw = this.contractWrapper;
|
||||
if (this.isSinglePhaseDropContract(cw)) {
|
||||
if (this.isLegacySinglePhaseDrop(cw)) {
|
||||
encoded.push(
|
||||
cw.readContract.interface.encodeFunctionData("setClaimConditions", [
|
||||
sortedConditions[0],
|
||||
abstractContractModelToLegacy(sortedConditions[0]),
|
||||
resetClaimEligibilityForAll,
|
||||
]),
|
||||
);
|
||||
} else if (this.isMultiPhaseDropContract(cw)) {
|
||||
} else if (this.isLegacyMultiPhaseDrop(cw)) {
|
||||
encoded.push(
|
||||
cw.readContract.interface.encodeFunctionData("setClaimConditions", [
|
||||
sortedConditions,
|
||||
sortedConditions.map(abstractContractModelToLegacy),
|
||||
resetClaimEligibilityForAll,
|
||||
]),
|
||||
);
|
||||
} else if (this.isNewSinglePhaseDrop(cw)) {
|
||||
encoded.push(
|
||||
cw.readContract.interface.encodeFunctionData("setClaimConditions", [
|
||||
abstractContractModelToNew(sortedConditions[0]),
|
||||
resetClaimEligibilityForAll,
|
||||
]),
|
||||
);
|
||||
} else if (this.isNewMultiphaseDrop(cw)) {
|
||||
encoded.push(
|
||||
cw.readContract.interface.encodeFunctionData("setClaimConditions", [
|
||||
sortedConditions.map(abstractContractModelToNew),
|
||||
resetClaimEligibilityForAll,
|
||||
]),
|
||||
);
|
||||
@@ -515,8 +742,13 @@ export class DropClaimConditions<
|
||||
quantity: BigNumberish,
|
||||
checkERC20Allowance: boolean,
|
||||
decimals = 0,
|
||||
address?: string,
|
||||
): Promise<ClaimVerification> {
|
||||
const addressToClaim = address
|
||||
? address
|
||||
: await this.contractWrapper.getSignerAddress();
|
||||
return prepareClaim(
|
||||
addressToClaim,
|
||||
quantity,
|
||||
await this.getActive(),
|
||||
async () => (await this.metadata.get()).merkle,
|
||||
@@ -524,21 +756,147 @@ export class DropClaimConditions<
|
||||
this.contractWrapper,
|
||||
this.storage,
|
||||
checkERC20Allowance,
|
||||
this.getSnapshotFormatVersion(),
|
||||
);
|
||||
}
|
||||
|
||||
private isSinglePhaseDropContract(
|
||||
public async getClaimArguments(
|
||||
destinationAddress: string,
|
||||
quantity: BigNumberish,
|
||||
claimVerification: ClaimVerification,
|
||||
): Promise<any[]> {
|
||||
if (this.isLegacyMultiPhaseDrop(this.contractWrapper)) {
|
||||
return [
|
||||
destinationAddress,
|
||||
quantity,
|
||||
claimVerification.currencyAddress,
|
||||
claimVerification.price,
|
||||
claimVerification.proofs,
|
||||
claimVerification.maxClaimable,
|
||||
];
|
||||
} else if (this.isLegacySinglePhaseDrop(this.contractWrapper)) {
|
||||
return [
|
||||
destinationAddress,
|
||||
quantity,
|
||||
claimVerification.currencyAddress,
|
||||
claimVerification.price,
|
||||
{
|
||||
proof: claimVerification.proofs,
|
||||
maxQuantityInAllowlist: claimVerification.maxClaimable,
|
||||
} as IDropSinglePhase_V1.AllowlistProofStruct,
|
||||
ethers.utils.toUtf8Bytes(""),
|
||||
];
|
||||
}
|
||||
return [
|
||||
destinationAddress,
|
||||
quantity,
|
||||
claimVerification.currencyAddress,
|
||||
claimVerification.price,
|
||||
{
|
||||
proof: claimVerification.proofs,
|
||||
quantityLimitPerWallet: claimVerification.maxClaimable,
|
||||
pricePerToken: claimVerification.priceInProof,
|
||||
currency: claimVerification.currencyAddressInProof,
|
||||
} as IDropSinglePhase.AllowlistProofStruct,
|
||||
ethers.utils.toUtf8Bytes(""),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a claim transaction without executing it.
|
||||
* This is useful for estimating the gas cost of a claim transaction, overriding transaction options and having fine grained control over the transaction execution.
|
||||
* @param destinationAddress
|
||||
* @param quantity
|
||||
* @param options
|
||||
*/
|
||||
public async getClaimTransaction(
|
||||
destinationAddress: string,
|
||||
quantity: BigNumberish,
|
||||
options?: ClaimOptions,
|
||||
): Promise<TransactionTask> {
|
||||
if (options?.pricePerToken) {
|
||||
throw new Error(
|
||||
"Price per token is be set via claim conditions by calling `contract.erc721.claimConditions.set()`",
|
||||
);
|
||||
}
|
||||
const claimVerification = await this.prepareClaim(
|
||||
quantity,
|
||||
options?.checkERC20Allowance === undefined
|
||||
? true
|
||||
: options.checkERC20Allowance,
|
||||
await this.getTokenDecimals(),
|
||||
);
|
||||
|
||||
return TransactionTask.make({
|
||||
contractWrapper: this.contractWrapper,
|
||||
functionName: "claim",
|
||||
args: await this.getClaimArguments(
|
||||
destinationAddress,
|
||||
quantity,
|
||||
claimVerification,
|
||||
),
|
||||
overrides: claimVerification.overrides,
|
||||
});
|
||||
}
|
||||
|
||||
isNewSinglePhaseDrop(
|
||||
contractWrapper: ContractWrapper<any>,
|
||||
): contractWrapper is ContractWrapper<DropSinglePhase> {
|
||||
return !hasFunction<DropSinglePhase>(
|
||||
"getClaimConditionById",
|
||||
contractWrapper,
|
||||
return (
|
||||
detectContractFeature<DropSinglePhase>(
|
||||
contractWrapper,
|
||||
"ERC721ClaimConditionsV2",
|
||||
) ||
|
||||
detectContractFeature<DropSinglePhase>(
|
||||
contractWrapper,
|
||||
"ERC20ClaimConditionsV2",
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private isMultiPhaseDropContract(
|
||||
isNewMultiphaseDrop(
|
||||
contractWrapper: ContractWrapper<any>,
|
||||
): contractWrapper is ContractWrapper<DropERC721 | DropERC20> {
|
||||
return hasFunction<DropERC721>("getClaimConditionById", contractWrapper);
|
||||
): contractWrapper is ContractWrapper<Drop> {
|
||||
return (
|
||||
detectContractFeature<Drop>(contractWrapper, "ERC721ClaimPhasesV2") ||
|
||||
detectContractFeature<Drop>(contractWrapper, "ERC20ClaimPhasesV2")
|
||||
);
|
||||
}
|
||||
|
||||
isLegacySinglePhaseDrop(
|
||||
contractWrapper: ContractWrapper<any>,
|
||||
): contractWrapper is ContractWrapper<DropSinglePhase_V1> {
|
||||
return (
|
||||
detectContractFeature<DropSinglePhase_V1>(
|
||||
contractWrapper,
|
||||
"ERC721ClaimConditionsV1",
|
||||
) ||
|
||||
detectContractFeature<DropSinglePhase_V1>(
|
||||
contractWrapper,
|
||||
"ERC20ClaimConditionsV1",
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
isLegacyMultiPhaseDrop(
|
||||
contractWrapper: ContractWrapper<any>,
|
||||
): contractWrapper is ContractWrapper<DropERC721_V3 | DropERC20_V2> {
|
||||
return (
|
||||
detectContractFeature<DropERC721_V3 | DropERC20_V2>(
|
||||
contractWrapper,
|
||||
"ERC721ClaimPhasesV1",
|
||||
) ||
|
||||
detectContractFeature<DropERC721_V3 | DropERC20_V2>(
|
||||
contractWrapper,
|
||||
"ERC20ClaimPhasesV1",
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private getSnapshotFormatVersion(): SnapshotFormatVersion {
|
||||
return this.isLegacyMultiPhaseDrop(this.contractWrapper) ||
|
||||
this.isLegacySinglePhaseDrop(this.contractWrapper)
|
||||
? SnapshotFormatVersion.V1
|
||||
: SnapshotFormatVersion.V2;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,35 +1,55 @@
|
||||
import { includesErrorMessage } from "../../common";
|
||||
import {
|
||||
getClaimerProofs,
|
||||
abstractContractModelToLegacy,
|
||||
abstractContractModelToNew,
|
||||
fetchSnapshotEntryForAddress,
|
||||
legacyContractModelToAbstract,
|
||||
newContractModelToAbstract,
|
||||
prepareClaim,
|
||||
processClaimConditionInputs,
|
||||
transformResultToClaimCondition,
|
||||
updateExistingClaimConditions,
|
||||
} from "../../common/claim-conditions";
|
||||
import { isNativeToken } from "../../common/currency";
|
||||
import { hasFunction } from "../../common/feature-detection";
|
||||
import {
|
||||
detectContractFeature,
|
||||
hasFunction,
|
||||
} from "../../common/feature-detection";
|
||||
import { SnapshotFormatVersion } from "../../common/sharded-merkle-tree";
|
||||
import { isNode } from "../../common/utils";
|
||||
import { NATIVE_TOKEN_ADDRESS } from "../../constants/index";
|
||||
import { ClaimEligibility } from "../../enums";
|
||||
import {
|
||||
AbstractClaimConditionContractStruct,
|
||||
SnapshotEntryWithProof,
|
||||
} from "../../schema";
|
||||
import {
|
||||
ClaimCondition,
|
||||
ClaimConditionFetchOptions,
|
||||
ClaimConditionInput,
|
||||
ClaimConditionsForToken,
|
||||
ClaimOptions,
|
||||
ClaimVerification,
|
||||
} from "../../types";
|
||||
import { BaseClaimConditionERC1155 } from "../../types/eips";
|
||||
import { TransactionResult } from "../index";
|
||||
import {
|
||||
BaseClaimConditionERC1155,
|
||||
PrebuiltEditionDrop,
|
||||
} from "../../types/eips";
|
||||
import { TransactionResult, TransactionTask } from "../index";
|
||||
import { ContractMetadata } from "./contract-metadata";
|
||||
import { ContractWrapper } from "./contract-wrapper";
|
||||
import type {
|
||||
ContractMetadata as ContractMetadataContract,
|
||||
DropERC1155,
|
||||
Drop1155,
|
||||
DropERC1155_V2,
|
||||
DropSinglePhase1155,
|
||||
DropSinglePhase1155_V1,
|
||||
IDropSinglePhase,
|
||||
IDropSinglePhase_V1,
|
||||
IERC20,
|
||||
} from "@thirdweb-dev/contracts-js";
|
||||
import IERC20ABI from "@thirdweb-dev/contracts-js/dist/abis/IERC20.json";
|
||||
import { IDropClaimCondition } from "@thirdweb-dev/contracts-js/dist/declarations/src/DropERC1155";
|
||||
import type { IDropClaimCondition_V2 } from "@thirdweb-dev/contracts-js/dist/declarations/src/DropERC20_V2";
|
||||
import type { IClaimCondition } from "@thirdweb-dev/contracts-js/src/IDrop";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
import { BigNumber, BigNumberish, constants, ethers, utils } from "ethers";
|
||||
import deepEqual from "fast-deep-equal";
|
||||
@@ -39,7 +59,7 @@ import deepEqual from "fast-deep-equal";
|
||||
* @public
|
||||
*/
|
||||
export class DropErc1155ClaimConditions<
|
||||
TContract extends DropERC1155 | BaseClaimConditionERC1155,
|
||||
TContract extends PrebuiltEditionDrop | BaseClaimConditionERC1155,
|
||||
> {
|
||||
private contractWrapper;
|
||||
private metadata;
|
||||
@@ -82,20 +102,45 @@ export class DropErc1155ClaimConditions<
|
||||
|
||||
private async get(
|
||||
tokenId: BigNumberish,
|
||||
): Promise<IDropClaimCondition.ClaimConditionStructOutput> {
|
||||
if (this.isSinglePhaseDropContract(this.contractWrapper)) {
|
||||
return (await this.contractWrapper.readContract.claimCondition(
|
||||
tokenId,
|
||||
)) as IDropClaimCondition.ClaimConditionStructOutput;
|
||||
} else if (this.isMultiPhaseDropContract(this.contractWrapper)) {
|
||||
conditionId?: BigNumberish,
|
||||
): Promise<AbstractClaimConditionContractStruct> {
|
||||
if (this.isLegacySinglePhaseDrop(this.contractWrapper)) {
|
||||
const contractModel = await (
|
||||
this.contractWrapper.readContract as DropSinglePhase1155_V1
|
||||
).claimCondition(tokenId);
|
||||
return legacyContractModelToAbstract(contractModel);
|
||||
} else if (this.isLegacyMultiPhaseDrop(this.contractWrapper)) {
|
||||
const id =
|
||||
await this.contractWrapper.readContract.getActiveClaimConditionId(
|
||||
conditionId !== undefined
|
||||
? conditionId
|
||||
: await this.contractWrapper.readContract.getActiveClaimConditionId(
|
||||
tokenId,
|
||||
);
|
||||
const contractModel =
|
||||
(await this.contractWrapper.readContract.getClaimConditionById(
|
||||
tokenId,
|
||||
);
|
||||
return await this.contractWrapper.readContract.getClaimConditionById(
|
||||
tokenId,
|
||||
id,
|
||||
);
|
||||
id,
|
||||
)) as IDropClaimCondition_V2.ClaimConditionStructOutput;
|
||||
return legacyContractModelToAbstract(contractModel);
|
||||
} else if (this.isNewSinglePhaseDrop(this.contractWrapper)) {
|
||||
const contractModel =
|
||||
(await this.contractWrapper.readContract.claimCondition(
|
||||
tokenId,
|
||||
)) as IClaimCondition.ClaimConditionStructOutput;
|
||||
return newContractModelToAbstract(contractModel);
|
||||
} else if (this.isNewMultiphaseDrop(this.contractWrapper)) {
|
||||
const id =
|
||||
conditionId !== undefined
|
||||
? conditionId
|
||||
: await this.contractWrapper.readContract.getActiveClaimConditionId(
|
||||
tokenId,
|
||||
);
|
||||
const contractModel =
|
||||
(await this.contractWrapper.readContract.getClaimConditionById(
|
||||
tokenId,
|
||||
id,
|
||||
)) as IClaimCondition.ClaimConditionStruct;
|
||||
return newContractModelToAbstract(contractModel);
|
||||
} else {
|
||||
throw new Error("Contract does not support claim conditions");
|
||||
}
|
||||
@@ -110,7 +155,10 @@ export class DropErc1155ClaimConditions<
|
||||
tokenId: BigNumberish,
|
||||
options?: ClaimConditionFetchOptions,
|
||||
): Promise<ClaimCondition[]> {
|
||||
if (this.isMultiPhaseDropContract(this.contractWrapper)) {
|
||||
if (
|
||||
this.isLegacyMultiPhaseDrop(this.contractWrapper) ||
|
||||
this.isNewMultiphaseDrop(this.contractWrapper)
|
||||
) {
|
||||
const claimCondition =
|
||||
(await this.contractWrapper.readContract.claimCondition(tokenId)) as {
|
||||
currentStartId: BigNumber;
|
||||
@@ -118,14 +166,9 @@ export class DropErc1155ClaimConditions<
|
||||
};
|
||||
const startId = claimCondition.currentStartId.toNumber();
|
||||
const count = claimCondition.count.toNumber();
|
||||
const conditions: IDropClaimCondition.ClaimConditionStructOutput[] = [];
|
||||
const conditions: AbstractClaimConditionContractStruct[] = [];
|
||||
for (let i = startId; i < startId + count; i++) {
|
||||
conditions.push(
|
||||
await this.contractWrapper.readContract.getClaimConditionById(
|
||||
tokenId,
|
||||
i,
|
||||
),
|
||||
);
|
||||
conditions.push(await this.get(tokenId, i));
|
||||
}
|
||||
const metadata = await this.metadata.get();
|
||||
return Promise.all(
|
||||
@@ -141,7 +184,7 @@ export class DropErc1155ClaimConditions<
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return [await this.getActive(tokenId)];
|
||||
return [await this.getActive(tokenId, options)];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -230,52 +273,140 @@ export class DropErc1155ClaimConditions<
|
||||
const merkleRootArray = ethers.utils.stripZeros(
|
||||
claimCondition.merkleRootHash,
|
||||
);
|
||||
if (merkleRootArray.length > 0) {
|
||||
const merkleLower = claimCondition.merkleRootHash.toString();
|
||||
const metadata = await this.metadata.get();
|
||||
const proofs = await getClaimerProofs(
|
||||
addressToCheck,
|
||||
merkleLower,
|
||||
0,
|
||||
metadata.merkle,
|
||||
this.storage,
|
||||
);
|
||||
try {
|
||||
let validMerkleProof;
|
||||
if (this.isMultiPhaseDropContract(this.contractWrapper)) {
|
||||
activeConditionIndex =
|
||||
await this.contractWrapper.readContract.getActiveClaimConditionId(
|
||||
const hasAllowList = merkleRootArray.length > 0;
|
||||
let allowListEntry: SnapshotEntryWithProof | null = null;
|
||||
if (hasAllowList) {
|
||||
allowListEntry = await this.getClaimerProofs(tokenId, addressToCheck);
|
||||
if (
|
||||
!allowListEntry &&
|
||||
(this.isLegacySinglePhaseDrop(this.contractWrapper) ||
|
||||
this.isLegacyMultiPhaseDrop(this.contractWrapper))
|
||||
) {
|
||||
// exclusive allowlist behavior
|
||||
reasons.push(ClaimEligibility.AddressNotAllowed);
|
||||
return reasons;
|
||||
}
|
||||
|
||||
if (allowListEntry) {
|
||||
try {
|
||||
const claimVerification = await this.prepareClaim(
|
||||
tokenId,
|
||||
quantity,
|
||||
false,
|
||||
addressToCheck,
|
||||
);
|
||||
|
||||
let validMerkleProof;
|
||||
if (this.isLegacyMultiPhaseDrop(this.contractWrapper)) {
|
||||
activeConditionIndex =
|
||||
await this.contractWrapper.readContract.getActiveClaimConditionId(
|
||||
tokenId,
|
||||
);
|
||||
// legacy verifyClaimerMerkleProofs function
|
||||
[validMerkleProof] =
|
||||
await this.contractWrapper.readContract.verifyClaimMerkleProof(
|
||||
activeConditionIndex,
|
||||
addressToCheck,
|
||||
tokenId,
|
||||
quantity,
|
||||
claimVerification.proofs,
|
||||
claimVerification.maxClaimable,
|
||||
);
|
||||
if (!validMerkleProof) {
|
||||
reasons.push(ClaimEligibility.AddressNotAllowed);
|
||||
return reasons;
|
||||
}
|
||||
} else if (this.isLegacySinglePhaseDrop(this.contractWrapper)) {
|
||||
[validMerkleProof] =
|
||||
await this.contractWrapper.readContract.verifyClaimMerkleProof(
|
||||
tokenId,
|
||||
addressToCheck,
|
||||
quantity,
|
||||
{
|
||||
proof: claimVerification.proofs,
|
||||
maxQuantityInAllowlist: claimVerification.maxClaimable,
|
||||
},
|
||||
);
|
||||
if (!validMerkleProof) {
|
||||
reasons.push(ClaimEligibility.AddressNotAllowed);
|
||||
return reasons;
|
||||
}
|
||||
} else if (this.isNewSinglePhaseDrop(this.contractWrapper)) {
|
||||
await this.contractWrapper.readContract.verifyClaim(
|
||||
tokenId,
|
||||
addressToCheck,
|
||||
quantity,
|
||||
claimVerification.currencyAddress,
|
||||
claimVerification.price,
|
||||
{
|
||||
proof: claimVerification.proofs,
|
||||
quantityLimitPerWallet: claimVerification.maxClaimable,
|
||||
currency: claimVerification.currencyAddressInProof,
|
||||
pricePerToken: claimVerification.priceInProof,
|
||||
} as IDropSinglePhase.AllowlistProofStruct,
|
||||
);
|
||||
[validMerkleProof] =
|
||||
await this.contractWrapper.readContract.verifyClaimMerkleProof(
|
||||
// TODO (cc) in new override format, anyone can claim (no allow list restriction)
|
||||
// TODO (cc) instead check if maxClaimablePerWallet is 0 and this address has no overrides
|
||||
// TODO (cc) meaning this address is not allowed to claim
|
||||
if (
|
||||
(claimCondition.maxClaimablePerWallet === "0" &&
|
||||
claimVerification.maxClaimable ===
|
||||
ethers.constants.MaxUint256) ||
|
||||
claimVerification.maxClaimable === BigNumber.from(0)
|
||||
) {
|
||||
reasons.push(ClaimEligibility.AddressNotAllowed);
|
||||
return reasons;
|
||||
}
|
||||
} else if (this.isNewMultiphaseDrop(this.contractWrapper)) {
|
||||
activeConditionIndex =
|
||||
await this.contractWrapper.readContract.getActiveClaimConditionId(
|
||||
tokenId,
|
||||
);
|
||||
await this.contractWrapper.readContract.verifyClaim(
|
||||
activeConditionIndex,
|
||||
addressToCheck,
|
||||
tokenId,
|
||||
quantity,
|
||||
proofs.proof,
|
||||
proofs.maxClaimable,
|
||||
);
|
||||
} else if (this.isSinglePhaseDropContract(this.contractWrapper)) {
|
||||
[validMerkleProof] =
|
||||
await this.contractWrapper.readContract.verifyClaimMerkleProof(
|
||||
tokenId,
|
||||
addressToCheck,
|
||||
quantity,
|
||||
claimVerification.currencyAddress,
|
||||
claimVerification.price,
|
||||
{
|
||||
proof: proofs.proof,
|
||||
maxQuantityInAllowlist: proofs.maxClaimable,
|
||||
},
|
||||
proof: claimVerification.proofs,
|
||||
quantityLimitPerWallet: claimVerification.maxClaimable,
|
||||
currency: claimVerification.currencyAddressInProof,
|
||||
pricePerToken: claimVerification.priceInProof,
|
||||
} as IDropSinglePhase.AllowlistProofStruct,
|
||||
);
|
||||
}
|
||||
|
||||
if (!validMerkleProof) {
|
||||
if (
|
||||
(claimCondition.maxClaimablePerWallet === "0" &&
|
||||
claimVerification.maxClaimable ===
|
||||
ethers.constants.MaxUint256) ||
|
||||
claimVerification.maxClaimable === BigNumber.from(0)
|
||||
) {
|
||||
reasons.push(ClaimEligibility.AddressNotAllowed);
|
||||
return reasons;
|
||||
}
|
||||
}
|
||||
} catch (e: any) {
|
||||
console.warn(
|
||||
"Merkle proof verification failed:",
|
||||
"reason" in e ? e.reason : e,
|
||||
);
|
||||
reasons.push(ClaimEligibility.AddressNotAllowed);
|
||||
return reasons;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO (cc) check for max claimable per wallet and how much each wallet has claimed
|
||||
if (
|
||||
this.isNewSinglePhaseDrop(this.contractWrapper) ||
|
||||
this.isNewMultiphaseDrop(this.contractWrapper)
|
||||
) {
|
||||
if (!hasAllowList || (hasAllowList && !allowListEntry)) {
|
||||
if (claimCondition.maxClaimablePerWallet === "0") {
|
||||
reasons.push(ClaimEligibility.AddressNotAllowed);
|
||||
return reasons;
|
||||
}
|
||||
} catch (e) {
|
||||
reasons.push(ClaimEligibility.AddressNotAllowed);
|
||||
return reasons;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -284,7 +415,7 @@ export class DropErc1155ClaimConditions<
|
||||
BigNumber.from(0),
|
||||
BigNumber.from(0),
|
||||
];
|
||||
if (this.isMultiPhaseDropContract(this.contractWrapper)) {
|
||||
if (this.isLegacyMultiPhaseDrop(this.contractWrapper)) {
|
||||
activeConditionIndex =
|
||||
await this.contractWrapper.readContract.getActiveClaimConditionId(
|
||||
tokenId,
|
||||
@@ -295,7 +426,7 @@ export class DropErc1155ClaimConditions<
|
||||
activeConditionIndex,
|
||||
addressToCheck,
|
||||
);
|
||||
} else if (this.isSinglePhaseDropContract(this.contractWrapper)) {
|
||||
} else if (this.isLegacySinglePhaseDrop(this.contractWrapper)) {
|
||||
[lastClaimedTimestamp, timestampForNextClaim] =
|
||||
await this.contractWrapper.readContract.getClaimTimestamp(
|
||||
tokenId,
|
||||
@@ -342,6 +473,35 @@ export class DropErc1155ClaimConditions<
|
||||
return reasons;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns allow list information and merkle proofs for the given address.
|
||||
* @param tokenId - the token ID to check
|
||||
* @param claimerAddress - the claimer address
|
||||
* @param claimConditionId - optional the claim condition id to get the proofs for
|
||||
*/
|
||||
public async getClaimerProofs(
|
||||
tokenId: BigNumberish,
|
||||
claimerAddress: string,
|
||||
claimConditionId?: BigNumberish,
|
||||
): Promise<SnapshotEntryWithProof | null> {
|
||||
const claimCondition = await this.get(tokenId, claimConditionId);
|
||||
const merkleRoot = claimCondition.merkleRoot;
|
||||
const merkleRootArray = ethers.utils.stripZeros(merkleRoot);
|
||||
if (merkleRootArray.length > 0) {
|
||||
const metadata = await this.metadata.get();
|
||||
return await fetchSnapshotEntryForAddress(
|
||||
claimerAddress,
|
||||
merkleRoot.toString(),
|
||||
metadata.merkle,
|
||||
this.contractWrapper.getProvider(),
|
||||
this.storage,
|
||||
this.getSnapshotFormatVersion(),
|
||||
);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/** ***************************************
|
||||
* WRITE FUNCTIONS
|
||||
*****************************************/
|
||||
@@ -440,16 +600,16 @@ export class DropErc1155ClaimConditions<
|
||||
claimConditionsForToken.map(async ({ tokenId, claimConditions }) => {
|
||||
// sanitize for single phase deletions
|
||||
let claimConditionsProcessed = claimConditions;
|
||||
if (this.isSinglePhaseDropContract(this.contractWrapper)) {
|
||||
if (this.isLegacySinglePhaseDrop(this.contractWrapper)) {
|
||||
resetClaimEligibilityForAll = true;
|
||||
if (claimConditions.length === 0) {
|
||||
claimConditionsProcessed = [
|
||||
{
|
||||
startTime: new Date(0),
|
||||
currencyAddress: NATIVE_TOKEN_ADDRESS,
|
||||
currencyAddress: ethers.constants.AddressZero,
|
||||
price: 0,
|
||||
maxQuantity: 0,
|
||||
quantityLimitPerTransaction: 0,
|
||||
maxClaimableSupply: 0,
|
||||
maxClaimablePerWallet: 0,
|
||||
waitInSeconds: 0,
|
||||
merkleRootHash: utils.hexZeroPad([0], 32),
|
||||
snapshot: [],
|
||||
@@ -461,6 +621,47 @@ export class DropErc1155ClaimConditions<
|
||||
);
|
||||
}
|
||||
}
|
||||
// if using new snapshot format, make sure that maxClaimablePerWallet is set if allowlist is set as well
|
||||
if (
|
||||
this.isNewSinglePhaseDrop(this.contractWrapper) ||
|
||||
this.isNewMultiphaseDrop(this.contractWrapper)
|
||||
) {
|
||||
claimConditionsProcessed.forEach((cc) => {
|
||||
if (
|
||||
cc.snapshot &&
|
||||
cc.snapshot.length > 0 &&
|
||||
(cc.maxClaimablePerWallet === undefined ||
|
||||
cc.maxClaimablePerWallet === "unlimited")
|
||||
) {
|
||||
throw new Error(
|
||||
"maxClaimablePerWallet must be set to a specific value when an allowlist is set.\n" +
|
||||
"Set it to 0 to only allow addresses in the allowlist to claim the amount specified in the allowlist." +
|
||||
"\n\nex:\n" +
|
||||
"contract.claimConditions.set(tokenId, [{ snapshot: [{ address: '0x...', maxClaimable: 1 }], maxClaimablePerWallet: 0 }])",
|
||||
);
|
||||
}
|
||||
if (
|
||||
cc.snapshot &&
|
||||
cc.snapshot.length > 0 &&
|
||||
cc.maxClaimablePerWallet?.toString() === "0" &&
|
||||
cc.snapshot
|
||||
.map((s) => {
|
||||
if (typeof s === "string") {
|
||||
return 0;
|
||||
} else {
|
||||
return Number(s.maxClaimable?.toString() || 0);
|
||||
}
|
||||
})
|
||||
.reduce((acc, current) => {
|
||||
return acc + current;
|
||||
}, 0) === 0
|
||||
) {
|
||||
throw new Error(
|
||||
"maxClaimablePerWallet is set to 0, and all addresses in the allowlist have max claimable 0. This means that no one can claim.",
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
// process inputs
|
||||
const { snapshotInfos, sortedConditions } =
|
||||
await processClaimConditionInputs(
|
||||
@@ -468,6 +669,7 @@ export class DropErc1155ClaimConditions<
|
||||
0,
|
||||
this.contractWrapper.getProvider(),
|
||||
this.storage,
|
||||
this.getSnapshotFormatVersion(),
|
||||
);
|
||||
|
||||
snapshotInfos.forEach((s) => {
|
||||
@@ -519,18 +721,48 @@ export class DropErc1155ClaimConditions<
|
||||
}
|
||||
|
||||
processedClaimConditions.forEach(({ tokenId, sortedConditions }) => {
|
||||
if (this.isSinglePhaseDropContract(this.contractWrapper)) {
|
||||
if (this.isLegacySinglePhaseDrop(this.contractWrapper)) {
|
||||
encoded.push(
|
||||
this.contractWrapper.readContract.interface.encodeFunctionData(
|
||||
"setClaimConditions",
|
||||
[tokenId, sortedConditions[0], resetClaimEligibilityForAll],
|
||||
[
|
||||
tokenId,
|
||||
abstractContractModelToLegacy(sortedConditions[0]),
|
||||
resetClaimEligibilityForAll,
|
||||
],
|
||||
),
|
||||
);
|
||||
} else if (this.isMultiPhaseDropContract(this.contractWrapper)) {
|
||||
} else if (this.isLegacyMultiPhaseDrop(this.contractWrapper)) {
|
||||
encoded.push(
|
||||
this.contractWrapper.readContract.interface.encodeFunctionData(
|
||||
"setClaimConditions",
|
||||
[tokenId, sortedConditions, resetClaimEligibilityForAll],
|
||||
[
|
||||
tokenId,
|
||||
sortedConditions.map(abstractContractModelToLegacy),
|
||||
resetClaimEligibilityForAll,
|
||||
],
|
||||
),
|
||||
);
|
||||
} else if (this.isNewSinglePhaseDrop(this.contractWrapper)) {
|
||||
encoded.push(
|
||||
this.contractWrapper.readContract.interface.encodeFunctionData(
|
||||
"setClaimConditions",
|
||||
[
|
||||
tokenId,
|
||||
abstractContractModelToNew(sortedConditions[0]),
|
||||
resetClaimEligibilityForAll,
|
||||
],
|
||||
),
|
||||
);
|
||||
} else if (this.isNewMultiphaseDrop(this.contractWrapper)) {
|
||||
encoded.push(
|
||||
this.contractWrapper.readContract.interface.encodeFunctionData(
|
||||
"setClaimConditions",
|
||||
[
|
||||
tokenId,
|
||||
sortedConditions.map(abstractContractModelToNew),
|
||||
resetClaimEligibilityForAll,
|
||||
],
|
||||
),
|
||||
);
|
||||
} else {
|
||||
@@ -572,8 +804,13 @@ export class DropErc1155ClaimConditions<
|
||||
tokenId: BigNumberish,
|
||||
quantity: BigNumberish,
|
||||
checkERC20Allowance: boolean,
|
||||
address?: string,
|
||||
): Promise<ClaimVerification> {
|
||||
const addressToClaim = address
|
||||
? address
|
||||
: await this.contractWrapper.getSignerAddress();
|
||||
return prepareClaim(
|
||||
addressToClaim,
|
||||
quantity,
|
||||
await this.getActive(tokenId),
|
||||
async () => (await this.metadata.get()).merkle,
|
||||
@@ -581,21 +818,132 @@ export class DropErc1155ClaimConditions<
|
||||
this.contractWrapper,
|
||||
this.storage,
|
||||
checkERC20Allowance,
|
||||
this.getSnapshotFormatVersion(),
|
||||
);
|
||||
}
|
||||
|
||||
private isSinglePhaseDropContract(
|
||||
public async getClaimArguments(
|
||||
tokenId: BigNumberish,
|
||||
destinationAddress: string,
|
||||
quantity: BigNumberish,
|
||||
claimVerification: ClaimVerification,
|
||||
): Promise<any[]> {
|
||||
if (this.isLegacyMultiPhaseDrop(this.contractWrapper)) {
|
||||
return [
|
||||
destinationAddress,
|
||||
tokenId,
|
||||
quantity,
|
||||
claimVerification.currencyAddress,
|
||||
claimVerification.price,
|
||||
claimVerification.proofs,
|
||||
claimVerification.maxClaimable,
|
||||
];
|
||||
} else if (this.isLegacySinglePhaseDrop(this.contractWrapper)) {
|
||||
return [
|
||||
destinationAddress,
|
||||
tokenId,
|
||||
quantity,
|
||||
claimVerification.currencyAddress,
|
||||
claimVerification.price,
|
||||
{
|
||||
proof: claimVerification.proofs,
|
||||
maxQuantityInAllowlist: claimVerification.maxClaimable,
|
||||
} as IDropSinglePhase_V1.AllowlistProofStruct,
|
||||
ethers.utils.toUtf8Bytes(""),
|
||||
];
|
||||
}
|
||||
return [
|
||||
destinationAddress,
|
||||
tokenId,
|
||||
quantity,
|
||||
claimVerification.currencyAddress,
|
||||
claimVerification.price,
|
||||
{
|
||||
proof: claimVerification.proofs,
|
||||
quantityLimitPerWallet: claimVerification.maxClaimable,
|
||||
pricePerToken: claimVerification.priceInProof,
|
||||
currency: claimVerification.currencyAddressInProof,
|
||||
} as IDropSinglePhase.AllowlistProofStruct,
|
||||
ethers.utils.toUtf8Bytes(""),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a claim transaction without executing it.
|
||||
* This is useful for estimating the gas cost of a claim transaction, overriding transaction options and having fine grained control over the transaction execution.
|
||||
* @param destinationAddress - Address you want to send the token to
|
||||
* @param tokenId - Id of the token you want to claim
|
||||
* @param quantity - Quantity of the tokens you want to claim
|
||||
*/
|
||||
public async getClaimTransaction(
|
||||
destinationAddress: string,
|
||||
tokenId: BigNumberish,
|
||||
quantity: BigNumberish,
|
||||
options?: ClaimOptions,
|
||||
): Promise<TransactionTask> {
|
||||
if (options?.pricePerToken) {
|
||||
throw new Error(
|
||||
"Price per token should be set via claim conditions by calling `contract.erc1155.claimConditions.set()`",
|
||||
);
|
||||
}
|
||||
const claimVerification = await this.prepareClaim(
|
||||
tokenId,
|
||||
quantity,
|
||||
options?.checkERC20Allowance || true,
|
||||
);
|
||||
return TransactionTask.make({
|
||||
contractWrapper: this.contractWrapper,
|
||||
functionName: "claim",
|
||||
args: await this.getClaimArguments(
|
||||
tokenId,
|
||||
destinationAddress,
|
||||
quantity,
|
||||
claimVerification,
|
||||
),
|
||||
overrides: claimVerification.overrides,
|
||||
});
|
||||
}
|
||||
|
||||
isNewSinglePhaseDrop(
|
||||
contractWrapper: ContractWrapper<any>,
|
||||
): contractWrapper is ContractWrapper<DropSinglePhase1155> {
|
||||
return !hasFunction<DropSinglePhase1155>(
|
||||
"getClaimConditionById",
|
||||
return detectContractFeature<DropSinglePhase1155>(
|
||||
contractWrapper,
|
||||
"ERC1155ClaimConditionsV2",
|
||||
);
|
||||
}
|
||||
|
||||
private isMultiPhaseDropContract(
|
||||
isNewMultiphaseDrop(
|
||||
contractWrapper: ContractWrapper<any>,
|
||||
): contractWrapper is ContractWrapper<DropERC1155> {
|
||||
return hasFunction<DropERC1155>("getClaimConditionById", contractWrapper);
|
||||
): contractWrapper is ContractWrapper<Drop1155> {
|
||||
return detectContractFeature<Drop1155>(
|
||||
contractWrapper,
|
||||
"ERC1155ClaimPhasesV2",
|
||||
);
|
||||
}
|
||||
|
||||
isLegacySinglePhaseDrop(
|
||||
contractWrapper: ContractWrapper<any>,
|
||||
): contractWrapper is ContractWrapper<DropSinglePhase1155_V1> {
|
||||
return detectContractFeature<DropSinglePhase1155_V1>(
|
||||
contractWrapper,
|
||||
"ERC1155ClaimConditionsV1",
|
||||
);
|
||||
}
|
||||
|
||||
isLegacyMultiPhaseDrop(
|
||||
contractWrapper: ContractWrapper<any>,
|
||||
): contractWrapper is ContractWrapper<DropERC1155_V2> {
|
||||
return detectContractFeature<DropERC1155_V2>(
|
||||
contractWrapper,
|
||||
"ERC1155ClaimPhasesV1",
|
||||
);
|
||||
}
|
||||
|
||||
private getSnapshotFormatVersion(): SnapshotFormatVersion {
|
||||
return this.isLegacyMultiPhaseDrop(this.contractWrapper) ||
|
||||
this.isLegacySinglePhaseDrop(this.contractWrapper)
|
||||
? SnapshotFormatVersion.V1
|
||||
: SnapshotFormatVersion.V2;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { PrebuiltEditionDrop } from "../../types/eips";
|
||||
import { ContractEvents } from "./contract-events";
|
||||
import type { DropERC1155 } from "@thirdweb-dev/contracts-js";
|
||||
import { BigNumber, BigNumberish } from "ethers";
|
||||
|
||||
/**
|
||||
@@ -9,7 +9,7 @@ import { BigNumber, BigNumberish } from "ethers";
|
||||
export class DropErc1155History {
|
||||
private events;
|
||||
|
||||
constructor(events: ContractEvents<DropERC1155>) {
|
||||
constructor(events: ContractEvents<PrebuiltEditionDrop>) {
|
||||
this.events = events;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,20 +1,14 @@
|
||||
import { hasFunction } from "../../common";
|
||||
import { FEATURE_EDITION_CLAIMABLE_WITH_CONDITIONS } from "../../constants/erc1155-features";
|
||||
import { FEATURE_EDITION_CLAIM_CONDITIONS_V2 } from "../../constants/erc1155-features";
|
||||
import { CustomContractSchema } from "../../schema/contracts/custom";
|
||||
import {
|
||||
ClaimOptions,
|
||||
ClaimVerification,
|
||||
} from "../../types/claim-conditions/claim-conditions";
|
||||
import { ClaimOptions } from "../../types/claim-conditions/claim-conditions";
|
||||
import { BaseClaimConditionERC1155 } from "../../types/eips";
|
||||
import { DetectableFeature } from "../interfaces/DetectableFeature";
|
||||
import { TransactionResult } from "../types";
|
||||
import { TransactionTask } from "./TransactionTask";
|
||||
import { ContractMetadata } from "./contract-metadata";
|
||||
import { ContractWrapper } from "./contract-wrapper";
|
||||
import { DropErc1155ClaimConditions } from "./drop-erc1155-claim-conditions";
|
||||
import type { DropERC1155 } from "@thirdweb-dev/contracts-js";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
import { BigNumberish, ethers } from "ethers";
|
||||
import { BigNumberish } from "ethers";
|
||||
|
||||
/**
|
||||
* Configure and claim ERC1155 NFTs
|
||||
@@ -27,7 +21,7 @@ import { BigNumberish, ethers } from "ethers";
|
||||
* ```
|
||||
*/
|
||||
export class Erc1155ClaimableWithConditions implements DetectableFeature {
|
||||
featureName = FEATURE_EDITION_CLAIMABLE_WITH_CONDITIONS.name;
|
||||
featureName = FEATURE_EDITION_CLAIM_CONDITIONS_V2.name;
|
||||
|
||||
public conditions: DropErc1155ClaimConditions<BaseClaimConditionERC1155>;
|
||||
private contractWrapper: ContractWrapper<BaseClaimConditionERC1155>;
|
||||
@@ -52,42 +46,6 @@ export class Erc1155ClaimableWithConditions implements DetectableFeature {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a claim transaction without executing it.
|
||||
* This is useful for estimating the gas cost of a claim transaction, overriding transaction options and having fine grained control over the transaction execution.
|
||||
* @param destinationAddress - Address you want to send the token to
|
||||
* @param tokenId - Id of the token you want to claim
|
||||
* @param quantity - Quantity of the tokens you want to claim
|
||||
*/
|
||||
public async getClaimTransaction(
|
||||
destinationAddress: string,
|
||||
tokenId: BigNumberish,
|
||||
quantity: BigNumberish,
|
||||
options?: ClaimOptions,
|
||||
): Promise<TransactionTask> {
|
||||
if (options?.pricePerToken) {
|
||||
throw new Error(
|
||||
"In ERC1155ClaimableWithConditions, price per token is be set via claim conditions by calling `contract.erc1155.claimConditions.set()`",
|
||||
);
|
||||
}
|
||||
const claimVerification = await this.conditions.prepareClaim(
|
||||
tokenId,
|
||||
quantity,
|
||||
options?.checkERC20Allowance || true,
|
||||
);
|
||||
return TransactionTask.make({
|
||||
contractWrapper: this.contractWrapper,
|
||||
functionName: "claim",
|
||||
args: await this.getArgs(
|
||||
tokenId,
|
||||
destinationAddress,
|
||||
quantity,
|
||||
claimVerification,
|
||||
),
|
||||
overrides: claimVerification.overrides,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Claim NFTs to a specific Wallet
|
||||
*
|
||||
@@ -115,7 +73,7 @@ export class Erc1155ClaimableWithConditions implements DetectableFeature {
|
||||
quantity: BigNumberish,
|
||||
options?: ClaimOptions,
|
||||
): Promise<TransactionResult> {
|
||||
const tx = await this.getClaimTransaction(
|
||||
const tx = await this.conditions.getClaimTransaction(
|
||||
destinationAddress,
|
||||
tokenId,
|
||||
quantity,
|
||||
@@ -123,51 +81,4 @@ export class Erc1155ClaimableWithConditions implements DetectableFeature {
|
||||
);
|
||||
return await tx.execute();
|
||||
}
|
||||
|
||||
private async getArgs(
|
||||
tokenId: BigNumberish,
|
||||
destinationAddress: string,
|
||||
quantity: BigNumberish,
|
||||
claimVerification: ClaimVerification,
|
||||
): Promise<any[]> {
|
||||
const isLegacyNFTContract = await this.isLegacyNFTContract();
|
||||
if (isLegacyNFTContract) {
|
||||
return [
|
||||
destinationAddress,
|
||||
tokenId,
|
||||
quantity,
|
||||
claimVerification.currencyAddress,
|
||||
claimVerification.price,
|
||||
claimVerification.proofs,
|
||||
claimVerification.maxQuantityPerTransaction,
|
||||
];
|
||||
}
|
||||
return [
|
||||
destinationAddress,
|
||||
tokenId,
|
||||
quantity,
|
||||
claimVerification.currencyAddress,
|
||||
claimVerification.price,
|
||||
{
|
||||
proof: claimVerification.proofs,
|
||||
maxQuantityInAllowlist: claimVerification.maxQuantityPerTransaction,
|
||||
},
|
||||
ethers.utils.toUtf8Bytes(""),
|
||||
];
|
||||
}
|
||||
|
||||
private async isLegacyNFTContract() {
|
||||
if (hasFunction<DropERC1155>("contractType", this.contractWrapper)) {
|
||||
try {
|
||||
const contractType = ethers.utils.toUtf8String(
|
||||
await this.contractWrapper.readContract.contractType(),
|
||||
);
|
||||
return contractType.includes("DropERC1155");
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { calculateClaimCost } from "../../common/claim-conditions";
|
||||
import { FEATURE_EDITION_CLAIMABLE } from "../../constants/erc1155-features";
|
||||
import { FEATURE_EDITION_CLAIM_CUSTOM } from "../../constants/erc1155-features";
|
||||
import { ClaimOptions } from "../../types";
|
||||
import { DetectableFeature } from "../interfaces/DetectableFeature";
|
||||
import { TransactionResult } from "../types";
|
||||
@@ -18,7 +18,7 @@ import { BigNumberish, CallOverrides } from "ethers";
|
||||
* ```
|
||||
*/
|
||||
export class ERC1155Claimable implements DetectableFeature {
|
||||
featureName = FEATURE_EDITION_CLAIMABLE.name;
|
||||
featureName = FEATURE_EDITION_CLAIM_CUSTOM.name;
|
||||
|
||||
private contractWrapper: ContractWrapper<IClaimableERC1155>;
|
||||
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
import { NFTMetadata, NFTMetadataOrUri } from "../../../core/schema/nft";
|
||||
import {
|
||||
detectContractFeature,
|
||||
hasFunction,
|
||||
} from "../../common/feature-detection";
|
||||
import { detectContractFeature } from "../../common/feature-detection";
|
||||
import { getPrebuiltInfo } from "../../common/legacy";
|
||||
import { uploadOrExtractURIs } from "../../common/nft";
|
||||
import {
|
||||
FEATURE_EDITION_LAZY_MINTABLE,
|
||||
FEATURE_EDITION_LAZY_MINTABLE_V2,
|
||||
FEATURE_EDITION_REVEALABLE,
|
||||
} from "../../constants/erc1155-features";
|
||||
import {
|
||||
@@ -21,16 +19,13 @@ import { DelayedReveal } from "./delayed-reveal";
|
||||
import { Erc1155 } from "./erc-1155";
|
||||
import { ERC1155Claimable } from "./erc-1155-claimable";
|
||||
import { Erc1155ClaimableWithConditions } from "./erc-1155-claimable-with-conditions";
|
||||
import type {
|
||||
IClaimableERC1155,
|
||||
TokenERC721,
|
||||
} from "@thirdweb-dev/contracts-js";
|
||||
import type { IClaimableERC1155 } from "@thirdweb-dev/contracts-js";
|
||||
import { TokensLazyMintedEvent } from "@thirdweb-dev/contracts-js/dist/declarations/src/LazyMint";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
import { ethers } from "ethers";
|
||||
|
||||
export class Erc1155LazyMintable implements DetectableFeature {
|
||||
featureName = FEATURE_EDITION_LAZY_MINTABLE.name;
|
||||
featureName = FEATURE_EDITION_LAZY_MINTABLE_V2.name;
|
||||
|
||||
/**
|
||||
* Delayed reveal
|
||||
@@ -93,7 +88,7 @@ export class Erc1155LazyMintable implements DetectableFeature {
|
||||
this.storage = storage;
|
||||
this.claim = this.detectErc1155Claimable();
|
||||
this.claimWithConditions = this.detectErc1155ClaimableWithConditions();
|
||||
this.revealer = this.detectErc721Revealable();
|
||||
this.revealer = this.detectErc1155Revealable();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -186,9 +181,8 @@ export class Erc1155LazyMintable implements DetectableFeature {
|
||||
if (
|
||||
detectContractFeature<IClaimableERC1155>(
|
||||
this.contractWrapper,
|
||||
"ERC1155Claimable",
|
||||
) &&
|
||||
!hasFunction("setClaimConditions", this.contractWrapper)
|
||||
"ERC1155ClaimCustom",
|
||||
)
|
||||
) {
|
||||
return new ERC1155Claimable(this.contractWrapper);
|
||||
}
|
||||
@@ -201,7 +195,19 @@ export class Erc1155LazyMintable implements DetectableFeature {
|
||||
if (
|
||||
detectContractFeature<BaseClaimConditionERC1155>(
|
||||
this.contractWrapper,
|
||||
"ERC1155ClaimableWithConditions",
|
||||
"ERC1155ClaimConditionsV1",
|
||||
) ||
|
||||
detectContractFeature<BaseClaimConditionERC1155>(
|
||||
this.contractWrapper,
|
||||
"ERC1155ClaimConditionsV2",
|
||||
) ||
|
||||
detectContractFeature<BaseClaimConditionERC1155>(
|
||||
this.contractWrapper,
|
||||
"ERC1155ClaimPhasesV1",
|
||||
) ||
|
||||
detectContractFeature<BaseClaimConditionERC1155>(
|
||||
this.contractWrapper,
|
||||
"ERC1155ClaimPhasesV2",
|
||||
)
|
||||
) {
|
||||
return new Erc1155ClaimableWithConditions(
|
||||
@@ -212,7 +218,7 @@ export class Erc1155LazyMintable implements DetectableFeature {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private detectErc721Revealable():
|
||||
private detectErc1155Revealable():
|
||||
| DelayedReveal<BaseDelayedRevealERC1155>
|
||||
| undefined {
|
||||
if (
|
||||
@@ -232,17 +238,10 @@ export class Erc1155LazyMintable implements DetectableFeature {
|
||||
}
|
||||
|
||||
private async isLegacyEditionDropContract() {
|
||||
if (hasFunction<TokenERC721>("contractType", this.contractWrapper)) {
|
||||
try {
|
||||
const contractType = ethers.utils.toUtf8String(
|
||||
await this.contractWrapper.readContract.contractType(),
|
||||
);
|
||||
return contractType.includes("DropERC1155");
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
const info = await getPrebuiltInfo(
|
||||
this.contractWrapper.readContract.address,
|
||||
this.contractWrapper.getProvider(),
|
||||
);
|
||||
return info && info.type === "DropERC1155" && info.version < 3;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -172,7 +172,7 @@ export class Erc1155SignatureMintable implements DetectableFeature {
|
||||
* const endTime = new Date(Date.now() + 60 * 60 * 24 * 1000);
|
||||
* const payload = {
|
||||
* metadata: nftMetadata, // The NFT to mint
|
||||
* to: {{wallet_address}}, // Who will receive the NFT (or AddressZero for anyone)
|
||||
* to: {{wallet_address}}, // Who will receive the NFT
|
||||
* quantity: 2, // the quantity of NFTs to mint
|
||||
* price: 0.5, // the price per NFT
|
||||
* currencyAddress: NATIVE_TOKEN_ADDRESS, // the currency to pay with
|
||||
@@ -214,7 +214,7 @@ export class Erc1155SignatureMintable implements DetectableFeature {
|
||||
* const endTime = new Date(Date.now() + 60 * 60 * 24 * 1000);
|
||||
* const payload = {
|
||||
* metadata: nftMetadata, // The NFT to mint
|
||||
* to: {{wallet_address}}, // Who will receive the NFT (or AddressZero for anyone)
|
||||
* to: {{wallet_address}}, // Who will receive the NFT
|
||||
* quantity: 2, // the quantity of NFTs to mint
|
||||
* price: 0.5, // the price per NFT
|
||||
* currencyAddress: NATIVE_TOKEN_ADDRESS, // the currency to pay with
|
||||
|
||||
@@ -12,13 +12,13 @@ import {
|
||||
FEATURE_EDITION,
|
||||
FEATURE_EDITION_BATCH_MINTABLE,
|
||||
FEATURE_EDITION_BURNABLE,
|
||||
FEATURE_EDITION_LAZY_MINTABLE,
|
||||
FEATURE_EDITION_ENUMERABLE,
|
||||
FEATURE_EDITION_MINTABLE,
|
||||
FEATURE_EDITION_REVEALABLE,
|
||||
FEATURE_EDITION_SIGNATURE_MINTABLE,
|
||||
FEATURE_EDITION_CLAIMABLE_WITH_CONDITIONS,
|
||||
FEATURE_EDITION_CLAIMABLE,
|
||||
FEATURE_EDITION_CLAIM_CUSTOM,
|
||||
FEATURE_EDITION_CLAIM_CONDITIONS_V2,
|
||||
FEATURE_EDITION_LAZY_MINTABLE_V2,
|
||||
} from "../../constants/erc1155-features";
|
||||
import { AirdropInputSchema } from "../../schema/contracts/common/airdrop";
|
||||
import { EditionMetadataOrUri } from "../../schema/tokens/edition";
|
||||
@@ -770,7 +770,7 @@ export class Erc1155<
|
||||
): Promise<TransactionResultWithId<NFTMetadata>[]> {
|
||||
return assertEnabled(
|
||||
this.lazyMintable,
|
||||
FEATURE_EDITION_LAZY_MINTABLE,
|
||||
FEATURE_EDITION_LAZY_MINTABLE_V2,
|
||||
).lazyMint(metadatas, options);
|
||||
}
|
||||
|
||||
@@ -793,7 +793,7 @@ export class Erc1155<
|
||||
const claimWithConditions = this.lazyMintable?.claimWithConditions;
|
||||
const claim = this.lazyMintable?.claim;
|
||||
if (claimWithConditions) {
|
||||
return claimWithConditions.getClaimTransaction(
|
||||
return claimWithConditions.conditions.getClaimTransaction(
|
||||
destinationAddress,
|
||||
tokenId,
|
||||
quantity,
|
||||
@@ -808,7 +808,7 @@ export class Erc1155<
|
||||
options,
|
||||
);
|
||||
}
|
||||
throw new ExtensionNotImplementedError(FEATURE_EDITION_CLAIMABLE);
|
||||
throw new ExtensionNotImplementedError(FEATURE_EDITION_CLAIM_CUSTOM);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -887,7 +887,7 @@ export class Erc1155<
|
||||
if (claim) {
|
||||
return claim.to(destinationAddress, tokenId, quantity, options);
|
||||
}
|
||||
throw new ExtensionNotImplementedError(FEATURE_EDITION_CLAIMABLE);
|
||||
throw new ExtensionNotImplementedError(FEATURE_EDITION_CLAIM_CUSTOM);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -916,7 +916,7 @@ export class Erc1155<
|
||||
get claimConditions() {
|
||||
return assertEnabled(
|
||||
this.lazyMintable?.claimWithConditions,
|
||||
FEATURE_EDITION_CLAIMABLE_WITH_CONDITIONS,
|
||||
FEATURE_EDITION_CLAIM_CONDITIONS_V2,
|
||||
).conditions;
|
||||
}
|
||||
|
||||
@@ -1041,7 +1041,11 @@ export class Erc1155<
|
||||
if (
|
||||
detectContractFeature<BaseDropERC1155>(
|
||||
this.contractWrapper,
|
||||
"ERC1155LazyMintable",
|
||||
"ERC1155LazyMintableV1",
|
||||
) ||
|
||||
detectContractFeature<BaseDropERC1155>(
|
||||
this.contractWrapper,
|
||||
"ERC1155LazyMintableV2",
|
||||
)
|
||||
) {
|
||||
return new Erc1155LazyMintable(this, this.contractWrapper, this.storage);
|
||||
|
||||
+10
-33
@@ -1,6 +1,6 @@
|
||||
import { FEATURE_TOKEN_CLAIMABLE_WITH_CONDITIONS } from "../../constants/erc20-features";
|
||||
import { FEATURE_TOKEN_CLAIM_CONDITIONS_V2 } from "../../constants/erc20-features";
|
||||
import { CustomContractSchema } from "../../schema/contracts/custom";
|
||||
import { ClaimVerification } from "../../types";
|
||||
import { ClaimOptions } from "../../types";
|
||||
import { Amount } from "../../types/currency";
|
||||
import { BaseDropERC20 } from "../../types/eips";
|
||||
import { DetectableFeature } from "../interfaces/DetectableFeature";
|
||||
@@ -20,8 +20,8 @@ import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
* await contract.token.drop.claim.to("0x...", quantity);
|
||||
* ```
|
||||
*/
|
||||
export class Erc20Claimable implements DetectableFeature {
|
||||
featureName = FEATURE_TOKEN_CLAIMABLE_WITH_CONDITIONS.name;
|
||||
export class Erc20ClaimableWithConditions implements DetectableFeature {
|
||||
featureName = FEATURE_TOKEN_CLAIM_CONDITIONS_V2.name;
|
||||
/**
|
||||
* Configure claim conditions
|
||||
* @remarks Define who can claim NFTs in the collection, when and how many.
|
||||
@@ -93,37 +93,14 @@ export class Erc20Claimable implements DetectableFeature {
|
||||
public async to(
|
||||
destinationAddress: string,
|
||||
amount: Amount,
|
||||
checkERC20Allowance = true,
|
||||
claimData?: ClaimVerification,
|
||||
options?: ClaimOptions,
|
||||
): Promise<TransactionResult> {
|
||||
const quantity = await this.erc20.normalizeAmount(amount);
|
||||
|
||||
let claimVerification = claimData;
|
||||
if (this.conditions && !claimData) {
|
||||
claimVerification = await this.conditions.prepareClaim(
|
||||
quantity,
|
||||
checkERC20Allowance,
|
||||
await this.contractWrapper.readContract.decimals(),
|
||||
);
|
||||
}
|
||||
if (!claimVerification) {
|
||||
throw new Error(
|
||||
"Claim verification Data is required - either pass it in as 'claimData' or set claim conditions via 'conditions.set()'",
|
||||
);
|
||||
}
|
||||
|
||||
const receipt = await this.contractWrapper.sendTransaction(
|
||||
"claim",
|
||||
[
|
||||
destinationAddress,
|
||||
quantity,
|
||||
claimVerification.currencyAddress,
|
||||
claimVerification.price,
|
||||
claimVerification.proofs,
|
||||
claimVerification.maxQuantityPerTransaction,
|
||||
],
|
||||
claimVerification.overrides,
|
||||
const task = await this.conditions.getClaimTransaction(
|
||||
destinationAddress,
|
||||
quantity,
|
||||
options,
|
||||
);
|
||||
return { receipt };
|
||||
return await task.execute();
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import { BaseDropERC20 } from "../../types/eips";
|
||||
import { ContractWrapper } from "./contract-wrapper";
|
||||
import { Erc20 } from "./erc-20";
|
||||
import { Erc20Claimable } from "./erc-20-claimable";
|
||||
import { Erc20ClaimableWithConditions } from "./erc-20-claimable-with-conditions";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
|
||||
/**
|
||||
@@ -36,7 +36,7 @@ export class Erc20Droppable {
|
||||
* await contract.nft.drop.claim.conditions.set(claimConditions);
|
||||
* ```
|
||||
*/
|
||||
public claim: Erc20Claimable;
|
||||
public claim: Erc20ClaimableWithConditions;
|
||||
private contractWrapper: ContractWrapper<BaseDropERC20>;
|
||||
private erc20: Erc20;
|
||||
private storage: ThirdwebStorage;
|
||||
@@ -50,7 +50,7 @@ export class Erc20Droppable {
|
||||
this.contractWrapper = contractWrapper;
|
||||
|
||||
this.storage = storage;
|
||||
this.claim = new Erc20Claimable(
|
||||
this.claim = new Erc20ClaimableWithConditions(
|
||||
this.erc20,
|
||||
this.contractWrapper,
|
||||
this.storage,
|
||||
|
||||
@@ -123,7 +123,7 @@ export class Erc20SignatureMintable implements DetectableFeature {
|
||||
* const endTime = new Date(Date.now() + 60 * 60 * 24 * 1000);
|
||||
* const payload = {
|
||||
* quantity: 4.2, // The quantity of tokens to be minted
|
||||
* to: {{wallet_address}}, // Who will receive the tokens (or AddressZero for anyone)
|
||||
* to: {{wallet_address}}, // Who will receive the tokens
|
||||
* price: 0.5, // the price to pay for minting those tokens
|
||||
* currencyAddress: NATIVE_TOKEN_ADDRESS, // the currency to pay with
|
||||
* mintStartTime: startTime, // can mint anytime from now
|
||||
@@ -156,7 +156,7 @@ export class Erc20SignatureMintable implements DetectableFeature {
|
||||
* const endTime = new Date(Date.now() + 60 * 60 * 24 * 1000);
|
||||
* const payload = {
|
||||
* quantity: 4.2, // The quantity of tokens to be minted
|
||||
* to: {{wallet_address}}, // Who will receive the tokens (or AddressZero for anyone)
|
||||
* to: {{wallet_address}}, // Who will receive the tokens
|
||||
* price: 0.5, // the price to pay for minting those tokens
|
||||
* currencyAddress: NATIVE_TOKEN_ADDRESS, // the currency to pay with
|
||||
* mintStartTime: startTime, // can mint anytime from now
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { NetworkOrSignerOrProvider, TransactionResult } from "..";
|
||||
import { AmountSchema } from "../../../core/schema/shared";
|
||||
import { assertEnabled, detectContractFeature } from "../../common";
|
||||
import {
|
||||
fetchCurrencyMetadata,
|
||||
@@ -10,15 +11,10 @@ import {
|
||||
FEATURE_TOKEN_BATCH_MINTABLE,
|
||||
FEATURE_TOKEN_BURNABLE,
|
||||
FEATURE_TOKEN_SIGNATURE_MINTABLE,
|
||||
FEATURE_TOKEN_CLAIMABLE_WITH_CONDITIONS,
|
||||
FEATURE_TOKEN_CLAIM_CONDITIONS_V2,
|
||||
} from "../../constants/erc20-features";
|
||||
import { TokenMintInput, AmountSchema } from "../../schema";
|
||||
import {
|
||||
Currency,
|
||||
CurrencyValue,
|
||||
Amount,
|
||||
ClaimVerification,
|
||||
} from "../../types";
|
||||
import { TokenMintInput } from "../../schema";
|
||||
import { Currency, CurrencyValue, Amount, ClaimOptions } from "../../types";
|
||||
import {
|
||||
BaseERC20,
|
||||
BaseSignatureMintERC20,
|
||||
@@ -483,14 +479,12 @@ export class Erc20<
|
||||
*/
|
||||
public async claim(
|
||||
amount: Amount,
|
||||
checkERC20Allowance = true,
|
||||
claimData?: ClaimVerification,
|
||||
options?: ClaimOptions,
|
||||
): Promise<TransactionResult> {
|
||||
return this.claimTo(
|
||||
await this.contractWrapper.getSignerAddress(),
|
||||
amount,
|
||||
checkERC20Allowance,
|
||||
claimData,
|
||||
options,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -518,13 +512,12 @@ export class Erc20<
|
||||
public async claimTo(
|
||||
destinationAddress: string,
|
||||
amount: Amount,
|
||||
checkERC20Allowance = true,
|
||||
claimData?: ClaimVerification,
|
||||
options?: ClaimOptions,
|
||||
): Promise<TransactionResult> {
|
||||
return assertEnabled(
|
||||
this.droppable?.claim,
|
||||
FEATURE_TOKEN_CLAIMABLE_WITH_CONDITIONS,
|
||||
).to(destinationAddress, amount, checkERC20Allowance, claimData);
|
||||
FEATURE_TOKEN_CLAIM_CONDITIONS_V2,
|
||||
).to(destinationAddress, amount, options);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -553,7 +546,7 @@ export class Erc20<
|
||||
get claimConditions() {
|
||||
return assertEnabled(
|
||||
this.droppable?.claim,
|
||||
FEATURE_TOKEN_CLAIMABLE_WITH_CONDITIONS,
|
||||
FEATURE_TOKEN_CLAIM_CONDITIONS_V2,
|
||||
).conditions;
|
||||
}
|
||||
|
||||
@@ -627,7 +620,19 @@ export class Erc20<
|
||||
if (
|
||||
detectContractFeature<BaseDropERC20>(
|
||||
this.contractWrapper,
|
||||
"ERC20ClaimableWithConditions",
|
||||
"ERC20ClaimConditionsV1",
|
||||
) ||
|
||||
detectContractFeature<BaseDropERC20>(
|
||||
this.contractWrapper,
|
||||
"ERC20ClaimConditionsV2",
|
||||
) ||
|
||||
detectContractFeature<BaseDropERC20>(
|
||||
this.contractWrapper,
|
||||
"ERC20ClaimPhasesV1",
|
||||
) ||
|
||||
detectContractFeature<BaseDropERC20>(
|
||||
this.contractWrapper,
|
||||
"ERC20ClaimPhasesV2",
|
||||
)
|
||||
) {
|
||||
return new Erc20Droppable(this, this.contractWrapper, this.storage);
|
||||
|
||||
@@ -1,20 +1,17 @@
|
||||
import { NFT } from "../../../core/schema/nft";
|
||||
import { hasFunction } from "../../common";
|
||||
import { FEATURE_NFT_CLAIMABLE_WITH_CONDITIONS } from "../../constants/erc721-features";
|
||||
import { FEATURE_NFT_CLAIM_CONDITIONS_V2 } from "../../constants/erc721-features";
|
||||
import { CustomContractSchema } from "../../schema/contracts/custom";
|
||||
import { ClaimOptions, ClaimVerification } from "../../types";
|
||||
import { ClaimOptions } from "../../types";
|
||||
import { BaseClaimConditionERC721 } from "../../types/eips";
|
||||
import { DetectableFeature } from "../interfaces/DetectableFeature";
|
||||
import { TransactionResultWithId } from "../types";
|
||||
import { TransactionTask } from "./TransactionTask";
|
||||
import { ContractMetadata } from "./contract-metadata";
|
||||
import { ContractWrapper } from "./contract-wrapper";
|
||||
import { DropClaimConditions } from "./drop-claim-conditions";
|
||||
import { Erc721 } from "./erc-721";
|
||||
import type { DropERC721 } from "@thirdweb-dev/contracts-js";
|
||||
import { TokensClaimedEvent } from "@thirdweb-dev/contracts-js/dist/declarations/src/Drop";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
import { BigNumber, BigNumberish, ethers } from "ethers";
|
||||
import { BigNumber, BigNumberish } from "ethers";
|
||||
|
||||
/**
|
||||
* Configure and claim ERC721 NFTs
|
||||
@@ -27,7 +24,7 @@ import { BigNumber, BigNumberish, ethers } from "ethers";
|
||||
* ```
|
||||
*/
|
||||
export class Erc721ClaimableWithConditions implements DetectableFeature {
|
||||
featureName = FEATURE_NFT_CLAIMABLE_WITH_CONDITIONS.name;
|
||||
featureName = FEATURE_NFT_CLAIM_CONDITIONS_V2.name;
|
||||
|
||||
/**
|
||||
* Configure claim conditions
|
||||
@@ -77,38 +74,6 @@ export class Erc721ClaimableWithConditions implements DetectableFeature {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a claim transaction without executing it.
|
||||
* This is useful for estimating the gas cost of a claim transaction, overriding transaction options and having fine grained control over the transaction execution.
|
||||
* @param destinationAddress
|
||||
* @param quantity
|
||||
* @param options
|
||||
*/
|
||||
public async getClaimTransaction(
|
||||
destinationAddress: string,
|
||||
quantity: BigNumberish,
|
||||
options?: ClaimOptions,
|
||||
): Promise<TransactionTask> {
|
||||
if (options?.pricePerToken) {
|
||||
throw new Error(
|
||||
"In ERC721ClaimableWithConditions, price per token is be set via claim conditions by calling `contract.erc721.claimConditions.set()`",
|
||||
);
|
||||
}
|
||||
const claimVerification = await this.conditions.prepareClaim(
|
||||
quantity,
|
||||
options?.checkERC20Allowance === undefined
|
||||
? true
|
||||
: options.checkERC20Allowance,
|
||||
);
|
||||
|
||||
return TransactionTask.make({
|
||||
contractWrapper: this.contractWrapper,
|
||||
functionName: "claim",
|
||||
args: await this.getArgs(destinationAddress, quantity, claimVerification),
|
||||
overrides: claimVerification.overrides,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Claim unique NFTs to a specific Wallet
|
||||
*
|
||||
@@ -127,6 +92,7 @@ export class Erc721ClaimableWithConditions implements DetectableFeature {
|
||||
*
|
||||
* @param destinationAddress - Address you want to send the token to
|
||||
* @param quantity - Quantity of the tokens you want to claim
|
||||
* @param options
|
||||
* @returns - an array of results containing the id of the token claimed, the transaction receipt and a promise to optionally fetch the nft metadata
|
||||
*/
|
||||
public async to(
|
||||
@@ -134,7 +100,7 @@ export class Erc721ClaimableWithConditions implements DetectableFeature {
|
||||
quantity: BigNumberish,
|
||||
options?: ClaimOptions,
|
||||
): Promise<TransactionResultWithId<NFT>[]> {
|
||||
const task = await this.getClaimTransaction(
|
||||
const task = await this.conditions.getClaimTransaction(
|
||||
destinationAddress,
|
||||
quantity,
|
||||
options,
|
||||
@@ -156,48 +122,4 @@ export class Erc721ClaimableWithConditions implements DetectableFeature {
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
private async getArgs(
|
||||
destinationAddress: string,
|
||||
quantity: BigNumberish,
|
||||
claimVerification: ClaimVerification,
|
||||
): Promise<any[]> {
|
||||
const isLegacyNFTContract = await this.isLegacyNFTContract();
|
||||
if (isLegacyNFTContract) {
|
||||
return [
|
||||
destinationAddress,
|
||||
quantity,
|
||||
claimVerification.currencyAddress,
|
||||
claimVerification.price,
|
||||
claimVerification.proofs,
|
||||
claimVerification.maxQuantityPerTransaction,
|
||||
];
|
||||
}
|
||||
return [
|
||||
destinationAddress,
|
||||
quantity,
|
||||
claimVerification.currencyAddress,
|
||||
claimVerification.price,
|
||||
{
|
||||
proof: claimVerification.proofs,
|
||||
maxQuantityInAllowlist: claimVerification.maxQuantityPerTransaction,
|
||||
},
|
||||
ethers.utils.toUtf8Bytes(""),
|
||||
];
|
||||
}
|
||||
|
||||
private async isLegacyNFTContract() {
|
||||
if (hasFunction<DropERC721>("contractType", this.contractWrapper)) {
|
||||
try {
|
||||
const contractType = ethers.utils.toUtf8String(
|
||||
await this.contractWrapper.readContract.contractType(),
|
||||
);
|
||||
return contractType.includes("DropERC721");
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { NFT } from "../../../core/schema/nft";
|
||||
import { calculateClaimCost } from "../../common/claim-conditions";
|
||||
import { FEATURE_NFT_CLAIMABLE } from "../../constants/erc721-features";
|
||||
import { FEATURE_NFT_CLAIM_CUSTOM } from "../../constants/erc721-features";
|
||||
import { ClaimOptions } from "../../types";
|
||||
import { DetectableFeature } from "../interfaces/DetectableFeature";
|
||||
import { TransactionResultWithId } from "../types";
|
||||
@@ -21,7 +21,7 @@ import { BigNumber, BigNumberish, CallOverrides } from "ethers";
|
||||
* ```
|
||||
*/
|
||||
export class Erc721Claimable implements DetectableFeature {
|
||||
featureName = FEATURE_NFT_CLAIMABLE.name;
|
||||
featureName = FEATURE_NFT_CLAIM_CUSTOM.name;
|
||||
|
||||
private erc721: Erc721;
|
||||
private contractWrapper: ContractWrapper<IClaimableERC721>;
|
||||
@@ -77,7 +77,7 @@ export class Erc721Claimable implements DetectableFeature {
|
||||
* const quantity = 1; // how many NFTs you want to claim
|
||||
*
|
||||
* const tx = await contract.erc721.claimTo(address, tokenId, quantity);
|
||||
* const receipt = tx.receipt; // the transaction receipt
|
||||
* const receipt = tx[0].receipt; // the transaction receipt
|
||||
* ```
|
||||
*
|
||||
* @param destinationAddress - Address you want to send the token to
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
import { NFTMetadata, NFTMetadataOrUri } from "../../../core/schema/nft";
|
||||
import {
|
||||
detectContractFeature,
|
||||
hasFunction,
|
||||
} from "../../common/feature-detection";
|
||||
import { detectContractFeature } from "../../common/feature-detection";
|
||||
import { getBaseUriFromBatch, uploadOrExtractURIs } from "../../common/nft";
|
||||
import {
|
||||
FEATURE_NFT_LAZY_MINTABLE,
|
||||
@@ -194,7 +191,19 @@ export class Erc721LazyMintable implements DetectableFeature {
|
||||
if (
|
||||
detectContractFeature<BaseClaimConditionERC721>(
|
||||
this.contractWrapper,
|
||||
"ERC721ClaimableWithConditions",
|
||||
"ERC721ClaimConditionsV1",
|
||||
) ||
|
||||
detectContractFeature<BaseClaimConditionERC721>(
|
||||
this.contractWrapper,
|
||||
"ERC721ClaimConditionsV2",
|
||||
) ||
|
||||
detectContractFeature<BaseClaimConditionERC721>(
|
||||
this.contractWrapper,
|
||||
"ERC721ClaimPhasesV1",
|
||||
) ||
|
||||
detectContractFeature<BaseClaimConditionERC721>(
|
||||
this.contractWrapper,
|
||||
"ERC721ClaimPhasesV2",
|
||||
)
|
||||
) {
|
||||
return new Erc721ClaimableWithConditions(
|
||||
@@ -210,9 +219,8 @@ export class Erc721LazyMintable implements DetectableFeature {
|
||||
if (
|
||||
detectContractFeature<IClaimableERC721>(
|
||||
this.contractWrapper,
|
||||
"ERC721Claimable",
|
||||
) &&
|
||||
!hasFunction("setClaimConditions", this.contractWrapper)
|
||||
"ERC721ClaimCustom",
|
||||
)
|
||||
) {
|
||||
return new Erc721Claimable(this.erc721, this.contractWrapper);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,405 @@
|
||||
import {
|
||||
CommonNFTInput,
|
||||
NFT,
|
||||
NFTMetadata,
|
||||
NFTMetadataInput,
|
||||
NFTMetadataOrUri,
|
||||
} from "../../../core/schema/nft";
|
||||
import { normalizePriceValue, setErc20Allowance } from "../../common/currency";
|
||||
import { getBaseUriFromBatch, uploadOrExtractURIs } from "../../common/nft";
|
||||
import { FEATURE_NFT_TIERED_DROP } from "../../constants/erc721-features";
|
||||
import { GenericRequest } from "../../schema";
|
||||
import {
|
||||
TieredDropPayloadInput,
|
||||
TieredDropPayloadOutput,
|
||||
TieredDropPayloadSchema,
|
||||
TieredDropPayloadWithSignature,
|
||||
} from "../../schema/contracts/tiered-drop";
|
||||
import { UploadProgressEvent } from "../../types/events";
|
||||
import { DetectableFeature } from "../interfaces/DetectableFeature";
|
||||
import { TransactionResult, TransactionResultWithId } from "../types";
|
||||
import { ContractWrapper } from "./contract-wrapper";
|
||||
import { Erc721 } from "./erc-721";
|
||||
import type { TieredDrop, ISignatureAction } from "@thirdweb-dev/contracts-js";
|
||||
import {
|
||||
TokensLazyMintedEvent,
|
||||
TokensClaimedEvent,
|
||||
} from "@thirdweb-dev/contracts-js/dist/declarations/src/TieredDrop";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
import { BigNumberish, ethers } from "ethers";
|
||||
import invariant from "tiny-invariant";
|
||||
|
||||
export class Erc721TieredDrop implements DetectableFeature {
|
||||
featureName = FEATURE_NFT_TIERED_DROP.name;
|
||||
|
||||
private contractWrapper: ContractWrapper<TieredDrop>;
|
||||
private erc721: Erc721;
|
||||
private storage: ThirdwebStorage;
|
||||
|
||||
constructor(
|
||||
erc721: Erc721,
|
||||
contractWrapper: ContractWrapper<TieredDrop>,
|
||||
storage: ThirdwebStorage,
|
||||
) {
|
||||
this.erc721 = erc721;
|
||||
this.contractWrapper = contractWrapper;
|
||||
this.storage = storage;
|
||||
}
|
||||
|
||||
public async getMetadataInTier(
|
||||
tier: string,
|
||||
): Promise<Omit<NFTMetadata, "id">[]> {
|
||||
const tiers =
|
||||
await this.contractWrapper.readContract.getMetadataForAllTiers();
|
||||
const batches = tiers.find((t) => t.tier === tier);
|
||||
|
||||
if (!batches) {
|
||||
throw new Error("Tier not found in contract.");
|
||||
}
|
||||
|
||||
const nfts = await Promise.all(
|
||||
batches.ranges
|
||||
.map((range, i) => {
|
||||
const nftsInRange = [];
|
||||
const baseUri = batches.baseURIs[i];
|
||||
for (
|
||||
let j = range.startIdInclusive.toNumber();
|
||||
j < range.endIdNonInclusive.toNumber();
|
||||
j++
|
||||
) {
|
||||
const uri = `${baseUri}/${j}`;
|
||||
const metadata = this.storage.downloadJSON(uri);
|
||||
nftsInRange.push(metadata);
|
||||
}
|
||||
|
||||
return nftsInRange;
|
||||
})
|
||||
.flat(),
|
||||
);
|
||||
|
||||
return nfts;
|
||||
}
|
||||
|
||||
public async getTokensInTier(tier: string): Promise<NFT[]> {
|
||||
const endIndex =
|
||||
await this.contractWrapper.readContract.getTokensInTierLen();
|
||||
if (endIndex.eq(0)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const ranges = await this.contractWrapper.readContract.getTokensInTier(
|
||||
tier,
|
||||
0,
|
||||
endIndex,
|
||||
);
|
||||
|
||||
const nfts = await Promise.all(
|
||||
ranges
|
||||
.map((range) => {
|
||||
const nftsInRange = [];
|
||||
for (
|
||||
let i = range.startIdInclusive.toNumber();
|
||||
i < range.endIdNonInclusive.toNumber();
|
||||
i++
|
||||
) {
|
||||
nftsInRange.push(this.erc721.get(i));
|
||||
}
|
||||
return nftsInRange;
|
||||
})
|
||||
.flat(),
|
||||
);
|
||||
|
||||
return nfts;
|
||||
}
|
||||
|
||||
public async createBatchWithTier(
|
||||
metadatas: NFTMetadataOrUri[],
|
||||
tier: string,
|
||||
options?: {
|
||||
onProgress: (event: UploadProgressEvent) => void;
|
||||
},
|
||||
): Promise<TransactionResultWithId<NFTMetadata>[]> {
|
||||
// TODO: Change this to on extension
|
||||
const startFileNumber = await this.erc721.nextTokenIdToMint();
|
||||
const batch = await uploadOrExtractURIs(
|
||||
metadatas,
|
||||
this.storage,
|
||||
startFileNumber.toNumber(),
|
||||
options,
|
||||
);
|
||||
const baseUri = getBaseUriFromBatch(batch);
|
||||
|
||||
const receipt = await this.contractWrapper.sendTransaction("lazyMint", [
|
||||
batch.length,
|
||||
baseUri.endsWith("/") ? baseUri : `${baseUri}/`,
|
||||
tier,
|
||||
ethers.utils.toUtf8Bytes(""),
|
||||
]);
|
||||
|
||||
const event = this.contractWrapper.parseLogs<TokensLazyMintedEvent>(
|
||||
"TokensLazyMinted",
|
||||
receipt?.logs,
|
||||
);
|
||||
|
||||
const startingIndex = event[0].args[1];
|
||||
const endingIndex = event[0].args[2];
|
||||
const results: TransactionResultWithId<NFTMetadata>[] = [];
|
||||
for (let id = startingIndex; id.lte(endingIndex); id = id.add(1)) {
|
||||
results.push({
|
||||
id,
|
||||
receipt,
|
||||
data: () => this.erc721.getTokenMetadata(id),
|
||||
});
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
public async createDelayedRevealBatchWithTier(
|
||||
placeholder: NFTMetadataInput,
|
||||
metadatas: NFTMetadataInput[],
|
||||
password: string,
|
||||
tier: string,
|
||||
options?: {
|
||||
onProgress: (event: UploadProgressEvent) => void;
|
||||
},
|
||||
): Promise<TransactionResultWithId<NFTMetadata>[]> {
|
||||
if (!password) {
|
||||
throw new Error("Password is required");
|
||||
}
|
||||
|
||||
const placeholderUris = await this.storage.uploadBatch(
|
||||
[CommonNFTInput.parse(placeholder)],
|
||||
{
|
||||
rewriteFileNames: {
|
||||
fileStartNumber: 0,
|
||||
},
|
||||
},
|
||||
);
|
||||
const placeholderUri = getBaseUriFromBatch(placeholderUris);
|
||||
const startFileNumber = await this.erc721.nextTokenIdToMint();
|
||||
const uris = await this.storage.uploadBatch(
|
||||
metadatas.map((m) => CommonNFTInput.parse(m)),
|
||||
{
|
||||
onProgress: options?.onProgress,
|
||||
rewriteFileNames: {
|
||||
fileStartNumber: startFileNumber.toNumber(),
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const baseUri = getBaseUriFromBatch(uris);
|
||||
const baseUriId = await this.contractWrapper.readContract.getBaseURICount();
|
||||
const chainId = await this.contractWrapper.getChainID();
|
||||
const hashedPassword = ethers.utils.solidityKeccak256(
|
||||
["string", "uint256", "uint256", "address"],
|
||||
[password, chainId, baseUriId, this.contractWrapper.readContract.address],
|
||||
);
|
||||
|
||||
const encryptedBaseUri =
|
||||
await this.contractWrapper.readContract.encryptDecrypt(
|
||||
ethers.utils.toUtf8Bytes(baseUri),
|
||||
hashedPassword,
|
||||
);
|
||||
|
||||
let data: string;
|
||||
const provenanceHash = ethers.utils.solidityKeccak256(
|
||||
["bytes", "bytes", "uint256"],
|
||||
[ethers.utils.toUtf8Bytes(baseUri), hashedPassword, chainId],
|
||||
);
|
||||
data = ethers.utils.defaultAbiCoder.encode(
|
||||
["bytes", "bytes32"],
|
||||
[encryptedBaseUri, provenanceHash],
|
||||
);
|
||||
|
||||
const receipt = await this.contractWrapper.sendTransaction("lazyMint", [
|
||||
uris.length,
|
||||
placeholderUri.endsWith("/") ? placeholderUri : `${placeholderUri}/`,
|
||||
tier,
|
||||
data,
|
||||
]);
|
||||
|
||||
const event = this.contractWrapper.parseLogs<TokensLazyMintedEvent>(
|
||||
"TokensLazyMinted",
|
||||
receipt?.logs,
|
||||
);
|
||||
const startingIndex = event[0].args[1];
|
||||
const endingIndex = event[0].args[2];
|
||||
const results: TransactionResultWithId<NFTMetadata>[] = [];
|
||||
for (let id = startingIndex; id.lte(endingIndex); id = id.add(1)) {
|
||||
results.push({
|
||||
id,
|
||||
receipt,
|
||||
data: () => this.erc721.getTokenMetadata(id),
|
||||
});
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
public async reveal(
|
||||
batchId: BigNumberish,
|
||||
password: string,
|
||||
): Promise<TransactionResult> {
|
||||
if (!password) {
|
||||
throw new Error("Password is required");
|
||||
}
|
||||
const chainId = await this.contractWrapper.getChainID();
|
||||
const key = ethers.utils.solidityKeccak256(
|
||||
["string", "uint256", "uint256", "address"],
|
||||
[password, chainId, batchId, this.contractWrapper.readContract.address],
|
||||
);
|
||||
// performing the reveal locally to make sure it'd succeed before sending the transaction
|
||||
try {
|
||||
const decryptedUri = await this.contractWrapper
|
||||
.callStatic()
|
||||
.reveal(batchId, key);
|
||||
// basic sanity check for making sure decryptedUri is valid
|
||||
// this is optional because invalid decryption key would result in non-utf8 bytes and
|
||||
// ethers would throw when trying to decode it
|
||||
if (!decryptedUri.includes("://") || !decryptedUri.endsWith("/")) {
|
||||
throw new Error("invalid password");
|
||||
}
|
||||
} catch (e) {
|
||||
throw new Error("invalid password");
|
||||
}
|
||||
|
||||
return {
|
||||
receipt: await this.contractWrapper.sendTransaction("reveal", [
|
||||
batchId,
|
||||
key,
|
||||
]),
|
||||
};
|
||||
}
|
||||
|
||||
public async generate(
|
||||
payloadToSign: TieredDropPayloadInput,
|
||||
): Promise<TieredDropPayloadWithSignature> {
|
||||
const [payload] = await this.generateBatch([payloadToSign]);
|
||||
return payload;
|
||||
}
|
||||
|
||||
public async generateBatch(
|
||||
payloadsToSign: TieredDropPayloadInput[],
|
||||
): Promise<TieredDropPayloadWithSignature[]> {
|
||||
const parsedPayloads = payloadsToSign.map((payload) =>
|
||||
TieredDropPayloadSchema.parse(payload),
|
||||
);
|
||||
const chainId = await this.contractWrapper.getChainID();
|
||||
const signer = this.contractWrapper.getSigner();
|
||||
invariant(signer, "No signer available");
|
||||
|
||||
return await Promise.all(
|
||||
parsedPayloads.map(async (payload) => {
|
||||
const signature = await this.contractWrapper.signTypedData(
|
||||
signer,
|
||||
{
|
||||
name: "SignatureAction",
|
||||
version: "1",
|
||||
chainId,
|
||||
verifyingContract: this.contractWrapper.readContract.address,
|
||||
},
|
||||
{ GenericRequest: GenericRequest },
|
||||
await this.mapPayloadToContractStruct(payload),
|
||||
);
|
||||
|
||||
return { payload, signature: signature.toString() };
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
public async verify(
|
||||
signedPayload: TieredDropPayloadWithSignature,
|
||||
): Promise<boolean> {
|
||||
const message = await this.mapPayloadToContractStruct(
|
||||
signedPayload.payload,
|
||||
);
|
||||
const verification = await this.contractWrapper.readContract.verify(
|
||||
message,
|
||||
signedPayload.signature,
|
||||
);
|
||||
return verification[0];
|
||||
}
|
||||
|
||||
public async claimWithSignature(
|
||||
signedPayload: TieredDropPayloadWithSignature,
|
||||
): Promise<TransactionResultWithId<NFT>[]> {
|
||||
const message = await this.mapPayloadToContractStruct(
|
||||
signedPayload.payload,
|
||||
);
|
||||
const pricePerToken = await normalizePriceValue(
|
||||
this.contractWrapper.getProvider(),
|
||||
signedPayload.payload.price,
|
||||
signedPayload.payload.currencyAddress,
|
||||
);
|
||||
const price = pricePerToken.mul(signedPayload.payload.quantity);
|
||||
|
||||
const overrides = await this.contractWrapper.getCallOverrides();
|
||||
await setErc20Allowance(
|
||||
this.contractWrapper,
|
||||
price,
|
||||
signedPayload.payload.currencyAddress,
|
||||
overrides,
|
||||
);
|
||||
|
||||
const receipt = await this.contractWrapper.sendTransaction(
|
||||
"claimWithSignature",
|
||||
[message, signedPayload.signature],
|
||||
overrides,
|
||||
);
|
||||
const event = this.contractWrapper.parseLogs<TokensClaimedEvent>(
|
||||
"TokensClaimed",
|
||||
receipt?.logs,
|
||||
);
|
||||
const startingIndex = event[0].args.startTokenId;
|
||||
const endingIndex = startingIndex.add(event[0].args.quantityClaimed);
|
||||
const results: TransactionResultWithId<NFT>[] = [];
|
||||
for (let id = startingIndex; id.lt(endingIndex); id = id.add(1)) {
|
||||
results.push({
|
||||
id,
|
||||
receipt,
|
||||
data: () => this.erc721.get(id),
|
||||
});
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
private async mapPayloadToContractStruct(
|
||||
payload: TieredDropPayloadOutput,
|
||||
): Promise<ISignatureAction.GenericRequestStruct> {
|
||||
const normalizedPricePerToken = await normalizePriceValue(
|
||||
this.contractWrapper.getProvider(),
|
||||
payload.price,
|
||||
payload.currencyAddress,
|
||||
);
|
||||
const data = ethers.utils.defaultAbiCoder.encode(
|
||||
[
|
||||
"string[]",
|
||||
"address",
|
||||
"address",
|
||||
"uint256",
|
||||
"address",
|
||||
"uint256",
|
||||
"uint256",
|
||||
"address",
|
||||
],
|
||||
[
|
||||
payload.tierPriority,
|
||||
payload.to,
|
||||
payload.royaltyRecipient,
|
||||
payload.royaltyBps,
|
||||
payload.primarySaleRecipient,
|
||||
payload.quantity,
|
||||
normalizedPricePerToken,
|
||||
payload.currencyAddress,
|
||||
],
|
||||
);
|
||||
|
||||
return {
|
||||
uid: payload.uid,
|
||||
validityStartTimestamp: payload.mintStartTime,
|
||||
validityEndTimestamp: payload.mintEndTime,
|
||||
data,
|
||||
} as ISignatureAction.GenericRequestStruct;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import { hasFunction } from "../../common";
|
||||
import { normalizePriceValue, setErc20Allowance } from "../../common/currency";
|
||||
import { uploadOrExtractURIs } from "../../common/nft";
|
||||
import { FEATURE_NFT_SIGNATURE_MINTABLE } from "../../constants/erc721-features";
|
||||
import { FEATURE_NFT_SIGNATURE_MINTABLE_V2 } from "../../constants/erc721-features";
|
||||
import {
|
||||
MintRequest721,
|
||||
MintRequest721withQuantity,
|
||||
@@ -30,7 +30,7 @@ import invariant from "tiny-invariant";
|
||||
* @public
|
||||
*/
|
||||
export class Erc721WithQuantitySignatureMintable implements DetectableFeature {
|
||||
featureName = FEATURE_NFT_SIGNATURE_MINTABLE.name;
|
||||
featureName = FEATURE_NFT_SIGNATURE_MINTABLE_V2.name;
|
||||
|
||||
private contractWrapper: ContractWrapper<SignatureMintERC721 | TokenERC721>;
|
||||
|
||||
@@ -190,7 +190,7 @@ export class Erc721WithQuantitySignatureMintable implements DetectableFeature {
|
||||
* const endTime = new Date(Date.now() + 60 * 60 * 24 * 1000);
|
||||
* const payload = {
|
||||
* metadata: nftMetadata, // The NFT to mint
|
||||
* to: {{wallet_address}}, // Who will receive the NFT (or AddressZero for anyone)
|
||||
* to: {{wallet_address}}, // Who will receive the NFT
|
||||
* quantity: 2, // the quantity of NFTs to mint
|
||||
* price: 0.5, // the price per NFT
|
||||
* currencyAddress: NATIVE_TOKEN_ADDRESS, // the currency to pay with
|
||||
@@ -247,7 +247,7 @@ export class Erc721WithQuantitySignatureMintable implements DetectableFeature {
|
||||
* const endTime = new Date(Date.now() + 60 * 60 * 24 * 1000);
|
||||
* const payload = {
|
||||
* metadata: nftMetadata, // The NFT to mint
|
||||
* to: {{wallet_address}}, // Who will receive the NFT (or AddressZero for anyone)
|
||||
* to: {{wallet_address}}, // Who will receive the NFT
|
||||
* quantity: 2, // the quantity of NFTs to mint
|
||||
* price: 0.5, // the price per NFT
|
||||
* currencyAddress: NATIVE_TOKEN_ADDRESS, // the currency to pay with
|
||||
|
||||
@@ -12,13 +12,14 @@ import {
|
||||
FEATURE_NFT,
|
||||
FEATURE_NFT_BATCH_MINTABLE,
|
||||
FEATURE_NFT_BURNABLE,
|
||||
FEATURE_NFT_CLAIMABLE,
|
||||
FEATURE_NFT_CLAIMABLE_WITH_CONDITIONS,
|
||||
FEATURE_NFT_CLAIM_CUSTOM,
|
||||
FEATURE_NFT_CLAIM_CONDITIONS_V2,
|
||||
FEATURE_NFT_LAZY_MINTABLE,
|
||||
FEATURE_NFT_MINTABLE,
|
||||
FEATURE_NFT_REVEALABLE,
|
||||
FEATURE_NFT_SIGNATURE_MINTABLE,
|
||||
FEATURE_NFT_SUPPLY,
|
||||
FEATURE_NFT_TIERED_DROP,
|
||||
FEATURE_NFT_SIGNATURE_MINTABLE_V2,
|
||||
} from "../../constants/erc721-features";
|
||||
import { BaseDropERC721, BaseERC721 } from "../../types/eips";
|
||||
import { ClaimOptions, UploadProgressEvent } from "../../types/index";
|
||||
@@ -34,6 +35,7 @@ import { Erc721Burnable } from "./erc-721-burnable";
|
||||
import { Erc721LazyMintable } from "./erc-721-lazymintable";
|
||||
import { Erc721Mintable } from "./erc-721-mintable";
|
||||
import { Erc721Supply } from "./erc-721-supply";
|
||||
import { Erc721TieredDrop } from "./erc-721-tiered-drop";
|
||||
import { Erc721WithQuantitySignatureMintable } from "./erc-721-with-quantity-signature-mintable";
|
||||
import type {
|
||||
DropERC721,
|
||||
@@ -43,6 +45,7 @@ import type {
|
||||
ISignatureMintERC721,
|
||||
Multiwrap,
|
||||
SignatureDrop,
|
||||
TieredDrop,
|
||||
TokenERC721,
|
||||
} from "@thirdweb-dev/contracts-js";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
@@ -72,6 +75,7 @@ export class Erc721<
|
||||
private mintable: Erc721Mintable | undefined;
|
||||
private burnable: Erc721Burnable | undefined;
|
||||
private lazyMintable: Erc721LazyMintable | undefined;
|
||||
private tieredDropable: Erc721TieredDrop | undefined;
|
||||
private signatureMintable: Erc721WithQuantitySignatureMintable | undefined;
|
||||
protected contractWrapper: ContractWrapper<T>;
|
||||
protected storage: ThirdwebStorage;
|
||||
@@ -92,6 +96,7 @@ export class Erc721<
|
||||
this.mintable = this.detectErc721Mintable();
|
||||
this.burnable = this.detectErc721Burnable();
|
||||
this.lazyMintable = this.detectErc721LazyMintable();
|
||||
this.tieredDropable = this.detectErc721TieredDrop();
|
||||
this.signatureMintable = this.detectErc721SignatureMintable();
|
||||
this._chainId = chainId;
|
||||
}
|
||||
@@ -595,7 +600,7 @@ export class Erc721<
|
||||
if (claim) {
|
||||
return claim.to(destinationAddress, quantity, options);
|
||||
}
|
||||
throw new ExtensionNotImplementedError(FEATURE_NFT_CLAIMABLE);
|
||||
throw new ExtensionNotImplementedError(FEATURE_NFT_CLAIM_CUSTOM);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -613,7 +618,7 @@ export class Erc721<
|
||||
const claimWithConditions = this.lazyMintable?.claimWithConditions;
|
||||
const claim = this.lazyMintable?.claim;
|
||||
if (claimWithConditions) {
|
||||
return claimWithConditions.getClaimTransaction(
|
||||
return claimWithConditions.conditions.getClaimTransaction(
|
||||
destinationAddress,
|
||||
quantity,
|
||||
options,
|
||||
@@ -622,7 +627,7 @@ export class Erc721<
|
||||
if (claim) {
|
||||
return claim.getClaimTransaction(destinationAddress, quantity, options);
|
||||
}
|
||||
throw new ExtensionNotImplementedError(FEATURE_NFT_CLAIMABLE);
|
||||
throw new ExtensionNotImplementedError(FEATURE_NFT_CLAIM_CUSTOM);
|
||||
}
|
||||
|
||||
public async totalClaimedSupply(): Promise<BigNumber> {
|
||||
@@ -682,10 +687,20 @@ export class Erc721<
|
||||
get claimConditions() {
|
||||
return assertEnabled(
|
||||
this.lazyMintable?.claimWithConditions,
|
||||
FEATURE_NFT_CLAIMABLE_WITH_CONDITIONS,
|
||||
FEATURE_NFT_CLAIM_CONDITIONS_V2,
|
||||
).conditions;
|
||||
}
|
||||
|
||||
////// ERC721 Tiered Drop Extension //////
|
||||
|
||||
/**
|
||||
* Tiered Drop
|
||||
* @remarks Drop lazy minted NFTs using a tiered drop mechanism.
|
||||
*/
|
||||
get tieredDrop() {
|
||||
return assertEnabled(this.tieredDropable, FEATURE_NFT_TIERED_DROP);
|
||||
}
|
||||
|
||||
////// ERC721 SignatureMint Extension //////
|
||||
|
||||
/**
|
||||
@@ -706,7 +721,7 @@ export class Erc721<
|
||||
get signature() {
|
||||
return assertEnabled(
|
||||
this.signatureMintable,
|
||||
FEATURE_NFT_SIGNATURE_MINTABLE,
|
||||
FEATURE_NFT_SIGNATURE_MINTABLE_V2,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -828,13 +843,29 @@ export class Erc721<
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private detectErc721TieredDrop(): Erc721TieredDrop | undefined {
|
||||
if (
|
||||
detectContractFeature<TieredDrop>(
|
||||
this.contractWrapper,
|
||||
"ERC721TieredDrop",
|
||||
)
|
||||
) {
|
||||
return new Erc721TieredDrop(this, this.contractWrapper, this.storage);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private detectErc721SignatureMintable():
|
||||
| Erc721WithQuantitySignatureMintable
|
||||
| undefined {
|
||||
if (
|
||||
detectContractFeature<ISignatureMintERC721>(
|
||||
this.contractWrapper,
|
||||
"ERC721SignatureMint",
|
||||
"ERC721SignatureMintV1",
|
||||
) ||
|
||||
detectContractFeature<ISignatureMintERC721>(
|
||||
this.contractWrapper,
|
||||
"ERC721SignatureMintV2",
|
||||
)
|
||||
) {
|
||||
return new Erc721WithQuantitySignatureMintable(
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
import { TransactionError } from "../../common";
|
||||
import { CONTRACT_ADDRESSES, SUPPORTED_CHAIN_IDS } from "../../constants";
|
||||
import {
|
||||
getApprovedImplementation,
|
||||
getDefaultTrustedForwarders,
|
||||
} from "../../constants";
|
||||
import {
|
||||
EditionDropInitializer,
|
||||
EditionInitializer,
|
||||
getContractName,
|
||||
MarketplaceInitializer,
|
||||
MultiwrapInitializer,
|
||||
NFTCollectionInitializer,
|
||||
@@ -41,6 +44,22 @@ import { z } from "zod";
|
||||
export class ContractFactory extends ContractWrapper<TWFactory> {
|
||||
private storage: ThirdwebStorage;
|
||||
|
||||
// Map from contract type to version to deploy specific versions by default
|
||||
private DEFAULT_VERSION_MAP: Record<PrebuiltContractType, number> = {
|
||||
[NFTDropInitializer.contractType]: 3,
|
||||
[NFTCollectionInitializer.contractType]: 1,
|
||||
[SignatureDropInitializer.contractType]: 4,
|
||||
[MultiwrapInitializer.contractType]: 1,
|
||||
[EditionDropInitializer.contractType]: 2,
|
||||
[EditionInitializer.contractType]: 1,
|
||||
[TokenDropInitializer.contractType]: 2,
|
||||
[TokenInitializer.contractType]: 1,
|
||||
[VoteInitializer.contractType]: 1,
|
||||
[SplitInitializer.contractType]: 1,
|
||||
[MarketplaceInitializer.contractType]: 2,
|
||||
[PackInitializer.contractType]: 2,
|
||||
};
|
||||
|
||||
constructor(
|
||||
factoryAddr: string,
|
||||
network: NetworkOrSignerOrProvider,
|
||||
@@ -56,6 +75,7 @@ export class ContractFactory extends ContractWrapper<TWFactory> {
|
||||
contractMetadata: z.input<
|
||||
DeploySchemaForPrebuiltContractType<TContractType>
|
||||
>,
|
||||
version?: number,
|
||||
): Promise<string> {
|
||||
const contract = PREBUILT_CONTRACTS_MAP[contractType];
|
||||
const metadata = contract.schema.deploy.parse(contractMetadata);
|
||||
@@ -63,42 +83,35 @@ export class ContractFactory extends ContractWrapper<TWFactory> {
|
||||
// TODO: is there any special pre-processing we need to do before uploading?
|
||||
const contractURI = await this.storage.upload(metadata);
|
||||
|
||||
const ABI = await contract.getAbi();
|
||||
const implementationAddress = await this.getImplementation(
|
||||
contract,
|
||||
version,
|
||||
);
|
||||
|
||||
if (
|
||||
!implementationAddress ||
|
||||
implementationAddress === constants.AddressZero
|
||||
) {
|
||||
throw new Error(`No implementation found for ${contractType}`);
|
||||
}
|
||||
|
||||
const ABI = await contract.getAbi(
|
||||
implementationAddress,
|
||||
this.getProvider(),
|
||||
);
|
||||
|
||||
const encodedFunc = Contract.getInterface(ABI).encodeFunctionData(
|
||||
"initialize",
|
||||
await this.getDeployArguments(contractType, metadata, contractURI),
|
||||
);
|
||||
|
||||
const encodedType = ethers.utils.formatBytes32String(contract.name);
|
||||
let receipt;
|
||||
try {
|
||||
receipt = await this.sendTransaction("deployProxy", [
|
||||
encodedType,
|
||||
encodedFunc,
|
||||
]);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
// if the error is caused by user cancelling the transaction, just re-throw it
|
||||
if (
|
||||
(e as TransactionError).message
|
||||
.toLowerCase()
|
||||
.includes("user rejected transaction") ||
|
||||
(e as TransactionError).reason
|
||||
.toLowerCase()
|
||||
.includes("user rejected transaction")
|
||||
) {
|
||||
throw e;
|
||||
}
|
||||
|
||||
// deploy might fail due to salt already used, fallback to deterministic deploy
|
||||
const blockNumber = await this.getProvider().getBlockNumber();
|
||||
receipt = await this.sendTransaction("deployProxyDeterministic", [
|
||||
encodedType,
|
||||
encodedFunc,
|
||||
ethers.utils.formatBytes32String(blockNumber.toString()),
|
||||
]);
|
||||
}
|
||||
const blockNumber = await this.getProvider().getBlockNumber();
|
||||
const salt = ethers.utils.formatBytes32String(blockNumber.toString());
|
||||
const receipt = await this.sendTransaction("deployProxyByImplementation", [
|
||||
implementationAddress,
|
||||
encodedFunc,
|
||||
salt,
|
||||
]);
|
||||
|
||||
const events = this.parseLogs<ProxyDeployedEvent>(
|
||||
"ProxyDeployed",
|
||||
@@ -140,7 +153,15 @@ export class ContractFactory extends ContractWrapper<TWFactory> {
|
||||
return events[0].args.proxy;
|
||||
}
|
||||
|
||||
private async getDeployArguments<TContractType extends PrebuiltContractType>(
|
||||
/**
|
||||
*
|
||||
* @param contractType
|
||||
* @param metadata
|
||||
* @param contractURI
|
||||
* @returns
|
||||
* @internal
|
||||
*/
|
||||
public async getDeployArguments<TContractType extends PrebuiltContractType>(
|
||||
contractType: TContractType,
|
||||
metadata: z.input<DeploySchemaForPrebuiltContractType<TContractType>>,
|
||||
contractURI: string,
|
||||
@@ -274,15 +295,41 @@ export class ContractFactory extends ContractWrapper<TWFactory> {
|
||||
|
||||
private async getDefaultTrustedForwarders(): Promise<string[]> {
|
||||
const chainId = await this.getChainID();
|
||||
const chainEnum = SUPPORTED_CHAIN_IDS.find((c) => c === chainId);
|
||||
const biconomyForwarder = chainEnum
|
||||
? CONTRACT_ADDRESSES[chainEnum].biconomyForwarder
|
||||
: constants.AddressZero;
|
||||
const openzeppelinForwarder = chainEnum
|
||||
? CONTRACT_ADDRESSES[chainEnum].openzeppelinForwarder
|
||||
: constants.AddressZero;
|
||||
return biconomyForwarder !== constants.AddressZero
|
||||
? [openzeppelinForwarder, biconomyForwarder]
|
||||
: [openzeppelinForwarder];
|
||||
return getDefaultTrustedForwarders(chainId);
|
||||
}
|
||||
|
||||
private async getImplementation(
|
||||
contract: typeof PREBUILT_CONTRACTS_MAP[PrebuiltContractType],
|
||||
version?: number,
|
||||
) {
|
||||
const encodedType = ethers.utils.formatBytes32String(contract.name);
|
||||
const chainId = await this.getChainID();
|
||||
const approvedImplementation = getApprovedImplementation(
|
||||
chainId,
|
||||
contract.contractType,
|
||||
);
|
||||
// return approved implementation if it exists and we're not overriding the version
|
||||
if (
|
||||
approvedImplementation &&
|
||||
approvedImplementation.length > 0 &&
|
||||
version === undefined
|
||||
) {
|
||||
return approvedImplementation;
|
||||
}
|
||||
return this.readContract.getImplementation(
|
||||
encodedType,
|
||||
version !== undefined
|
||||
? version
|
||||
: this.DEFAULT_VERSION_MAP[contract.contractType],
|
||||
);
|
||||
}
|
||||
|
||||
public async getLatestVersion(contractType: PrebuiltContractType) {
|
||||
const name = getContractName(contractType);
|
||||
if (!name) {
|
||||
throw new Error(`Invalid contract type ${contractType}`);
|
||||
}
|
||||
const encodedType = ethers.utils.formatBytes32String(name);
|
||||
return this.readContract.currentVersion(encodedType);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,9 +26,13 @@ import {
|
||||
Offer,
|
||||
} from "../../types/marketplace";
|
||||
import { TransactionResult, TransactionResultWithId } from "../types";
|
||||
import { ContractEncoder } from "./contract-encoder";
|
||||
import { ContractWrapper } from "./contract-wrapper";
|
||||
import type { IMarketplace, Marketplace } from "@thirdweb-dev/contracts-js";
|
||||
import { ListingAddedEvent } from "@thirdweb-dev/contracts-js/dist/declarations/src/Marketplace";
|
||||
import {
|
||||
ListingAddedEvent,
|
||||
Marketplace as MarketplaceContract,
|
||||
} from "@thirdweb-dev/contracts-js/dist/declarations/src/Marketplace";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
import { BigNumber, BigNumberish, ethers, constants } from "ethers";
|
||||
import invariant from "tiny-invariant";
|
||||
@@ -40,6 +44,7 @@ import invariant from "tiny-invariant";
|
||||
export class MarketplaceAuction {
|
||||
private contractWrapper: ContractWrapper<Marketplace>;
|
||||
private storage: ThirdwebStorage;
|
||||
public encoder: ContractEncoder<MarketplaceContract>;
|
||||
|
||||
constructor(
|
||||
contractWrapper: ContractWrapper<Marketplace>,
|
||||
@@ -47,6 +52,7 @@ export class MarketplaceAuction {
|
||||
) {
|
||||
this.contractWrapper = contractWrapper;
|
||||
this.storage = storage;
|
||||
this.encoder = new ContractEncoder<Marketplace>(contractWrapper);
|
||||
}
|
||||
|
||||
getAddress(): string {
|
||||
@@ -401,9 +407,9 @@ export class MarketplaceAuction {
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the Auction
|
||||
* Close the Auction for the buyer or the seller
|
||||
*
|
||||
* @remarks Closes the Auction and executes the sale.
|
||||
* @remarks Closes the Auction and executes the sale for the buyer or the seller.
|
||||
*
|
||||
* @example
|
||||
* ```javascript
|
||||
@@ -442,6 +448,49 @@ export class MarketplaceAuction {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the Auction Sale
|
||||
*
|
||||
* @remarks Closes the Auction and executes the sale for both parties.
|
||||
*
|
||||
* @example
|
||||
* ```javascript
|
||||
* // The listing ID of the auction listing you want to close
|
||||
* const listingId = "0";
|
||||
* await contract.auction.executeSale(listingId);
|
||||
* ```
|
||||
*
|
||||
* @param listingId - the auction listing ud to close
|
||||
*/
|
||||
public async executeSale(listingId: BigNumberish) {
|
||||
const listing = await this.validateListing(BigNumber.from(listingId));
|
||||
try {
|
||||
const winningBid = await this.getWinningBid(listingId);
|
||||
invariant(winningBid, "No winning bid found");
|
||||
const closeForSeller = this.encoder.encode("closeAuction", [
|
||||
listingId,
|
||||
listing.sellerAddress,
|
||||
]);
|
||||
const closeForBuyer = this.encoder.encode("closeAuction", [
|
||||
listingId,
|
||||
winningBid.buyerAddress,
|
||||
]);
|
||||
return await this.contractWrapper.multiCall([
|
||||
closeForSeller,
|
||||
closeForBuyer,
|
||||
]);
|
||||
} catch (err: any) {
|
||||
if (err.message.includes("cannot close auction before it has ended")) {
|
||||
throw new AuctionHasNotEndedError(
|
||||
listingId.toString(),
|
||||
listing.endTimeInEpochSeconds.toString(),
|
||||
);
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update an Auction listing with new metadata
|
||||
* @param listing - the listing id to update
|
||||
|
||||
@@ -401,7 +401,7 @@ export class ThirdwebSDK extends RPCConnectionHandler {
|
||||
// otherwise if it's a prebuilt contract we can just use the contract type
|
||||
const contractAbi = await PREBUILT_CONTRACTS_MAP[
|
||||
resolvedContractType
|
||||
].getAbi();
|
||||
].getAbi(address, this.getProvider());
|
||||
newContract = await this.getContractFromAbi(address, contractAbi);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,10 +139,31 @@ export class UserWallet {
|
||||
* const address = await sdk.wallet.getAddress();
|
||||
* ```
|
||||
*/
|
||||
async getAddress(): Promise<string> {
|
||||
public async getAddress(): Promise<string> {
|
||||
return await this.requireWallet().getAddress();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the currently connected wallet's chainId
|
||||
* @internal
|
||||
*/
|
||||
public async getChainId(): Promise<number> {
|
||||
return await this.requireWallet().getChainId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether there's a signer connected with the SDK
|
||||
* @internal
|
||||
*/
|
||||
public isConnected(): boolean {
|
||||
try {
|
||||
this.requireWallet();
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sign any message with the connected wallet private key
|
||||
* @param message - the message to sign
|
||||
@@ -157,7 +178,7 @@ export class UserWallet {
|
||||
* const signature = await sdk.wallet.sign(message);
|
||||
* ```
|
||||
*/
|
||||
async sign(message: string): Promise<string> {
|
||||
public async sign(message: string): Promise<string> {
|
||||
const signer = this.requireWallet();
|
||||
return await signer.signMessage(message);
|
||||
}
|
||||
@@ -187,7 +208,7 @@ export class UserWallet {
|
||||
* Send a raw transaction to the blockchain from the connected wallet
|
||||
* @param transactionRequest - raw transaction data to send to the blockchain
|
||||
*/
|
||||
async sendRawTransaction(
|
||||
public async sendRawTransaction(
|
||||
transactionRequest: providers.TransactionRequest,
|
||||
): Promise<TransactionResult> {
|
||||
const signer = this.requireWallet();
|
||||
|
||||
@@ -29,7 +29,7 @@ export * from "./constants";
|
||||
export * from "./contracts";
|
||||
|
||||
// export integration things
|
||||
export * from "./integrations/paper-xyz";
|
||||
export * from "./integrations/thirdweb-checkout";
|
||||
|
||||
// explcitly export the *TYPES* of prebuilt contracts
|
||||
export type { Edition } from "./contracts/prebuilt-implementations/edition";
|
||||
|
||||
+7
-8
@@ -1,10 +1,9 @@
|
||||
import { ChainId } from "../constants";
|
||||
import { ContractWrapper } from "../core/classes/contract-wrapper";
|
||||
import { SignedPayload721WithQuantitySignature } from "../schema/contracts/common/signature";
|
||||
import { DropERC721 } from "@thirdweb-dev/contracts-js/dist/declarations/src/DropERC721";
|
||||
import { DropERC1155 } from "@thirdweb-dev/contracts-js/dist/declarations/src/DropERC1155";
|
||||
import { PrebuiltEditionDrop, PrebuiltNFTDrop } from "../types/eips";
|
||||
import { SignatureDrop } from "@thirdweb-dev/contracts-js/dist/declarations/src/SignatureDrop";
|
||||
import fetch from "cross-fetch";
|
||||
// import fetch from "cross-fetch";
|
||||
import invariant from "tiny-invariant";
|
||||
|
||||
const PAPER_API_BASE = `https://paper.xyz/api` as const;
|
||||
@@ -133,9 +132,9 @@ export type PaperCreateCheckoutLinkShardParams = {
|
||||
* @internal
|
||||
*/
|
||||
export type PaperCreateCheckoutLinkIntentParams<
|
||||
TContract extends DropERC721 | DropERC1155 | SignatureDrop,
|
||||
TContract extends PrebuiltNFTDrop | PrebuiltEditionDrop | SignatureDrop,
|
||||
> = PaperCreateCheckoutLinkShardParams &
|
||||
(TContract extends DropERC1155
|
||||
(TContract extends PrebuiltEditionDrop
|
||||
? {
|
||||
contractArgs: { tokenId: string };
|
||||
}
|
||||
@@ -143,7 +142,7 @@ export type PaperCreateCheckoutLinkIntentParams<
|
||||
? {
|
||||
contractArgs: SignedPayload721WithQuantitySignature;
|
||||
}
|
||||
: TContract extends DropERC721
|
||||
: TContract extends PrebuiltNFTDrop
|
||||
? {}
|
||||
: never);
|
||||
|
||||
@@ -165,7 +164,7 @@ const DEFAULT_PARAMS: Partial<PaperCreateCheckoutLinkShardParams> = {
|
||||
* @internal
|
||||
*/
|
||||
export async function createCheckoutLinkIntent<
|
||||
TContract extends DropERC721 | DropERC1155 | SignatureDrop,
|
||||
TContract extends PrebuiltNFTDrop | PrebuiltEditionDrop | SignatureDrop,
|
||||
>(
|
||||
contractId: string,
|
||||
params: PaperCreateCheckoutLinkIntentParams<TContract>,
|
||||
@@ -203,7 +202,7 @@ export async function createCheckoutLinkIntent<
|
||||
* @internal
|
||||
*/
|
||||
export class PaperCheckout<
|
||||
TContract extends DropERC721 | DropERC1155 | SignatureDrop,
|
||||
TContract extends PrebuiltNFTDrop | PrebuiltEditionDrop | SignatureDrop,
|
||||
> {
|
||||
private contractWrapper;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user