Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
130 changes: 130 additions & 0 deletions packages/sdk/src/evm/contracts/erc721Methods.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
import { type BigNumberish, constants, BigNumber } from "ethers";
import { ContractWrapper } from "../core/classes/contract-wrapper";
import type {
BaseClaimConditionERC721,
BaseDropERC721,
BaseERC721,
} from "../types/eips";
import type { NFT, NFTMetadata } from "../../core/schema/nft";
import { NotFoundError } from "../common/error";
import { FALLBACK_METADATA, fetchTokenMetadata } from "../common/nft";
import type { ThirdwebStorage } from "@thirdweb-dev/storage";
import type {
DropERC721,
IClaimableERC721,
IERC721AQueryableUpgradeable,
IERC721Supply,
IMintableERC721,
IMulticall,
OpenEditionERC721,
SignatureDrop,
TieredDrop,
TokenERC721,
Zora_IERC721Drop,
} from "@thirdweb-dev/contracts-js";
import { hasFunction } from "../common/feature-detection/hasFunction";

/**
* @internal
*/
type Erc721Extensions =
| BaseERC721
| Zora_IERC721Drop
| IClaimableERC721
| IMintableERC721
| BaseClaimConditionERC721
| (IMintableERC721 & IMulticall)
| (BaseERC721 & IERC721AQueryableUpgradeable);

export async function ownerOfErc721(
tokenId: BigNumberish,
contractWrapper: ContractWrapper<Erc721Extensions>,
): Promise<string> {
return await (contractWrapper as ContractWrapper<BaseERC721>).read(
"ownerOf",
[tokenId],
);
}

export async function getTokenMetadata(
tokenId: BigNumberish,
contractWrapper: ContractWrapper<Erc721Extensions>,
storage: ThirdwebStorage,
): Promise<NFTMetadata> {
const tokenUri = await (contractWrapper as ContractWrapper<BaseERC721>).read(
"tokenURI",
[tokenId],
);
if (!tokenUri) {
throw new NotFoundError();
}
return fetchTokenMetadata(tokenId, tokenUri, storage);
}

export async function getErc721Token(
tokenId: BigNumberish,
contractWrapper: ContractWrapper<Erc721Extensions>,
storage: ThirdwebStorage,
): Promise<NFT> {
const [owner, metadata] = await Promise.all([
ownerOfErc721(tokenId, contractWrapper).catch(() => constants.AddressZero),
getTokenMetadata(tokenId, contractWrapper, storage).catch(() => ({
id: tokenId.toString(),
uri: "",
...FALLBACK_METADATA,
})),
]);
return { owner, metadata, type: "ERC721", supply: "1" };
}

export async function nextTokenIdToMint(
contractWrapper: ContractWrapper<
TokenERC721 | TieredDrop | (BaseERC721 & IERC721Supply) | BaseDropERC721
>,
): Promise<BigNumber> {
if (hasFunction<TokenERC721>("nextTokenIdToMint", contractWrapper)) {
let _nextTokenIdToMint = await (
contractWrapper as ContractWrapper<TokenERC721>
).read("nextTokenIdToMint", []);
// handle open editions and contracts with startTokenId
if (hasFunction<OpenEditionERC721>("startTokenId", contractWrapper)) {
_nextTokenIdToMint = _nextTokenIdToMint.sub(
await (contractWrapper as ContractWrapper<OpenEditionERC721>).read(
"startTokenId",
[],
),
);
}
return _nextTokenIdToMint;
} else if (hasFunction<TokenERC721>("totalSupply", contractWrapper)) {
return await (contractWrapper as ContractWrapper<TokenERC721>).read(
"totalSupply",
[],
);
} else {
throw new Error(
"Contract requires either `nextTokenIdToMint` or `totalSupply` function available to determine the next token ID to mint",
);
}
}

export async function totalClaimedSupply(
contractWrapper: ContractWrapper<
SignatureDrop | DropERC721 | (BaseERC721 & IERC721Supply)
>,
): Promise<BigNumber> {
const contract = contractWrapper;
if (hasFunction<SignatureDrop>("totalMinted", contract)) {
return (contractWrapper as ContractWrapper<SignatureDrop>).read(
"totalMinted",
[],
);
}
if (hasFunction<DropERC721>("nextTokenIdToClaim", contract)) {
return (contractWrapper as ContractWrapper<DropERC721>).read(
"nextTokenIdToClaim",
[],
);
}
throw new Error("No function found on contract to get total claimed supply");
}
8 changes: 3 additions & 5 deletions packages/sdk/src/evm/core/classes/erc-721-batch-mintable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import { DetectableFeature } from "../interfaces/DetectableFeature";
import type { TransactionResultWithId } from "../types";
import { ContractEncoder } from "./contract-encoder";
import type { ContractWrapper } from "./contract-wrapper";
import type { Erc721 } from "./erc-721";
import { Transaction } from "./transactions";
import { getErc721Token } from "../../contracts/erc721Methods";

/**
* Mint Many ERC721 NFTs at once
Expand All @@ -29,14 +29,11 @@ export class Erc721BatchMintable implements DetectableFeature {
featureName = FEATURE_NFT_BATCH_MINTABLE.name;
private contractWrapper: ContractWrapper<IMintableERC721 & IMulticall>;
private storage: ThirdwebStorage;
private erc721: Erc721;

constructor(
erc721: Erc721,
contractWrapper: ContractWrapper<IMintableERC721 & IMulticall>,
storage: ThirdwebStorage,
) {
this.erc721 = erc721;
this.contractWrapper = contractWrapper;
this.storage = storage;
}
Expand Down Expand Up @@ -99,7 +96,8 @@ export class Erc721BatchMintable implements DetectableFeature {
return {
id,
receipt,
data: () => this.erc721.get(id),
data: () =>
getErc721Token(id, this.contractWrapper, this.storage),
};
});
},
Expand Down
7 changes: 2 additions & 5 deletions packages/sdk/src/evm/core/classes/erc-721-claim-conditions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { TokensClaimedEvent } from "@thirdweb-dev/contracts-js/dist/declarations
import { CustomContractSchema } from "../../schema/contracts/custom";
import { ContractMetadata } from "./contract-metadata";
import { DropClaimConditions } from "./drop-claim-conditions";
import type { Erc721 } from "./erc-721";
import { getErc721Token } from "../../contracts/erc721Methods";

/**
* Configure and claim ERC721 NFTs
Expand Down Expand Up @@ -54,15 +54,12 @@ export class Erc721ClaimableWithConditions implements DetectableFeature {
*/
public conditions: DropClaimConditions<BaseClaimConditionERC721>;
private contractWrapper: ContractWrapper<BaseClaimConditionERC721>;
private erc721: Erc721;
private storage: ThirdwebStorage;

constructor(
erc721: Erc721,
contractWrapper: ContractWrapper<BaseClaimConditionERC721>,
storage: ThirdwebStorage,
) {
this.erc721 = erc721;
this.contractWrapper = contractWrapper;

this.storage = storage;
Expand Down Expand Up @@ -123,7 +120,7 @@ export class Erc721ClaimableWithConditions implements DetectableFeature {
results.push({
id,
receipt,
data: () => this.erc721.get(id),
data: () => getErc721Token(id, this.contractWrapper, this.storage),
});
}
return results;
Expand Down
11 changes: 6 additions & 5 deletions packages/sdk/src/evm/core/classes/erc-721-claim-zora.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ import type { ClaimOptions } from "../../types/claim-conditions/claim-conditions
import { DetectableFeature } from "../interfaces/DetectableFeature";
import { TransactionResultWithId } from "../types";
import { ContractWrapper } from "./contract-wrapper";
import type { Erc721 } from "./erc-721";
import { Transaction } from "./transactions";
import type { ThirdwebStorage } from "@thirdweb-dev/storage";
import { getErc721Token } from "../../contracts/erc721Methods";

/**
* Claim ERC721 NFTs from a Zora Drop
Expand All @@ -28,14 +29,14 @@ import { Transaction } from "./transactions";
export class Erc721ClaimableZora implements DetectableFeature {
featureName = FEATURE_NFT_CLAIM_ZORA.name;

private erc721: Erc721;
private contractWrapper: ContractWrapper<Zora_IERC721Drop>;
protected storage: ThirdwebStorage;

constructor(
erc721: Erc721,
contractWrapper: ContractWrapper<Zora_IERC721Drop>,
storage: ThirdwebStorage,
) {
this.erc721 = erc721;
this.storage = storage;
this.contractWrapper = contractWrapper;
}

Expand Down Expand Up @@ -107,7 +108,7 @@ export class Erc721ClaimableZora implements DetectableFeature {
results.push({
id,
receipt,
data: () => this.erc721.get(id),
data: () => getErc721Token(id, this.contractWrapper, this.storage),
});
}
return results;
Expand Down
11 changes: 6 additions & 5 deletions packages/sdk/src/evm/core/classes/erc-721-claimable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ import type { IClaimableERC721 } from "@thirdweb-dev/contracts-js";
import { BigNumber, BigNumberish, CallOverrides } from "ethers";
import { TokensClaimedEvent } from "@thirdweb-dev/contracts-js/dist/declarations/src/TieredDrop";
import { calculateClaimCost } from "../../common/claim-conditions/calculateClaimCost";
import type { Erc721 } from "./erc-721";
import { type ThirdwebStorage } from "@thirdweb-dev/storage";
import { getErc721Token } from "../../contracts/erc721Methods";

/**
* Configure and claim ERC721 NFTs
Expand All @@ -26,15 +27,15 @@ import type { Erc721 } from "./erc-721";
export class Erc721Claimable implements DetectableFeature {
featureName = FEATURE_NFT_CLAIM_CUSTOM.name;

private erc721: Erc721;
private contractWrapper: ContractWrapper<IClaimableERC721>;
protected storage: ThirdwebStorage;

constructor(
erc721: Erc721,
contractWrapper: ContractWrapper<IClaimableERC721>,
storage: ThirdwebStorage,
) {
this.erc721 = erc721;
this.contractWrapper = contractWrapper;
this.storage = storage;
}

/**
Expand Down Expand Up @@ -115,7 +116,7 @@ export class Erc721Claimable implements DetectableFeature {
results.push({
id,
receipt,
data: () => this.erc721.get(id),
data: () => getErc721Token(id, this.contractWrapper, this.storage),
});
}
return results;
Expand Down
14 changes: 8 additions & 6 deletions packages/sdk/src/evm/core/classes/erc-721-enumerable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@ import type { AddressOrEns } from "../../schema/shared/AddressOrEnsSchema";
import type { BaseERC721 } from "../../types/eips";
import { DetectableFeature } from "../interfaces/DetectableFeature";
import type { ContractWrapper } from "./contract-wrapper";
import type { Erc721 } from "./erc-721";
import {
DEFAULT_QUERY_ALL_COUNT,
QueryAllParams,
} from "../../../core/schema/QueryParams";
import { type ThirdwebStorage } from "@thirdweb-dev/storage";
import { getErc721Token } from "../../contracts/erc721Methods";

/**
* List owned ERC721 NFTs
Expand All @@ -28,14 +29,13 @@ import {
export class Erc721Enumerable implements DetectableFeature {
featureName = FEATURE_NFT_ENUMERABLE.name;
private contractWrapper: ContractWrapper<BaseERC721 & IERC721Enumerable>;
private erc721: Erc721;

private storage: ThirdwebStorage;
constructor(
erc721: Erc721,
contractWrapper: ContractWrapper<BaseERC721 & IERC721Enumerable>,
storage: ThirdwebStorage,
) {
this.erc721 = erc721;
this.contractWrapper = contractWrapper;
this.storage = storage;
}

/**
Expand Down Expand Up @@ -64,7 +64,9 @@ export class Erc721Enumerable implements DetectableFeature {
tokenIds = tokenIds.slice(start, start + count);
}
return await Promise.all(
tokenIds.map((tokenId) => this.erc721.get(tokenId.toString())),
tokenIds.map((tokenId) =>
getErc721Token(tokenId.toString(), this.contractWrapper, this.storage),
),
);
}

Expand Down
16 changes: 8 additions & 8 deletions packages/sdk/src/evm/core/classes/erc-721-lazy-mintable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@ import { getBaseUriFromBatch, uploadOrExtractURIs } from "../../common/nft";
import type { BaseDelayedRevealERC721 } from "../../types/eips";
import { DelayedReveal } from "./delayed-reveal";
import type { TokensLazyMintedEvent } from "@thirdweb-dev/contracts-js/dist/declarations/src/LazyMint";
import type { Erc721 } from "./erc-721";
import {
nextTokenIdToMint,
getTokenMetadata,
} from "../../contracts/erc721Methods";

/**
* Lazily mint and claim ERC721 NFTs
Expand Down Expand Up @@ -66,17 +69,13 @@ export class Erc721LazyMintable implements DetectableFeature {
public revealer: DelayedReveal<BaseDelayedRevealERC721> | undefined;

private contractWrapper: ContractWrapper<BaseDropERC721>;
private erc721: Erc721;
private storage: ThirdwebStorage;

constructor(
erc721: Erc721,
contractWrapper: ContractWrapper<BaseDropERC721>,
storage: ThirdwebStorage,
) {
this.erc721 = erc721;
this.contractWrapper = contractWrapper;

this.storage = storage;
this.revealer = this.detectErc721Revealable();
}
Expand Down Expand Up @@ -114,7 +113,7 @@ export class Erc721LazyMintable implements DetectableFeature {
onProgress: (event: UploadProgressEvent) => void;
},
): Promise<Transaction<TransactionResultWithId<NFTMetadata>[]>> => {
const startFileNumber = await this.erc721.nextTokenIdToMint();
const startFileNumber = await nextTokenIdToMint(this.contractWrapper);
const batch = await uploadOrExtractURIs(
metadatas,
this.storage,
Expand Down Expand Up @@ -144,7 +143,8 @@ export class Erc721LazyMintable implements DetectableFeature {
results.push({
id,
receipt,
data: () => this.erc721.getTokenMetadata(id),
data: () =>
getTokenMetadata(id, this.contractWrapper, this.storage),
});
}
return results;
Expand All @@ -169,7 +169,7 @@ export class Erc721LazyMintable implements DetectableFeature {
this.contractWrapper,
this.storage,
FEATURE_NFT_REVEALABLE.name,
() => this.erc721.nextTokenIdToMint(),
() => nextTokenIdToMint(this.contractWrapper),
);
}
return undefined;
Expand Down
Loading