Compare commits
34
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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,21 @@
|
||||
# @thirdweb-dev/auth
|
||||
|
||||
## 2.0.29
|
||||
|
||||
## 2.0.28
|
||||
|
||||
## 2.0.27
|
||||
|
||||
## 2.0.26
|
||||
|
||||
## 2.0.25
|
||||
|
||||
## 2.0.24
|
||||
|
||||
## 2.0.23
|
||||
|
||||
## 2.0.22
|
||||
|
||||
## 2.0.21
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@thirdweb-dev/auth",
|
||||
"version": "2.0.21",
|
||||
"version": "2.0.29",
|
||||
"exports": {
|
||||
"./next": {
|
||||
"module": "./next/dist/thirdweb-dev-auth-next.esm.js",
|
||||
|
||||
@@ -1,5 +1,61 @@
|
||||
# thirdweb
|
||||
|
||||
## 0.8.15
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`3eba69a`](https://github.com/thirdweb-dev/js/commit/3eba69afd78b7aa472a9c00c9d4220bb69ad1d3e), [`4af785c`](https://github.com/thirdweb-dev/js/commit/4af785c37c831ecd7087c1c9eb9095922cac0855), [`6f818f3`](https://github.com/thirdweb-dev/js/commit/6f818f393eb9023b53a3358d62d60ec23a9246bd), [`b79dc18`](https://github.com/thirdweb-dev/js/commit/b79dc18f7ab5155bbf4af02dc1a953546160bad0)]:
|
||||
- @thirdweb-dev/sdk@3.5.2
|
||||
|
||||
## 0.8.14
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`4e7b293`](https://github.com/thirdweb-dev/js/commit/4e7b293c543244de79c150ca8c37d941221fc9a3), [`c9c0e4f`](https://github.com/thirdweb-dev/js/commit/c9c0e4ff39b054af746f3aec75fcbd0a3ef7239d)]:
|
||||
- @thirdweb-dev/sdk@3.5.1
|
||||
|
||||
## 0.8.13
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`5200d5d`](https://github.com/thirdweb-dev/js/commit/5200d5d7e730da58bd4d3e7c222e3c661265f913), [`42a6065`](https://github.com/thirdweb-dev/js/commit/42a606588dfed03c67c57357dd0ee1ad19fad6ea)]:
|
||||
- @thirdweb-dev/sdk@3.5.0
|
||||
|
||||
## 0.8.12
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`e731c1a`](https://github.com/thirdweb-dev/js/commit/e731c1a7c341e6f2b9cc42c88d5c1b036dcf4a7c)]:
|
||||
- @thirdweb-dev/sdk@3.4.5
|
||||
|
||||
## 0.8.11
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies []:
|
||||
- @thirdweb-dev/sdk@3.4.4
|
||||
|
||||
## 0.8.10
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`1c24c3c`](https://github.com/thirdweb-dev/js/commit/1c24c3c3c48476a824f817e09e7bf0fbe67c1db5), [`65fc7ba`](https://github.com/thirdweb-dev/js/commit/65fc7ba6fff093a971b53cb0979c39768e090ba1)]:
|
||||
- @thirdweb-dev/sdk@3.4.3
|
||||
|
||||
## 0.8.9
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`21c21c1`](https://github.com/thirdweb-dev/js/commit/21c21c1adfd09b60ad563bfd4c308597140de33c), [`3522917`](https://github.com/thirdweb-dev/js/commit/352291791ee900e1500f84f095290497934d2f60)]:
|
||||
- @thirdweb-dev/sdk@3.4.2
|
||||
|
||||
## 0.8.8
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`72227b2`](https://github.com/thirdweb-dev/js/commit/72227b2e166a3a68bbb41cf2b389322f5b7547a2)]:
|
||||
- @thirdweb-dev/sdk@3.4.1
|
||||
|
||||
## 0.8.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "thirdweb",
|
||||
"main": "dist/cli/index.js",
|
||||
"version": "0.8.7",
|
||||
"version": "0.8.15",
|
||||
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/cli",
|
||||
"author": "thirdweb eng <[email protected]>",
|
||||
"license": "Apache-2.0",
|
||||
|
||||
@@ -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,12 +1,12 @@
|
||||
{
|
||||
"name": "@thirdweb-dev/contracts-js",
|
||||
"version": "1.1.9",
|
||||
"version": "1.2.1",
|
||||
"main": "dist/thirdweb-dev-contracts-js.cjs.js",
|
||||
"module": "dist/thirdweb-dev-contracts-js.esm.js",
|
||||
"license": "Apache-2.0",
|
||||
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/contracts-js",
|
||||
"dependencies": {
|
||||
"@thirdweb-dev/contracts": "3.1.8-0"
|
||||
"@thirdweb-dev/contracts": "^3.2.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/preset-env": "^7.18.10",
|
||||
|
||||
@@ -1,5 +1,50 @@
|
||||
# @thirdweb-dev/react
|
||||
|
||||
## 3.5.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#369](https://github.com/thirdweb-dev/js/pull/369) [`2d3fca5`](https://github.com/thirdweb-dev/js/commit/2d3fca52373046c88392139922e9c9da79f9a2d4) Thanks [@nachoiacovino](https://github.com/nachoiacovino)! - Add useClaimerProofs hook
|
||||
|
||||
- [#378](https://github.com/thirdweb-dev/js/pull/378) [`32d91ef`](https://github.com/thirdweb-dev/js/commit/32d91ef709c8fb92a903e52c65b27538a80ea4eb) Thanks [@mykcryptodev](https://github.com/mykcryptodev)! - round borders for buttons and menus on safari
|
||||
|
||||
- [#377](https://github.com/thirdweb-dev/js/pull/377) [`b79dc18`](https://github.com/thirdweb-dev/js/commit/b79dc18f7ab5155bbf4af02dc1a953546160bad0) Thanks [@adam-maj](https://github.com/adam-maj)! - Add event filters
|
||||
|
||||
## 3.5.1
|
||||
|
||||
## 3.5.0
|
||||
|
||||
## 3.4.5
|
||||
|
||||
## 3.4.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#356](https://github.com/thirdweb-dev/js/pull/356) [`cc4613b`](https://github.com/thirdweb-dev/js/commit/cc4613b5fd69840f4f9cfd0ac1c4e6743e62fe52) Thanks [@nachoiacovino](https://github.com/nachoiacovino)! - Add Pack and Multiwrap as NFTContract types
|
||||
|
||||
## 3.4.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#353](https://github.com/thirdweb-dev/js/pull/353) [`1c24c3c`](https://github.com/thirdweb-dev/js/commit/1c24c3c3c48476a824f817e09e7bf0fbe67c1db5) Thanks [@nachoiacovino](https://github.com/nachoiacovino)! - Add useUpdateCreators hook, fix CreatorInput schema
|
||||
|
||||
## 3.4.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#348](https://github.com/thirdweb-dev/js/pull/348) [`7d9a4c6`](https://github.com/thirdweb-dev/js/commit/7d9a4c6abcad1b4f43e431ce7b0b38db9016ea9a) Thanks [@nachoiacovino](https://github.com/nachoiacovino)! - [SOL] - add `useUpdateRoyaltySettings()` hook
|
||||
[SOL] - change `useRoyalty()` hook -> `useRoyaltySettings()` for consistency
|
||||
|
||||
- [#352](https://github.com/thirdweb-dev/js/pull/352) [`3522917`](https://github.com/thirdweb-dev/js/commit/352291791ee900e1500f84f095290497934d2f60) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - expose useExecuteAuctionSale and useAcceptDirectListingOffer
|
||||
|
||||
- [#351](https://github.com/thirdweb-dev/js/pull/351) [`aabbb14`](https://github.com/thirdweb-dev/js/commit/aabbb149e81d6824015a4797b2b6c929ca359545) Thanks [@jnsdls](https://github.com/jnsdls)! - [EVM] - fix wallet connection during dev (hot reload)
|
||||
|
||||
## 3.4.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#343](https://github.com/thirdweb-dev/js/pull/343) [`72227b2`](https://github.com/thirdweb-dev/js/commit/72227b2e166a3a68bbb41cf2b389322f5b7547a2) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Expose new useOffers and useMakeOffer hooks
|
||||
|
||||
## 3.4.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@thirdweb-dev/react",
|
||||
"version": "3.4.0",
|
||||
"version": "3.5.2",
|
||||
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/react",
|
||||
"author": "thirdweb eng <[email protected]>",
|
||||
"license": "Apache-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;
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -45,7 +45,7 @@ export type SetClaimConditionsParams = {
|
||||
/** **********************/
|
||||
|
||||
/**
|
||||
* Use this to get the active claim conditon for ERC20, ERC721 or ERC1155 based contracts. They need to extend the `claimCondition` extension for this hook to work.
|
||||
* Use this to get the active claim condition for ERC20, ERC721 or ERC1155 based contracts. They need to extend the `claimCondition` extension for this hook to work.
|
||||
*
|
||||
* @example
|
||||
* ```javascript
|
||||
@@ -60,7 +60,7 @@ export type SetClaimConditionsParams = {
|
||||
* const { data: activeClaimCondition, isLoading, error } = useActiveClaimCondition(<YourERC1155ContractInstance>, <tokenId>);
|
||||
* ```
|
||||
*
|
||||
* @param contract - an instance of a contract that extends the ERC721 or ERC1155 spec and implements the `claimConditions` extension.
|
||||
* @param contract - an instance of a contract that extends the ERC721, ERC1155 or ERC20 spec and implements the `claimConditions` extension.
|
||||
* @param tokenId - the id of the token to fetch the claim conditions for (if the contract is an ERC1155 contract)
|
||||
* @returns a response object with the currently active claim condition
|
||||
* @twfeature ERC721ClaimableWithConditions | ERC1155ClaimableWithConditions | ERC20ClaimableWithConditions
|
||||
@@ -106,7 +106,70 @@ export function useActiveClaimCondition(
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this to get all claim conditons for ERC20, ERC721 or ERC1155 based contracts. They need to extend the `claimCondition` extension for this hook to work.
|
||||
* Use this to get the claimer proofs for an adddress for ERC20, ERC721 or ERC1155 based contracts. They need to extend the `claimCondition` extension for this hook to work.
|
||||
*
|
||||
* @example
|
||||
* ```javascript
|
||||
* const { data: claimerProofs, isLoading, error } = useClaimerProofs(<YourERC20ContractInstance>);
|
||||
* ```
|
||||
* @example
|
||||
* ```javascript
|
||||
* const { data: claimerProofs, isLoading, error } = useClaimerProofs(<YourERC721ContractInstance>);
|
||||
* ```
|
||||
* @example
|
||||
* ```javascript
|
||||
* const { data: claimerProofs, isLoading, error } = useClaimerProofs(<YourERC1155ContractInstance>, <tokenId>);
|
||||
* ```
|
||||
*
|
||||
* @param contract - an instance of a contract that extends the ERC721, ERC1155 or ERC20 spec and implements the `claimConditions` extension.
|
||||
* @param claimerAddress - the address of the claimer to fetch the claimer proofs for
|
||||
* @param tokenId - the id of the token to fetch the claimer proofs for (if the contract is an ERC1155 contract)
|
||||
* @param claimConditionId - optional the claim condition id to get the proofs for
|
||||
* @returns a response object with the snapshot for the provided address
|
||||
* @twfeature ERC721ClaimableWithConditions | ERC1155ClaimableWithConditions | ERC20ClaimableWithConditions
|
||||
* @beta
|
||||
*/
|
||||
export function useClaimerProofs(
|
||||
contract: RequiredParam<DropContract>,
|
||||
claimerAddress: string,
|
||||
tokenId?: BigNumberish,
|
||||
claimConditionId?: BigNumberish,
|
||||
) {
|
||||
const contractAddress = contract?.getAddress();
|
||||
const { erc1155, erc721, erc20 } = getErcs(contract);
|
||||
|
||||
return useQueryWithNetwork(
|
||||
cacheKeys.extensions.claimConditions.getClaimerProofs(
|
||||
contractAddress,
|
||||
tokenId,
|
||||
),
|
||||
() => {
|
||||
if (erc1155) {
|
||||
requiredParamInvariant(
|
||||
tokenId,
|
||||
"tokenId is required for ERC1155 claim conditions",
|
||||
);
|
||||
return erc1155.claimConditions.getClaimerProofs(tokenId, claimerAddress, claimConditionId);
|
||||
}
|
||||
if (erc721) {
|
||||
return erc721.claimConditions.getClaimerProofs(claimerAddress, claimConditionId);
|
||||
}
|
||||
if (erc20) {
|
||||
return erc20.claimConditions.getClaimerProofs(claimerAddress, claimConditionId);
|
||||
}
|
||||
throw new Error("Contract must be ERC721, ERC1155 or ERC20");
|
||||
},
|
||||
{
|
||||
// Checks that happen here:
|
||||
// 1. if the contract is based on ERC1155 contract => tokenId cannot be `undefined`
|
||||
// 2. if the contract is NOT based on ERC1155 => we have to have either an ERC721 or ERC20 contract
|
||||
enabled: erc1155 ? tokenId !== undefined : !!erc721 || !!erc20,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this to get all claim conditions for ERC20, ERC721 or ERC1155 based contracts. They need to extend the `claimCondition` extension for this hook to work.
|
||||
*
|
||||
* @example
|
||||
* ```javascript
|
||||
@@ -121,7 +184,7 @@ export function useActiveClaimCondition(
|
||||
* const { data: claimConditions, isLoading, error } = useClaimConditions(<YourERC1155ContractInstance>, <tokenId>);
|
||||
* ```
|
||||
*
|
||||
* @param contract - an instance of a contract that extends the ERC721 or ERC1155 spec and implements the `claimConditions` extension.
|
||||
* @param contract - an instance of a contract that extends the ERC721, ERC1155 or ERC20 spec and implements the `claimConditions` extension.
|
||||
* @param tokenId - the id of the token to fetch the claim conditions for (if the contract is an ERC1155 contract)
|
||||
* @returns a response object with the list of claim conditions
|
||||
* @twfeature ERC721ClaimableWithConditions | ERC1155ClaimableWithConditions | ERC20ClaimableWithConditions
|
||||
@@ -368,8 +431,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 },
|
||||
|
||||
+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 { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import type { CreatorInput, NFTCollection, NFTDrop } from "@thirdweb-dev/sdk/solana";
|
||||
|
||||
/**
|
||||
* Update the creators for an NFT program
|
||||
* @param program - The NFT program instance to update the creators for
|
||||
*
|
||||
* @example
|
||||
* ```jsx
|
||||
* import { useProgram, useUpdateCreators } from "@thirdweb-dev/react/solana";
|
||||
*
|
||||
* export default function Component() {
|
||||
* const { program } = useProgram("{{program_address}}");
|
||||
* const { mutateAsync: updateCreators, isLoading, error } = useUpdateCreators(program);
|
||||
*
|
||||
* return (
|
||||
* <button
|
||||
* onClick={() => updateCreators([{ address: "0x...", share: 10 }])}
|
||||
* >
|
||||
* Update Creators
|
||||
* </button>
|
||||
* )
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export function useUpdateCreators(
|
||||
program: RequiredParam<NFTCollection | NFTDrop>,
|
||||
) {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation(
|
||||
async (creators: CreatorInput[]) => {
|
||||
requiredParamInvariant(program, "program is required");
|
||||
return await program.updateCreators(creators);
|
||||
},
|
||||
{
|
||||
onSettled: () =>
|
||||
queryClient.invalidateQueries(createSOLProgramQueryKey(program)),
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
import { createSOLProgramQueryKey } from "../../../core/query-utils/query-key";
|
||||
import {
|
||||
requiredParamInvariant,
|
||||
RequiredParam,
|
||||
} from "../../../core/query-utils/required-param";
|
||||
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import type { NFTCollection, NFTDrop } from "@thirdweb-dev/sdk/solana";
|
||||
|
||||
/**
|
||||
* Update the royalty for an NFT program
|
||||
* @param program - The NFT program instance to update the royalty for
|
||||
*
|
||||
* @example
|
||||
* ```jsx
|
||||
* import { useProgram, useUpdateRoyaltySettings } from "@thirdweb-dev/react/solana";
|
||||
*
|
||||
* export default function Component() {
|
||||
* const { program } = useProgram("{{program_address}}");
|
||||
* const { mutateAsync: updateRoyalties, isLoading, error } = useUpdateRoyaltySettings(program);
|
||||
*
|
||||
* return (
|
||||
* <button
|
||||
* onClick={() => updateRoyalties(300)}
|
||||
* >
|
||||
* Update Royalties
|
||||
* </button>
|
||||
* )
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export function useUpdateRoyaltySettings(
|
||||
program: RequiredParam<NFTCollection | NFTDrop>,
|
||||
) {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation(
|
||||
async (sellerFeeBasisPoints: number) => {
|
||||
requiredParamInvariant(program, "program is required");
|
||||
return await program.updateRoyalty(sellerFeeBasisPoints);
|
||||
},
|
||||
{
|
||||
onSettled: () =>
|
||||
queryClient.invalidateQueries(createSOLProgramQueryKey(program)),
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -4,7 +4,8 @@ import {
|
||||
RequiredParam,
|
||||
} from "../../../core/query-utils/required-param";
|
||||
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import type { Amount, Token } from "@thirdweb-dev/sdk/solana";
|
||||
import type { Amount } from "@thirdweb-dev/sdk";
|
||||
import type { Token } from "@thirdweb-dev/sdk/solana";
|
||||
|
||||
export type TransferTokenMutationParams = {
|
||||
amount: Amount;
|
||||
|
||||
@@ -14,6 +14,10 @@ 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";
|
||||
|
||||
// NFT Collection
|
||||
export * from "./hooks/nft/collection/useMintNFT";
|
||||
|
||||
@@ -1,5 +1,84 @@
|
||||
# @thirdweb-dev/sdk
|
||||
|
||||
## 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@thirdweb-dev/sdk",
|
||||
"version": "3.4.0",
|
||||
"version": "3.5.2",
|
||||
"description": "The main thirdweb SDK.",
|
||||
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/sdk",
|
||||
"license": "Apache-2.0",
|
||||
@@ -108,7 +108,6 @@
|
||||
"@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",
|
||||
|
||||
@@ -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 { IDropClaimCondition_V2 } from "@thirdweb-dev/contracts-js/dist/declarations/src/IDropERC20_V2";
|
||||
import { IClaimCondition } from "@thirdweb-dev/contracts-js/src/IDrop";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
import {
|
||||
BigNumber,
|
||||
@@ -52,27 +57,58 @@ 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,9 @@ export async function prepareClaim(
|
||||
return {
|
||||
overrides,
|
||||
proofs,
|
||||
maxQuantityPerTransaction: maxClaimable,
|
||||
price,
|
||||
currencyAddress,
|
||||
maxClaimable,
|
||||
price: priceInProof,
|
||||
currencyAddress: currencyAddressInProof,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -139,6 +185,8 @@ export async function fetchSnapshot(
|
||||
return snapshotData.claims.map((claim) => ({
|
||||
address: claim.address,
|
||||
maxClaimable: claim.maxClaimable,
|
||||
price: claim.price,
|
||||
currencyAddress: claim.currencyAddress,
|
||||
}));
|
||||
}
|
||||
}
|
||||
@@ -150,7 +198,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 +213,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);
|
||||
@@ -238,6 +292,8 @@ export async function updateExistingClaimConditions(
|
||||
* @param tokenDecimals
|
||||
* @param merkleMetadata
|
||||
* @param storage
|
||||
* @param provider
|
||||
* @param snapshotFormatVersion
|
||||
* @returns - The proof for the current signer for the specified condition.
|
||||
*/
|
||||
export async function getClaimerProofs(
|
||||
@@ -246,38 +302,63 @@ export async function getClaimerProofs(
|
||||
tokenDecimals: number,
|
||||
merkleMetadata: Record<string, string>,
|
||||
storage: ThirdwebStorage,
|
||||
): Promise<{ maxClaimable: BigNumber; proof: string[] }> {
|
||||
provider: ethers.providers.Provider,
|
||||
snapshotFormatVersion: SnapshotFormatVersion,
|
||||
): Promise<
|
||||
| {
|
||||
proof: string[];
|
||||
maxClaimable: BigNumber;
|
||||
price: BigNumber;
|
||||
currencyAddress: string;
|
||||
}
|
||||
| undefined
|
||||
> {
|
||||
const claim = await fetchSnapshotEntryForAddress(
|
||||
addressToClaim,
|
||||
merkleRoot,
|
||||
merkleMetadata,
|
||||
provider,
|
||||
storage,
|
||||
snapshotFormatVersion,
|
||||
);
|
||||
if (!claim) {
|
||||
return {
|
||||
proof: [],
|
||||
maxClaimable: BigNumber.from(0),
|
||||
};
|
||||
return undefined;
|
||||
}
|
||||
const price =
|
||||
claim.price === "unlimited" || claim.price === undefined
|
||||
? ethers.constants.MaxUint256
|
||||
: await normalizePriceValue(
|
||||
provider,
|
||||
claim.price,
|
||||
claim.currencyAddress || ethers.constants.AddressZero,
|
||||
);
|
||||
const maxClaimable =
|
||||
claim.maxClaimable === "unlimited"
|
||||
? ethers.constants.MaxUint256
|
||||
: ethers.utils.parseUnits(claim.maxClaimable, tokenDecimals);
|
||||
return {
|
||||
proof: claim.proof,
|
||||
maxClaimable: ethers.utils.parseUnits(claim.maxClaimable, tokenDecimals),
|
||||
maxClaimable,
|
||||
price,
|
||||
currencyAddress: claim.currencyAddress || ethers.constants.AddressZero,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 +366,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 +382,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 +439,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 +549,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 +566,8 @@ export async function transformResultToClaimCondition(
|
||||
pm.maxClaimableSupply,
|
||||
tokenDecimals,
|
||||
);
|
||||
const quantityLimitPerTransaction = convertToReadableQuantity(
|
||||
pm.quantityLimitPerTransaction,
|
||||
const maxClaimablePerWallet = convertToReadableQuantity(
|
||||
pm.maxClaimablePerWallet,
|
||||
tokenDecimals,
|
||||
);
|
||||
const availableSupply = convertToReadableQuantity(
|
||||
@@ -403,11 +580,11 @@ export async function transformResultToClaimCondition(
|
||||
);
|
||||
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 +593,19 @@ export async function transformResultToClaimCondition(
|
||||
snapshot: shouldDownloadSnapshot
|
||||
? await fetchSnapshot(pm.merkleRoot, merkleMetadata, storage)
|
||||
: undefined,
|
||||
metadata: pm.metadata,
|
||||
});
|
||||
}
|
||||
|
||||
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 +613,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";
|
||||
|
||||
@@ -6,6 +6,7 @@ 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 {
|
||||
AbiEvent,
|
||||
AbiFunction,
|
||||
@@ -23,7 +24,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";
|
||||
|
||||
|
||||
@@ -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)],
|
||||
);
|
||||
}
|
||||
|
||||
@@ -17,7 +17,9 @@ import {
|
||||
FEATURE_NFT_REVEALABLE,
|
||||
FEATURE_NFT_SIGNATURE_MINTABLE,
|
||||
FEATURE_NFT_SUPPLY,
|
||||
FEATURE_NFT_CLAIMABLE_WITH_CONDITIONS,
|
||||
FEATURE_NFT_CLAIMABLE_WITH_CONDITIONS_V1,
|
||||
FEATURE_NFT_CLAIMABLE_WITH_CONDITIONS_V2,
|
||||
FEATURE_NFT_TIERED_DROP,
|
||||
} from "./erc721-features";
|
||||
import {
|
||||
FEATURE_EDITION,
|
||||
@@ -59,10 +61,12 @@ export type Feature =
|
||||
| typeof FEATURE_NFT_BATCH_MINTABLE
|
||||
| typeof FEATURE_NFT_LAZY_MINTABLE
|
||||
| typeof FEATURE_NFT_CLAIMABLE
|
||||
| typeof FEATURE_NFT_CLAIMABLE_WITH_CONDITIONS
|
||||
| typeof FEATURE_NFT_CLAIMABLE_WITH_CONDITIONS_V1
|
||||
| typeof FEATURE_NFT_CLAIMABLE_WITH_CONDITIONS_V2
|
||||
| typeof FEATURE_NFT_REVEALABLE
|
||||
| typeof FEATURE_NFT_SIGNATURE_MINTABLE
|
||||
| typeof FEATURE_NFT_BURNABLE
|
||||
| typeof FEATURE_NFT_TIERED_DROP
|
||||
| typeof FEATURE_EDITION
|
||||
| typeof FEATURE_EDITION_ENUMERABLE
|
||||
| typeof FEATURE_EDITION_MINTABLE
|
||||
|
||||
@@ -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,7 @@
|
||||
import DelayedRevealAbi from "@thirdweb-dev/contracts-js/dist/abis/DelayedReveal.json";
|
||||
import DropSinglePhase1155 from "@thirdweb-dev/contracts-js/dist/abis/DropSinglePhase1155.json";
|
||||
import IBurnableERC1155Abi from "@thirdweb-dev/contracts-js/dist/abis/IBurnableERC1155.json";
|
||||
import IClaimableERC1155 from "@thirdweb-dev/contracts-js/dist/abis/IClaimableERC1155.json";
|
||||
import IDropSinglePhase1155 from "@thirdweb-dev/contracts-js/dist/abis/IDropSinglePhase1155.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";
|
||||
@@ -27,7 +27,7 @@ export const FEATURE_EDITION_CLAIMABLE_WITH_CONDITIONS = {
|
||||
sdk: "sdk.erc1155claimable",
|
||||
contracts: "DropSinglePhase1155",
|
||||
},
|
||||
abis: [Erc1155Abi, ILazyMintAbi, DropSinglePhase1155],
|
||||
abis: [Erc1155Abi, ILazyMintAbi, IDropSinglePhase1155],
|
||||
features: {},
|
||||
} as const;
|
||||
|
||||
|
||||
@@ -2,12 +2,14 @@ import IBurnableERC721Abi from "@thirdweb-dev/contracts-js/dist/abis/IBurnableER
|
||||
import IClaimableERC721 from "@thirdweb-dev/contracts-js/dist/abis/IClaimableERC721.json";
|
||||
import DelayedRevealAbi from "@thirdweb-dev/contracts-js/dist/abis/IDelayedReveal.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 TieredDropAbi from "@thirdweb-dev/contracts-js/dist/abis/LazyMintWithTier.json";
|
||||
import SignatureMintERC721Abi from "@thirdweb-dev/contracts-js/dist/abis/SignatureMintERC721.json";
|
||||
|
||||
export const FEATURE_NFT_BURNABLE = {
|
||||
@@ -25,15 +27,26 @@ 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_CLAIMABLE_WITH_CONDITIONS_V2 = {
|
||||
name: "ERC721ClaimableWithConditionsV2",
|
||||
namespace: "nft.drop.claim",
|
||||
docLinks: {
|
||||
sdk: "sdk.erc721claimable",
|
||||
@@ -43,6 +56,17 @@ export const FEATURE_NFT_CLAIMABLE_WITH_CONDITIONS = {
|
||||
features: {},
|
||||
} as const;
|
||||
|
||||
export const FEATURE_NFT_CLAIMABLE_WITH_CONDITIONS_V1 = {
|
||||
name: "ERC721ClaimableWithConditionsV1",
|
||||
namespace: "nft.drop.claim",
|
||||
docLinks: {
|
||||
sdk: "sdk.erc721claimable",
|
||||
contracts: "DropSinglePhase_V1",
|
||||
},
|
||||
abis: [Erc721Abi, ILazyMintAbi, IDropSinglePhaseV1],
|
||||
features: {},
|
||||
} as const;
|
||||
|
||||
export const FEATURE_NFT_CLAIMABLE = {
|
||||
name: "ERC721Claimable",
|
||||
namespace: "nft.drop.claim",
|
||||
@@ -65,8 +89,10 @@ export const FEATURE_NFT_LAZY_MINTABLE = {
|
||||
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_CLAIMABLE_WITH_CONDITIONS_V2.name]:
|
||||
FEATURE_NFT_CLAIMABLE_WITH_CONDITIONS_V2,
|
||||
[FEATURE_NFT_CLAIMABLE_WITH_CONDITIONS_V1.name]:
|
||||
FEATURE_NFT_CLAIMABLE_WITH_CONDITIONS_V1,
|
||||
},
|
||||
} as const;
|
||||
|
||||
@@ -143,5 +169,6 @@ export const FEATURE_NFT = {
|
||||
[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_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,
|
||||
@@ -34,8 +40,12 @@ export const EditionDropInitializer = {
|
||||
...[network, address, storage, options]: InitalizeParams
|
||||
) => {
|
||||
const [, provider] = getSignerAndProvider(network, options);
|
||||
const contractInfo = await getPrebuiltInfo(address, provider);
|
||||
if (!contractInfo || contractInfo.type !== "DropERC1155") {
|
||||
throw new Error("Contract is not a DropERC1155");
|
||||
}
|
||||
const [abi, contract, _network] = await Promise.all([
|
||||
EditionDropInitializer.getAbi(),
|
||||
await EditionDropInitializer.getAbi(address, provider),
|
||||
import("./prebuilt-implementations/edition-drop"),
|
||||
provider.getNetwork(),
|
||||
]);
|
||||
@@ -49,9 +59,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 +86,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 +100,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 +118,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 +132,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 +150,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 +164,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 +182,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 +196,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 = {
|
||||
@@ -180,8 +213,12 @@ export const NFTDropInitializer = {
|
||||
...[network, address, storage, options]: InitalizeParams
|
||||
) => {
|
||||
const [, provider] = getSignerAndProvider(network, options);
|
||||
const contractInfo = await getPrebuiltInfo(address, provider);
|
||||
if (!contractInfo || contractInfo.type !== "DropERC721") {
|
||||
throw new Error("Contract is not a DropERC721");
|
||||
}
|
||||
const [abi, contract, _network] = await Promise.all([
|
||||
NFTDropInitializer.getAbi(),
|
||||
NFTDropInitializer.getAbi(address, provider),
|
||||
import("./prebuilt-implementations/nft-drop"),
|
||||
provider.getNetwork(),
|
||||
]);
|
||||
@@ -195,9 +232,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 +260,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 +274,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 +292,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 +306,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 +337,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 +351,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 = {
|
||||
@@ -298,8 +368,12 @@ export const TokenDropInitializer = {
|
||||
...[network, address, storage, options]: InitalizeParams
|
||||
) => {
|
||||
const [, provider] = getSignerAndProvider(network, options);
|
||||
const contractInfo = await getPrebuiltInfo(address, provider);
|
||||
if (!contractInfo || contractInfo.type !== "DropERC20") {
|
||||
throw new Error("Contract is not a DropERC20");
|
||||
}
|
||||
const [abi, contract, _network] = await Promise.all([
|
||||
TokenDropInitializer.getAbi(),
|
||||
TokenDropInitializer.getAbi(address, provider),
|
||||
import("./prebuilt-implementations/token-drop"),
|
||||
provider.getNetwork(),
|
||||
]);
|
||||
@@ -313,9 +387,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 +411,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 +425,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 +444,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 +458,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 +517,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
|
||||
@@ -324,22 +327,22 @@ export class Marketplace implements UpdateableNetwork {
|
||||
// get only the events for this listing id
|
||||
const listingEvents = events.filter((e) => e.data.listingId.eq(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 +394,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(
|
||||
|
||||
@@ -387,6 +387,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 +397,25 @@ export class ContractDeployer extends RPCConnectionHandler {
|
||||
contractMetadata: z.input<
|
||||
DeploySchemaForPrebuiltContractType<TContractType>
|
||||
>,
|
||||
version?: number,
|
||||
): Promise<string> {
|
||||
const parsed =
|
||||
PREBUILT_CONTRACTS_MAP[contractType].schema.deploy.parse(
|
||||
contractMetadata,
|
||||
);
|
||||
const factory = await this.getFactory();
|
||||
return await factory.deploy(contractType, parsed);
|
||||
return await factory.deploy(contractType, parsed, version);
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @param contractType
|
||||
*/
|
||||
public async getLatestBuiltInContractVersion<
|
||||
TContractType extends PrebuiltContractType,
|
||||
>(contractType: TContractType) {
|
||||
const factory = await this.getFactory();
|
||||
return await factory.getLatestVersion(contractType);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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,13 @@
|
||||
import { AmountSchema } from "../../../core/schema/shared";
|
||||
import { includesErrorMessage } from "../../common";
|
||||
import {
|
||||
abstractContractModelToLegacy,
|
||||
abstractContractModelToNew,
|
||||
convertQuantityToBigNumber,
|
||||
fetchSnapshotEntryForAddress,
|
||||
getClaimerProofs,
|
||||
legacyContractModelToAbstract,
|
||||
newContractModelToAbstract,
|
||||
prepareClaim,
|
||||
processClaimConditionInputs,
|
||||
transformResultToClaimCondition,
|
||||
@@ -11,32 +18,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 +68,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 +111,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 +155,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 +166,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 +278,181 @@ export class DropClaimConditions<
|
||||
const merkleRootArray = ethers.utils.stripZeros(
|
||||
claimCondition.merkleRootHash,
|
||||
);
|
||||
if (merkleRootArray.length > 0) {
|
||||
const hasAllowList = merkleRootArray.length > 0;
|
||||
let allowListEntry;
|
||||
if (hasAllowList) {
|
||||
const merkleLower = claimCondition.merkleRootHash.toString();
|
||||
const metadata = await this.metadata.get();
|
||||
const proofs = await getClaimerProofs(
|
||||
allowListEntry = await getClaimerProofs(
|
||||
addressToCheck,
|
||||
merkleLower,
|
||||
await this.getTokenDecimals(),
|
||||
metadata.merkle,
|
||||
this.storage,
|
||||
this.contractWrapper.getProvider(),
|
||||
this.getSnapshotFormatVersion(),
|
||||
);
|
||||
|
||||
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) {
|
||||
if (
|
||||
!allowListEntry &&
|
||||
(this.isLegacySinglePhaseDrop(this.contractWrapper) ||
|
||||
this.isLegacyMultiPhaseDrop(this.contractWrapper))
|
||||
) {
|
||||
// exclusive allowlist behavior
|
||||
reasons.push(ClaimEligibility.AddressNotAllowed);
|
||||
return reasons;
|
||||
}
|
||||
|
||||
if (allowListEntry) {
|
||||
try {
|
||||
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,
|
||||
allowListEntry.proof,
|
||||
allowListEntry.maxClaimable,
|
||||
);
|
||||
if (!validMerkleProof) {
|
||||
reasons.push(ClaimEligibility.AddressNotAllowed);
|
||||
return reasons;
|
||||
}
|
||||
} else if (this.isLegacySinglePhaseDrop(this.contractWrapper)) {
|
||||
[validMerkleProof] =
|
||||
await this.contractWrapper.readContract.verifyClaimMerkleProof(
|
||||
addressToCheck,
|
||||
quantity,
|
||||
{
|
||||
proof: allowListEntry.proof,
|
||||
maxQuantityInAllowlist: allowListEntry.maxClaimable,
|
||||
},
|
||||
);
|
||||
if (!validMerkleProof) {
|
||||
reasons.push(ClaimEligibility.AddressNotAllowed);
|
||||
return reasons;
|
||||
}
|
||||
} else if (this.isNewSinglePhaseDrop(this.contractWrapper)) {
|
||||
await this.contractWrapper.readContract.verifyClaim(
|
||||
addressToCheck,
|
||||
quantity,
|
||||
claimCondition.currencyAddress,
|
||||
claimCondition.price,
|
||||
{
|
||||
proof: allowListEntry.proof,
|
||||
quantityLimitPerWallet: allowListEntry.maxClaimable,
|
||||
currency: allowListEntry.currencyAddress,
|
||||
pricePerToken: allowListEntry.price,
|
||||
} as IDropSinglePhase.AllowlistProofStruct,
|
||||
);
|
||||
// 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 (
|
||||
(convertQuantityToBigNumber(
|
||||
claimCondition.maxClaimablePerWallet,
|
||||
decimals,
|
||||
).eq(0) &&
|
||||
allowListEntry.maxClaimable === ethers.constants.MaxUint256) ||
|
||||
allowListEntry.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,
|
||||
claimCondition.currencyAddress,
|
||||
claimCondition.price,
|
||||
{
|
||||
proof: allowListEntry.proof,
|
||||
quantityLimitPerWallet: allowListEntry.maxClaimable,
|
||||
currency: allowListEntry.currencyAddress,
|
||||
pricePerToken: allowListEntry.price,
|
||||
} as IDropSinglePhase.AllowlistProofStruct,
|
||||
);
|
||||
if (
|
||||
(convertQuantityToBigNumber(
|
||||
claimCondition.maxClaimablePerWallet,
|
||||
decimals,
|
||||
).eq(0) &&
|
||||
allowListEntry.maxClaimable === ethers.constants.MaxUint256) ||
|
||||
allowListEntry.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 +483,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 +554,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: [],
|
||||
@@ -411,6 +585,7 @@ export class DropClaimConditions<
|
||||
await this.getTokenDecimals(),
|
||||
this.contractWrapper.getProvider(),
|
||||
this.storage,
|
||||
this.getSnapshotFormatVersion(),
|
||||
);
|
||||
|
||||
const merkleInfo: { [key: string]: string } = {};
|
||||
@@ -431,6 +606,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 +628,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,
|
||||
]),
|
||||
);
|
||||
@@ -524,21 +715,132 @@ export class DropClaimConditions<
|
||||
this.contractWrapper,
|
||||
this.storage,
|
||||
checkERC20Allowance,
|
||||
this.getSnapshotFormatVersion(),
|
||||
);
|
||||
}
|
||||
|
||||
private isSinglePhaseDropContract(
|
||||
public async getClaimArguments(
|
||||
destinationAddress: string,
|
||||
quantity: BigNumberish,
|
||||
claimVerification: ClaimVerification,
|
||||
): Promise<any[]> {
|
||||
const activeClaimCondition = await this.getActive();
|
||||
if (this.isLegacyMultiPhaseDrop(this.contractWrapper)) {
|
||||
return [
|
||||
destinationAddress,
|
||||
quantity,
|
||||
activeClaimCondition.currencyAddress,
|
||||
activeClaimCondition.price,
|
||||
claimVerification.proofs,
|
||||
claimVerification.maxClaimable,
|
||||
];
|
||||
} else if (this.isLegacySinglePhaseDrop(this.contractWrapper)) {
|
||||
return [
|
||||
destinationAddress,
|
||||
quantity,
|
||||
activeClaimCondition.currencyAddress,
|
||||
activeClaimCondition.price,
|
||||
{
|
||||
proof: claimVerification.proofs,
|
||||
maxQuantityInAllowlist: claimVerification.maxClaimable,
|
||||
} as IDropSinglePhase_V1.AllowlistProofStruct,
|
||||
ethers.utils.toUtf8Bytes(""),
|
||||
];
|
||||
}
|
||||
return [
|
||||
destinationAddress,
|
||||
quantity,
|
||||
activeClaimCondition.currencyAddress,
|
||||
activeClaimCondition.price,
|
||||
{
|
||||
proof: claimVerification.proofs,
|
||||
quantityLimitPerWallet: claimVerification.maxClaimable,
|
||||
pricePerToken: claimVerification.price,
|
||||
currency: claimVerification.currencyAddress,
|
||||
} 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 (
|
||||
!hasFunction<DropSinglePhase>("getClaimConditionById", contractWrapper) &&
|
||||
hasFunction<DropSinglePhase>("getSupplyClaimedByWallet", contractWrapper)
|
||||
);
|
||||
}
|
||||
|
||||
private isMultiPhaseDropContract(
|
||||
isNewMultiphaseDrop(
|
||||
contractWrapper: ContractWrapper<any>,
|
||||
): contractWrapper is ContractWrapper<DropERC721 | DropERC20> {
|
||||
return hasFunction<DropERC721>("getClaimConditionById", contractWrapper);
|
||||
): contractWrapper is ContractWrapper<Drop> {
|
||||
return (
|
||||
hasFunction<Drop>("getClaimConditionById", contractWrapper) &&
|
||||
hasFunction<Drop>("getSupplyClaimedByWallet", contractWrapper)
|
||||
);
|
||||
}
|
||||
|
||||
isLegacySinglePhaseDrop(
|
||||
contractWrapper: ContractWrapper<any>,
|
||||
): contractWrapper is ContractWrapper<DropSinglePhase_V1> {
|
||||
return (
|
||||
!hasFunction<DropSinglePhase_V1>(
|
||||
"getClaimConditionById",
|
||||
contractWrapper,
|
||||
) && hasFunction<DropSinglePhase_V1>("getClaimTimestamp", contractWrapper)
|
||||
);
|
||||
}
|
||||
|
||||
isLegacyMultiPhaseDrop(
|
||||
contractWrapper: ContractWrapper<any>,
|
||||
): contractWrapper is ContractWrapper<DropERC721_V3 | DropERC20_V2> {
|
||||
return (
|
||||
hasFunction<DropERC721_V3>("getClaimConditionById", contractWrapper) &&
|
||||
hasFunction<DropERC721_V3>("setWalletClaimCount", contractWrapper)
|
||||
);
|
||||
}
|
||||
|
||||
private getSnapshotFormatVersion(): SnapshotFormatVersion {
|
||||
return this.isLegacyMultiPhaseDrop(this.contractWrapper) ||
|
||||
this.isLegacySinglePhaseDrop(this.contractWrapper)
|
||||
? SnapshotFormatVersion.V1
|
||||
: SnapshotFormatVersion.V2;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
import { includesErrorMessage } from "../../common";
|
||||
import {
|
||||
abstractContractModelToLegacy,
|
||||
abstractContractModelToNew,
|
||||
fetchSnapshotEntryForAddress,
|
||||
getClaimerProofs,
|
||||
legacyContractModelToAbstract,
|
||||
newContractModelToAbstract,
|
||||
prepareClaim,
|
||||
processClaimConditionInputs,
|
||||
transformResultToClaimCondition,
|
||||
@@ -8,28 +13,41 @@ import {
|
||||
} from "../../common/claim-conditions";
|
||||
import { isNativeToken } from "../../common/currency";
|
||||
import { 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 +57,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 +100,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 +153,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 +164,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 +182,7 @@ export class DropErc1155ClaimConditions<
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return [await this.getActive(tokenId)];
|
||||
return [await this.getActive(tokenId, options)];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -230,52 +271,142 @@ export class DropErc1155ClaimConditions<
|
||||
const merkleRootArray = ethers.utils.stripZeros(
|
||||
claimCondition.merkleRootHash,
|
||||
);
|
||||
if (merkleRootArray.length > 0) {
|
||||
const hasAllowList = merkleRootArray.length > 0;
|
||||
let allowListEntry;
|
||||
if (hasAllowList) {
|
||||
const merkleLower = claimCondition.merkleRootHash.toString();
|
||||
const metadata = await this.metadata.get();
|
||||
const proofs = await getClaimerProofs(
|
||||
allowListEntry = await getClaimerProofs(
|
||||
addressToCheck,
|
||||
merkleLower,
|
||||
0,
|
||||
metadata.merkle,
|
||||
this.storage,
|
||||
this.contractWrapper.getProvider(),
|
||||
this.getSnapshotFormatVersion(),
|
||||
);
|
||||
try {
|
||||
let validMerkleProof;
|
||||
if (this.isMultiPhaseDropContract(this.contractWrapper)) {
|
||||
activeConditionIndex =
|
||||
await this.contractWrapper.readContract.getActiveClaimConditionId(
|
||||
|
||||
if (
|
||||
!allowListEntry &&
|
||||
(this.isLegacySinglePhaseDrop(this.contractWrapper) ||
|
||||
this.isLegacyMultiPhaseDrop(this.contractWrapper))
|
||||
) {
|
||||
// exclusive allowlist behavior
|
||||
reasons.push(ClaimEligibility.AddressNotAllowed);
|
||||
return reasons;
|
||||
}
|
||||
|
||||
if (allowListEntry) {
|
||||
try {
|
||||
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,
|
||||
allowListEntry.proof,
|
||||
allowListEntry.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: allowListEntry.proof,
|
||||
maxQuantityInAllowlist: allowListEntry.maxClaimable,
|
||||
},
|
||||
);
|
||||
if (!validMerkleProof) {
|
||||
reasons.push(ClaimEligibility.AddressNotAllowed);
|
||||
return reasons;
|
||||
}
|
||||
} else if (this.isNewSinglePhaseDrop(this.contractWrapper)) {
|
||||
await this.contractWrapper.readContract.verifyClaim(
|
||||
tokenId,
|
||||
addressToCheck,
|
||||
quantity,
|
||||
claimCondition.currencyAddress,
|
||||
claimCondition.price,
|
||||
{
|
||||
proof: allowListEntry.proof,
|
||||
quantityLimitPerWallet: allowListEntry.maxClaimable,
|
||||
currency: allowListEntry.currencyAddress,
|
||||
pricePerToken: allowListEntry.price,
|
||||
} 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" &&
|
||||
allowListEntry.maxClaimable === ethers.constants.MaxUint256) ||
|
||||
allowListEntry.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,
|
||||
claimCondition.currencyAddress,
|
||||
claimCondition.price,
|
||||
{
|
||||
proof: proofs.proof,
|
||||
maxQuantityInAllowlist: proofs.maxClaimable,
|
||||
},
|
||||
proof: allowListEntry.proof,
|
||||
quantityLimitPerWallet: allowListEntry.maxClaimable,
|
||||
currency: allowListEntry.currencyAddress,
|
||||
pricePerToken: allowListEntry.price,
|
||||
} as IDropSinglePhase.AllowlistProofStruct,
|
||||
);
|
||||
}
|
||||
|
||||
if (!validMerkleProof) {
|
||||
if (
|
||||
(claimCondition.maxClaimablePerWallet === "0" &&
|
||||
allowListEntry.maxClaimable === ethers.constants.MaxUint256) ||
|
||||
allowListEntry.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: [],
|
||||
@@ -468,6 +628,7 @@ export class DropErc1155ClaimConditions<
|
||||
0,
|
||||
this.contractWrapper.getProvider(),
|
||||
this.storage,
|
||||
this.getSnapshotFormatVersion(),
|
||||
);
|
||||
|
||||
snapshotInfos.forEach((s) => {
|
||||
@@ -519,18 +680,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 {
|
||||
@@ -581,21 +772,142 @@ 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[]> {
|
||||
const activeClaimCondition = await this.getActive(tokenId);
|
||||
if (this.isLegacyMultiPhaseDrop(this.contractWrapper)) {
|
||||
return [
|
||||
destinationAddress,
|
||||
tokenId,
|
||||
quantity,
|
||||
activeClaimCondition.currencyAddress,
|
||||
activeClaimCondition.price,
|
||||
claimVerification.proofs,
|
||||
claimVerification.maxClaimable,
|
||||
];
|
||||
} else if (this.isLegacySinglePhaseDrop(this.contractWrapper)) {
|
||||
return [
|
||||
destinationAddress,
|
||||
tokenId,
|
||||
quantity,
|
||||
activeClaimCondition.currencyAddress,
|
||||
activeClaimCondition.price,
|
||||
{
|
||||
proof: claimVerification.proofs,
|
||||
maxQuantityInAllowlist: claimVerification.maxClaimable,
|
||||
} as IDropSinglePhase_V1.AllowlistProofStruct,
|
||||
ethers.utils.toUtf8Bytes(""),
|
||||
];
|
||||
}
|
||||
return [
|
||||
destinationAddress,
|
||||
tokenId,
|
||||
quantity,
|
||||
activeClaimCondition.currencyAddress,
|
||||
activeClaimCondition.price,
|
||||
{
|
||||
proof: claimVerification.proofs,
|
||||
quantityLimitPerWallet: claimVerification.maxClaimable,
|
||||
pricePerToken: claimVerification.price,
|
||||
currency: claimVerification.currencyAddress,
|
||||
} 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",
|
||||
contractWrapper,
|
||||
return (
|
||||
!hasFunction<DropSinglePhase1155>(
|
||||
"getClaimConditionById",
|
||||
contractWrapper,
|
||||
) &&
|
||||
hasFunction<DropSinglePhase1155>(
|
||||
"getSupplyClaimedByWallet",
|
||||
contractWrapper,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private isMultiPhaseDropContract(
|
||||
isNewMultiphaseDrop(
|
||||
contractWrapper: ContractWrapper<any>,
|
||||
): contractWrapper is ContractWrapper<DropERC1155> {
|
||||
return hasFunction<DropERC1155>("getClaimConditionById", contractWrapper);
|
||||
): contractWrapper is ContractWrapper<Drop1155> {
|
||||
return (
|
||||
hasFunction<Drop1155>("getClaimConditionById", contractWrapper) &&
|
||||
hasFunction<Drop1155>("getSupplyClaimedByWallet", contractWrapper)
|
||||
);
|
||||
}
|
||||
|
||||
isLegacySinglePhaseDrop(
|
||||
contractWrapper: ContractWrapper<any>,
|
||||
): contractWrapper is ContractWrapper<DropSinglePhase1155_V1> {
|
||||
return (
|
||||
!hasFunction<DropSinglePhase1155_V1>(
|
||||
"getClaimConditionById",
|
||||
contractWrapper,
|
||||
) &&
|
||||
hasFunction<DropSinglePhase1155_V1>("getClaimTimestamp", contractWrapper)
|
||||
);
|
||||
}
|
||||
|
||||
isLegacyMultiPhaseDrop(
|
||||
contractWrapper: ContractWrapper<any>,
|
||||
): contractWrapper is ContractWrapper<DropERC1155_V2> {
|
||||
return (
|
||||
hasFunction<DropERC1155_V2>("getClaimConditionById", contractWrapper) &&
|
||||
hasFunction<DropERC1155_V2>("setWalletClaimCount", contractWrapper)
|
||||
);
|
||||
}
|
||||
|
||||
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 { 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
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
detectContractFeature,
|
||||
hasFunction,
|
||||
} from "../../common/feature-detection";
|
||||
import { getPrebuiltInfo } from "../../common/legacy";
|
||||
import { uploadOrExtractURIs } from "../../common/nft";
|
||||
import {
|
||||
FEATURE_EDITION_LAZY_MINTABLE,
|
||||
@@ -21,10 +22,7 @@ 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";
|
||||
@@ -232,17 +230,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
|
||||
|
||||
@@ -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,
|
||||
|
||||
+8
-31
@@ -1,6 +1,6 @@
|
||||
import { FEATURE_TOKEN_CLAIMABLE_WITH_CONDITIONS } 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,7 +20,7 @@ import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
* await contract.token.drop.claim.to("0x...", quantity);
|
||||
* ```
|
||||
*/
|
||||
export class Erc20Claimable implements DetectableFeature {
|
||||
export class Erc20ClaimableWithConditions implements DetectableFeature {
|
||||
featureName = FEATURE_TOKEN_CLAIMABLE_WITH_CONDITIONS.name;
|
||||
/**
|
||||
* Configure claim conditions
|
||||
@@ -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,
|
||||
@@ -12,13 +13,8 @@ import {
|
||||
FEATURE_TOKEN_SIGNATURE_MINTABLE,
|
||||
FEATURE_TOKEN_CLAIMABLE_WITH_CONDITIONS,
|
||||
} 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);
|
||||
).to(destinationAddress, amount, options);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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_CLAIMABLE_WITH_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_CLAIMABLE_WITH_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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -194,7 +194,11 @@ export class Erc721LazyMintable implements DetectableFeature {
|
||||
if (
|
||||
detectContractFeature<BaseClaimConditionERC721>(
|
||||
this.contractWrapper,
|
||||
"ERC721ClaimableWithConditions",
|
||||
"ERC721ClaimableWithConditionsV1",
|
||||
) ||
|
||||
detectContractFeature<BaseClaimConditionERC721>(
|
||||
this.contractWrapper,
|
||||
"ERC721ClaimableWithConditionsV2",
|
||||
)
|
||||
) {
|
||||
return new Erc721ClaimableWithConditions(
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -13,12 +13,13 @@ import {
|
||||
FEATURE_NFT_BATCH_MINTABLE,
|
||||
FEATURE_NFT_BURNABLE,
|
||||
FEATURE_NFT_CLAIMABLE,
|
||||
FEATURE_NFT_CLAIMABLE_WITH_CONDITIONS,
|
||||
FEATURE_NFT_CLAIMABLE_WITH_CONDITIONS_V2,
|
||||
FEATURE_NFT_LAZY_MINTABLE,
|
||||
FEATURE_NFT_MINTABLE,
|
||||
FEATURE_NFT_REVEALABLE,
|
||||
FEATURE_NFT_SIGNATURE_MINTABLE,
|
||||
FEATURE_NFT_SUPPLY,
|
||||
FEATURE_NFT_TIERED_DROP,
|
||||
} 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;
|
||||
}
|
||||
@@ -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,
|
||||
@@ -682,10 +687,20 @@ export class Erc721<
|
||||
get claimConditions() {
|
||||
return assertEnabled(
|
||||
this.lazyMintable?.claimWithConditions,
|
||||
FEATURE_NFT_CLAIMABLE_WITH_CONDITIONS,
|
||||
FEATURE_NFT_CLAIMABLE_WITH_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 //////
|
||||
|
||||
/**
|
||||
@@ -828,6 +843,18 @@ 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 {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { TransactionError } from "../../common";
|
||||
import { CONTRACT_ADDRESSES, SUPPORTED_CHAIN_IDS } from "../../constants";
|
||||
import {
|
||||
EditionDropInitializer,
|
||||
EditionInitializer,
|
||||
getContractName,
|
||||
MarketplaceInitializer,
|
||||
MultiwrapInitializer,
|
||||
NFTCollectionInitializer,
|
||||
@@ -41,6 +41,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 +72,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 +80,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",
|
||||
@@ -285,4 +295,26 @@ export class ContractFactory extends ContractWrapper<TWFactory> {
|
||||
? [openzeppelinForwarder, biconomyForwarder]
|
||||
: [openzeppelinForwarder];
|
||||
}
|
||||
|
||||
private async getImplementation(
|
||||
contract: typeof PREBUILT_CONTRACTS_MAP[PrebuiltContractType],
|
||||
version?: number,
|
||||
) {
|
||||
const encodedType = ethers.utils.formatBytes32String(contract.name);
|
||||
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
@@ -1,4 +1,5 @@
|
||||
import { AddressSchema, AmountSchema } from "../../shared";
|
||||
import { AmountSchema } from "../../../../core/schema/shared";
|
||||
import { AddressSchema } from "../../shared";
|
||||
import { z } from "zod";
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
import { QuantitySchema } from "../../../../core/schema/shared";
|
||||
import {
|
||||
AmountSchema,
|
||||
BytesLikeSchema,
|
||||
QuantitySchema,
|
||||
} from "../../../../core/schema/shared";
|
||||
import { NATIVE_TOKEN_ADDRESS } from "../../../constants/currency";
|
||||
import {
|
||||
BigNumberishSchema,
|
||||
BigNumberSchema,
|
||||
BytesLikeSchema,
|
||||
AmountSchema,
|
||||
StartDateSchema,
|
||||
} from "../../shared";
|
||||
import { CurrencyValueSchema } from "./currency";
|
||||
import { SnapshotInputSchema } from "./snapshots";
|
||||
import { BigNumber, utils } from "ethers";
|
||||
import { BigNumber, BigNumberish, utils } from "ethers";
|
||||
import { z } from "zod";
|
||||
|
||||
/**
|
||||
@@ -19,11 +21,12 @@ export const ClaimConditionInputSchema = z.object({
|
||||
startTime: StartDateSchema,
|
||||
currencyAddress: z.string().default(NATIVE_TOKEN_ADDRESS),
|
||||
price: AmountSchema.default(0),
|
||||
maxQuantity: QuantitySchema,
|
||||
quantityLimitPerTransaction: QuantitySchema,
|
||||
maxClaimableSupply: QuantitySchema,
|
||||
maxClaimablePerWallet: QuantitySchema,
|
||||
waitInSeconds: BigNumberishSchema.default(0),
|
||||
merkleRootHash: BytesLikeSchema.default(utils.hexZeroPad([0], 32)),
|
||||
snapshot: z.optional(SnapshotInputSchema).nullable(),
|
||||
metadata: z.union([z.string(), z.object({})]).optional(),
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -54,4 +57,17 @@ export const ClaimConditionOutputSchema = ClaimConditionInputSchema.extend({
|
||||
waitInSeconds: BigNumberSchema,
|
||||
startTime: BigNumberSchema.transform((n) => new Date(n.toNumber() * 1000)),
|
||||
snapshot: SnapshotInputSchema.optional().nullable(),
|
||||
metadata: z.string().optional(),
|
||||
});
|
||||
|
||||
export type AbstractClaimConditionContractStruct = {
|
||||
startTimestamp: BigNumberish;
|
||||
maxClaimableSupply: BigNumberish;
|
||||
supplyClaimed: BigNumberish;
|
||||
maxClaimablePerWallet: BigNumberish;
|
||||
merkleRoot: string;
|
||||
pricePerToken: BigNumberish;
|
||||
currency: string;
|
||||
waitTimeInSecondsBetweenClaims?: BigNumberish; // only in legacy claim conditions
|
||||
metadata?: string; // only for new claim conditions
|
||||
};
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import {
|
||||
AddressSchema,
|
||||
BasisPointsSchema,
|
||||
FileOrBufferOrStringSchema,
|
||||
} from "../../shared";
|
||||
} from "../../../../core/schema/shared";
|
||||
import { AddressSchema } from "../../shared";
|
||||
import { constants } from "ethers";
|
||||
import { z } from "zod";
|
||||
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
import { NFTInputOrUriSchema } from "../../../../core/schema/nft";
|
||||
import {
|
||||
AmountSchema,
|
||||
BasisPointsSchema,
|
||||
} from "../../../../core/schema/shared";
|
||||
import { resolveOrGenerateId } from "../../../common/signature-minting";
|
||||
import { NATIVE_TOKEN_ADDRESS } from "../../../constants/currency";
|
||||
import {
|
||||
BasisPointsSchema,
|
||||
BigNumberishSchema,
|
||||
BigNumberSchema,
|
||||
EndDateSchema,
|
||||
AmountSchema,
|
||||
StartDateSchema,
|
||||
} from "../../shared";
|
||||
import { constants } from "ethers";
|
||||
@@ -16,7 +18,11 @@ import { z } from "zod";
|
||||
* @internal
|
||||
*/
|
||||
export const BaseSignaturePayloadInput = z.object({
|
||||
to: z.string().default(constants.AddressZero),
|
||||
to: z
|
||||
.string()
|
||||
.refine((address) => address.toLowerCase() !== constants.AddressZero, {
|
||||
message: "Cannot create payload to mint to zero address",
|
||||
}),
|
||||
price: AmountSchema.default(0),
|
||||
currencyAddress: z.string().default(NATIVE_TOKEN_ADDRESS),
|
||||
mintStartTime: StartDateSchema,
|
||||
@@ -255,3 +261,10 @@ export const MintRequest721withQuantity = [
|
||||
{ name: "validityEndTimestamp", type: "uint128" },
|
||||
{ name: "uid", type: "bytes32" },
|
||||
];
|
||||
|
||||
export const GenericRequest = [
|
||||
{ name: "validityStartTimestamp", type: "uint128" },
|
||||
{ name: "validityEndTimestamp", type: "uint128" },
|
||||
{ name: "uid", type: "bytes32" },
|
||||
{ name: "data", type: "bytes" },
|
||||
];
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import { AddressSchema, AmountSchema } from "../../shared";
|
||||
import { QuantitySchema } from "../../../../core/schema/shared";
|
||||
import { AddressSchema } from "../../shared";
|
||||
import { ethers } from "ethers";
|
||||
import { z } from "zod";
|
||||
|
||||
/**
|
||||
@@ -8,12 +10,13 @@ export const MerkleSchema = z.object({
|
||||
merkle: z.record(z.string()).default({}),
|
||||
});
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export const SnapshotEntryInput = z.object({
|
||||
address: AddressSchema,
|
||||
maxClaimable: AmountSchema.default(0),
|
||||
maxClaimable: QuantitySchema.default(0), // defaults to 0
|
||||
price: QuantitySchema.optional(), // defaults to unlimited, but can be undefined in old snapshots
|
||||
currencyAddress: AddressSchema.default(
|
||||
ethers.constants.AddressZero,
|
||||
).optional(), // defaults to AddressZero, but can be undefined for old snapshots
|
||||
});
|
||||
|
||||
export type SnapshotEntry = z.output<typeof SnapshotEntryInput>;
|
||||
@@ -49,7 +52,7 @@ export const SnapshotInputSchema = z.union([
|
||||
z.array(SnapshotEntryInput),
|
||||
]);
|
||||
|
||||
const SnapshotEntryWithProofSchema = SnapshotEntryInput.extend({
|
||||
export const SnapshotEntryWithProofSchema = SnapshotEntryInput.extend({
|
||||
proof: z.array(z.string()),
|
||||
});
|
||||
/**
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { FileOrBufferOrStringSchema } from "../../../core/schema/shared";
|
||||
import { toSemver } from "../../common/index";
|
||||
import {
|
||||
AddressSchema,
|
||||
BigNumberishSchema,
|
||||
BigNumberTransformSchema,
|
||||
FileOrBufferOrStringSchema,
|
||||
} from "../shared";
|
||||
import {
|
||||
CommonContractOutputSchema,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { AddressSchema, BasisPointsSchema } from "../shared";
|
||||
import { BasisPointsSchema } from "../../../core/schema/shared";
|
||||
import { AddressSchema } from "../shared";
|
||||
import {
|
||||
CommonContractOutputSchema,
|
||||
CommonContractSchema,
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
import { BasisPointsSchema } from "../../../core/schema/shared";
|
||||
import { BigNumberSchema } from "../shared";
|
||||
import { BaseSignaturePayloadInput } from "./common";
|
||||
import { constants } from "ethers";
|
||||
import { z } from "zod";
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export const TieredDropPayloadSchema = BaseSignaturePayloadInput.extend({
|
||||
tierPriority: z.array(z.string()),
|
||||
royaltyRecipient: z.string().default(constants.AddressZero),
|
||||
royaltyBps: BasisPointsSchema.default(0),
|
||||
quantity: BigNumberSchema.default(1),
|
||||
});
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type TieredDropPayloadInput = z.input<typeof TieredDropPayloadSchema>;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export type TieredDropPayloadOutput = z.output<typeof TieredDropPayloadSchema>;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export type TieredDropPayloadWithSignature = {
|
||||
payload: TieredDropPayloadOutput;
|
||||
signature: string;
|
||||
};
|
||||
|
||||
export const GenericRequest = [
|
||||
{
|
||||
name: "validityStartTimestamp",
|
||||
type: "uint128",
|
||||
},
|
||||
{
|
||||
name: "validityEndTimestamp",
|
||||
type: "uint128",
|
||||
},
|
||||
{
|
||||
name: "uid",
|
||||
type: "bytes32",
|
||||
},
|
||||
{
|
||||
name: "data",
|
||||
type: "bytes",
|
||||
},
|
||||
];
|
||||
@@ -1,9 +1,5 @@
|
||||
import {
|
||||
AddressSchema,
|
||||
BigNumberishSchema,
|
||||
BigNumberSchema,
|
||||
PercentSchema,
|
||||
} from "../shared";
|
||||
import { PercentSchema } from "../../../core/schema/shared";
|
||||
import { AddressSchema, BigNumberishSchema, BigNumberSchema } from "../shared";
|
||||
import {
|
||||
CommonContractOutputSchema,
|
||||
CommonContractSchema,
|
||||
|
||||
@@ -1,34 +1,6 @@
|
||||
import { BigNumber, CallOverrides, utils } from "ethers";
|
||||
import { z } from "zod";
|
||||
|
||||
const isBrowser = () => typeof window !== "undefined";
|
||||
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
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export const FileOrBufferSchema = z.union([
|
||||
FileOrBufferUnionSchema,
|
||||
z.object({
|
||||
data: z.union([FileOrBufferUnionSchema, z.string()]),
|
||||
name: z.string(),
|
||||
}),
|
||||
]);
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export const FileOrBufferOrStringSchema = z.union([
|
||||
FileOrBufferSchema,
|
||||
z.string(),
|
||||
]);
|
||||
|
||||
export const MAX_BPS = 10000;
|
||||
|
||||
export const BytesLikeSchema = z.union([z.array(z.number()), z.string()]);
|
||||
|
||||
export const BigNumberSchema = z
|
||||
.union([
|
||||
z.string(),
|
||||
@@ -55,16 +27,6 @@ export const BigNumberTransformSchema = z
|
||||
return BigNumber.from(arg).toString();
|
||||
});
|
||||
|
||||
export const BasisPointsSchema = z
|
||||
.number()
|
||||
.max(MAX_BPS, "Cannot exeed 100%")
|
||||
.min(0, "Cannot be below 0%");
|
||||
|
||||
export const PercentSchema = z
|
||||
.number()
|
||||
.max(100, "Cannot exeed 100%")
|
||||
.min(0, "Cannot be below 0%");
|
||||
|
||||
export const AddressSchema = z.string().refine(
|
||||
(arg) => utils.isAddress(arg),
|
||||
(out) => {
|
||||
@@ -74,13 +36,6 @@ export const AddressSchema = z.string().refine(
|
||||
},
|
||||
);
|
||||
|
||||
export const AmountSchema = z
|
||||
.union([
|
||||
z.string().regex(/^([0-9]+\.?[0-9]*|\.[0-9]+)$/, "Invalid amount"),
|
||||
z.number().min(0, "Amount cannot be negative"),
|
||||
])
|
||||
.transform((arg) => (typeof arg === "number" ? arg.toString() : arg));
|
||||
|
||||
export const RawDateSchema = z.date().transform((i) => {
|
||||
return BigNumber.from(Math.floor(i.getTime() / 1000));
|
||||
});
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { AddressSchema, BigNumberishSchema, AmountSchema } from "../../shared";
|
||||
import { AmountSchema } from "../../../../core/schema/shared";
|
||||
import { AddressSchema, BigNumberishSchema } from "../../shared";
|
||||
import { z } from "zod";
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { NFTInputOrUriSchema } from "../../../core/schema/nft";
|
||||
import { BigNumberishSchema, AmountSchema, RawDateSchema } from "../shared";
|
||||
import { AmountSchema } from "../../../core/schema/shared";
|
||||
import { BigNumberishSchema, RawDateSchema } from "../shared";
|
||||
import {
|
||||
ERC1155WrappableSchema,
|
||||
ERC20WrappableSchema,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { AddressSchema, AmountSchema } from "../shared";
|
||||
import { AmountSchema } from "../../../core/schema/shared";
|
||||
import { AddressSchema } from "../shared";
|
||||
import { z } from "zod";
|
||||
|
||||
/**
|
||||
|
||||
@@ -53,7 +53,7 @@ export type FilledConditionInput = z.output<typeof ClaimConditionInputSchema>;
|
||||
export type ClaimVerification = {
|
||||
overrides: CallOverrides;
|
||||
proofs: BytesLike[];
|
||||
maxQuantityPerTransaction: BigNumber;
|
||||
maxClaimable: BigNumber;
|
||||
price: BigNumber;
|
||||
currencyAddress: string;
|
||||
};
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
* Currency metadata.
|
||||
* @public
|
||||
*/
|
||||
import { AmountSchema } from "../../core/schema/shared";
|
||||
import {
|
||||
CurrencySchema,
|
||||
CurrencyValueSchema,
|
||||
} from "../schema/contracts/common/currency";
|
||||
import { AmountSchema } from "../schema/shared";
|
||||
import { z } from "zod";
|
||||
|
||||
/**
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user