Compare commits
15
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
78bae497ba | ||
|
|
3e425bf8e6 | ||
|
|
4dca5b21a6 | ||
|
|
42c79e93dc | ||
|
|
2951b5533c | ||
|
|
fe8751eeae | ||
|
|
a0cd47ec35 | ||
|
|
3902e7aba4 | ||
|
|
8717cb2c05 | ||
|
|
d57ab03f5d | ||
|
|
5aa3c433d7 | ||
|
|
98dd64a375 | ||
|
|
e25dd1ac8e | ||
|
|
e68f9685b3 | ||
|
|
84beda1cbb |
@@ -1,5 +1,19 @@
|
||||
# @thirdweb-dev/auth
|
||||
|
||||
## 2.0.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`42c79e9`](https://github.com/thirdweb-dev/js/commit/42c79e93dc958ca46a55d705aeea44ffdbbcc5f6), [`fe8751e`](https://github.com/thirdweb-dev/js/commit/fe8751eeae7ad013b890a8092ddbd091ecbd6708)]:
|
||||
- @thirdweb-dev/sdk@3.0.2
|
||||
|
||||
## 2.0.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`98dd64a`](https://github.com/thirdweb-dev/js/commit/98dd64a375c302a879aab3c628ecfb84b4dd19da)]:
|
||||
- @thirdweb-dev/sdk@3.0.1
|
||||
|
||||
## 2.0.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@thirdweb-dev/auth",
|
||||
"version": "2.0.0",
|
||||
"version": "2.0.2",
|
||||
"exports": {
|
||||
"./package.json": "./package.json",
|
||||
"./express": {
|
||||
|
||||
@@ -1,5 +1,21 @@
|
||||
# thirdweb
|
||||
|
||||
## 0.7.13
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#128](https://github.com/thirdweb-dev/js/pull/128) [`4dca5b2`](https://github.com/thirdweb-dev/js/commit/4dca5b21a66fe8ff4c4b24663e1b9ad6c446b7e2) Thanks [@jarrodwatts](https://github.com/jarrodwatts)! - Add new documentation for detectable features
|
||||
|
||||
- Updated dependencies [[`42c79e9`](https://github.com/thirdweb-dev/js/commit/42c79e93dc958ca46a55d705aeea44ffdbbcc5f6), [`fe8751e`](https://github.com/thirdweb-dev/js/commit/fe8751eeae7ad013b890a8092ddbd091ecbd6708)]:
|
||||
- @thirdweb-dev/sdk@3.0.2
|
||||
|
||||
## 0.7.12
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`98dd64a`](https://github.com/thirdweb-dev/js/commit/98dd64a375c302a879aab3c628ecfb84b4dd19da)]:
|
||||
- @thirdweb-dev/sdk@3.0.1
|
||||
|
||||
## 0.7.11
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "thirdweb",
|
||||
"main": "dist/cli/index.js",
|
||||
"version": "0.7.11",
|
||||
"version": "0.7.13",
|
||||
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/cli",
|
||||
"author": "thirdweb eng <[email protected]>",
|
||||
"license": "Apache-2.0",
|
||||
|
||||
@@ -70,13 +70,13 @@ export async function detectExtensions(options: any) {
|
||||
const enabledFeatures: Feature[] = features.enabledFeatures.map(
|
||||
(feature) => ({
|
||||
name: feature.name,
|
||||
reference: `https://portal.thirdweb.com/contracts/${feature.docLinks.contracts}`,
|
||||
reference: `https://portal.thirdweb.com/extensions/${feature.name.toLowerCase()}`,
|
||||
}),
|
||||
);
|
||||
const suggestedFeatures: Feature[] = features.suggestedFeatures.map(
|
||||
(feature) => ({
|
||||
name: feature.name,
|
||||
reference: `https://portal.thirdweb.com/contracts/${feature.docLinks.contracts}`,
|
||||
reference: `https://portal.thirdweb.com/extensions/${feature.name.toLowerCase()}`,
|
||||
}),
|
||||
);
|
||||
|
||||
|
||||
@@ -1,5 +1,19 @@
|
||||
# @thirdweb-dev/react
|
||||
|
||||
## 3.0.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`42c79e9`](https://github.com/thirdweb-dev/js/commit/42c79e93dc958ca46a55d705aeea44ffdbbcc5f6), [`fe8751e`](https://github.com/thirdweb-dev/js/commit/fe8751eeae7ad013b890a8092ddbd091ecbd6708)]:
|
||||
- @thirdweb-dev/sdk@3.0.2
|
||||
|
||||
## 3.0.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`98dd64a`](https://github.com/thirdweb-dev/js/commit/98dd64a375c302a879aab3c628ecfb84b4dd19da)]:
|
||||
- @thirdweb-dev/sdk@3.0.1
|
||||
|
||||
## 3.0.0
|
||||
|
||||
### Major Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@thirdweb-dev/react",
|
||||
"version": "3.0.0",
|
||||
"version": "3.0.2",
|
||||
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/react",
|
||||
"author": "thirdweb eng <[email protected]>",
|
||||
"license": "Apache-2.0",
|
||||
|
||||
@@ -115,13 +115,13 @@ const NETWORK_INFO_HOOKS = ["useChainId", "useNetwork", "useNetworkMismatch"];
|
||||
|
||||
const CUSTOM_CONTRACT_HOOKS = [
|
||||
"useContract",
|
||||
"useContractData",
|
||||
"useContractRead",
|
||||
"useContractWrite",
|
||||
"useAllContractEvents",
|
||||
"useContractEvents",
|
||||
"useContractType",
|
||||
"useContractPublishMetadata",
|
||||
"useContractMetadata",
|
||||
"useContractCall",
|
||||
];
|
||||
|
||||
const CONTRACT_SETTINGS_HOOKS = [
|
||||
|
||||
@@ -134,6 +134,18 @@ export type UseContractResult<
|
||||
contract: TContract | undefined;
|
||||
};
|
||||
|
||||
/**
|
||||
* Use this resolve a contract address to a smart contract instance.
|
||||
*
|
||||
* @example
|
||||
* ```javascript
|
||||
* const { contract, isLoading, error } = useContract("{{contract_address}}");
|
||||
* ```
|
||||
*
|
||||
* @param contractAddress - the address of the deployed contract
|
||||
* @returns a response object that includes the contract once it is resolved
|
||||
* @public
|
||||
*/
|
||||
export function useContract<
|
||||
TContract extends ValidContractInstance = SmartContract,
|
||||
>(contractAddress: RequiredParam<ContractAddress>) {
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
docs/
|
||||
etc/
|
||||
temp/
|
||||
@@ -1,5 +1,19 @@
|
||||
# @thirdweb-dev/sdk
|
||||
|
||||
## 3.0.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#131](https://github.com/thirdweb-dev/js/pull/131) [`42c79e9`](https://github.com/thirdweb-dev/js/commit/42c79e93dc958ca46a55d705aeea44ffdbbcc5f6) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Fix editing releases from the dashboard
|
||||
|
||||
- [#124](https://github.com/thirdweb-dev/js/pull/124) [`fe8751e`](https://github.com/thirdweb-dev/js/commit/fe8751eeae7ad013b890a8092ddbd091ecbd6708) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Add feature detection for Ownable
|
||||
|
||||
## 3.0.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#122](https://github.com/thirdweb-dev/js/pull/122) [`98dd64a`](https://github.com/thirdweb-dev/js/commit/98dd64a375c302a879aab3c628ecfb84b4dd19da) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - handle legacy claim method signature in extensions
|
||||
|
||||
## 3.0.0
|
||||
|
||||
### Major Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@thirdweb-dev/sdk",
|
||||
"version": "3.0.0",
|
||||
"version": "3.0.2",
|
||||
"description": "The main thirdweb SDK.",
|
||||
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/sdk",
|
||||
"license": "Apache-2.0",
|
||||
@@ -20,7 +20,7 @@
|
||||
"build": "tsc && preconstruct build",
|
||||
"test:all": "SWC_NODE_PROJECT=./tsconfig.test.json mocha --timeout 30000 --parallel './test/**/*.test.ts'",
|
||||
"test": "make test",
|
||||
"test:single": "SWC_NODE_PROJECT=./tsconfig.test.json mocha --timeout 30000 --"
|
||||
"test:single": "SWC_NODE_PROJECT=./tsconfig.test.json mocha --timeout 30000"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@esbuild-plugins/node-modules-polyfill": "^0.1.4",
|
||||
|
||||
@@ -65,14 +65,12 @@ const additionalClassesToParse = [
|
||||
"ContractPlatformFee",
|
||||
"WalletAuthenticator",
|
||||
"UserWallet",
|
||||
"SmartContract",
|
||||
];
|
||||
|
||||
const modules = json.members[0].members.filter(
|
||||
(m) =>
|
||||
(m.kind === "Class" &&
|
||||
m.members
|
||||
.filter((cMember) => cMember.kind === "Property")
|
||||
.findIndex((property) => property.name === "contractType") > -1) ||
|
||||
(m.kind === "Class" && m.name.endsWith("Impl")) ||
|
||||
additionalClassesToParse.includes(m.name),
|
||||
);
|
||||
|
||||
@@ -154,7 +152,7 @@ const moduleMap = modules.reduce((acc, m) => {
|
||||
const baseClassMembers = baseClassCode
|
||||
? parseMembers(baseClassCode.members, "Method", baseClassCode.name)
|
||||
: [];
|
||||
acc[m.name] = {
|
||||
acc[m.name.replace("Impl", "")] = {
|
||||
name: m.name,
|
||||
summary: Formatter.renderDocNode(docComment.summarySection),
|
||||
remarks: docComment.remarksBlock
|
||||
|
||||
@@ -33,6 +33,7 @@ import {
|
||||
} from "./erc1155-features";
|
||||
import {
|
||||
FEATURE_METADATA,
|
||||
FEATURE_OWNER,
|
||||
FEATURE_PERMISSIONS,
|
||||
FEATURE_PLATFORM_FEE,
|
||||
FEATURE_PRIMARY_SALE,
|
||||
@@ -74,7 +75,8 @@ export type Feature =
|
||||
| typeof FEATURE_PLATFORM_FEE
|
||||
| typeof FEATURE_PRIMARY_SALE
|
||||
| typeof FEATURE_PERMISSIONS
|
||||
| typeof FEATURE_METADATA;
|
||||
| typeof FEATURE_METADATA
|
||||
| typeof FEATURE_OWNER;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
@@ -100,4 +102,5 @@ export const SUPPORTED_FEATURES: Record<string, Feature> = {
|
||||
[FEATURE_PRIMARY_SALE.name]: FEATURE_PRIMARY_SALE,
|
||||
[FEATURE_PERMISSIONS.name]: FEATURE_PERMISSIONS,
|
||||
[FEATURE_METADATA.name]: FEATURE_METADATA,
|
||||
[FEATURE_OWNER.name]: FEATURE_OWNER,
|
||||
};
|
||||
|
||||
@@ -3,6 +3,7 @@ import IPermissionsAbi from "@thirdweb-dev/contracts-js/dist/abis/IPermissions.j
|
||||
import IThirdwebPlatformFeeAbi from "@thirdweb-dev/contracts-js/dist/abis/IPlatformFee.json";
|
||||
import IThirdwebPrimarySaleAbi from "@thirdweb-dev/contracts-js/dist/abis/IPrimarySale.json";
|
||||
import IThirdwebRoyaltyAbi from "@thirdweb-dev/contracts-js/dist/abis/IRoyalty.json";
|
||||
import IOwnableAbi from "@thirdweb-dev/contracts-js/dist/abis/Ownable.json";
|
||||
|
||||
export const FEATURE_ROYALTY = {
|
||||
name: "Royalty",
|
||||
@@ -58,3 +59,14 @@ export const FEATURE_METADATA = {
|
||||
abis: [IContractMetadataAbi],
|
||||
features: {},
|
||||
} as const;
|
||||
|
||||
export const FEATURE_OWNER = {
|
||||
name: "Ownable",
|
||||
namespace: "owner",
|
||||
docLinks: {
|
||||
sdk: "sdk.owner",
|
||||
contracts: "Ownable",
|
||||
},
|
||||
abis: [IOwnableAbi],
|
||||
features: {},
|
||||
} as const;
|
||||
|
||||
@@ -281,25 +281,12 @@ export class EditionDropImpl extends StandardErc1155<DropERC1155> {
|
||||
quantity: BigNumberish,
|
||||
checkERC20Allowance = true, // TODO split up allowance checks
|
||||
): Promise<TransactionTask> {
|
||||
const claimVerification = await this.erc1155.claimConditions.prepareClaim(
|
||||
return this.erc1155.getClaimTransaction(
|
||||
destinationAddress,
|
||||
tokenId,
|
||||
quantity,
|
||||
checkERC20Allowance,
|
||||
{ checkERC20Allowance },
|
||||
);
|
||||
return TransactionTask.make({
|
||||
contractWrapper: this.contractWrapper,
|
||||
functionName: "claim",
|
||||
args: [
|
||||
destinationAddress,
|
||||
tokenId,
|
||||
quantity,
|
||||
claimVerification.currencyAddress,
|
||||
claimVerification.price,
|
||||
claimVerification.proofs,
|
||||
claimVerification.maxQuantityPerTransaction,
|
||||
],
|
||||
overrides: claimVerification.overrides,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -331,13 +318,9 @@ export class EditionDropImpl extends StandardErc1155<DropERC1155> {
|
||||
quantity: BigNumberish,
|
||||
checkERC20Allowance = true,
|
||||
): Promise<TransactionResult> {
|
||||
const tx = await this.getClaimTransaction(
|
||||
destinationAddress,
|
||||
tokenId,
|
||||
quantity,
|
||||
return this.erc1155.claimTo(destinationAddress, tokenId, quantity, {
|
||||
checkERC20Allowance,
|
||||
);
|
||||
return await tx.execute();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -440,24 +440,10 @@ export class NFTDropImpl extends StandardErc721<DropERC721> {
|
||||
public async getClaimTransaction(
|
||||
destinationAddress: string,
|
||||
quantity: BigNumberish,
|
||||
checkERC20Allowance = true, // TODO split up allowance checks
|
||||
checkERC20Allowance = true,
|
||||
): Promise<TransactionTask> {
|
||||
const claimVerification = await this.prepareClaim(
|
||||
quantity,
|
||||
return this.erc721.getClaimTransaction(destinationAddress, quantity, {
|
||||
checkERC20Allowance,
|
||||
);
|
||||
return TransactionTask.make({
|
||||
contractWrapper: this.contractWrapper,
|
||||
functionName: "claim",
|
||||
args: [
|
||||
destinationAddress,
|
||||
quantity,
|
||||
claimVerification.currencyAddress,
|
||||
claimVerification.price,
|
||||
claimVerification.proofs,
|
||||
claimVerification.maxQuantityPerTransaction,
|
||||
],
|
||||
overrides: claimVerification.overrides,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -488,27 +474,9 @@ export class NFTDropImpl extends StandardErc721<DropERC721> {
|
||||
quantity: BigNumberish,
|
||||
checkERC20Allowance = true,
|
||||
): Promise<TransactionResultWithId<NFTMetadataOwner>[]> {
|
||||
const task = await this.getClaimTransaction(
|
||||
destinationAddress,
|
||||
quantity,
|
||||
return this.erc721.claimTo(destinationAddress, quantity, {
|
||||
checkERC20Allowance,
|
||||
);
|
||||
const { receipt } = await task.execute();
|
||||
const event = this.contractWrapper.parseLogs<TokensClaimedEvent>(
|
||||
"TokensClaimed",
|
||||
receipt?.logs,
|
||||
);
|
||||
const startingIndex: BigNumber = event[0].args.startTokenId;
|
||||
const endingIndex = startingIndex.add(quantity);
|
||||
const results: TransactionResultWithId<NFTMetadataOwner>[] = [];
|
||||
for (let id = startingIndex; id.lt(endingIndex); id = id.add(1)) {
|
||||
results.push({
|
||||
id,
|
||||
receipt,
|
||||
data: () => this.get(id),
|
||||
});
|
||||
}
|
||||
return results;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,7 +2,18 @@ import { ALL_ROLES, assertEnabled, detectContractFeature } from "../common";
|
||||
import { FEATURE_TOKEN } from "../constants/erc20-features";
|
||||
import { FEATURE_NFT } from "../constants/erc721-features";
|
||||
import { FEATURE_EDITION } from "../constants/erc1155-features";
|
||||
import { ContractEncoder, NetworkOrSignerOrProvider } from "../core";
|
||||
import {
|
||||
FEATURE_OWNER,
|
||||
FEATURE_PERMISSIONS,
|
||||
FEATURE_PLATFORM_FEE,
|
||||
FEATURE_PRIMARY_SALE,
|
||||
FEATURE_ROYALTY,
|
||||
} from "../constants/thirdweb-features";
|
||||
import {
|
||||
ContractEncoder,
|
||||
ContractOwner,
|
||||
NetworkOrSignerOrProvider,
|
||||
} from "../core";
|
||||
import { ContractEvents } from "../core/classes/contract-events";
|
||||
import { ContractInterceptor } from "../core/classes/contract-interceptor";
|
||||
import { ContractMetadata } from "../core/classes/contract-metadata";
|
||||
@@ -25,6 +36,7 @@ import type {
|
||||
IPlatformFee,
|
||||
IPrimarySale,
|
||||
IRoyalty,
|
||||
Ownable,
|
||||
} from "@thirdweb-dev/contracts-js";
|
||||
import { IStorage } from "@thirdweb-dev/storage";
|
||||
import { BaseContract, CallOverrides, ContractInterface } from "ethers";
|
||||
@@ -44,10 +56,10 @@ import { BaseContract, CallOverrides, ContractInterface } from "ethers";
|
||||
* await contract.call("myCustomFunction", param1, param2);
|
||||
*
|
||||
* // if your contract follows the ERC721 standard, contract.nft will be present
|
||||
* const allNFTs = await contract.nft.query.all()
|
||||
* const allNFTs = await contract.erc721.query.all()
|
||||
*
|
||||
* // if your contract extends IMintableERC721, contract.nft.mint() will be available
|
||||
* const tx = await contract.nft.mint({
|
||||
* const tx = await contract.erc721.mint({
|
||||
* name: "Cool NFT",
|
||||
* image: readFileSync("some_image.png"),
|
||||
* });
|
||||
@@ -60,7 +72,6 @@ export class SmartContract<TContract extends BaseContract = BaseContract>
|
||||
{
|
||||
private contractWrapper;
|
||||
private storage;
|
||||
private options;
|
||||
|
||||
// utilities
|
||||
public events: ContractEvents<TContract>;
|
||||
@@ -69,37 +80,62 @@ export class SmartContract<TContract extends BaseContract = BaseContract>
|
||||
public estimator: GasCostEstimator<TContract>;
|
||||
public publishedMetadata: ContractPublishedMetadata<TContract>;
|
||||
public abi: ContractInterface;
|
||||
|
||||
// features
|
||||
public metadata: ContractMetadata<BaseContract, any>;
|
||||
public royalties: ContractRoyalty<IRoyalty, any> | undefined;
|
||||
public roles: ContractRoles<IPermissions, any> | undefined;
|
||||
public sales: ContractPrimarySale<IPrimarySale> | undefined;
|
||||
public platformFees: ContractPlatformFee<IPlatformFee> | undefined;
|
||||
|
||||
private token: Erc20 | undefined;
|
||||
private nft: Erc721 | undefined;
|
||||
private edition: Erc1155 | undefined;
|
||||
/**
|
||||
* Handle royalties
|
||||
*/
|
||||
get royalties(): ContractRoyalty<IRoyalty, any> {
|
||||
return assertEnabled(this.detectRoyalties(), FEATURE_ROYALTY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle permissions
|
||||
*/
|
||||
get roles(): ContractRoles<IPermissions, any> {
|
||||
return assertEnabled(this.detectRoles(), FEATURE_PERMISSIONS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle primary sales
|
||||
*/
|
||||
get sales(): ContractPrimarySale<IPrimarySale> {
|
||||
return assertEnabled(this.detectPrimarySales(), FEATURE_PRIMARY_SALE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle platform fees
|
||||
*/
|
||||
get platformFees(): ContractPlatformFee<IPlatformFee> {
|
||||
return assertEnabled(this.detectPlatformFees(), FEATURE_PLATFORM_FEE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set and get the owner of the contract
|
||||
*/
|
||||
get owner(): ContractOwner<Ownable> {
|
||||
return assertEnabled(this.detectOwnable(), FEATURE_OWNER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Auto-detects ERC20 standard functions.
|
||||
*/
|
||||
get erc20(): Erc20 {
|
||||
return assertEnabled(this.token, FEATURE_TOKEN);
|
||||
return assertEnabled(this.detectErc20(), FEATURE_TOKEN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Auto-detects ERC721 standard functions.
|
||||
*/
|
||||
get erc721(): Erc721 {
|
||||
return assertEnabled(this.nft, FEATURE_NFT);
|
||||
return assertEnabled(this.detectErc721(), FEATURE_NFT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Auto-detects ERC1155 standard functions.
|
||||
*/
|
||||
get erc1155(): Erc1155 {
|
||||
return assertEnabled(this.edition, FEATURE_EDITION);
|
||||
return assertEnabled(this.detectErc1155(), FEATURE_EDITION);
|
||||
}
|
||||
|
||||
constructor(
|
||||
@@ -115,7 +151,6 @@ export class SmartContract<TContract extends BaseContract = BaseContract>
|
||||
options,
|
||||
),
|
||||
) {
|
||||
this.options = options;
|
||||
this.storage = storage;
|
||||
this.contractWrapper = contractWrapper;
|
||||
this.abi = abi;
|
||||
@@ -134,16 +169,6 @@ export class SmartContract<TContract extends BaseContract = BaseContract>
|
||||
CustomContractSchema,
|
||||
this.storage,
|
||||
);
|
||||
|
||||
// feature detection
|
||||
this.royalties = this.detectRoyalties();
|
||||
this.roles = this.detectRoles();
|
||||
this.sales = this.detectPrimarySales();
|
||||
this.platformFees = this.detectPlatformFees();
|
||||
|
||||
this.token = this.detectErc20();
|
||||
this.nft = this.detectErc721();
|
||||
this.edition = this.detectErc1155();
|
||||
}
|
||||
|
||||
onNetworkUpdated(network: NetworkOrSignerOrProvider): void {
|
||||
@@ -247,4 +272,11 @@ export class SmartContract<TContract extends BaseContract = BaseContract>
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private detectOwnable() {
|
||||
if (detectContractFeature<Ownable>(this.contractWrapper, "Ownable")) {
|
||||
return new ContractOwner(this.contractWrapper);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import {
|
||||
detectContractFeature,
|
||||
ExtensionNotImplementedError,
|
||||
fetchContractMetadataFromAddress,
|
||||
hasFunction,
|
||||
} from "../../common";
|
||||
@@ -141,7 +142,7 @@ export class ContractMetadata<
|
||||
z.output<TSchema["output"]>
|
||||
>;
|
||||
} else {
|
||||
throw new Error("Contract does not support updating contract metadata");
|
||||
throw new ExtensionNotImplementedError(FEATURE_METADATA);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
import { FEATURE_OWNER } from "../../constants/thirdweb-features";
|
||||
import { DetectableFeature } from "../interfaces/DetectableFeature";
|
||||
import { TransactionResult } from "../types";
|
||||
import { ContractWrapper } from "./contract-wrapper";
|
||||
import type { Ownable } from "@thirdweb-dev/contracts-js";
|
||||
|
||||
/**
|
||||
* Encodes and decodes Contract functions
|
||||
* @public
|
||||
*/
|
||||
export class ContractOwner<TContract extends Ownable>
|
||||
implements DetectableFeature
|
||||
{
|
||||
featureName = FEATURE_OWNER.name;
|
||||
private contractWrapper;
|
||||
|
||||
constructor(contractWrapper: ContractWrapper<TContract>) {
|
||||
this.contractWrapper = contractWrapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current owner of the contract
|
||||
* @returns the owner address
|
||||
*/
|
||||
public async get(): Promise<string> {
|
||||
return this.contractWrapper.readContract.owner();
|
||||
}
|
||||
|
||||
/**
|
||||
* set tje new owner of the contract
|
||||
*/
|
||||
public async set(address: string): Promise<TransactionResult> {
|
||||
return {
|
||||
receipt: await this.contractWrapper.sendTransaction("setOwner", [
|
||||
address,
|
||||
]),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -333,7 +333,10 @@ export class ContractPublisher extends RPCConnectionHandler {
|
||||
|
||||
const fullMetadata = FullPublishMetadataSchemaInput.parse({
|
||||
...extraMetadata,
|
||||
...predeployMetadata,
|
||||
metadataUri: predeployMetadata.metadataUri,
|
||||
bytecodeUri: predeployMetadata.bytecodeUri,
|
||||
name: predeployMetadata.name,
|
||||
analytics: predeployMetadata.analytics,
|
||||
publisher,
|
||||
});
|
||||
const fullMetadataUri = await this.storage.uploadMetadata(fullMetadata);
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import { hasFunction } from "../../common";
|
||||
import { FEATURE_EDITION_CLAIMABLE_WITH_CONDITIONS } from "../../constants/erc1155-features";
|
||||
import { CustomContractSchema } from "../../schema/contracts/custom";
|
||||
import { ClaimOptions } from "../../types/claim-conditions/claim-conditions";
|
||||
import {
|
||||
ClaimOptions,
|
||||
ClaimVerification,
|
||||
} from "../../types/claim-conditions/claim-conditions";
|
||||
import { BaseClaimConditionERC1155 } from "../../types/eips";
|
||||
import { DetectableFeature } from "../interfaces/DetectableFeature";
|
||||
import { TransactionResult } from "../types";
|
||||
@@ -8,6 +12,7 @@ import { TransactionTask } from "./TransactionTask";
|
||||
import { ContractMetadata } from "./contract-metadata";
|
||||
import { ContractWrapper } from "./contract-wrapper";
|
||||
import { DropErc1155ClaimConditions } from "./drop-erc1155-claim-conditions";
|
||||
import { DropERC1155 } from "@thirdweb-dev/contracts-js";
|
||||
import { IStorage } from "@thirdweb-dev/storage";
|
||||
import { BigNumberish, ethers } from "ethers";
|
||||
|
||||
@@ -73,18 +78,12 @@ export class Erc1155ClaimableWithConditions implements DetectableFeature {
|
||||
return TransactionTask.make({
|
||||
contractWrapper: this.contractWrapper,
|
||||
functionName: "claim",
|
||||
args: [
|
||||
destinationAddress,
|
||||
args: await this.getArgs(
|
||||
tokenId,
|
||||
destinationAddress,
|
||||
quantity,
|
||||
claimVerification.currencyAddress,
|
||||
claimVerification.price,
|
||||
{
|
||||
proof: claimVerification.proofs,
|
||||
maxQuantityInAllowlist: claimVerification.maxQuantityPerTransaction,
|
||||
},
|
||||
ethers.utils.toUtf8Bytes(""),
|
||||
],
|
||||
claimVerification,
|
||||
),
|
||||
overrides: claimVerification.overrides,
|
||||
});
|
||||
}
|
||||
@@ -124,4 +123,51 @@ export class Erc1155ClaimableWithConditions implements DetectableFeature {
|
||||
);
|
||||
return await tx.execute();
|
||||
}
|
||||
|
||||
private async getArgs(
|
||||
tokenId: BigNumberish,
|
||||
destinationAddress: string,
|
||||
quantity: BigNumberish,
|
||||
claimVerification: ClaimVerification,
|
||||
): Promise<any[]> {
|
||||
const isLegacyNFTContract = await this.isLegacyNFTContract();
|
||||
if (isLegacyNFTContract) {
|
||||
return [
|
||||
destinationAddress,
|
||||
tokenId,
|
||||
quantity,
|
||||
claimVerification.currencyAddress,
|
||||
claimVerification.price,
|
||||
claimVerification.proofs,
|
||||
claimVerification.maxQuantityPerTransaction,
|
||||
];
|
||||
}
|
||||
return [
|
||||
destinationAddress,
|
||||
tokenId,
|
||||
quantity,
|
||||
claimVerification.currencyAddress,
|
||||
claimVerification.price,
|
||||
{
|
||||
proof: claimVerification.proofs,
|
||||
maxQuantityInAllowlist: claimVerification.maxQuantityPerTransaction,
|
||||
},
|
||||
ethers.utils.toUtf8Bytes(""),
|
||||
];
|
||||
}
|
||||
|
||||
private async isLegacyNFTContract() {
|
||||
if (hasFunction<DropERC1155>("contractType", this.contractWrapper)) {
|
||||
try {
|
||||
const contractType = ethers.utils.toUtf8String(
|
||||
await this.contractWrapper.readContract.contractType(),
|
||||
);
|
||||
return contractType.includes("DropERC1155");
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { hasFunction } from "../../common";
|
||||
import { FEATURE_NFT_CLAIMABLE_WITH_CONDITIONS } from "../../constants/erc721-features";
|
||||
import { NFTMetadataOwner } from "../../schema";
|
||||
import { CustomContractSchema } from "../../schema/contracts/custom";
|
||||
import { ClaimOptions } from "../../types";
|
||||
import { ClaimOptions, ClaimVerification } from "../../types";
|
||||
import { BaseClaimConditionERC721 } from "../../types/eips";
|
||||
import { DetectableFeature } from "../interfaces/DetectableFeature";
|
||||
import { TransactionResultWithId } from "../types";
|
||||
@@ -10,6 +11,7 @@ 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 { IStorage } from "@thirdweb-dev/storage";
|
||||
import { BigNumber, BigNumberish, ethers } from "ethers";
|
||||
@@ -96,20 +98,11 @@ export class Erc721ClaimableWithConditions implements DetectableFeature {
|
||||
quantity,
|
||||
options?.checkERC20Allowance || true,
|
||||
);
|
||||
|
||||
return TransactionTask.make({
|
||||
contractWrapper: this.contractWrapper,
|
||||
functionName: "claim",
|
||||
args: [
|
||||
destinationAddress,
|
||||
quantity,
|
||||
claimVerification.currencyAddress,
|
||||
claimVerification.price,
|
||||
{
|
||||
proof: claimVerification.proofs,
|
||||
maxQuantityInAllowlist: claimVerification.maxQuantityPerTransaction,
|
||||
},
|
||||
ethers.utils.toUtf8Bytes(""),
|
||||
],
|
||||
args: await this.getArgs(destinationAddress, quantity, claimVerification),
|
||||
overrides: claimVerification.overrides,
|
||||
});
|
||||
}
|
||||
@@ -161,4 +154,48 @@ 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,3 +41,4 @@ export * from "./contract-events";
|
||||
export * from "./contract-interceptor";
|
||||
export * from "./contract-platform-fee";
|
||||
export * from "./contract-published-metadata";
|
||||
export * from "./contract-owner";
|
||||
|
||||
@@ -33,3 +33,17 @@ export * from "./enums";
|
||||
export * from "./common";
|
||||
export * from "./constants";
|
||||
export * from "./contracts";
|
||||
|
||||
// explcitly export the *TYPES* of prebuilt contracts
|
||||
export type { EditionImpl } from "./contracts/prebuilt-implementations/edition";
|
||||
export type { EditionDropImpl } from "./contracts/prebuilt-implementations/edition-drop";
|
||||
export type { MarketplaceImpl } from "./contracts/prebuilt-implementations/marketplace";
|
||||
export type { MultiwrapImpl } from "./contracts/prebuilt-implementations/multiwrap";
|
||||
export type { NFTCollectionImpl } from "./contracts/prebuilt-implementations/nft-collection";
|
||||
export type { NFTDropImpl } from "./contracts/prebuilt-implementations/nft-drop";
|
||||
export type { PackImpl } from "./contracts/prebuilt-implementations/pack";
|
||||
export type { SignatureDropImpl } from "./contracts/prebuilt-implementations/signature-drop";
|
||||
export type { SplitImpl } from "./contracts/prebuilt-implementations/split";
|
||||
export type { TokenImpl } from "./contracts/prebuilt-implementations/token";
|
||||
export type { TokenDropImpl } from "./contracts/prebuilt-implementations/token-drop";
|
||||
export type { VoteImpl } from "./contracts/prebuilt-implementations/vote";
|
||||
|
||||
@@ -32,7 +32,7 @@ describe("Custom Contracts", async () => {
|
||||
let sdk: ThirdwebSDK;
|
||||
let simpleContractUri: string;
|
||||
|
||||
beforeAll(async () => {
|
||||
before(async () => {
|
||||
[adminWallet, samWallet, bobWallet] = signers;
|
||||
sdk = new ThirdwebSDK(adminWallet);
|
||||
simpleContractUri =
|
||||
@@ -227,7 +227,14 @@ describe("Custom Contracts", async () => {
|
||||
const c = await sdk.getContractFromAbi("", VoteERC20__factory.abi);
|
||||
invariant(c, "Contract undefined");
|
||||
invariant(c.metadata, "Metadata undefined");
|
||||
expect(c.roles).to.eq(undefined);
|
||||
try {
|
||||
c.roles.get("admin");
|
||||
} catch (e) {
|
||||
expectError(
|
||||
e,
|
||||
"contract does not implement the 'PermissionsEnumerable' Extension",
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it("should detect feature: erc20", async () => {
|
||||
|
||||
@@ -1,5 +1,25 @@
|
||||
# @thirdweb-dev/solana
|
||||
|
||||
## 0.2.10
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#129](https://github.com/thirdweb-dev/js/pull/129) [`a0cd47e`](https://github.com/thirdweb-dev/js/commit/a0cd47ec3596d23f974e58d668ce02e34bfe3528) Thanks [@jnsdls](https://github.com/jnsdls)! - explicitly export the program schemata from the package
|
||||
|
||||
- [#132](https://github.com/thirdweb-dev/js/pull/132) [`3e425bf`](https://github.com/thirdweb-dev/js/commit/3e425bf8e65db99fc4043ea5d2bc4b863eabce52) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Get account type for a given address
|
||||
|
||||
- [#130](https://github.com/thirdweb-dev/js/pull/130) [`2951b55`](https://github.com/thirdweb-dev/js/commit/2951b5533c13d1d7633c491df7e5710b0a6608dd) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - New return type for `registry.getAccountsForWallet`
|
||||
|
||||
## 0.2.9
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#120](https://github.com/thirdweb-dev/js/pull/120) [`e25dd1a`](https://github.com/thirdweb-dev/js/commit/e25dd1ac8e5ef25d844b2d570ac557b0440e6e51) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Proper handling of WalletAdapter connections
|
||||
|
||||
- [#117](https://github.com/thirdweb-dev/js/pull/117) [`84beda1`](https://github.com/thirdweb-dev/js/commit/84beda1cbb17f1e5ffb1edbea2acb7c42f47fbab) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Account registry implementation
|
||||
|
||||
- [#123](https://github.com/thirdweb-dev/js/pull/123) [`5aa3c43`](https://github.com/thirdweb-dev/js/commit/5aa3c433d790a2f65515758171f5822864826367) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Add ability to fetch native balance for the connected wallet
|
||||
|
||||
## 0.2.8
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@thirdweb-dev/solana",
|
||||
"version": "0.2.8",
|
||||
"version": "0.2.10",
|
||||
"description": "thirdweb Solana SDK",
|
||||
"main": "dist/thirdweb-dev-solana.cjs.js",
|
||||
"module": "dist/thirdweb-dev-solana.esm.js",
|
||||
@@ -61,6 +61,7 @@
|
||||
"@solana/spl-token": "^0.2.0",
|
||||
"@solana/web3.js": "^1.50.1",
|
||||
"@thirdweb-dev/storage": "*",
|
||||
"bn.js": "^5.2.1",
|
||||
"eventemitter3": "^4.0.7",
|
||||
"form-data": "^4.0.0",
|
||||
"formdata-polyfill": "^4.0.10",
|
||||
|
||||
@@ -7,7 +7,6 @@ import {
|
||||
NFTDropContractSchema,
|
||||
NFTDropMetadataInput,
|
||||
} from "../types/contracts/nft-drop";
|
||||
import { UserWallet } from "./user-wallet";
|
||||
import { findMetadataPda, Metaplex, token } from "@metaplex-foundation/js";
|
||||
import {
|
||||
createCreateMetadataAccountV2Instruction,
|
||||
@@ -15,24 +14,21 @@ import {
|
||||
} from "@metaplex-foundation/mpl-token-metadata";
|
||||
import { Keypair, PublicKey } from "@solana/web3.js";
|
||||
import { IStorage } from "@thirdweb-dev/storage";
|
||||
import invariant from "tiny-invariant";
|
||||
|
||||
export class Deployer {
|
||||
private wallet: UserWallet;
|
||||
private metaplex: Metaplex;
|
||||
private storage: IStorage;
|
||||
|
||||
constructor(metaplex: Metaplex, wallet: UserWallet, storage: IStorage) {
|
||||
constructor(metaplex: Metaplex, storage: IStorage) {
|
||||
this.metaplex = metaplex;
|
||||
this.wallet = wallet;
|
||||
this.storage = storage;
|
||||
}
|
||||
|
||||
async createToken(tokenMetadata: TokenMetadataInput): Promise<string> {
|
||||
const tokenMetadataParsed = TokenMetadataInputSchema.parse(tokenMetadata);
|
||||
const uri = await this.storage.uploadMetadata(tokenMetadataParsed);
|
||||
const owner = this.wallet.getSigner().publicKey;
|
||||
const mint = Keypair.generate();
|
||||
const owner = this.metaplex.identity().publicKey;
|
||||
const mintTx = await this.metaplex
|
||||
.tokens()
|
||||
.builders()
|
||||
@@ -43,9 +39,6 @@ export class Deployer {
|
||||
tokenMetadataParsed.decimals,
|
||||
),
|
||||
mint,
|
||||
mintAuthority: this.wallet.getSigner(),
|
||||
payer: this.wallet.getSigner(),
|
||||
owner,
|
||||
});
|
||||
|
||||
const data: DataV2 = {
|
||||
@@ -75,7 +68,7 @@ export class Deployer {
|
||||
{ createMetadataAccountArgsV2: { data, isMutable: false } },
|
||||
);
|
||||
await mintTx
|
||||
.add({ instruction: metaTx, signers: [this.wallet.getSigner()] })
|
||||
.add({ instruction: metaTx, signers: [this.metaplex.identity()] })
|
||||
.sendAndConfirm(this.metaplex);
|
||||
|
||||
return mint.publicKey.toBase58();
|
||||
@@ -94,9 +87,6 @@ export class Deployer {
|
||||
sellerFeeBasisPoints: 0,
|
||||
uri,
|
||||
isCollection: true,
|
||||
collectionAuthority: this.wallet.getSigner(),
|
||||
updateAuthority: this.wallet.getSigner(),
|
||||
mintAuthority: this.wallet.getSigner(),
|
||||
creators: collectionMetadata.creators?.map((creator) => ({
|
||||
address: new PublicKey(creator.address),
|
||||
share: creator.share,
|
||||
@@ -107,13 +97,31 @@ export class Deployer {
|
||||
}
|
||||
|
||||
async createNftDrop(metadata: NFTDropMetadataInput): Promise<string> {
|
||||
invariant(this.wallet.signer, "Wallet is not connected");
|
||||
const parsed = NFTDropContractSchema.parse(metadata);
|
||||
|
||||
// TODO create a collection with metadata and associate it with the drop
|
||||
// TODO make it a single tx
|
||||
const collection = await this.createNftCollection(metadata);
|
||||
const creators =
|
||||
parsed.creators.length > 0
|
||||
? parsed.creators.map((creator) => ({
|
||||
address: new PublicKey(creator.address),
|
||||
share: creator.share,
|
||||
verified: creator.verified,
|
||||
}))
|
||||
: [
|
||||
{
|
||||
address: this.metaplex.identity().publicKey,
|
||||
share: 100,
|
||||
verified: true,
|
||||
},
|
||||
];
|
||||
const { candyMachine: nftDrop } = await this.metaplex
|
||||
.candyMachines()
|
||||
.create({ ...parsed })
|
||||
.create({
|
||||
...parsed,
|
||||
collection: new PublicKey(collection),
|
||||
creators,
|
||||
})
|
||||
.run();
|
||||
|
||||
return nftDrop.address.toBase58();
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
import { WalletAccount } from "../types/common";
|
||||
import {
|
||||
CandyMachine,
|
||||
Metadata,
|
||||
Metaplex,
|
||||
TokenMetadataProgram,
|
||||
} from "@metaplex-foundation/js";
|
||||
import { TokenStandard } from "@metaplex-foundation/mpl-token-metadata";
|
||||
import { PublicKey } from "@solana/web3.js";
|
||||
|
||||
export class Registry {
|
||||
private metaplex: Metaplex;
|
||||
|
||||
constructor(metaplex: Metaplex) {
|
||||
this.metaplex = metaplex;
|
||||
}
|
||||
|
||||
public async getAccountType(address: string) {
|
||||
try {
|
||||
const metadata = await this.metaplex
|
||||
.nfts()
|
||||
.findByMint({ mintAddress: new PublicKey(address) })
|
||||
.run();
|
||||
if (metadata) {
|
||||
if (metadata.collectionDetails) {
|
||||
return "nft-collection";
|
||||
} else {
|
||||
if (metadata.tokenStandard === TokenStandard.Fungible) {
|
||||
return "token";
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
try {
|
||||
const candyMachine = await this.metaplex
|
||||
.candyMachines()
|
||||
.findByAddress({ address: new PublicKey(address) })
|
||||
.run();
|
||||
if (candyMachine) {
|
||||
return "nft-drop";
|
||||
}
|
||||
} catch (err) {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
public async getAccountsForWallet(
|
||||
walletAddress: string,
|
||||
): Promise<WalletAccount[]> {
|
||||
const pubKeys = await this.getMetadataAddressesForWallet(walletAddress);
|
||||
const metadatas = await this.metaplex
|
||||
.nfts()
|
||||
.findAllByMintList({ mints: pubKeys })
|
||||
.run();
|
||||
|
||||
const candyMachines = await this.metaplex
|
||||
.candyMachines()
|
||||
.findAllBy({
|
||||
type: "wallet",
|
||||
publicKey: new PublicKey(walletAddress),
|
||||
})
|
||||
.run();
|
||||
|
||||
return metadatas
|
||||
.map((mintMetadata) => {
|
||||
const meta = mintMetadata as Metadata;
|
||||
if (!meta) {
|
||||
return undefined;
|
||||
}
|
||||
if (meta?.collectionDetails) {
|
||||
// check if it's part of a candy machine
|
||||
const drop = this.getDropForCollection(candyMachines, meta);
|
||||
if (drop) {
|
||||
return {
|
||||
type: "nft-drop",
|
||||
address: drop.address.toBase58(),
|
||||
name: meta.name,
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
type: "nft-collection",
|
||||
address: meta.mintAddress.toBase58(),
|
||||
name: meta.name,
|
||||
};
|
||||
}
|
||||
} else {
|
||||
if (meta.tokenStandard === TokenStandard.Fungible) {
|
||||
return {
|
||||
type: "token",
|
||||
address: meta.mintAddress.toBase58(),
|
||||
name: meta.name,
|
||||
};
|
||||
}
|
||||
}
|
||||
})
|
||||
.filter((account) => account !== undefined) as WalletAccount[];
|
||||
}
|
||||
|
||||
private getDropForCollection(candyMachines: CandyMachine[], meta: Metadata) {
|
||||
return candyMachines.find(
|
||||
(candyMachine) =>
|
||||
candyMachine.collectionMintAddress?.toBase58() ===
|
||||
meta.mintAddress.toBase58(),
|
||||
);
|
||||
}
|
||||
|
||||
public async getMetadataAddressesForWallet(walletAddress: string) {
|
||||
return await TokenMetadataProgram.metadataV1Accounts(this.metaplex)
|
||||
.selectMint()
|
||||
.whereCreator(1, new PublicKey(walletAddress))
|
||||
.getDataAsPublicKeys();
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,17 @@
|
||||
// import { getPayer } from "../utils/local-config";
|
||||
import { Signer } from "@metaplex-foundation/js";
|
||||
import { CurrencyValue, WalletSigner } from "../types/common";
|
||||
import { toCurrencyValue } from "../utils/token";
|
||||
import {
|
||||
guestIdentity,
|
||||
isIdentitySigner,
|
||||
isKeypairSigner,
|
||||
keypairIdentity,
|
||||
Metaplex,
|
||||
Signer,
|
||||
sol,
|
||||
walletAdapterIdentity,
|
||||
} from "@metaplex-foundation/js";
|
||||
import { Keypair } from "@solana/web3.js";
|
||||
import EventEmitter from "eventemitter3";
|
||||
import invariant from "tiny-invariant";
|
||||
|
||||
@@ -13,7 +25,7 @@ export interface UserWalletEvents {
|
||||
/**
|
||||
* Emitted when `sdk.wallet.connect()` is called.
|
||||
*/
|
||||
connected: [Signer];
|
||||
connected: [WalletSigner];
|
||||
/**
|
||||
* Emitted when `sdk.wallet.disconnect()` is called.
|
||||
*/
|
||||
@@ -23,23 +35,46 @@ export interface UserWalletEvents {
|
||||
export class UserWallet {
|
||||
public signer: Signer | undefined;
|
||||
public events = new EventEmitter<UserWalletEvents>();
|
||||
private metaplex: Metaplex;
|
||||
|
||||
public connect(wallet: Signer) {
|
||||
this.signer = wallet;
|
||||
constructor(metaplex: Metaplex) {
|
||||
this.metaplex = metaplex;
|
||||
}
|
||||
|
||||
public connect(wallet: WalletSigner) {
|
||||
this.connectToMetaplex(wallet);
|
||||
this.events.emit("connected", wallet);
|
||||
}
|
||||
|
||||
public disconnect() {
|
||||
this.signer = undefined;
|
||||
// TODO implement our own read only identity plugin with our own error messages
|
||||
this.metaplex.use(guestIdentity());
|
||||
this.events.emit("disconnected");
|
||||
}
|
||||
|
||||
public getAddress() {
|
||||
return this.signer?.publicKey.toBase58();
|
||||
return this.metaplex.identity().publicKey.toBase58();
|
||||
}
|
||||
|
||||
public getSigner() {
|
||||
invariant(this.signer, "Wallet is not connected");
|
||||
return this.signer;
|
||||
return this.metaplex.identity();
|
||||
}
|
||||
|
||||
public async getBalance(): Promise<CurrencyValue> {
|
||||
const value = await this.metaplex.connection.getBalance(
|
||||
this.metaplex.identity().publicKey,
|
||||
);
|
||||
return toCurrencyValue(sol(value));
|
||||
}
|
||||
|
||||
private connectToMetaplex(signer: WalletSigner) {
|
||||
invariant(signer, "Wallet is not connected");
|
||||
const plugin = isKeypairSigner(signer)
|
||||
? keypairIdentity(Keypair.fromSecretKey(signer.secretKey))
|
||||
: isIdentitySigner(signer)
|
||||
? walletAdapterIdentity(signer)
|
||||
: undefined;
|
||||
invariant(plugin, "Wallet is not compatible with Metaplex");
|
||||
this.metaplex.use(plugin);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { NFTHelper } from "../classes/helpers/nft-helper";
|
||||
import { UserWallet } from "../classes/user-wallet";
|
||||
import { METAPLEX_PROGRAM_ID } from "../constants/addresses";
|
||||
import { TransactionResult } from "../types/common";
|
||||
import {
|
||||
@@ -20,7 +19,6 @@ import { ConfirmedSignatureInfo, PublicKey } from "@solana/web3.js";
|
||||
import { IStorage } from "@thirdweb-dev/storage";
|
||||
|
||||
export class NFTCollection {
|
||||
private wallet: UserWallet;
|
||||
private metaplex: Metaplex;
|
||||
private storage: IStorage;
|
||||
private nft: NFTHelper;
|
||||
@@ -29,10 +27,8 @@ export class NFTCollection {
|
||||
constructor(
|
||||
collectionMintAddress: string,
|
||||
metaplex: Metaplex,
|
||||
wallet: UserWallet,
|
||||
storage: IStorage,
|
||||
) {
|
||||
this.wallet = wallet;
|
||||
this.storage = storage;
|
||||
this.metaplex = metaplex;
|
||||
this.nft = new NFTHelper(metaplex);
|
||||
@@ -213,11 +209,12 @@ export class NFTCollection {
|
||||
const { nft } = await this.metaplex
|
||||
.nfts()
|
||||
.create({
|
||||
// useExistingMint: newMint,
|
||||
name: metadata.name || "",
|
||||
uri,
|
||||
sellerFeeBasisPoints: 0,
|
||||
collection: this.collectionMintAddress,
|
||||
collectionAuthority: this.wallet.signer,
|
||||
collectionAuthority: this.metaplex.identity(),
|
||||
tokenOwner: new PublicKey(to),
|
||||
// Always sets max supply to unlimited so editions can be minted
|
||||
maxSupply: null,
|
||||
|
||||
@@ -1,36 +1,42 @@
|
||||
import { ClaimConditions } from "../classes/claim-conditions";
|
||||
import { NFTHelper } from "../classes/helpers/nft-helper";
|
||||
import { UserWallet } from "../classes/user-wallet";
|
||||
import { TransactionResult } from "../types/common";
|
||||
import { CommonNFTInput, NFTMetadata, NFTMetadataInput } from "../types/nft";
|
||||
import {
|
||||
CommonNFTInput,
|
||||
NFTCollectionMetadata,
|
||||
NFTMetadata,
|
||||
NFTMetadataInput,
|
||||
} from "../types/nft";
|
||||
import { Metaplex } from "@metaplex-foundation/js";
|
||||
import { Connection, PublicKey } from "@solana/web3.js";
|
||||
import { PublicKey } from "@solana/web3.js";
|
||||
import { IStorage } from "@thirdweb-dev/storage";
|
||||
import invariant from "tiny-invariant";
|
||||
|
||||
export class NFTDrop {
|
||||
private connection: Connection;
|
||||
private wallet: UserWallet;
|
||||
private metaplex: Metaplex;
|
||||
private storage: IStorage;
|
||||
private nft: NFTHelper;
|
||||
public dropMintAddress: PublicKey;
|
||||
public claimConditions: ClaimConditions;
|
||||
|
||||
constructor(
|
||||
dropMintAddress: string,
|
||||
metaplex: Metaplex,
|
||||
wallet: UserWallet,
|
||||
storage: IStorage,
|
||||
) {
|
||||
this.wallet = wallet;
|
||||
constructor(dropMintAddress: string, metaplex: Metaplex, storage: IStorage) {
|
||||
this.storage = storage;
|
||||
this.metaplex = metaplex;
|
||||
this.connection = metaplex.connection;
|
||||
this.nft = new NFTHelper(metaplex);
|
||||
this.dropMintAddress = new PublicKey(dropMintAddress);
|
||||
this.claimConditions = new ClaimConditions(dropMintAddress, metaplex);
|
||||
}
|
||||
|
||||
async getMetadata(): Promise<NFTCollectionMetadata> {
|
||||
const info = await this.getCandyMachine();
|
||||
invariant(info.collectionMintAddress, "Collection mint address not found");
|
||||
const metadata = await this.metaplex
|
||||
.nfts()
|
||||
.findByMint({ mintAddress: info.collectionMintAddress })
|
||||
.run();
|
||||
return this.nft.toNFTMetadata(metadata);
|
||||
}
|
||||
|
||||
async get(mintAddress: string): Promise<NFTMetadata> {
|
||||
return this.nft.get(mintAddress);
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import { Signer } from "@solana/web3.js";
|
||||
export class Program {
|
||||
private program: AnchorProgram<Idl>;
|
||||
|
||||
constructor(programAddress: string, idl: Idl, provider: AnchorProvider) {
|
||||
constructor(programAddress: string, idl: Idl, provider?: AnchorProvider) {
|
||||
this.program = new AnchorProgram(idl, programAddress, provider);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
import { UserWallet } from "../classes/user-wallet";
|
||||
import { Amount, AmountSchema, TransactionResult } from "../types/common";
|
||||
import {
|
||||
Amount,
|
||||
AmountSchema,
|
||||
CurrencyValue,
|
||||
TransactionResult,
|
||||
} from "../types/common";
|
||||
import { TokenMetadata } from "../types/nft";
|
||||
import { toCurrencyValue } from "../utils/token";
|
||||
import {
|
||||
findMetadataPda,
|
||||
Metaplex,
|
||||
toBigNumber,
|
||||
token,
|
||||
toMetadata,
|
||||
toMetadataAccount,
|
||||
@@ -14,18 +20,11 @@ import { IStorage } from "@thirdweb-dev/storage";
|
||||
|
||||
export class Token {
|
||||
private connection: Connection;
|
||||
private wallet: UserWallet;
|
||||
private metaplex: Metaplex;
|
||||
private storage: IStorage;
|
||||
tokenMintAddress: PublicKey;
|
||||
|
||||
constructor(
|
||||
tokenMintAddress: string,
|
||||
metaplex: Metaplex,
|
||||
wallet: UserWallet,
|
||||
storage: IStorage,
|
||||
) {
|
||||
this.wallet = wallet;
|
||||
constructor(tokenMintAddress: string, metaplex: Metaplex, storage: IStorage) {
|
||||
this.storage = storage;
|
||||
this.metaplex = metaplex;
|
||||
this.connection = metaplex.connection;
|
||||
@@ -40,6 +39,7 @@ export class Token {
|
||||
}
|
||||
|
||||
async getMetadata(): Promise<TokenMetadata> {
|
||||
const mint = await this.getMint();
|
||||
const addr = findMetadataPda(this.tokenMintAddress);
|
||||
const account = await this.metaplex.rpc().getAccount(addr);
|
||||
const meta = toMetadata(toMetadataAccount(account));
|
||||
@@ -48,15 +48,15 @@ export class Token {
|
||||
uri: meta.uri,
|
||||
name: meta.name,
|
||||
symbol: meta.symbol,
|
||||
decimals: mint.decimals,
|
||||
supply: toCurrencyValue(mint.supply),
|
||||
...meta.json,
|
||||
} as TokenMetadata;
|
||||
}
|
||||
|
||||
async totalSupply(): Promise<bigint> {
|
||||
async totalSupply(): Promise<CurrencyValue> {
|
||||
const info = await this.getMint();
|
||||
const value = BigInt(info.supply.basisPoints.toString());
|
||||
// TODO use CurrencyValue to provide a human readable display value
|
||||
return value;
|
||||
return toCurrencyValue(info.supply);
|
||||
}
|
||||
|
||||
async mint(amount: Amount) {
|
||||
@@ -92,20 +92,28 @@ export class Token {
|
||||
};
|
||||
}
|
||||
|
||||
async balance(): Promise<bigint> {
|
||||
return this.balanceOf(this.wallet.getSigner().publicKey.toBase58());
|
||||
async balance(): Promise<CurrencyValue> {
|
||||
return this.balanceOf(this.metaplex.identity().publicKey.toBase58());
|
||||
}
|
||||
|
||||
async balanceOf(walletAddress: string): Promise<bigint> {
|
||||
async balanceOf(walletAddress: string): Promise<CurrencyValue> {
|
||||
const mint = await this.getMint();
|
||||
const addr = await getAssociatedTokenAddress(
|
||||
this.tokenMintAddress,
|
||||
new PublicKey(walletAddress),
|
||||
);
|
||||
try {
|
||||
const account = await getAccount(this.connection, addr);
|
||||
return account.amount;
|
||||
return toCurrencyValue({
|
||||
basisPoints: toBigNumber(account.amount.toString()),
|
||||
currency: {
|
||||
symbol: "",
|
||||
decimals: mint.decimals,
|
||||
namespace: "spl-token",
|
||||
},
|
||||
});
|
||||
} catch (e) {
|
||||
return 0n;
|
||||
throw Error(`No balance found for address '${walletAddress}' - ${e}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,3 +6,10 @@ export * from "./classes/user-wallet";
|
||||
export * from "./contracts/nft-collection";
|
||||
// sdk
|
||||
export * from "./sdk";
|
||||
|
||||
//schema for dashboard use
|
||||
export {
|
||||
TokenMetadataInputSchema,
|
||||
NFTCollectionMetadataInputSchema,
|
||||
} from "./types/contracts";
|
||||
export { NFTDropContractSchema } from "./types/contracts/nft-drop";
|
||||
|
||||
+22
-36
@@ -1,4 +1,5 @@
|
||||
import { Deployer } from "./classes/deployer";
|
||||
import { Registry } from "./classes/registry";
|
||||
import { UserWallet } from "./classes/user-wallet";
|
||||
import { DEFAULT_IPFS_GATEWAY } from "./constants/urls";
|
||||
import { NFTCollection } from "./contracts/nft-collection";
|
||||
@@ -7,22 +8,15 @@ import { Program } from "./contracts/program";
|
||||
import { Token } from "./contracts/token";
|
||||
import { Network } from "./types";
|
||||
import { getUrlForNetwork } from "./utils/urls";
|
||||
import {
|
||||
isIdentitySigner,
|
||||
isKeypairSigner,
|
||||
keypairIdentity,
|
||||
Metaplex,
|
||||
Signer,
|
||||
walletAdapterIdentity,
|
||||
} from "@metaplex-foundation/js";
|
||||
import { Metaplex } from "@metaplex-foundation/js";
|
||||
import {
|
||||
AnchorProvider,
|
||||
Idl,
|
||||
Program as AnchorProgram,
|
||||
setProvider,
|
||||
} from "@project-serum/anchor";
|
||||
import { Connection, Keypair } from "@solana/web3.js";
|
||||
import { Connection } from "@solana/web3.js";
|
||||
import { IpfsStorage, IStorage, PinataUploader } from "@thirdweb-dev/storage";
|
||||
import invariant from "tiny-invariant";
|
||||
|
||||
export class ThirdwebSDK {
|
||||
static fromNetwork(network: Network, storage?: IStorage): ThirdwebSDK {
|
||||
@@ -34,6 +28,7 @@ export class ThirdwebSDK {
|
||||
private anchorProvider: AnchorProvider;
|
||||
private storage: IStorage;
|
||||
|
||||
public registry: Registry;
|
||||
public deployer: Deployer;
|
||||
public wallet: UserWallet;
|
||||
|
||||
@@ -50,34 +45,32 @@ export class ThirdwebSDK {
|
||||
this.connection = connection;
|
||||
this.storage = storage;
|
||||
this.metaplex = Metaplex.make(this.connection);
|
||||
this.wallet = new UserWallet();
|
||||
this.deployer = new Deployer(this.metaplex, this.wallet, this.storage);
|
||||
this.wallet = new UserWallet(this.metaplex);
|
||||
this.deployer = new Deployer(this.metaplex, this.storage);
|
||||
this.registry = new Registry(this.metaplex);
|
||||
this.anchorProvider = new AnchorProvider(
|
||||
this.metaplex.connection,
|
||||
this.metaplex.identity(),
|
||||
{},
|
||||
);
|
||||
// when there is a new signer connected in the wallet sdk, update that signer
|
||||
this.wallet.events.on("connected", (s) => {
|
||||
this.propagateSignerUpdated(s);
|
||||
this.wallet.events.on("connected", () => {
|
||||
this.propagateWalletConnected();
|
||||
});
|
||||
// when the wallet disconnects, update the signer to undefined
|
||||
this.wallet.events.on("disconnected", () => {
|
||||
// reset connection to Metaplex
|
||||
this.metaplex = Metaplex.make(this.connection);
|
||||
this.propagateWalletDisconnected();
|
||||
});
|
||||
}
|
||||
|
||||
public async getNFTCollection(address: string): Promise<NFTCollection> {
|
||||
return new NFTCollection(address, this.metaplex, this.wallet, this.storage);
|
||||
return new NFTCollection(address, this.metaplex, this.storage);
|
||||
}
|
||||
|
||||
public async getNFTDrop(address: string): Promise<NFTDrop> {
|
||||
return new NFTDrop(address, this.metaplex, this.wallet, this.storage);
|
||||
return new NFTDrop(address, this.metaplex, this.storage);
|
||||
}
|
||||
|
||||
public async getToken(address: string): Promise<Token> {
|
||||
return new Token(address, this.metaplex, this.wallet, this.storage);
|
||||
return new Token(address, this.metaplex, this.storage);
|
||||
}
|
||||
|
||||
public async getProgram(address: string) {
|
||||
@@ -94,23 +87,16 @@ export class ThirdwebSDK {
|
||||
return new Program(address, idl, this.anchorProvider);
|
||||
}
|
||||
|
||||
private propagateSignerUpdated(signer: Signer) {
|
||||
this.metaplex = this.connectToMetaplex(signer, this.metaplex);
|
||||
this.anchorProvider = new AnchorProvider(
|
||||
this.metaplex.connection,
|
||||
this.metaplex.identity(),
|
||||
{},
|
||||
private propagateWalletConnected() {
|
||||
setProvider(
|
||||
new AnchorProvider(this.connection, this.metaplex.identity(), {}),
|
||||
);
|
||||
}
|
||||
|
||||
private connectToMetaplex(signer: Signer, metaplex: Metaplex) {
|
||||
invariant(signer, "Wallet is not connected");
|
||||
const plugin = isKeypairSigner(signer)
|
||||
? keypairIdentity(Keypair.fromSecretKey(signer.secretKey))
|
||||
: isIdentitySigner(signer)
|
||||
? walletAdapterIdentity(signer)
|
||||
: undefined;
|
||||
invariant(plugin, "Wallet is not compatible with Metaplex");
|
||||
return metaplex.use(plugin);
|
||||
private propagateWalletDisconnected() {
|
||||
// metaplex.identity() will return a guest identity
|
||||
setProvider(
|
||||
new AnchorProvider(this.connection, this.metaplex.identity(), {}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { Signer, WalletAdapter } from "@metaplex-foundation/js";
|
||||
import { z } from "zod";
|
||||
|
||||
export const MAX_BPS = 10_000;
|
||||
@@ -57,7 +58,7 @@ export const AmountSchema = z
|
||||
export type Amount = z.input<typeof AmountSchema>;
|
||||
|
||||
export const CurrencyValueSchema = z.object({
|
||||
value: z.bigint(),
|
||||
value: z.string(),
|
||||
displayValue: z.string(),
|
||||
});
|
||||
|
||||
@@ -66,3 +67,11 @@ export type CurrencyValue = z.input<typeof CurrencyValueSchema>;
|
||||
export type TransactionResult = {
|
||||
signature: string;
|
||||
};
|
||||
|
||||
export type WalletSigner = Signer | WalletAdapter;
|
||||
export type AccountType = "nft-collection" | "nft-drop" | "token";
|
||||
export type WalletAccount = {
|
||||
type: AccountType;
|
||||
address: string;
|
||||
name: string;
|
||||
};
|
||||
|
||||
@@ -25,10 +25,11 @@ export const CommonContractOutputSchema = CommonContractSchema.extend({
|
||||
export const NFTCollectionCreatorInputSchema = z.object({
|
||||
address: z.string(),
|
||||
share: z.number(),
|
||||
verified: z.boolean().default(false),
|
||||
});
|
||||
|
||||
export const NFTCollectionMetadataInputSchema = CommonContractSchema.extend({
|
||||
creators: z.array(NFTCollectionCreatorInputSchema).optional(),
|
||||
creators: z.array(NFTCollectionCreatorInputSchema).default([]),
|
||||
});
|
||||
|
||||
export type NFTCollectionMetadataInput = z.input<
|
||||
|
||||
@@ -7,7 +7,7 @@ import { z } from "zod";
|
||||
* @internal
|
||||
*/
|
||||
// TODO: Handle allow lists and end times
|
||||
export const NFTDropContractSchema = z.object({
|
||||
export const NFTDropConditionsSchema = z.object({
|
||||
price: z
|
||||
.number()
|
||||
.default(0)
|
||||
@@ -35,12 +35,16 @@ export const NFTDropContractSchema = z.object({
|
||||
.optional(),
|
||||
});
|
||||
|
||||
export const NFTDropContractSchema = NFTCollectionMetadataInputSchema.merge(
|
||||
NFTDropConditionsSchema,
|
||||
);
|
||||
|
||||
export type NFTDropMetadataInput = z.input<typeof NFTDropContractSchema>;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export const NFTDropClaimSchema = NFTDropContractSchema.extend({
|
||||
export const NFTDropClaimSchema = NFTDropConditionsSchema.extend({
|
||||
price: z
|
||||
.number()
|
||||
.transform((p) => sol(p))
|
||||
@@ -55,7 +59,7 @@ export const NFTDropClaimSchema = NFTDropContractSchema.extend({
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export const NFTDropOutputSchema = z.object({
|
||||
export const NFTDropClaimOutputSchema = z.object({
|
||||
price: z.bigint(),
|
||||
sellerFeeBasisPoints: z.bigint(),
|
||||
itemsAvailable: z.bigint(),
|
||||
@@ -64,4 +68,4 @@ export const NFTDropOutputSchema = z.object({
|
||||
|
||||
export type NFTDropClaimInput = z.input<typeof NFTDropClaimSchema>;
|
||||
|
||||
export type NFTDropClaimOutput = z.output<typeof NFTDropOutputSchema>;
|
||||
export type NFTDropClaimOutput = z.output<typeof NFTDropClaimOutputSchema>;
|
||||
|
||||
@@ -46,10 +46,17 @@ export const CommonNFTOutput = CommonTokenOutput.extend({
|
||||
animation_url: z.string().nullable().optional(),
|
||||
});
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export const CommonFungibleTokenOutput = CommonTokenOutput.extend({
|
||||
decimals: z.number(),
|
||||
});
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type TokenMetadata = z.output<typeof CommonTokenOutput>;
|
||||
export type TokenMetadata = z.output<typeof CommonFungibleTokenOutput>;
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
import { CurrencyValue } from "../types/common";
|
||||
import { Amount } from "@metaplex-foundation/js";
|
||||
import BN from "bn.js";
|
||||
|
||||
export function toCurrencyValue(amount: Amount): CurrencyValue {
|
||||
return {
|
||||
value: amount.basisPoints.toString(),
|
||||
displayValue: toDisplayValue(amount),
|
||||
};
|
||||
}
|
||||
|
||||
const toDisplayValue = (value: Amount): string => {
|
||||
if (value.currency.decimals === 0) {
|
||||
return `${value.currency.symbol} ${value.basisPoints.toString()}`;
|
||||
}
|
||||
|
||||
const power = new BN(10).pow(new BN(value.currency.decimals));
|
||||
const basisPoints = value.basisPoints as unknown as BN & {
|
||||
divmod: (other: BN) => { div: BN; mod: BN };
|
||||
};
|
||||
|
||||
const { div, mod } = basisPoints.divmod(power);
|
||||
const units = `${div.toString()}.${mod
|
||||
.abs()
|
||||
.toString(10, value.currency.decimals)}`;
|
||||
|
||||
return `${units}`;
|
||||
};
|
||||
@@ -8,7 +8,7 @@ import { Amman } from "@metaplex-foundation/amman-client";
|
||||
import { TOKEN_PROGRAM_ID } from "@solana/spl-token";
|
||||
import { Connection, Keypair } from "@solana/web3.js";
|
||||
|
||||
const createTestSDK = async (
|
||||
export const createTestSDK = async (
|
||||
solsToAirdrop: number = 100,
|
||||
): Promise<ThirdwebSDK> => {
|
||||
const connection = new Connection("http://localhost:8899", "confirmed");
|
||||
|
||||
@@ -8,6 +8,7 @@ describe("NFTDrop", async () => {
|
||||
|
||||
before(async () => {
|
||||
const address = await sdk.deployer.createNftDrop({
|
||||
name: "Test Drop",
|
||||
price: 0,
|
||||
sellerFeeBasisPoints: 0,
|
||||
itemsAvailable: 5,
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
import { ThirdwebSDK } from "../src/index";
|
||||
import { createTestSDK } from "./before-setup";
|
||||
import { expect } from "chai";
|
||||
|
||||
describe("Registry", async () => {
|
||||
let nftColl: string;
|
||||
let nftDrop: string;
|
||||
let token: string;
|
||||
let freshSDK: ThirdwebSDK;
|
||||
|
||||
before(async () => {
|
||||
freshSDK = await createTestSDK();
|
||||
nftColl = await freshSDK.deployer.createNftCollection({
|
||||
name: "Reg Test Collection",
|
||||
description: "Test Description",
|
||||
symbol: "TC",
|
||||
});
|
||||
token = await freshSDK.deployer.createToken({
|
||||
name: "Reg Test Token",
|
||||
initialSupply: 100,
|
||||
});
|
||||
nftDrop = await freshSDK.deployer.createNftDrop({
|
||||
name: "Reg Test Drop",
|
||||
});
|
||||
});
|
||||
|
||||
it("should fetch accounts grouped by type", async () => {
|
||||
const t = await freshSDK.registry.getAccountsForWallet(
|
||||
freshSDK.wallet.getAddress() || "",
|
||||
);
|
||||
t.forEach((account) => {
|
||||
switch (account.type) {
|
||||
case "nft-collection":
|
||||
expect(account.name).to.equal("Reg Test Collection");
|
||||
expect(account.address).to.equal(nftColl);
|
||||
break;
|
||||
case "nft-drop":
|
||||
expect(account.name).to.equal("Reg Test Drop");
|
||||
expect(account.address).to.equal(nftDrop);
|
||||
break;
|
||||
case "token":
|
||||
expect(account.name).to.equal("Reg Test Token");
|
||||
expect(account.address).to.equal(token);
|
||||
break;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("should resolve account addresses", async () => {
|
||||
expect(await freshSDK.registry.getAccountType(nftColl)).to.eq(
|
||||
"nft-collection",
|
||||
);
|
||||
expect(await freshSDK.registry.getAccountType(nftDrop)).to.eq("nft-drop");
|
||||
expect(await freshSDK.registry.getAccountType(token)).to.eq("token");
|
||||
});
|
||||
});
|
||||
@@ -9,6 +9,7 @@ describe("Token", async () => {
|
||||
before(async () => {
|
||||
const tokenAddr = await sdk.deployer.createToken({
|
||||
name: "My Token",
|
||||
symbol: "MTK",
|
||||
initialSupply: 100,
|
||||
});
|
||||
token = await sdk.getToken(tokenAddr);
|
||||
@@ -20,15 +21,15 @@ describe("Token", async () => {
|
||||
});
|
||||
|
||||
it("mint more should update total supply", async () => {
|
||||
expect((await token.totalSupply()).toString()).to.eq("100000000000");
|
||||
expect((await token.totalSupply()).displayValue).to.eq("100.000000000");
|
||||
await token.mint(3);
|
||||
expect((await token.totalSupply()).toString()).to.eq("103000000000");
|
||||
expect((await token.totalSupply()).displayValue).to.eq("103.000000000");
|
||||
});
|
||||
|
||||
it("transfer should update total supply", async () => {
|
||||
expect((await token.balance()).toString()).to.eq("103000000000");
|
||||
expect((await token.balance()).displayValue).to.eq("103.000000000");
|
||||
const receiver = Keypair.generate();
|
||||
await token.transfer(receiver.publicKey.toBase58(), 10);
|
||||
expect((await token.balance()).toString()).to.eq("93000000000");
|
||||
expect((await token.balance()).displayValue).to.eq("93.000000000");
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user