Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ec414bbc42 | ||
|
|
51dde28224 | ||
|
|
57432d21c4 | ||
|
|
19d87c2885 | ||
|
|
68812e9d27 | ||
|
|
90c53475f0 | ||
|
|
5a52585a7a | ||
|
|
6b48b9288b | ||
|
|
9d74a43aac | ||
|
|
9c7e5518ff | ||
|
|
b234c58d44 | ||
|
|
b1ae844e8b |
+1
-1
@@ -43,7 +43,7 @@
|
||||
"eslint": "^8.21.0",
|
||||
"postinstall-postinstall": "^2.1.0",
|
||||
"prettier": "^2.7.1",
|
||||
"turbo": "latest"
|
||||
"turbo": "^1.4.6"
|
||||
},
|
||||
"manypkg": {
|
||||
"defaultBranch": "main"
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# @thirdweb-dev/auth
|
||||
|
||||
## 2.0.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`51dde28`](https://github.com/thirdweb-dev/js/commit/51dde28224209f1b8b26f436c204a5e702281564), [`9d74a43`](https://github.com/thirdweb-dev/js/commit/9d74a43aac21448beba63ba4e2637945965a3634), [`b234c58`](https://github.com/thirdweb-dev/js/commit/b234c58d44d8322e44b2d2ba87ad4ec7d699e961)]:
|
||||
- @thirdweb-dev/sdk@3.0.3
|
||||
|
||||
## 2.0.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@thirdweb-dev/auth",
|
||||
"version": "2.0.2",
|
||||
"version": "2.0.3",
|
||||
"exports": {
|
||||
"./package.json": "./package.json",
|
||||
"./express": {
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
# thirdweb
|
||||
|
||||
## 0.7.14
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#134](https://github.com/thirdweb-dev/js/pull/134) [`b1ae844`](https://github.com/thirdweb-dev/js/commit/b1ae844e8bbed59c0225b32f4115ae384fa23ef5) Thanks [@adam-maj](https://github.com/adam-maj)! - Update ordering of commands in npx thirdweb
|
||||
|
||||
- Updated dependencies [[`51dde28`](https://github.com/thirdweb-dev/js/commit/51dde28224209f1b8b26f436c204a5e702281564), [`9d74a43`](https://github.com/thirdweb-dev/js/commit/9d74a43aac21448beba63ba4e2637945965a3634), [`b234c58`](https://github.com/thirdweb-dev/js/commit/b234c58d44d8322e44b2d2ba87ad4ec7d699e961)]:
|
||||
- @thirdweb-dev/sdk@3.0.3
|
||||
|
||||
## 0.7.13
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "thirdweb",
|
||||
"main": "dist/cli/index.js",
|
||||
"version": "0.7.13",
|
||||
"version": "0.7.14",
|
||||
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/cli",
|
||||
"author": "thirdweb eng <[email protected]>",
|
||||
"license": "Apache-2.0",
|
||||
@@ -81,4 +81,4 @@
|
||||
"sourcemap": true,
|
||||
"clean": true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -39,31 +39,34 @@ $$$$$$\\ $$$$$$$\\ $$\\ $$$$$$\\ $$$$$$$ |$$\\ $$\\ $$\\ $$$$$$\\ $$$$
|
||||
.version(cliVersion, "-v, --version");
|
||||
|
||||
program
|
||||
.command("detect")
|
||||
.command("create")
|
||||
.description(
|
||||
"Compile contracts and detect implemented thirdweb contract extensions",
|
||||
"Create a thirdweb app from any of our official templates. Checkout some examples you can use here: https://github.com/thirdweb-example/",
|
||||
)
|
||||
.option("-p, --path <project-path>", "path to project", ".")
|
||||
.option("-d, --debug", "show debug logs")
|
||||
.option("-a, --all", "run detection on all contracts")
|
||||
.action(async (options) => {
|
||||
await detectExtensions(options);
|
||||
});
|
||||
|
||||
program
|
||||
.command("release")
|
||||
.description(
|
||||
"Release contracts, making them available for others to deploy and unlocking SDKs, Dashboards and Analytics.",
|
||||
.option("--app", `Create a thirdweb app.`)
|
||||
.option("--contract", `Create a thirdweb contracts project.`)
|
||||
.option("--ts, --typescript", `Initialize as a TypeScript project.`)
|
||||
.option("--js, --javascript", `Initialize as a JavaScript project.`)
|
||||
.option("--forge", `Initialize as a Forge project.`)
|
||||
.option("--hardhat", `Initialize as a Hardhat project.`)
|
||||
.option("--cra", `Initialize as a Create React App project.`)
|
||||
.option("--next", `Initialize as a Next.js project.`)
|
||||
.option("--vite", `Initialize as a Vite project.`)
|
||||
.option(
|
||||
"--use-npm",
|
||||
`Explicitly tell the CLI to bootstrap the app using npm`,
|
||||
)
|
||||
.option(
|
||||
"--use-pnpm",
|
||||
`Explicitly tell the CLI to bootstrap the app using pnpm`,
|
||||
)
|
||||
.option("--framework [name]", `The preferred framework.`)
|
||||
.option(
|
||||
"-t, --template [name]",
|
||||
`A template to start your project from. You can use an template repository name from the official thirdweb-example org.`,
|
||||
)
|
||||
.option("-p, --path <project-path>", "path to project", ".")
|
||||
.option("--dry-run", "dry run (skip actually publishing)")
|
||||
.option("-d, --debug", "show debug logs")
|
||||
.option("--ci", "Continuous Integration mode")
|
||||
.action(async (options) => {
|
||||
const url = await processProject(options, "release");
|
||||
info(`Open this link to release your contracts:`);
|
||||
logger.info(chalk.blueBright(url.toString()));
|
||||
open(url.toString());
|
||||
await twCreate(options);
|
||||
});
|
||||
|
||||
program
|
||||
@@ -111,34 +114,31 @@ $$$$$$\\ $$$$$$$\\ $$\\ $$$$$$\\ $$$$$$$ |$$\\ $$\\ $$\\ $$$$$$\\ $$$$
|
||||
});
|
||||
|
||||
program
|
||||
.command("create")
|
||||
.command("release")
|
||||
.description(
|
||||
"Create a thirdweb app from any of our official templates. Checkout some examples you can use here: https://github.com/thirdweb-example/",
|
||||
)
|
||||
.option("--app", `Create a thirdweb app.`)
|
||||
.option("--contract", `Create a thirdweb contracts project.`)
|
||||
.option("--ts, --typescript", `Initialize as a TypeScript project.`)
|
||||
.option("--js, --javascript", `Initialize as a JavaScript project.`)
|
||||
.option("--forge", `Initialize as a Forge project.`)
|
||||
.option("--hardhat", `Initialize as a Hardhat project.`)
|
||||
.option("--cra", `Initialize as a Create React App project.`)
|
||||
.option("--next", `Initialize as a Next.js project.`)
|
||||
.option("--vite", `Initialize as a Vite project.`)
|
||||
.option(
|
||||
"--use-npm",
|
||||
`Explicitly tell the CLI to bootstrap the app using npm`,
|
||||
)
|
||||
.option(
|
||||
"--use-pnpm",
|
||||
`Explicitly tell the CLI to bootstrap the app using pnpm`,
|
||||
)
|
||||
.option("--framework [name]", `The preferred framework.`)
|
||||
.option(
|
||||
"-t, --template [name]",
|
||||
`A template to start your project from. You can use an template repository name from the official thirdweb-example org.`,
|
||||
"Release contracts, making them available for others to deploy and unlocking SDKs, Dashboards and Analytics.",
|
||||
)
|
||||
.option("-p, --path <project-path>", "path to project", ".")
|
||||
.option("--dry-run", "dry run (skip actually publishing)")
|
||||
.option("-d, --debug", "show debug logs")
|
||||
.option("--ci", "Continuous Integration mode")
|
||||
.action(async (options) => {
|
||||
await twCreate(options);
|
||||
const url = await processProject(options, "release");
|
||||
info(`Open this link to release your contracts:`);
|
||||
logger.info(chalk.blueBright(url.toString()));
|
||||
open(url.toString());
|
||||
});
|
||||
|
||||
program
|
||||
.command("detect")
|
||||
.description(
|
||||
"Compile contracts and detect implemented thirdweb contract extensions",
|
||||
)
|
||||
.option("-p, --path <project-path>", "path to project", ".")
|
||||
.option("-d, --debug", "show debug logs")
|
||||
.option("-a, --all", "run detection on all contracts")
|
||||
.action(async (options) => {
|
||||
await detectExtensions(options);
|
||||
});
|
||||
|
||||
program
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { spinner } from "../core/helpers/logger";
|
||||
import { IpfsStorage } from "@thirdweb-dev/storage";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
import { existsSync, mkdirSync, writeFileSync } from "fs";
|
||||
import path, { join } from "path";
|
||||
|
||||
@@ -13,10 +13,10 @@ export async function installGithubAction(options: any) {
|
||||
: path.resolve(`${projectPath}/${options.path}`);
|
||||
projectPath = resolvedPath;
|
||||
}
|
||||
const storage = new IpfsStorage();
|
||||
const storage = new ThirdwebStorage();
|
||||
const log = spinner("Installing thirdweb Github Action...");
|
||||
try {
|
||||
const ghActionData = await storage.getRaw(ghActionHash);
|
||||
const ghActionData = await (await storage.download(ghActionHash)).text();
|
||||
const dir = join(projectPath, ".github/workflows");
|
||||
if (!existsSync(dir)) {
|
||||
mkdirSync(dir, { recursive: true });
|
||||
|
||||
@@ -5,7 +5,7 @@ import { execute } from "../core/helpers/exec";
|
||||
import { error, info, logger, spinner } from "../core/helpers/logger";
|
||||
import { createContractsPrompt } from "../core/helpers/selector";
|
||||
import { ContractPayload } from "../core/interfaces/ContractPayload";
|
||||
import { IpfsStorage } from "@thirdweb-dev/storage";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
import chalk from "chalk";
|
||||
import { readFileSync } from "fs";
|
||||
import path from "path";
|
||||
@@ -15,7 +15,7 @@ export async function processProject(
|
||||
command: "deploy" | "release",
|
||||
) {
|
||||
// TODO: allow overriding the default storage
|
||||
const storage = new IpfsStorage();
|
||||
const storage = new ThirdwebStorage();
|
||||
|
||||
logger.setSettings({
|
||||
minLevel: options.debug ? "debug" : "info",
|
||||
@@ -129,7 +129,9 @@ export async function processProject(
|
||||
if (file.includes(soliditySDKPackage)) {
|
||||
usesSoliditySDK = true;
|
||||
}
|
||||
return await storage.uploadSingle(file);
|
||||
return await storage.upload(file, {
|
||||
uploadWithoutDirectory: true,
|
||||
});
|
||||
}),
|
||||
);
|
||||
}
|
||||
@@ -140,14 +142,16 @@ export async function processProject(
|
||||
const metadataURIs = await Promise.all(
|
||||
selectedContracts.map(async (c) => {
|
||||
logger.debug(`Uploading ${c.name}...`);
|
||||
const hash = await storage.uploadSingle(c.metadata);
|
||||
const hash = await storage.upload(c.metadata, {
|
||||
uploadWithoutDirectory: true,
|
||||
});
|
||||
return `ipfs://${hash}`;
|
||||
}),
|
||||
);
|
||||
|
||||
// Upload batch all bytecodes
|
||||
const bytecodes = selectedContracts.map((c) => c.bytecode);
|
||||
const { uris: bytecodeURIs } = await storage.uploadBatch(bytecodes);
|
||||
const bytecodeURIs = await storage.uploadBatch(bytecodes);
|
||||
|
||||
const combinedContents = selectedContracts.map((c, i) => {
|
||||
// attach analytics blob to metadata
|
||||
@@ -169,13 +173,14 @@ export async function processProject(
|
||||
let combinedURIs: string[] = [];
|
||||
if (combinedContents.length === 1) {
|
||||
// use upload single if only one contract to get a clean IPFS hash
|
||||
const metadataUri = await storage.uploadSingle(
|
||||
const metadataUri = await storage.upload(
|
||||
JSON.stringify(combinedContents[0]),
|
||||
{ uploadWithoutDirectory: true },
|
||||
);
|
||||
combinedURIs.push(metadataUri);
|
||||
} else {
|
||||
// otherwise upload batch
|
||||
const { uris } = await storage.uploadMetadataBatch(combinedContents);
|
||||
const uris = await storage.uploadBatch(combinedContents);
|
||||
combinedURIs = uris;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
# @thirdweb-dev/react
|
||||
|
||||
## 3.0.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#143](https://github.com/thirdweb-dev/js/pull/143) [`51dde28`](https://github.com/thirdweb-dev/js/commit/51dde28224209f1b8b26f436c204a5e702281564) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Fix using external signers with ThirdwebSDKProvider
|
||||
|
||||
- Updated dependencies [[`51dde28`](https://github.com/thirdweb-dev/js/commit/51dde28224209f1b8b26f436c204a5e702281564), [`9d74a43`](https://github.com/thirdweb-dev/js/commit/9d74a43aac21448beba63ba4e2637945965a3634), [`b234c58`](https://github.com/thirdweb-dev/js/commit/b234c58d44d8322e44b2d2ba87ad4ec7d699e961)]:
|
||||
- @thirdweb-dev/sdk@3.0.3
|
||||
|
||||
## 3.0.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
+177
-54
@@ -13,9 +13,17 @@
|
||||
<p align="center"><strong>Ultimate collection of React hooks for your web3 apps</strong></p>
|
||||
<br />
|
||||
|
||||
<br />
|
||||
|
||||
## Installation
|
||||
|
||||
You can install this SDK with either `npm` or `yarn`:
|
||||
The easiest way to get started with the React SDK is to use the CLI:
|
||||
|
||||
```sh
|
||||
npx thirdweb create --app
|
||||
```
|
||||
|
||||
Alternatively, you can install the SDK into your existing project using npm or yarn:
|
||||
|
||||
```sh
|
||||
npm install @thirdweb-dev/react @thirdweb-dev/sdk ethers
|
||||
@@ -25,21 +33,19 @@ npm install @thirdweb-dev/react @thirdweb-dev/sdk ethers
|
||||
yarn add @thirdweb-dev/react @thirdweb-dev/sdk ethers
|
||||
```
|
||||
|
||||
## Starter Templates
|
||||
<br />
|
||||
|
||||
We provide template repositories that already have the thirdweb React SDK setup and ready-to-go to help you get started with thirdweb quickly. You can find all the available starter respositories below.
|
||||
## Getting Started
|
||||
|
||||
- Next.js ([typescript](https://github.com/thirdweb-example/next-typescript-starter) / [javascript](https://github.com/thirdweb-example/next-javascript-starter))
|
||||
- Create React App ([typescript](https://github.com/thirdweb-example/cra-typescript-starter) / [javascript](https://github.com/thirdweb-example/cra-javascript-starter))
|
||||
- Vite ([typescript](https://github.com/thirdweb-example/vite-typescript-starter) / [javascript](https://github.com/thirdweb-example/vite-javascript-starter))
|
||||
Our SDK uses a [Provider Pattern](https://flexiple.com/react/provider-pattern-with-react-context-api/); meaning any component within the `ThirdwebProvider` will have access to the SDK. If you use the CLI to initialize your project, this is already set up for you.
|
||||
|
||||
## Quick Start
|
||||
Let's take a look at a typical setup:
|
||||
|
||||
**Configure the thirdweb Provider**
|
||||
<br />
|
||||
|
||||
In order to use the hooks offered by the React SDK, you need to first setup a `ThirdwebProvider` for your app which lets you optionally configure your app. You can use this configuration to control what networks you want users to connect to, what types of wallets can connect to your app, and the settings for the [Typescript SDK](https://docs.thirdweb.com/typescript).
|
||||
### Configure the `ThirdwebProvider`
|
||||
|
||||
At the top level of your application, add a `ThirdwebProvider` as follows:
|
||||
Specify the network your smart contracts are deployed to in the `desiredChainId` prop and wrap your application like so:
|
||||
|
||||
```jsx title="App.jsx"
|
||||
import { ChainId, ThirdwebProvider } from "@thirdweb-dev/react";
|
||||
@@ -53,79 +59,196 @@ const App = () => {
|
||||
};
|
||||
```
|
||||
|
||||
Now you'll be able to use all the hooks provided by the React SDK! Let's take a look.
|
||||
Below are examples of where to set this up in your application:
|
||||
|
||||
**Let Users Connect Wallets**
|
||||
<p>
|
||||
<a href="https://github.com/thirdweb-example/cra-javascript-starter/blob/main/src/index.js">Create React App</a> •
|
||||
<a href="https://github.com/thirdweb-example/next-javascript-starter/blob/main/pages/_app.js">Next.js</a> •
|
||||
<a href="https://github.com/thirdweb-example/vite-javascript-starter/blob/main/src/main.jsx">Vite</a>
|
||||
</p>
|
||||
|
||||
Next, we'll add a button to our app which will let users connect their wallets. For now, we'll make it so that users with MetaMask wallets can connect.
|
||||
<br />
|
||||
|
||||
### Connect to a User's Wallet
|
||||
|
||||
Now the provider is set up, we can use all of the hooks and UI components available in the SDK, such as the [ConnectWallet](https://portal.thirdweb.com/sdk/ui-components/connectwalletbutton) component.
|
||||
|
||||
Once the user has connected their wallet, all the calls we make to interact with contracts using the SDK will be on behalf of the user.
|
||||
|
||||
```jsx title="ConnectMetamaskButton.jsx"
|
||||
import { useAddress, useDisconnect, useMetamask } from "@thirdweb-dev/react";
|
||||
import { ConnectWallet, useAddress } from "@thirdweb-dev/react";
|
||||
|
||||
export const ConnectMetamaskButtonComponent = () => {
|
||||
const connectWithMetamask = useMetamask();
|
||||
export const YourApp = () => {
|
||||
const address = useAddress();
|
||||
return (
|
||||
<div>
|
||||
{address ? (
|
||||
<h4>Connected as {address}</h4>
|
||||
<ConnectWallet />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
The `ConnectWallet` component handles everything for us, including switching networks, accounts, displaying balances and more.
|
||||
|
||||
We can then get the connected address using the `useAddress` hook anywhere in the app.
|
||||
|
||||
<br/>
|
||||
|
||||
### Interact With Contracts
|
||||
|
||||
Connect to your smart contract using the [`useContract`](https://portal.thirdweb.com/sdk/interacting-with-contracts/custom-contracts/getting-a-contract#connect-to-a-contract)
|
||||
hook like so:
|
||||
|
||||
```jsx title="pages/index.jsx"
|
||||
import { useContract } from "@thirdweb-dev/react";
|
||||
|
||||
export default function Home() {
|
||||
const { contract } = useContract("<CONTRACT_ADDRESS>");
|
||||
|
||||
// Now you can use the contract in the rest of the component!
|
||||
}
|
||||
```
|
||||
|
||||
You can then use [`useContractRead`](https://portal.thirdweb.com/sdk/interacting-with-contracts/custom-contracts/using-contracts) and [`useContractWrite`](https://portal.thirdweb.com/sdk/interacting-with-contracts/custom-contracts/using-contracts) to read data and write transactions to the contract.
|
||||
|
||||
You pass the `contract` object returned from `useContract` to these hooks as the first parameter and the name of the function (or view/mapping, etc.) on your smart contract as the second parameter. If your function requires parameters, you can pass them as additional arguments.
|
||||
|
||||
For example, we can read the `name` of our contract like so:
|
||||
|
||||
```jsx title="pages/index.jsx"
|
||||
import {
|
||||
useContract,
|
||||
useContractRead,
|
||||
useContractWrite,
|
||||
} from "@thirdweb-dev/react";
|
||||
|
||||
export default function Home() {
|
||||
const { contract } = useContract("<CONTRACT_ADDRESS>");
|
||||
const { data: name, isLoading: loadingName } = useContractRead(
|
||||
contract,
|
||||
"name", // The name of the view/mapping/variable on your contract
|
||||
);
|
||||
const { mutate: setName, isLoading: settingName } = useContractWrite(
|
||||
contract,
|
||||
"setName", // The name of the function on your contract
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
<br />
|
||||
|
||||
### Using Extensions
|
||||
|
||||
Each [extension](https://portal.thirdweb.com/extensions) you implement in your smart contract unlocks new functionality in the SDK.
|
||||
|
||||
These hooks make it easy to interact with your smart contracts by implementing the complex logic for you under the hood.
|
||||
|
||||
For example, if your smart contract implements [ERC721Supply](https://portal.thirdweb.com/extensions/erc721supply#unlocked-features), you unlock the ability to [view all NFTs](https://portal.thirdweb.com/sdk/interacting-with-contracts/erc721Supply#get-all-minted-nfts) on that contract using the SDK; which fetches all of your NFT metadata and the current owner of each NFT in parallel. In the React SDK, that is available using `useNFTs`:
|
||||
|
||||
```jsx
|
||||
import { useContract, useNFTs } from "@thirdweb-dev/react";
|
||||
|
||||
export default function Home() {
|
||||
const { contract } = useContract("<CONTRACT_ADDRESS>");
|
||||
const { data: nfts, isLoading: isReadingNfts } = useNFTs(contract);
|
||||
}
|
||||
```
|
||||
|
||||
If we want to mint an NFT and our contract implements [ERC721Mintable](https://portal.thirdweb.com/extensions/erc721mintable#unlocked-features), we can use the [`useMintNFT`](https://portal.thirdweb.com/sdk/interacting-with-contracts/erc721mintable) hook to mint an NFT from the connected wallet; handling all of the logic of uploading and pinning the metadata to IPFS for us behind the scenes.
|
||||
|
||||
```jsx
|
||||
import { useContract, useNFTs, useMintNFT } from "@thirdweb-dev/react";
|
||||
|
||||
export default function Home() {
|
||||
const { contract } = useContract("<CONTRACT_ADDRESS>");
|
||||
const { data: nfts, isLoading: isReadingNfts } = useNFTs(contract);
|
||||
const { mutate: mintNFT, isLoading: isMintingNFT } = useMintNFT(contract);
|
||||
}
|
||||
```
|
||||
|
||||
<br />
|
||||
|
||||
### UI Components
|
||||
|
||||
The SDK provides many UI components to help you build your application.
|
||||
|
||||
For example, we can render each of the NFTs using the [`NFT Media Renderer`](https://portal.thirdweb.com/sdk/ui-components/nft-renderer)
|
||||
component, making use of the loading state from `useNFTs`:
|
||||
|
||||
```jsx title="pages/index.jsx"
|
||||
import { useContract, useNFTs, ThirdwebNftMedia } from "@thirdweb-dev/react";
|
||||
|
||||
export default function Home() {
|
||||
const { contract } = useContract("<CONTRACT_ADDRESS>");
|
||||
const { data: nfts, isLoading: isReadingNfts } = useNFTs(contract);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h2>My NFTs</h2>
|
||||
{isReadingNfts ? (
|
||||
<p>Loading...</p>
|
||||
) : (
|
||||
<button onClick={connectWithMetamask}>Connect Metamask Wallet</button>
|
||||
<div>
|
||||
{nfts.map((nft) => (
|
||||
<ThirdwebNftMedia
|
||||
key={nft.tokenId}
|
||||
metadata={nft.metadata}
|
||||
height={200}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
Here, we use the `useMetamask` hook to handle metamask connection. When a user clicks the button, we'll call the `connectWithMetamask` function, which will prompt users to connect their metamask wallet.
|
||||
The [`Web3Button`](https://portal.thirdweb.com/sdk/ui-components/web3button) component ensures the user has connected their wallet and is currently configured to the same network as your smart contract before calling the function. It also has access to the `contract` directly, allowing you to perform any action on your smart contract when the button is clicked.
|
||||
|
||||
**Interact With Contracts**
|
||||
For example, we can mint an NFT like so:
|
||||
|
||||
The thirdweb React SDK also enables you to interact directly with contracts through simple hooks!
|
||||
```jsx title="pages/index.jsx"
|
||||
import {
|
||||
useContract,
|
||||
useNFTs,
|
||||
ThirdwebNftMedia,
|
||||
Web3Button,
|
||||
} from "@thirdweb-dev/react";
|
||||
|
||||
Let's setup a simple component to interact with an NFT Collection contract and get the data of all the NFTs on the contract.
|
||||
|
||||
```jsx title="NFTList.jsx"
|
||||
import { useMintNFT, useNFTCollection, useNFTs } from "@thirdweb-dev/react";
|
||||
|
||||
const NFTListComponent = () => {
|
||||
const address = useAddress();
|
||||
const nftCollection = useNFTCollection("<NFT-COLLECTION-CONTRACT-ADDRESS>");
|
||||
const { data: nfts } = useNFTs(nftCollection);
|
||||
const { mutate: mintNFT } = useMintNFT(nftCollection);
|
||||
|
||||
const mint = () => {
|
||||
mintNFT({
|
||||
to: address,
|
||||
metadata: {
|
||||
name: "Cool NFT",
|
||||
description: "Minted from react",
|
||||
},
|
||||
});
|
||||
};
|
||||
const contractAddress = "<CONTRACT_ADDRESS>";
|
||||
export default function Home() {
|
||||
const { contract } = useContract(contractAddress);
|
||||
const { data: nfts, isLoading: isReadingNfts } = useNFTs(contract);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<button onClick={mint}>Mint</button>
|
||||
<ul>
|
||||
{nfts?.map((nft) => (
|
||||
<li key={nft.metadata.id.toString()}>{nft.metadata.name}</li>
|
||||
))}
|
||||
</ul>
|
||||
{/* ... Existing Display Logic here ... */}
|
||||
|
||||
<Web3Button
|
||||
contractAddress={contractAddress}
|
||||
action={(contract) =>
|
||||
contract.erc721.mint({
|
||||
name: "Hello world!",
|
||||
image:
|
||||
// You can use a file or URL here!
|
||||
"ipfs://QmZbovNXznTHpYn2oqgCFQYP4ZCpKDquenv5rFCX8irseo/0.png",
|
||||
})
|
||||
}
|
||||
>
|
||||
Mint NFT
|
||||
</Web3Button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
Here, we get an `NFT Collection` contract instance from the [TypeScript SDK](https://docs.thirdweb.com/typescript). We can then use all the methods on this thirdweb contract SDK instance - here we use the `mintTo` method to mint an NFT on the contract, and we use `useNFTList` to display all the NFTs in the collection on the page.
|
||||
|
||||
And that's all for the setup! Just like that, you can setup a `ThirdwebProvider` and use all the hooks of the SDK, allowing you to let users connect wallets, interact with contracts, and more!
|
||||
<br />
|
||||
|
||||
## Advanced Configuration
|
||||
|
||||
The `ThirdwebProvider` offers a number of configuration options to control the behavior of the React and Typescript SDK.
|
||||
|
||||
These all the configuration options of the `<ThirdwebProvider />`.
|
||||
These are all the configuration options of the `<ThirdwebProvider />`.
|
||||
We provide defaults for all of these, but you customize them to suit your needs.
|
||||
|
||||
```jsx title="App.jsx"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@thirdweb-dev/react",
|
||||
"version": "3.0.2",
|
||||
"version": "3.0.3",
|
||||
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/react",
|
||||
"author": "thirdweb eng <[email protected]>",
|
||||
"license": "Apache-2.0",
|
||||
|
||||
@@ -27,9 +27,9 @@ import {
|
||||
getProviderForNetwork,
|
||||
SDKOptionsOutput,
|
||||
} from "@thirdweb-dev/sdk";
|
||||
import type { IStorage } from "@thirdweb-dev/storage";
|
||||
import type { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
import { Signer } from "ethers";
|
||||
import React, { createContext, useEffect, useMemo } from "react";
|
||||
import React, { createContext, useEffect, useMemo, useState } from "react";
|
||||
import invariant from "tiny-invariant";
|
||||
import {
|
||||
WagmiProvider,
|
||||
@@ -181,7 +181,7 @@ export interface ThirdwebProviderProps<
|
||||
/**
|
||||
* The storage interface to use with the sdk.
|
||||
*/
|
||||
storageInterface?: IStorage;
|
||||
storageInterface?: ThirdwebStorage;
|
||||
|
||||
/**
|
||||
* The react-query client to use. (Defaults to a default client.)
|
||||
@@ -587,3 +587,54 @@ export function useActiveChainId(): SUPPORTED_CHAIN_ID | undefined {
|
||||
const sdk = useSDK();
|
||||
return (sdk as any)?._chainId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export function useActiveSigner(): Signer | undefined {
|
||||
const sdk = useSDK();
|
||||
const [signer, setSigner] = useState<Signer | undefined>(sdk?.getSigner());
|
||||
useEffect(() => {
|
||||
if (sdk) {
|
||||
sdk.wallet.events.on("signerChanged", (newSigner: Signer | undefined) => {
|
||||
setSigner(newSigner);
|
||||
});
|
||||
return () => {
|
||||
sdk.wallet.events.off("signerChanged");
|
||||
};
|
||||
}
|
||||
}, [sdk]);
|
||||
return signer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export function useActiveSignerAddress(): string | undefined {
|
||||
const signer = useActiveSigner();
|
||||
const [address, setAddress] = useState<string | undefined>(undefined);
|
||||
useEffect(() => {
|
||||
if (signer) {
|
||||
signer.getAddress().then((a) => {
|
||||
setAddress(a);
|
||||
});
|
||||
}
|
||||
}, [signer]);
|
||||
return address;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export function useActiveSignerChainId(): number | undefined {
|
||||
const signer = useActiveSigner();
|
||||
const [chainId, setChainId] = useState<number | undefined>(undefined);
|
||||
useEffect(() => {
|
||||
if (signer) {
|
||||
signer.getChainId().then((id) => {
|
||||
setChainId(id);
|
||||
});
|
||||
}
|
||||
}, [signer]);
|
||||
return chainId;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
import { useActiveChainId, useSDK } from "../../Provider";
|
||||
import {
|
||||
useActiveChainId,
|
||||
useActiveSignerAddress,
|
||||
useActiveSignerChainId,
|
||||
useSDK,
|
||||
} from "../../Provider";
|
||||
import { ContractAddress, RequiredParam } from "../../types";
|
||||
import {
|
||||
cacheKeys,
|
||||
@@ -6,8 +11,6 @@ import {
|
||||
createContractCacheKey,
|
||||
} from "../../utils/cache-keys";
|
||||
import { useQueryWithNetwork } from "../query-utils/useQueryWithNetwork";
|
||||
import { useAddress } from "../useAddress";
|
||||
import { useChainId } from "../useChainId";
|
||||
import {
|
||||
useMutation,
|
||||
useQuery,
|
||||
@@ -152,8 +155,8 @@ export function useContract<
|
||||
const sdk = useSDK();
|
||||
const queryClient = useQueryClient();
|
||||
const activeChainId = useActiveChainId();
|
||||
const wallet = useAddress();
|
||||
const walletChainId = useChainId();
|
||||
const wallet = useActiveSignerAddress();
|
||||
const walletChainId = useActiveSignerChainId();
|
||||
|
||||
// it's there because we put it there.
|
||||
const sdkTimestamp = (sdk as any)?._constructedAt;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { SDKOptions, ThirdwebSDK } from "@thirdweb-dev/sdk";
|
||||
import type { IStorage } from "@thirdweb-dev/storage";
|
||||
import type { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
import { useMemo } from "react";
|
||||
|
||||
/**
|
||||
@@ -8,7 +8,7 @@ import { useMemo } from "react";
|
||||
export function useReadonlySDK(
|
||||
readonlyRpcUrl: string,
|
||||
sdkOptions: SDKOptions,
|
||||
storageInterface?: IStorage,
|
||||
storageInterface?: ThirdwebStorage,
|
||||
): ThirdwebSDK {
|
||||
return useMemo(() => {
|
||||
return new ThirdwebSDK(
|
||||
|
||||
@@ -1,5 +1,18 @@
|
||||
# @thirdweb-dev/sdk
|
||||
|
||||
## 3.0.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#143](https://github.com/thirdweb-dev/js/pull/143) [`51dde28`](https://github.com/thirdweb-dev/js/commit/51dde28224209f1b8b26f436c204a5e702281564) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Fix using external signers with ThirdwebSDKProvider
|
||||
|
||||
- [#133](https://github.com/thirdweb-dev/js/pull/133) [`9d74a43`](https://github.com/thirdweb-dev/js/commit/9d74a43aac21448beba63ba4e2637945965a3634) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Make contract events data generic for better DX
|
||||
|
||||
- [#135](https://github.com/thirdweb-dev/js/pull/135) [`b234c58`](https://github.com/thirdweb-dev/js/commit/b234c58d44d8322e44b2d2ba87ad4ec7d699e961) Thanks [@jnsdls](https://github.com/jnsdls)! - add a new StaticJsonRpcBatchProvider to stop calling `eth_getChainId` when possible
|
||||
|
||||
- Updated dependencies [[`57432d2`](https://github.com/thirdweb-dev/js/commit/57432d21c4c9e880a36c61f4988c60af61ac9d44)]:
|
||||
- @thirdweb-dev/storage@1.0.0
|
||||
|
||||
## 3.0.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
+19
-12
@@ -32,7 +32,8 @@ yarn add @thirdweb-dev/sdk ethers
|
||||
### 1. Deploy & customize your contracts
|
||||
|
||||
- Using your [thirdweb dashboard](https://thirdweb.com/dashboard) (recommended)
|
||||
- Using the SDK directly (for advanced use cases)
|
||||
- Using the [SDK directly](https://portal.thirdweb.com/typescript/sdk.contractdeployer)
|
||||
- Using the [thirdweb CLI](https://portal.thirdweb.com/deploy)
|
||||
|
||||
### 2. Reading data from your contracts
|
||||
|
||||
@@ -44,16 +45,18 @@ This will allow you to query data from any contract with no additional setup.
|
||||
import { ThirdwebSDK } from "@thirdweb-dev/sdk";
|
||||
|
||||
// instantiate the SDK in read-only mode (our example is running on `polygon` here)
|
||||
// all major chains and testnets are supported (e.g. `mainnet`, `rinkeby`, `goerli`, 'polygon', 'mumbai', etc.)
|
||||
// all major chains and testnets are supported (e.g. `mainnet`, 'optimism`, 'arbitrum', 'polygon', `goerli`, 'mumbai', etc.)
|
||||
const sdk = new ThirdwebSDK("polygon");
|
||||
|
||||
// access your deployed contracts
|
||||
const nftDrop = sdk.getNFTDrop("0x...");
|
||||
const marketplace = sdk.getMarketplace("0x...");
|
||||
const contract = await sdk.getContract("0x...");
|
||||
|
||||
// Read from your contracts
|
||||
const claimedNFTs = await nftDrop.getAllClaimed();
|
||||
const listings = await marketplace.getActiveListings();
|
||||
// Read data using direct calls to your contract
|
||||
const myData = await contract.call("myFunction");
|
||||
|
||||
// Or Using the extensions API matching to your contract extensions
|
||||
const allNFTs = await contract.erc721.getAll();
|
||||
const tokenSupply = await contract.erc20.totalSupply();
|
||||
```
|
||||
|
||||
You can execute this code as a node script by executing:
|
||||
@@ -62,6 +65,8 @@ You can execute this code as a node script by executing:
|
||||
node my_script.js
|
||||
```
|
||||
|
||||
Note that you can also access any deployed contract using its ABI, using `sdk.getContractFromAbi(address, abi)` and get the same functionality. For contracts deployed via thirdweb, we handle the ABI for you.
|
||||
|
||||
### 3. Executing transactions on your contracts
|
||||
|
||||
In order to execute transactions on your contract, the SDK needs to know which wallet is executing those transactions.
|
||||
@@ -83,18 +88,20 @@ const privateKey = "<your-private-key-here>";
|
||||
// instantiate the SDK based on your private key, with the desired chain to connect to
|
||||
const sdk = ThirdwebSDK.fromPrivateKey(privateKey, "polygon");
|
||||
|
||||
// deploy contracts
|
||||
// deploy existing contracts, or your own using the thirdweb CLI
|
||||
const deployedAddress = sdk.deployer.deployNFTCollection({
|
||||
name: "My NFT Collection",
|
||||
primary_sale_recipient: "0x...",
|
||||
});
|
||||
|
||||
// access your deployed contracts
|
||||
const nftCollection = sdk.getNFTCollection(deployedAddress);
|
||||
const contract = await sdk.getContract(deployedAddress);
|
||||
|
||||
// Execute transactions on your contracts from the connected wallet
|
||||
const walletAddress = "0x...";
|
||||
await nftCollection.mintTo(walletAddress, {
|
||||
// Execute any of your functions on your contracts from the connected wallet
|
||||
await contract.call("myFunction", arg1, arg2);
|
||||
|
||||
// Or execute transactions using the extensions API
|
||||
await contract.erc721.mint({
|
||||
name: "Cool NFT",
|
||||
description: "Minted NFT from code!",
|
||||
image: fs.readFileSync("path/to/image.png"), // This can be an image url or file
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@thirdweb-dev/sdk",
|
||||
"version": "3.0.2",
|
||||
"version": "3.0.3",
|
||||
"description": "The main thirdweb SDK.",
|
||||
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/sdk",
|
||||
"license": "Apache-2.0",
|
||||
|
||||
@@ -25,7 +25,7 @@ import {
|
||||
} from "./currency";
|
||||
import { createSnapshot } from "./snapshots";
|
||||
import { IDropClaimCondition } from "@thirdweb-dev/contracts-js/dist/declarations/src/DropERC20";
|
||||
import { IStorage } from "@thirdweb-dev/storage";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
import {
|
||||
BigNumber,
|
||||
BigNumberish,
|
||||
@@ -47,7 +47,7 @@ export async function prepareClaim(
|
||||
merkleMetadataFetcher: () => Promise<Record<string, string>>,
|
||||
tokenDecimals: number,
|
||||
contractWrapper: ContractWrapper<any>,
|
||||
storage: IStorage,
|
||||
storage: ThirdwebStorage,
|
||||
checkERC20Allowance: boolean,
|
||||
): Promise<ClaimVerification> {
|
||||
const addressToClaim = await contractWrapper.getSignerAddress();
|
||||
@@ -131,7 +131,7 @@ type Snapshot =
|
||||
export async function fetchSnapshot(
|
||||
merkleRoot: string,
|
||||
merkleMetadata: Record<string, string> | undefined,
|
||||
storage: IStorage,
|
||||
storage: ThirdwebStorage,
|
||||
): Promise<Snapshot> {
|
||||
if (!merkleMetadata) {
|
||||
return undefined;
|
||||
@@ -139,7 +139,7 @@ export async function fetchSnapshot(
|
||||
const snapshotUri = merkleMetadata[merkleRoot];
|
||||
let snapshot: Snapshot = undefined;
|
||||
if (snapshotUri) {
|
||||
const raw = await storage.get(snapshotUri);
|
||||
const raw = await storage.downloadJSON(snapshotUri);
|
||||
const snapshotData = SnapshotSchema.parse(raw);
|
||||
if (merkleRoot === snapshotData.merkleRoot) {
|
||||
snapshot = snapshotData.claims;
|
||||
@@ -211,7 +211,7 @@ export async function getClaimerProofs(
|
||||
merkleRoot: string,
|
||||
tokenDecimals: number,
|
||||
merkleMetadata: Record<string, string>,
|
||||
storage: IStorage,
|
||||
storage: ThirdwebStorage,
|
||||
): Promise<{ maxClaimable: BigNumber; proof: string[] }> {
|
||||
const claims: Snapshot = await fetchSnapshot(
|
||||
merkleRoot,
|
||||
@@ -252,7 +252,7 @@ export async function processClaimConditionInputs(
|
||||
claimConditionInputs: ClaimConditionInput[],
|
||||
tokenDecimals: number,
|
||||
provider: providers.Provider,
|
||||
storage: IStorage,
|
||||
storage: ThirdwebStorage,
|
||||
) {
|
||||
const snapshotInfos: SnapshotInfo[] = [];
|
||||
const inputsWithSnapshots = await Promise.all(
|
||||
@@ -355,7 +355,7 @@ export async function transformResultToClaimCondition(
|
||||
tokenDecimals: number,
|
||||
provider: providers.Provider,
|
||||
merkleMetadata: Record<string, string> | undefined,
|
||||
storage: IStorage,
|
||||
storage: ThirdwebStorage,
|
||||
): Promise<ClaimCondition> {
|
||||
const cv = await fetchCurrencyValue(provider, pm.currency, pm.pricePerToken);
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ import {
|
||||
PublishedMetadata,
|
||||
} from "../schema/contracts/custom";
|
||||
import { ExtensionNotImplementedError } from "./error";
|
||||
import { IStorage } from "@thirdweb-dev/storage";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
import { BaseContract, ethers } from "ethers";
|
||||
import { z } from "zod";
|
||||
|
||||
@@ -57,7 +57,7 @@ function matchesAbiInterface(
|
||||
*/
|
||||
export async function extractConstructorParams(
|
||||
predeployMetadataUri: string,
|
||||
storage: IStorage,
|
||||
storage: ThirdwebStorage,
|
||||
) {
|
||||
const meta = await fetchPreDeployMetadata(predeployMetadataUri, storage);
|
||||
return extractConstructorParamsFromAbi(meta.abi);
|
||||
@@ -70,7 +70,7 @@ export async function extractConstructorParams(
|
||||
*/
|
||||
export async function extractFunctions(
|
||||
predeployMetadataUri: string,
|
||||
storage: IStorage,
|
||||
storage: ThirdwebStorage,
|
||||
): Promise<AbiFunction[]> {
|
||||
const metadata = await fetchPreDeployMetadata(predeployMetadataUri, storage);
|
||||
return extractFunctionsFromAbi(metadata.abi, metadata.metadata);
|
||||
@@ -328,7 +328,7 @@ function isHexStrict(hex: string | number) {
|
||||
export async function fetchContractMetadataFromAddress(
|
||||
address: string,
|
||||
provider: ethers.providers.Provider,
|
||||
storage: IStorage,
|
||||
storage: ThirdwebStorage,
|
||||
) {
|
||||
const compilerMetadataUri = await resolveContractUriFromAddress(
|
||||
address,
|
||||
@@ -347,9 +347,9 @@ export async function fetchContractMetadataFromAddress(
|
||||
*/
|
||||
export async function fetchContractMetadata(
|
||||
compilerMetadataUri: string,
|
||||
storage: IStorage,
|
||||
storage: ThirdwebStorage,
|
||||
): Promise<PublishedMetadata> {
|
||||
const metadata = await storage.get(compilerMetadataUri);
|
||||
const metadata = await storage.downloadJSON(compilerMetadataUri);
|
||||
const abi = AbiSchema.parse(metadata.output.abi);
|
||||
const compilationTarget = metadata.settings.compilationTarget;
|
||||
const targets = Object.keys(compilationTarget);
|
||||
@@ -381,7 +381,7 @@ export async function fetchContractMetadata(
|
||||
*/
|
||||
export async function fetchSourceFilesFromMetadata(
|
||||
publishedMetadata: PublishedMetadata,
|
||||
storage: IStorage,
|
||||
storage: ThirdwebStorage,
|
||||
): Promise<ContractSource[]> {
|
||||
return await Promise.all(
|
||||
Object.entries(publishedMetadata.metadata.sources).map(
|
||||
@@ -395,7 +395,7 @@ export async function fetchSourceFilesFromMetadata(
|
||||
setTimeout(() => rej("timeout"), 5000),
|
||||
);
|
||||
const source = await Promise.race([
|
||||
storage.getRaw(`ipfs://${ipfsHash}`),
|
||||
(await storage.download(`ipfs://${ipfsHash}`)).text(),
|
||||
timeout,
|
||||
]);
|
||||
return {
|
||||
@@ -420,10 +420,10 @@ export async function fetchSourceFilesFromMetadata(
|
||||
*/
|
||||
export async function fetchRawPredeployMetadata(
|
||||
publishMetadataUri: string,
|
||||
storage: IStorage,
|
||||
storage: ThirdwebStorage,
|
||||
) {
|
||||
return PreDeployMetadata.parse(
|
||||
JSON.parse(await storage.getRaw(publishMetadataUri)),
|
||||
JSON.parse(await (await storage.download(publishMetadataUri)).text()),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -435,10 +435,12 @@ export async function fetchRawPredeployMetadata(
|
||||
*/
|
||||
export async function fetchPreDeployMetadata(
|
||||
publishMetadataUri: string,
|
||||
storage: IStorage,
|
||||
storage: ThirdwebStorage,
|
||||
): Promise<PreDeployMetadataFetched> {
|
||||
const rawMeta = await fetchRawPredeployMetadata(publishMetadataUri, storage);
|
||||
const deployBytecode = await storage.getRaw(rawMeta.bytecodeUri);
|
||||
const deployBytecode = await (
|
||||
await storage.download(rawMeta.bytecodeUri)
|
||||
).text();
|
||||
const parsedMeta = await fetchContractMetadata(rawMeta.metadataUri, storage);
|
||||
return PreDeployMetadataFetchedSchema.parse({
|
||||
...rawMeta,
|
||||
@@ -455,9 +457,9 @@ export async function fetchPreDeployMetadata(
|
||||
*/
|
||||
export async function fetchExtendedReleaseMetadata(
|
||||
publishMetadataUri: string,
|
||||
storage: IStorage,
|
||||
storage: ThirdwebStorage,
|
||||
): Promise<FullPublishMetadata> {
|
||||
const meta = await storage.getRaw(publishMetadataUri);
|
||||
const meta = await (await storage.download(publishMetadataUri)).text();
|
||||
return FullPublishMetadataSchemaOutput.parse(JSON.parse(meta));
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@ import {
|
||||
NFTMetadataInput,
|
||||
NFTMetadataOrUri,
|
||||
} from "../schema/tokens/common";
|
||||
import { UploadProgressEvent } from "../types/index";
|
||||
import { NotFoundError } from "./error";
|
||||
import type {
|
||||
IERC1155Metadata,
|
||||
@@ -19,7 +18,10 @@ import type {
|
||||
import ERC165MetadataAbi from "@thirdweb-dev/contracts-js/dist/abis/IERC165.json";
|
||||
import ERC721MetadataAbi from "@thirdweb-dev/contracts-js/dist/abis/IERC721Metadata.json";
|
||||
import ERC1155MetadataAbi from "@thirdweb-dev/contracts-js/dist/abis/IERC1155Metadata.json";
|
||||
import type { IStorage } from "@thirdweb-dev/storage";
|
||||
import type {
|
||||
ThirdwebStorage,
|
||||
UploadProgressEvent,
|
||||
} from "@thirdweb-dev/storage";
|
||||
import { BigNumber, BigNumberish, Contract, ethers, providers } from "ethers";
|
||||
|
||||
const FALLBACK_METADATA = {
|
||||
@@ -37,7 +39,7 @@ const FALLBACK_METADATA = {
|
||||
export async function fetchTokenMetadata(
|
||||
tokenId: BigNumberish,
|
||||
tokenUri: string,
|
||||
storage: IStorage,
|
||||
storage: ThirdwebStorage,
|
||||
): Promise<NFTMetadata> {
|
||||
const parsedUri = tokenUri.replace(
|
||||
"{id}",
|
||||
@@ -45,15 +47,15 @@ export async function fetchTokenMetadata(
|
||||
);
|
||||
let jsonMetadata;
|
||||
try {
|
||||
jsonMetadata = await storage.get(parsedUri);
|
||||
jsonMetadata = await storage.downloadJSON(parsedUri);
|
||||
} catch (err) {
|
||||
const unparsedTokenIdUri = tokenUri.replace(
|
||||
"{id}",
|
||||
BigNumber.from(tokenId).toString(),
|
||||
);
|
||||
try {
|
||||
jsonMetadata = await storage.get(unparsedTokenIdUri);
|
||||
} catch (e) {
|
||||
jsonMetadata = await storage.downloadJSON(unparsedTokenIdUri);
|
||||
} catch (e: any) {
|
||||
console.warn(
|
||||
`failed to get token metadata: ${JSON.stringify({
|
||||
tokenId: tokenId.toString(),
|
||||
@@ -83,7 +85,7 @@ export async function fetchTokenMetadataForContract(
|
||||
contractAddress: string,
|
||||
provider: providers.Provider,
|
||||
tokenId: BigNumberish,
|
||||
storage: IStorage,
|
||||
storage: ThirdwebStorage,
|
||||
) {
|
||||
let uri: string | undefined;
|
||||
const erc165 = new Contract(
|
||||
@@ -123,12 +125,12 @@ export async function fetchTokenMetadataForContract(
|
||||
*/
|
||||
export async function uploadOrExtractURI(
|
||||
metadata: NFTMetadataOrUri,
|
||||
storage: IStorage,
|
||||
storage: ThirdwebStorage,
|
||||
): Promise<string> {
|
||||
if (typeof metadata === "string") {
|
||||
return metadata;
|
||||
} else {
|
||||
return await storage.uploadMetadata(CommonNFTInput.parse(metadata));
|
||||
return await storage.upload(CommonNFTInput.parse(metadata));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,10 +145,8 @@ export async function uploadOrExtractURI(
|
||||
*/
|
||||
export async function uploadOrExtractURIs(
|
||||
metadatas: NFTMetadataOrUri[],
|
||||
storage: IStorage,
|
||||
storage: ThirdwebStorage,
|
||||
startNumber?: number,
|
||||
contractAddress?: string,
|
||||
signerAddress?: string,
|
||||
options?: {
|
||||
onProgress: (event: UploadProgressEvent) => void;
|
||||
},
|
||||
@@ -154,12 +154,14 @@ export async function uploadOrExtractURIs(
|
||||
if (isUriList(metadatas)) {
|
||||
return metadatas;
|
||||
} else if (isMetadataList(metadatas)) {
|
||||
const { uris } = await storage.uploadMetadataBatch(
|
||||
const uris = await storage.uploadBatch(
|
||||
metadatas.map((m) => CommonNFTInput.parse(m)),
|
||||
startNumber,
|
||||
contractAddress,
|
||||
signerAddress,
|
||||
options,
|
||||
{
|
||||
rewriteFileNames: {
|
||||
fileStartNumber: startNumber || 0,
|
||||
},
|
||||
onProgress: options?.onProgress,
|
||||
},
|
||||
);
|
||||
return uris;
|
||||
} else {
|
||||
@@ -169,6 +171,21 @@ export async function uploadOrExtractURIs(
|
||||
}
|
||||
}
|
||||
|
||||
export function getBaseUriFromBatch(uris: string[]): string {
|
||||
const baseUri = uris[0].substring(0, uris[0].lastIndexOf("/"));
|
||||
for (let i = 0; i < uris.length; i++) {
|
||||
const uri = uris[i].substring(0, uris[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}'`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure that baseUri ends with trailing slash
|
||||
return baseUri.replace(/\/$/, "") + "/";
|
||||
}
|
||||
|
||||
function isUriList(metadatas: NFTMetadataOrUri[]): metadatas is string[] {
|
||||
return metadatas.find((m) => typeof m !== "string") === undefined;
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
SnapshotInput,
|
||||
} from "../types/claim-conditions/claim-conditions";
|
||||
import { DuplicateLeafsError } from "./error";
|
||||
import { IStorage } from "@thirdweb-dev/storage";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
import { BigNumber, BigNumberish, utils } from "ethers";
|
||||
|
||||
/**
|
||||
@@ -21,7 +21,7 @@ import { BigNumber, BigNumberish, utils } from "ethers";
|
||||
export async function createSnapshot(
|
||||
snapshotInput: SnapshotInput,
|
||||
tokenDecimals: number,
|
||||
storage: IStorage,
|
||||
storage: ThirdwebStorage,
|
||||
): Promise<SnapshotInfo> {
|
||||
const input = SnapshotInputSchema.parse(snapshotInput);
|
||||
const addresses = input.map((i) => i.address);
|
||||
@@ -51,7 +51,7 @@ export async function createSnapshot(
|
||||
}),
|
||||
});
|
||||
|
||||
const uri = await storage.uploadMetadata(snapshot);
|
||||
const uri = await storage.upload(snapshot);
|
||||
return {
|
||||
merkleRoot: tree.getHexRoot(),
|
||||
snapshotUri: uri,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { SignerOrProvider } from "../core/types";
|
||||
import { StaticJsonRpcBatchProvider } from "../lib/static-batch-rpc";
|
||||
import { ethers, providers } from "ethers";
|
||||
|
||||
/**
|
||||
@@ -134,7 +135,12 @@ export function getReadOnlyProvider(network: string, chainId?: number) {
|
||||
if (match) {
|
||||
switch (match[1]) {
|
||||
case "http":
|
||||
return new providers.JsonRpcBatchProvider(network, chainId);
|
||||
return chainId
|
||||
? // if we know the chainId we should use the StaticJsonRpcBatchProvider
|
||||
new StaticJsonRpcBatchProvider(network, chainId)
|
||||
: // otherwise fall back to the built in json rpc batch provider
|
||||
new providers.JsonRpcBatchProvider(network, chainId);
|
||||
|
||||
case "ws":
|
||||
return new providers.WebSocketProvider(network, chainId);
|
||||
default:
|
||||
|
||||
@@ -15,12 +15,12 @@ import {
|
||||
import { CustomContractSchema } from "../schema/contracts/custom";
|
||||
import { DropErc20ContractSchema } from "../schema/contracts/drop-erc20";
|
||||
import { MultiwrapContractSchema } from "../schema/contracts/multiwrap";
|
||||
import type { IStorage } from "@thirdweb-dev/storage";
|
||||
import type { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
|
||||
type InitalizeParams = [
|
||||
network: NetworkOrSignerOrProvider,
|
||||
address: string,
|
||||
storage: IStorage,
|
||||
storage: ThirdwebStorage,
|
||||
options?: SDKOptions,
|
||||
];
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ import { NFTMetadata, NFTMetadataOrUri } from "../../schema/tokens/common";
|
||||
import { QueryAllParams, UploadProgressEvent } from "../../types";
|
||||
import type { DropERC1155 } from "@thirdweb-dev/contracts-js";
|
||||
import type ABI from "@thirdweb-dev/contracts-js/dist/abis/DropERC1155.json";
|
||||
import { IStorage } from "@thirdweb-dev/storage";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
import { BigNumber, BigNumberish, CallOverrides, constants } from "ethers";
|
||||
|
||||
/**
|
||||
@@ -113,7 +113,7 @@ export class EditionDropImpl extends StandardErc1155<DropERC1155> {
|
||||
constructor(
|
||||
network: NetworkOrSignerOrProvider,
|
||||
address: string,
|
||||
storage: IStorage,
|
||||
storage: ThirdwebStorage,
|
||||
options: SDKOptions = {},
|
||||
abi: typeof ABI,
|
||||
contractWrapper = new ContractWrapper<DropERC1155>(
|
||||
|
||||
@@ -27,7 +27,7 @@ import {
|
||||
import { QueryAllParams } from "../../types";
|
||||
import type { TokenERC1155 } from "@thirdweb-dev/contracts-js";
|
||||
import type ABI from "@thirdweb-dev/contracts-js/dist/abis/TokenERC1155.json";
|
||||
import { IStorage } from "@thirdweb-dev/storage";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
import { BigNumber, BigNumberish, CallOverrides, constants } from "ethers";
|
||||
|
||||
/**
|
||||
@@ -103,7 +103,7 @@ export class EditionImpl extends StandardErc1155<TokenERC1155> {
|
||||
constructor(
|
||||
network: NetworkOrSignerOrProvider,
|
||||
address: string,
|
||||
storage: IStorage,
|
||||
storage: ThirdwebStorage,
|
||||
options: SDKOptions = {},
|
||||
abi: typeof ABI,
|
||||
contractWrapper = new ContractWrapper<TokenERC1155>(
|
||||
|
||||
@@ -20,7 +20,7 @@ import { AuctionListing, DirectListing } from "../../types/marketplace";
|
||||
import { MarketplaceFilter } from "../../types/marketplace/MarketPlaceFilter";
|
||||
import type { Marketplace as MarketplaceContract } from "@thirdweb-dev/contracts-js";
|
||||
import type ABI from "@thirdweb-dev/contracts-js/dist/abis/Marketplace.json";
|
||||
import { IStorage } from "@thirdweb-dev/storage";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
import { BigNumber, BigNumberish, CallOverrides, constants } from "ethers";
|
||||
import invariant from "tiny-invariant";
|
||||
|
||||
@@ -43,7 +43,7 @@ export class MarketplaceImpl implements UpdateableNetwork {
|
||||
|
||||
public abi: typeof ABI;
|
||||
private contractWrapper: ContractWrapper<MarketplaceContract>;
|
||||
private storage: IStorage;
|
||||
private storage: ThirdwebStorage;
|
||||
|
||||
public encoder: ContractEncoder<MarketplaceContract>;
|
||||
public events: ContractEvents<MarketplaceContract>;
|
||||
@@ -135,7 +135,7 @@ export class MarketplaceImpl implements UpdateableNetwork {
|
||||
constructor(
|
||||
network: NetworkOrSignerOrProvider,
|
||||
address: string,
|
||||
storage: IStorage,
|
||||
storage: ThirdwebStorage,
|
||||
options: SDKOptions = {},
|
||||
abi: typeof ABI,
|
||||
contractWrapper = new ContractWrapper<MarketplaceContract>(
|
||||
|
||||
@@ -33,7 +33,7 @@ import {
|
||||
ITokenBundle,
|
||||
TokensWrappedEvent,
|
||||
} from "@thirdweb-dev/contracts-js/dist/declarations/src/Multiwrap";
|
||||
import { IStorage } from "@thirdweb-dev/storage";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
import { BigNumberish, CallOverrides, ethers } from "ethers";
|
||||
|
||||
/**
|
||||
@@ -91,7 +91,7 @@ export class MultiwrapImpl extends StandardErc721<MultiwrapContract> {
|
||||
constructor(
|
||||
network: NetworkOrSignerOrProvider,
|
||||
address: string,
|
||||
storage: IStorage,
|
||||
storage: ThirdwebStorage,
|
||||
options: SDKOptions = {},
|
||||
abi: typeof ABI,
|
||||
contractWrapper = new ContractWrapper<MultiwrapContract>(
|
||||
|
||||
@@ -22,7 +22,7 @@ import { TokenErc721ContractSchema } from "../../schema/contracts/token-erc721";
|
||||
import { SDKOptions } from "../../schema/sdk-options";
|
||||
import type { TokenERC721 } from "@thirdweb-dev/contracts-js";
|
||||
import type ABI from "@thirdweb-dev/contracts-js/dist/abis/TokenERC721.json";
|
||||
import { IStorage } from "@thirdweb-dev/storage";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
import { BigNumberish, CallOverrides, constants } from "ethers";
|
||||
|
||||
/**
|
||||
@@ -103,7 +103,7 @@ export class NFTCollectionImpl extends StandardErc721<TokenERC721> {
|
||||
constructor(
|
||||
network: NetworkOrSignerOrProvider,
|
||||
address: string,
|
||||
storage: IStorage,
|
||||
storage: ThirdwebStorage,
|
||||
options: SDKOptions = {},
|
||||
abi: typeof ABI,
|
||||
contractWrapper = new ContractWrapper<TokenERC721>(
|
||||
|
||||
@@ -41,7 +41,7 @@ import {
|
||||
TokensClaimedEvent,
|
||||
TokensLazyMintedEvent,
|
||||
} from "@thirdweb-dev/contracts-js/dist/declarations/src/DropERC721";
|
||||
import { IStorage } from "@thirdweb-dev/storage";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
import {
|
||||
BigNumber,
|
||||
BigNumberish,
|
||||
@@ -166,7 +166,7 @@ export class NFTDropImpl extends StandardErc721<DropERC721> {
|
||||
constructor(
|
||||
network: NetworkOrSignerOrProvider,
|
||||
address: string,
|
||||
storage: IStorage,
|
||||
storage: ThirdwebStorage,
|
||||
options: SDKOptions = {},
|
||||
abi: typeof ABI,
|
||||
contractWrapper = new ContractWrapper<DropERC721>(
|
||||
@@ -394,8 +394,6 @@ export class NFTDropImpl extends StandardErc721<DropERC721> {
|
||||
metadatas,
|
||||
this.storage,
|
||||
startFileNumber.toNumber(),
|
||||
this.contractWrapper.readContract.address,
|
||||
await this.contractWrapper.getSigner()?.getAddress(),
|
||||
options,
|
||||
);
|
||||
// ensure baseUri is the same for the entire batch
|
||||
|
||||
@@ -39,7 +39,7 @@ import {
|
||||
PackCreatedEvent,
|
||||
PackOpenedEvent,
|
||||
} from "@thirdweb-dev/contracts-js/dist/declarations/src/Pack";
|
||||
import { IStorage } from "@thirdweb-dev/storage";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
import { BigNumber, BigNumberish, CallOverrides, ethers } from "ethers";
|
||||
|
||||
/**
|
||||
@@ -96,7 +96,7 @@ export class PackImpl extends StandardErc1155<PackContract> {
|
||||
constructor(
|
||||
network: NetworkOrSignerOrProvider,
|
||||
address: string,
|
||||
storage: IStorage,
|
||||
storage: ThirdwebStorage,
|
||||
options: SDKOptions = {},
|
||||
abi: typeof ABI,
|
||||
contractWrapper = new ContractWrapper<PackContract>(
|
||||
|
||||
@@ -35,7 +35,7 @@ import {
|
||||
} from "../../types/QueryParams";
|
||||
import type { SignatureDrop as SignatureDropContract } from "@thirdweb-dev/contracts-js";
|
||||
import type ABI from "@thirdweb-dev/contracts-js/dist/abis/SignatureDrop.json";
|
||||
import { IStorage } from "@thirdweb-dev/storage";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
import { BigNumber, BigNumberish, CallOverrides, constants } from "ethers";
|
||||
|
||||
/**
|
||||
@@ -164,7 +164,7 @@ export class SignatureDropImpl extends StandardErc721<SignatureDropContract> {
|
||||
constructor(
|
||||
network: NetworkOrSignerOrProvider,
|
||||
address: string,
|
||||
storage: IStorage,
|
||||
storage: ThirdwebStorage,
|
||||
options: SDKOptions = {},
|
||||
abi: typeof ABI,
|
||||
contractWrapper = new ContractWrapper<SignatureDropContract>(
|
||||
|
||||
@@ -18,7 +18,7 @@ import type {
|
||||
} from "@thirdweb-dev/contracts-js";
|
||||
import ERC20Abi from "@thirdweb-dev/contracts-js/dist/abis/IERC20.json";
|
||||
import type ABI from "@thirdweb-dev/contracts-js/dist/abis/Split.json";
|
||||
import { IStorage } from "@thirdweb-dev/storage";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
import { BigNumber, CallOverrides, Contract } from "ethers";
|
||||
|
||||
/**
|
||||
@@ -39,7 +39,7 @@ export class SplitImpl implements UpdateableNetwork {
|
||||
static contractRoles = ["admin"] as const;
|
||||
|
||||
private contractWrapper: ContractWrapper<SplitContract>;
|
||||
private storage: IStorage;
|
||||
private storage: ThirdwebStorage;
|
||||
|
||||
public abi: typeof ABI;
|
||||
public metadata: ContractMetadata<SplitContract, typeof SplitsContractSchema>;
|
||||
@@ -58,7 +58,7 @@ export class SplitImpl implements UpdateableNetwork {
|
||||
constructor(
|
||||
network: NetworkOrSignerOrProvider,
|
||||
address: string,
|
||||
storage: IStorage,
|
||||
storage: ThirdwebStorage,
|
||||
options: SDKOptions = {},
|
||||
abi: typeof ABI,
|
||||
contractWrapper = new ContractWrapper<SplitContract>(
|
||||
|
||||
@@ -16,7 +16,7 @@ import { SDKOptions } from "../../schema/sdk-options";
|
||||
import { Amount, CurrencyValue } from "../../types";
|
||||
import type { DropERC20 } from "@thirdweb-dev/contracts-js";
|
||||
import type ABI from "@thirdweb-dev/contracts-js/dist/abis/DropERC20.json";
|
||||
import { IStorage } from "@thirdweb-dev/storage";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
import { CallOverrides, constants } from "ethers";
|
||||
|
||||
/**
|
||||
@@ -77,7 +77,7 @@ export class TokenDropImpl extends StandardErc20<DropERC20> {
|
||||
constructor(
|
||||
network: NetworkOrSignerOrProvider,
|
||||
address: string,
|
||||
storage: IStorage,
|
||||
storage: ThirdwebStorage,
|
||||
options: SDKOptions = {},
|
||||
abi: typeof ABI,
|
||||
contractWrapper = new ContractWrapper<DropERC20>(
|
||||
|
||||
@@ -18,7 +18,7 @@ import { TokenMintInput } from "../../schema/tokens/token";
|
||||
import { Amount, CurrencyValue } from "../../types";
|
||||
import type { TokenERC20 } from "@thirdweb-dev/contracts-js";
|
||||
import type ABI from "@thirdweb-dev/contracts-js/dist/abis/TokenERC20.json";
|
||||
import { IStorage } from "@thirdweb-dev/storage";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
import { CallOverrides, constants } from "ethers";
|
||||
|
||||
/**
|
||||
@@ -75,7 +75,7 @@ export class TokenImpl extends StandardErc20<TokenERC20> {
|
||||
constructor(
|
||||
network: NetworkOrSignerOrProvider,
|
||||
address: string,
|
||||
storage: IStorage,
|
||||
storage: ThirdwebStorage,
|
||||
options: SDKOptions = {},
|
||||
abi: typeof ABI,
|
||||
contractWrapper = new ContractWrapper<TokenERC20>(
|
||||
|
||||
@@ -28,7 +28,7 @@ import type { IERC20, VoteERC20 } from "@thirdweb-dev/contracts-js";
|
||||
import ERC20Abi from "@thirdweb-dev/contracts-js/dist/abis/IERC20.json";
|
||||
import type ABI from "@thirdweb-dev/contracts-js/dist/abis/VoteERC20.json";
|
||||
import { ProposalCreatedEvent } from "@thirdweb-dev/contracts-js/dist/declarations/src/VoteERC20";
|
||||
import { IStorage } from "@thirdweb-dev/storage";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
import {
|
||||
BigNumber,
|
||||
BigNumberish,
|
||||
@@ -53,7 +53,7 @@ import {
|
||||
*/
|
||||
export class VoteImpl implements UpdateableNetwork {
|
||||
private contractWrapper: ContractWrapper<VoteERC20>;
|
||||
private storage: IStorage;
|
||||
private storage: ThirdwebStorage;
|
||||
|
||||
public abi: typeof ABI;
|
||||
public metadata: ContractMetadata<VoteERC20, typeof VoteContractSchema>;
|
||||
@@ -68,7 +68,7 @@ export class VoteImpl implements UpdateableNetwork {
|
||||
constructor(
|
||||
network: NetworkOrSignerOrProvider,
|
||||
address: string,
|
||||
storage: IStorage,
|
||||
storage: ThirdwebStorage,
|
||||
options: SDKOptions = {},
|
||||
abi: typeof ABI,
|
||||
contractWrapper = new ContractWrapper<VoteERC20>(
|
||||
|
||||
@@ -38,7 +38,7 @@ import type {
|
||||
IRoyalty,
|
||||
Ownable,
|
||||
} from "@thirdweb-dev/contracts-js";
|
||||
import { IStorage } from "@thirdweb-dev/storage";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
import { BaseContract, CallOverrides, ContractInterface } from "ethers";
|
||||
|
||||
/**
|
||||
@@ -142,7 +142,7 @@ export class SmartContract<TContract extends BaseContract = BaseContract>
|
||||
network: NetworkOrSignerOrProvider,
|
||||
address: string,
|
||||
abi: ContractInterface,
|
||||
storage: IStorage,
|
||||
storage: ThirdwebStorage,
|
||||
options: SDKOptions = {},
|
||||
contractWrapper = new ContractWrapper<TContract>(
|
||||
network,
|
||||
|
||||
@@ -39,7 +39,7 @@ import {
|
||||
import { ContractFactory } from "./factory";
|
||||
import { ContractRegistry } from "./registry";
|
||||
import { RPCConnectionHandler } from "./rpc-connection-handler";
|
||||
import { IStorage } from "@thirdweb-dev/storage";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
import { BigNumber, BytesLike, ContractInterface, ethers } from "ethers";
|
||||
import invariant from "tiny-invariant";
|
||||
import { z } from "zod";
|
||||
@@ -59,12 +59,12 @@ export class ContractDeployer extends RPCConnectionHandler {
|
||||
* should never be accessed directly, use {@link ContractDeployer.getRegistry} instead
|
||||
*/
|
||||
private _registry: Promise<ContractRegistry> | undefined;
|
||||
private storage: IStorage;
|
||||
private storage: ThirdwebStorage;
|
||||
|
||||
constructor(
|
||||
network: NetworkOrSignerOrProvider,
|
||||
options: SDKOptions,
|
||||
storage: IStorage,
|
||||
storage: ThirdwebStorage,
|
||||
) {
|
||||
super(network, options);
|
||||
this.storage = storage;
|
||||
|
||||
@@ -64,9 +64,9 @@ export class ContractEvents<TContract extends BaseContract> {
|
||||
* @param listener - the callback function that will be called on every new event
|
||||
* @returns a function to un-subscribe from the event
|
||||
*/
|
||||
public addEventListener(
|
||||
public addEventListener<TEvent extends Record<string, any>>(
|
||||
eventName: keyof TContract["filters"] | (string & {}),
|
||||
listener: (event: ContractEvent) => void,
|
||||
listener: (event: ContractEvent<TEvent>) => void,
|
||||
) {
|
||||
// validates event, throws error if not found
|
||||
const event = this.contractWrapper.readContract.interface.getEvent(
|
||||
@@ -109,7 +109,9 @@ export class ContractEvents<TContract extends BaseContract> {
|
||||
* @param listener - the callback function that will be called on every new event
|
||||
* @returns A function that can be called to stop listening to events
|
||||
*/
|
||||
public listenToAllEvents(listener: (event: ContractEvent) => void) {
|
||||
public listenToAllEvents<TEvent extends Record<string, any>>(
|
||||
listener: (event: ContractEvent<TEvent>) => void,
|
||||
) {
|
||||
const address = this.contractWrapper.readContract.address;
|
||||
const filter = { address };
|
||||
|
||||
@@ -119,7 +121,11 @@ export class ContractEvents<TContract extends BaseContract> {
|
||||
this.contractWrapper.readContract.interface.parseLog(log);
|
||||
|
||||
listener(
|
||||
this.toContractEvent(parsedLog.eventFragment, parsedLog.args, log),
|
||||
this.toContractEvent<TEvent>(
|
||||
parsedLog.eventFragment,
|
||||
parsedLog.args,
|
||||
log,
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
console.error("Could not parse event:", log, e);
|
||||
@@ -191,13 +197,13 @@ export class ContractEvents<TContract extends BaseContract> {
|
||||
* @param filters - Specify the from and to block numbers to get events for, defaults to all blocks
|
||||
* @returns The event objects of the events emitted with event names and data for each event
|
||||
*/
|
||||
public async getAllEvents(
|
||||
public async getAllEvents<TEvent extends Record<string, any>>(
|
||||
filters: EventQueryFilter = {
|
||||
fromBlock: 0,
|
||||
toBlock: "latest",
|
||||
order: "desc",
|
||||
},
|
||||
): Promise<ContractEvent[]> {
|
||||
): Promise<ContractEvent<TEvent>[]> {
|
||||
const events = await this.contractWrapper.readContract.queryFilter(
|
||||
{},
|
||||
filters.fromBlock,
|
||||
@@ -234,14 +240,14 @@ export class ContractEvents<TContract extends BaseContract> {
|
||||
* @param filters - Specify the from and to block numbers to get events for, defaults to all blocks. @see EventQueryFilter
|
||||
* @returns The requested event objects with event data
|
||||
*/
|
||||
public async getEvents(
|
||||
public async getEvents<TEvent extends Record<string, any>>(
|
||||
eventName: string,
|
||||
filters: EventQueryFilter = {
|
||||
fromBlock: 0,
|
||||
toBlock: "latest",
|
||||
order: "desc",
|
||||
},
|
||||
): Promise<ContractEvent[]> {
|
||||
): Promise<ContractEvent<TEvent>[]> {
|
||||
const event = this.contractWrapper.readContract.interface.getEvent(
|
||||
eventName as string,
|
||||
);
|
||||
@@ -262,7 +268,9 @@ export class ContractEvents<TContract extends BaseContract> {
|
||||
return this.parseEvents(orderedEvents);
|
||||
}
|
||||
|
||||
private parseEvents(events: Event[]): ContractEvent[] {
|
||||
private parseEvents<TEvent extends Record<string, any>>(
|
||||
events: Event[],
|
||||
): ContractEvent<TEvent>[] {
|
||||
return events.map((e) => {
|
||||
const transaction = Object.fromEntries(
|
||||
Object.entries(e).filter(
|
||||
@@ -280,30 +288,30 @@ export class ContractEvents<TContract extends BaseContract> {
|
||||
|
||||
return {
|
||||
eventName: e.event || "",
|
||||
data,
|
||||
data: data as TEvent,
|
||||
transaction,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
eventName: e.event || "",
|
||||
data: {},
|
||||
data: {} as TEvent,
|
||||
transaction,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
private toContractEvent(
|
||||
private toContractEvent<TEvent extends Record<string, any>>(
|
||||
event: EventFragment,
|
||||
args: ReadonlyArray<any>,
|
||||
rawLog: providers.Log,
|
||||
): ContractEvent {
|
||||
): ContractEvent<TEvent> {
|
||||
const transaction = Object.fromEntries(
|
||||
Object.entries(rawLog).filter(
|
||||
(a) => typeof a[1] !== "function" && a[0] !== "args",
|
||||
),
|
||||
) as providers.Log;
|
||||
const results: Record<string, unknown> = {};
|
||||
const results: Record<string, any> = {};
|
||||
event.inputs.forEach((param, index) => {
|
||||
if (Array.isArray(args[index])) {
|
||||
const obj: Record<string, unknown> = {};
|
||||
@@ -322,7 +330,7 @@ export class ContractEvents<TContract extends BaseContract> {
|
||||
});
|
||||
return {
|
||||
eventName: event.name,
|
||||
data: results,
|
||||
data: results as TEvent,
|
||||
transaction,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import type {
|
||||
IContractMetadata,
|
||||
IERC20Metadata,
|
||||
} from "@thirdweb-dev/contracts-js";
|
||||
import { IStorage } from "@thirdweb-dev/storage";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
import { BaseContract } from "ethers";
|
||||
import { z } from "zod";
|
||||
|
||||
@@ -52,7 +52,7 @@ export class ContractMetadata<
|
||||
constructor(
|
||||
contractWrapper: ContractWrapper<TContract>,
|
||||
schema: TSchema,
|
||||
storage: IStorage,
|
||||
storage: ThirdwebStorage,
|
||||
) {
|
||||
this.contractWrapper = contractWrapper;
|
||||
this.schema = schema;
|
||||
@@ -87,7 +87,7 @@ export class ContractMetadata<
|
||||
if (this.supportsContractMetadata(this.contractWrapper)) {
|
||||
const uri = await this.contractWrapper.readContract.contractURI();
|
||||
if (uri && uri.includes("://")) {
|
||||
data = await this.storage.get(uri);
|
||||
data = await this.storage.downloadJSON(uri);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,7 +175,7 @@ export class ContractMetadata<
|
||||
*/
|
||||
public async _parseAndUploadMetadata(metadata: z.input<TSchema["input"]>) {
|
||||
const parsedMetadata = this.parseInputMetadata(metadata);
|
||||
return this.storage.uploadMetadata(parsedMetadata);
|
||||
return this.storage.upload(parsedMetadata);
|
||||
}
|
||||
|
||||
private supportsContractMetadata(
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
PublishedMetadata,
|
||||
} from "../../schema/contracts/custom";
|
||||
import { ContractWrapper } from "./contract-wrapper";
|
||||
import { IStorage } from "@thirdweb-dev/storage";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
import { BaseContract } from "ethers";
|
||||
|
||||
/**
|
||||
@@ -19,11 +19,14 @@ import { BaseContract } from "ethers";
|
||||
*/
|
||||
export class ContractPublishedMetadata<TContract extends BaseContract> {
|
||||
private contractWrapper;
|
||||
private storage: IStorage;
|
||||
private storage: ThirdwebStorage;
|
||||
|
||||
private _cachedMetadata: PublishedMetadata | undefined;
|
||||
|
||||
constructor(contractWrapper: ContractWrapper<TContract>, storage: IStorage) {
|
||||
constructor(
|
||||
contractWrapper: ContractWrapper<TContract>,
|
||||
storage: ThirdwebStorage,
|
||||
) {
|
||||
this.contractWrapper = contractWrapper;
|
||||
this.storage = storage;
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
fetchPreDeployMetadata,
|
||||
fetchRawPredeployMetadata,
|
||||
fetchSourceFilesFromMetadata,
|
||||
resolveContractUriFromAddress,
|
||||
} from "../../common/feature-detection";
|
||||
import { isIncrementalVersion } from "../../common/version-checker";
|
||||
import { getContractPublisherAddress } from "../../constants";
|
||||
@@ -34,7 +35,7 @@ import type {
|
||||
} from "@thirdweb-dev/contracts-js";
|
||||
import ContractPublisherAbi from "@thirdweb-dev/contracts-js/dist/abis/ContractPublisher.json";
|
||||
import { ContractPublishedEvent } from "@thirdweb-dev/contracts-js/dist/declarations/src/ContractPublisher";
|
||||
import { IStorage } from "@thirdweb-dev/storage";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
import { constants, utils } from "ethers";
|
||||
import invariant from "tiny-invariant";
|
||||
|
||||
@@ -43,13 +44,13 @@ import invariant from "tiny-invariant";
|
||||
* @internal
|
||||
*/
|
||||
export class ContractPublisher extends RPCConnectionHandler {
|
||||
private storage: IStorage;
|
||||
private storage: ThirdwebStorage;
|
||||
private publisher: ContractWrapper<OnChainContractPublisher>;
|
||||
|
||||
constructor(
|
||||
network: NetworkOrSignerOrProvider,
|
||||
options: SDKOptions,
|
||||
storage: IStorage,
|
||||
storage: ThirdwebStorage,
|
||||
) {
|
||||
super(network, options);
|
||||
this.storage = storage;
|
||||
@@ -190,6 +191,19 @@ export class ContractPublisher extends RPCConnectionHandler {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* TODO clean this up (see method above, too)
|
||||
*/
|
||||
public async resolveReleasesFromAddress(address: string) {
|
||||
const contractUri = await resolveContractUriFromAddress(
|
||||
address,
|
||||
this.getProvider(),
|
||||
);
|
||||
invariant(contractUri, "Could not resolve contract URI from address");
|
||||
return await this.resolvePublishMetadataFromCompilerMetadata(contractUri);
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @param address
|
||||
@@ -211,7 +225,7 @@ export class ContractPublisher extends RPCConnectionHandler {
|
||||
const signer = this.getSigner();
|
||||
invariant(signer, "A signer is required");
|
||||
const publisher = await signer.getAddress();
|
||||
const profileUri = await this.storage.uploadMetadata(profileMetadata);
|
||||
const profileUri = await this.storage.upload(profileMetadata);
|
||||
return {
|
||||
receipt: await this.publisher.sendTransaction("setPublisherProfileUri", [
|
||||
publisher,
|
||||
@@ -233,7 +247,9 @@ export class ContractPublisher extends RPCConnectionHandler {
|
||||
if (!profileUri || profileUri.length === 0) {
|
||||
return {};
|
||||
}
|
||||
return ProfileSchemaOutput.parse(await this.storage.get(profileUri));
|
||||
return ProfileSchemaOutput.parse(
|
||||
await this.storage.downloadJSON(profileUri),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -321,9 +337,9 @@ export class ContractPublisher extends RPCConnectionHandler {
|
||||
}
|
||||
}
|
||||
|
||||
const fetchedBytecode = await this.storage.getRaw(
|
||||
predeployMetadata.bytecodeUri,
|
||||
);
|
||||
const fetchedBytecode = await (
|
||||
await this.storage.download(predeployMetadata.bytecodeUri)
|
||||
).text();
|
||||
const bytecode = fetchedBytecode.startsWith("0x")
|
||||
? fetchedBytecode
|
||||
: `0x${fetchedBytecode}`;
|
||||
@@ -339,7 +355,7 @@ export class ContractPublisher extends RPCConnectionHandler {
|
||||
analytics: predeployMetadata.analytics,
|
||||
publisher,
|
||||
});
|
||||
const fullMetadataUri = await this.storage.uploadMetadata(fullMetadata);
|
||||
const fullMetadataUri = await this.storage.upload(fullMetadata);
|
||||
const receipt = await this.publisher.sendTransaction("publishContract", [
|
||||
publisher,
|
||||
contractId,
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import { hasFunction } from "../../common";
|
||||
import { fetchTokenMetadataForContract } from "../../common/nft";
|
||||
import {
|
||||
fetchTokenMetadataForContract,
|
||||
getBaseUriFromBatch,
|
||||
} from "../../common/nft";
|
||||
import { FeatureName } from "../../constants/contract-features";
|
||||
import {
|
||||
CommonNFTInput,
|
||||
@@ -21,7 +24,7 @@ import type {
|
||||
} from "@thirdweb-dev/contracts-js";
|
||||
import DeprecatedAbi from "@thirdweb-dev/contracts-js/dist/abis/IDelayedRevealDeprecated.json";
|
||||
import { TokensLazyMintedEvent } from "@thirdweb-dev/contracts-js/dist/declarations/src/DropERC721";
|
||||
import { IStorage } from "@thirdweb-dev/storage";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
import { BigNumber, BigNumberish, ethers } from "ethers";
|
||||
|
||||
/**
|
||||
@@ -38,12 +41,12 @@ export class DelayedReveal<
|
||||
featureName;
|
||||
|
||||
private contractWrapper: ContractWrapper<T>;
|
||||
private storage: IStorage;
|
||||
private storage: ThirdwebStorage;
|
||||
private nextTokenIdToMintFn: () => Promise<BigNumber>;
|
||||
|
||||
constructor(
|
||||
contractWrapper: ContractWrapper<T>,
|
||||
storage: IStorage,
|
||||
storage: ThirdwebStorage,
|
||||
fetureName: FeatureName,
|
||||
nextTokenIdToMintFn: () => Promise<BigNumber>,
|
||||
) {
|
||||
@@ -98,26 +101,29 @@ export class DelayedReveal<
|
||||
throw new Error("Password is required");
|
||||
}
|
||||
|
||||
const { baseUri: placeholderUri } = await this.storage.uploadMetadataBatch(
|
||||
const placeholderUris = await this.storage.uploadBatch(
|
||||
[CommonNFTInput.parse(placeholder)],
|
||||
0,
|
||||
this.contractWrapper.readContract.address,
|
||||
await this.contractWrapper.getSigner()?.getAddress(),
|
||||
{
|
||||
rewriteFileNames: {
|
||||
fileStartNumber: 0,
|
||||
},
|
||||
},
|
||||
);
|
||||
const placeholderUri = getBaseUriFromBatch(placeholderUris);
|
||||
|
||||
const startFileNumber = await this.nextTokenIdToMintFn();
|
||||
|
||||
const batch = await this.storage.uploadMetadataBatch(
|
||||
const uris = await this.storage.uploadBatch(
|
||||
metadatas.map((m) => CommonNFTInput.parse(m)),
|
||||
startFileNumber.toNumber(),
|
||||
this.contractWrapper.readContract.address,
|
||||
await this.contractWrapper.getSigner()?.getAddress(),
|
||||
options,
|
||||
{
|
||||
onProgress: options?.onProgress,
|
||||
rewriteFileNames: {
|
||||
fileStartNumber: startFileNumber.toNumber(),
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const baseUri = batch.baseUri.endsWith("/")
|
||||
? batch.baseUri
|
||||
: `${batch.baseUri}/`;
|
||||
const baseUri = getBaseUriFromBatch(uris);
|
||||
const baseUriId = await this.contractWrapper.readContract.getBaseURICount();
|
||||
const hashedPassword = await this.hashDelayRevealPasword(
|
||||
baseUriId,
|
||||
@@ -146,7 +152,7 @@ export class DelayedReveal<
|
||||
}
|
||||
|
||||
const receipt = await this.contractWrapper.sendTransaction("lazyMint", [
|
||||
batch.uris.length,
|
||||
uris.length,
|
||||
placeholderUri.endsWith("/") ? placeholderUri : `${placeholderUri}/`,
|
||||
data,
|
||||
]);
|
||||
|
||||
@@ -36,7 +36,7 @@ import type {
|
||||
} 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 { IStorage } from "@thirdweb-dev/storage";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
import { BigNumber, BigNumberish, constants, ethers, utils } from "ethers";
|
||||
import deepEqual from "fast-deep-equal";
|
||||
|
||||
@@ -54,12 +54,12 @@ export class DropClaimConditions<
|
||||
> {
|
||||
private contractWrapper;
|
||||
private metadata;
|
||||
private storage: IStorage;
|
||||
private storage: ThirdwebStorage;
|
||||
|
||||
constructor(
|
||||
contractWrapper: ContractWrapper<TContract>,
|
||||
metadata: ContractMetadata<TContract, any>,
|
||||
storage: IStorage,
|
||||
storage: ThirdwebStorage,
|
||||
) {
|
||||
this.storage = storage;
|
||||
this.contractWrapper = contractWrapper;
|
||||
|
||||
@@ -29,7 +29,7 @@ import type {
|
||||
} 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 { IStorage } from "@thirdweb-dev/storage";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
import { BigNumber, BigNumberish, constants, ethers, utils } from "ethers";
|
||||
import deepEqual from "fast-deep-equal";
|
||||
|
||||
@@ -42,12 +42,12 @@ export class DropErc1155ClaimConditions<
|
||||
> {
|
||||
private contractWrapper;
|
||||
private metadata;
|
||||
private storage: IStorage;
|
||||
private storage: ThirdwebStorage;
|
||||
|
||||
constructor(
|
||||
contractWrapper: ContractWrapper<TContract>,
|
||||
metadata: ContractMetadata<TContract, any>,
|
||||
storage: IStorage,
|
||||
storage: ThirdwebStorage,
|
||||
) {
|
||||
this.storage = storage;
|
||||
this.contractWrapper = contractWrapper;
|
||||
|
||||
@@ -7,7 +7,7 @@ import { ContractWrapper } from "./contract-wrapper";
|
||||
import { Erc1155 } from "./erc-1155";
|
||||
import type { IMintableERC1155, IMulticall } from "@thirdweb-dev/contracts-js";
|
||||
import { TokensMintedEvent } from "@thirdweb-dev/contracts-js/dist/declarations/src/TokenERC1155";
|
||||
import { IStorage } from "@thirdweb-dev/storage";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
import { ethers } from "ethers";
|
||||
|
||||
/**
|
||||
@@ -24,12 +24,12 @@ export class Erc1155BatchMintable implements DetectableFeature {
|
||||
featureName = FEATURE_EDITION_BATCH_MINTABLE.name;
|
||||
private contractWrapper: ContractWrapper<IMintableERC1155 & IMulticall>;
|
||||
private erc1155: Erc1155;
|
||||
private storage: IStorage;
|
||||
private storage: ThirdwebStorage;
|
||||
|
||||
constructor(
|
||||
erc1155: Erc1155,
|
||||
contractWrapper: ContractWrapper<IMintableERC1155 & IMulticall>,
|
||||
storage: IStorage,
|
||||
storage: ThirdwebStorage,
|
||||
) {
|
||||
this.erc1155 = erc1155;
|
||||
this.contractWrapper = contractWrapper;
|
||||
|
||||
@@ -13,7 +13,7 @@ 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 { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
import { BigNumberish, ethers } from "ethers";
|
||||
|
||||
/**
|
||||
@@ -31,11 +31,11 @@ export class Erc1155ClaimableWithConditions implements DetectableFeature {
|
||||
|
||||
public conditions: DropErc1155ClaimConditions<BaseClaimConditionERC1155>;
|
||||
private contractWrapper: ContractWrapper<BaseClaimConditionERC1155>;
|
||||
private storage: IStorage;
|
||||
private storage: ThirdwebStorage;
|
||||
|
||||
constructor(
|
||||
contractWrapper: ContractWrapper<BaseClaimConditionERC1155>,
|
||||
storage: IStorage,
|
||||
storage: ThirdwebStorage,
|
||||
) {
|
||||
this.contractWrapper = contractWrapper;
|
||||
this.storage = storage;
|
||||
|
||||
@@ -26,7 +26,7 @@ import type {
|
||||
TokenERC721,
|
||||
} from "@thirdweb-dev/contracts-js";
|
||||
import { TokensLazyMintedEvent } from "@thirdweb-dev/contracts-js/dist/declarations/src/LazyMint";
|
||||
import { IStorage } from "@thirdweb-dev/storage";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
import { ethers } from "ethers";
|
||||
|
||||
export class Erc1155LazyMintable implements DetectableFeature {
|
||||
@@ -80,12 +80,12 @@ export class Erc1155LazyMintable implements DetectableFeature {
|
||||
|
||||
private contractWrapper: ContractWrapper<BaseDropERC1155>;
|
||||
private erc1155: Erc1155;
|
||||
private storage: IStorage;
|
||||
private storage: ThirdwebStorage;
|
||||
|
||||
constructor(
|
||||
erc1155: Erc1155,
|
||||
contractWrapper: ContractWrapper<BaseDropERC1155>,
|
||||
storage: IStorage,
|
||||
storage: ThirdwebStorage,
|
||||
) {
|
||||
this.erc1155 = erc1155;
|
||||
this.contractWrapper = contractWrapper;
|
||||
@@ -133,8 +133,6 @@ export class Erc1155LazyMintable implements DetectableFeature {
|
||||
metadatas,
|
||||
this.storage,
|
||||
startFileNumber.toNumber(),
|
||||
this.contractWrapper.readContract.address,
|
||||
await this.contractWrapper.getSigner()?.getAddress(),
|
||||
options,
|
||||
);
|
||||
// ensure baseUri is the same for the entire batch
|
||||
|
||||
@@ -9,7 +9,7 @@ import { Erc1155 } from "./erc-1155";
|
||||
import { Erc1155BatchMintable } from "./erc-1155-batch-mintable";
|
||||
import type { IMintableERC1155, IMulticall } from "@thirdweb-dev/contracts-js";
|
||||
import { TransferSingleEvent } from "@thirdweb-dev/contracts-js/dist/declarations/src/ITokenERC1155";
|
||||
import { IStorage } from "@thirdweb-dev/storage";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
import { BigNumber, BigNumberish, ethers } from "ethers";
|
||||
|
||||
/**
|
||||
@@ -26,7 +26,7 @@ export class Erc1155Mintable implements DetectableFeature {
|
||||
featureName = FEATURE_EDITION_MINTABLE.name;
|
||||
private contractWrapper: ContractWrapper<IMintableERC1155>;
|
||||
private erc1155: Erc1155;
|
||||
private storage: IStorage;
|
||||
private storage: ThirdwebStorage;
|
||||
|
||||
/**
|
||||
* Batch mint Tokens to many addresses
|
||||
@@ -36,7 +36,7 @@ export class Erc1155Mintable implements DetectableFeature {
|
||||
constructor(
|
||||
erc1155: Erc1155,
|
||||
contractWrapper: ContractWrapper<IMintableERC1155>,
|
||||
storage: IStorage,
|
||||
storage: ThirdwebStorage,
|
||||
) {
|
||||
this.erc1155 = erc1155;
|
||||
this.contractWrapper = contractWrapper;
|
||||
|
||||
@@ -19,7 +19,7 @@ import { ContractRoles } from "./contract-roles";
|
||||
import { ContractWrapper } from "./contract-wrapper";
|
||||
import type { ITokenERC1155, TokenERC1155 } from "@thirdweb-dev/contracts-js";
|
||||
import { TokensMintedWithSignatureEvent } from "@thirdweb-dev/contracts-js/dist/declarations/src/ITokenERC1155";
|
||||
import { IStorage } from "@thirdweb-dev/storage";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
import { BigNumber, ethers } from "ethers";
|
||||
import invariant from "tiny-invariant";
|
||||
|
||||
@@ -33,14 +33,14 @@ export class Erc1155SignatureMintable implements DetectableFeature {
|
||||
private contractWrapper: ContractWrapper<
|
||||
BaseSignatureMintERC1155 | TokenERC1155
|
||||
>;
|
||||
private storage: IStorage;
|
||||
private storage: ThirdwebStorage;
|
||||
private roles:
|
||||
| ContractRoles<TokenERC1155, typeof NFTCollection.roles[number]>
|
||||
| undefined;
|
||||
|
||||
constructor(
|
||||
contractWrapper: ContractWrapper<BaseSignatureMintERC1155 | TokenERC1155>,
|
||||
storage: IStorage,
|
||||
storage: ThirdwebStorage,
|
||||
roles?: ContractRoles<TokenERC1155, typeof NFTCollection.roles[number]>,
|
||||
) {
|
||||
this.contractWrapper = contractWrapper;
|
||||
|
||||
@@ -6,7 +6,7 @@ import { NetworkOrSignerOrProvider, TransactionResult } from "../types";
|
||||
import { ContractWrapper } from "./contract-wrapper";
|
||||
import { Erc1155 } from "./erc-1155";
|
||||
import type { DropERC1155, TokenERC1155 } from "@thirdweb-dev/contracts-js";
|
||||
import { IStorage } from "@thirdweb-dev/storage";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
import { BigNumber, BigNumberish, BytesLike } from "ethers";
|
||||
|
||||
/**
|
||||
@@ -26,10 +26,10 @@ export class StandardErc1155<
|
||||
> implements UpdateableNetwork
|
||||
{
|
||||
protected contractWrapper: ContractWrapper<T>;
|
||||
protected storage: IStorage;
|
||||
protected storage: ThirdwebStorage;
|
||||
public erc1155: Erc1155;
|
||||
|
||||
constructor(contractWrapper: ContractWrapper<T>, storage: IStorage) {
|
||||
constructor(contractWrapper: ContractWrapper<T>, storage: ThirdwebStorage) {
|
||||
this.contractWrapper = contractWrapper;
|
||||
this.storage = storage;
|
||||
this.erc1155 = new Erc1155(this.contractWrapper, this.storage);
|
||||
|
||||
@@ -54,7 +54,7 @@ import type {
|
||||
IMintableERC1155,
|
||||
TokenERC1155,
|
||||
} from "@thirdweb-dev/contracts-js";
|
||||
import { IStorage } from "@thirdweb-dev/storage";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
import { BigNumber, BigNumberish, BytesLike } from "ethers";
|
||||
|
||||
/**
|
||||
@@ -81,9 +81,9 @@ export class Erc1155<
|
||||
private signatureMintable: Erc1155SignatureMintable | undefined;
|
||||
|
||||
protected contractWrapper: ContractWrapper<T>;
|
||||
protected storage: IStorage;
|
||||
protected storage: ThirdwebStorage;
|
||||
|
||||
constructor(contractWrapper: ContractWrapper<T>, storage: IStorage) {
|
||||
constructor(contractWrapper: ContractWrapper<T>, storage: ThirdwebStorage) {
|
||||
this.contractWrapper = contractWrapper;
|
||||
this.storage = storage;
|
||||
this.query = this.detectErc1155Enumerable();
|
||||
|
||||
@@ -9,7 +9,7 @@ import { ContractMetadata } from "./contract-metadata";
|
||||
import { ContractWrapper } from "./contract-wrapper";
|
||||
import { DropClaimConditions } from "./drop-claim-conditions";
|
||||
import { Erc20 } from "./erc-20";
|
||||
import { IStorage } from "@thirdweb-dev/storage";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
|
||||
/**
|
||||
* Configure and claim ERC20 tokens
|
||||
@@ -47,12 +47,12 @@ export class Erc20Claimable implements DetectableFeature {
|
||||
public conditions: DropClaimConditions<BaseDropERC20>;
|
||||
private contractWrapper: ContractWrapper<BaseDropERC20>;
|
||||
private erc20: Erc20;
|
||||
private storage: IStorage;
|
||||
private storage: ThirdwebStorage;
|
||||
|
||||
constructor(
|
||||
erc20: Erc20,
|
||||
contractWrapper: ContractWrapper<BaseDropERC20>,
|
||||
storage: IStorage,
|
||||
storage: ThirdwebStorage,
|
||||
) {
|
||||
this.erc20 = erc20;
|
||||
this.contractWrapper = contractWrapper;
|
||||
|
||||
@@ -2,7 +2,7 @@ import { BaseDropERC20 } from "../../types/eips";
|
||||
import { ContractWrapper } from "./contract-wrapper";
|
||||
import { Erc20 } from "./erc-20";
|
||||
import { Erc20Claimable } from "./erc-20-claimable";
|
||||
import { IStorage } from "@thirdweb-dev/storage";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
|
||||
/**
|
||||
* Configure and claim ERC20 tokens
|
||||
@@ -39,12 +39,12 @@ export class Erc20Droppable {
|
||||
public claim: Erc20Claimable;
|
||||
private contractWrapper: ContractWrapper<BaseDropERC20>;
|
||||
private erc20: Erc20;
|
||||
private storage: IStorage;
|
||||
private storage: ThirdwebStorage;
|
||||
|
||||
constructor(
|
||||
erc20: Erc20,
|
||||
contractWrapper: ContractWrapper<BaseDropERC20>,
|
||||
storage: IStorage,
|
||||
storage: ThirdwebStorage,
|
||||
) {
|
||||
this.erc20 = erc20;
|
||||
this.contractWrapper = contractWrapper;
|
||||
|
||||
@@ -3,11 +3,10 @@ import { Amount, Currency, CurrencyValue } from "../../types/currency";
|
||||
import { BaseERC20, BaseSignatureMintERC20 } from "../../types/eips";
|
||||
import { UpdateableNetwork } from "../interfaces/contract";
|
||||
import { NetworkOrSignerOrProvider, TransactionResult } from "../types";
|
||||
import { ContractPrimarySale } from "./contract-sales";
|
||||
import { ContractWrapper } from "./contract-wrapper";
|
||||
import { Erc20 } from "./erc-20";
|
||||
import type { DropERC20, TokenERC20 } from "@thirdweb-dev/contracts-js";
|
||||
import { IStorage } from "@thirdweb-dev/storage";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
|
||||
/**
|
||||
* Standard ERC20 Token functions
|
||||
@@ -26,11 +25,11 @@ export class StandardErc20<
|
||||
> implements UpdateableNetwork
|
||||
{
|
||||
protected contractWrapper: ContractWrapper<T>;
|
||||
protected storage: IStorage;
|
||||
protected storage: ThirdwebStorage;
|
||||
|
||||
public erc20: Erc20<T>;
|
||||
|
||||
constructor(contractWrapper: ContractWrapper<T>, storage: IStorage) {
|
||||
constructor(contractWrapper: ContractWrapper<T>, storage: ThirdwebStorage) {
|
||||
this.contractWrapper = contractWrapper;
|
||||
this.storage = storage;
|
||||
this.erc20 = new Erc20(this.contractWrapper, this.storage);
|
||||
|
||||
@@ -37,7 +37,7 @@ import type {
|
||||
IMintableERC20,
|
||||
IBurnableERC20,
|
||||
} from "@thirdweb-dev/contracts-js";
|
||||
import { IStorage } from "@thirdweb-dev/storage";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
import { ethers, BigNumber, BigNumberish } from "ethers";
|
||||
|
||||
/**
|
||||
@@ -65,9 +65,9 @@ export class Erc20<
|
||||
private droppable: Erc20Droppable | undefined;
|
||||
private signatureMintable: Erc20SignatureMintable | undefined;
|
||||
protected contractWrapper: ContractWrapper<T>;
|
||||
protected storage: IStorage;
|
||||
protected storage: ThirdwebStorage;
|
||||
|
||||
constructor(contractWrapper: ContractWrapper<T>, storage: IStorage) {
|
||||
constructor(contractWrapper: ContractWrapper<T>, storage: ThirdwebStorage) {
|
||||
this.contractWrapper = contractWrapper;
|
||||
this.storage = storage;
|
||||
this.mintable = this.detectErc20Mintable();
|
||||
|
||||
@@ -7,7 +7,7 @@ import { ContractWrapper } from "./contract-wrapper";
|
||||
import { Erc721 } from "./erc-721";
|
||||
import type { IMintableERC721, IMulticall } from "@thirdweb-dev/contracts-js";
|
||||
import { TokensMintedEvent } from "@thirdweb-dev/contracts-js/dist/declarations/src/IMintableERC721";
|
||||
import { IStorage } from "@thirdweb-dev/storage";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
|
||||
/**
|
||||
* Mint Many ERC721 NFTs at once
|
||||
@@ -22,13 +22,13 @@ import { IStorage } from "@thirdweb-dev/storage";
|
||||
export class Erc721BatchMintable implements DetectableFeature {
|
||||
featureName = FEATURE_NFT_BATCH_MINTABLE.name;
|
||||
private contractWrapper: ContractWrapper<IMintableERC721 & IMulticall>;
|
||||
private storage: IStorage;
|
||||
private storage: ThirdwebStorage;
|
||||
private erc721: Erc721;
|
||||
|
||||
constructor(
|
||||
erc721: Erc721,
|
||||
contractWrapper: ContractWrapper<IMintableERC721 & IMulticall>,
|
||||
storage: IStorage,
|
||||
storage: ThirdwebStorage,
|
||||
) {
|
||||
this.erc721 = erc721;
|
||||
this.contractWrapper = contractWrapper;
|
||||
|
||||
@@ -13,7 +13,7 @@ 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 { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
import { BigNumber, BigNumberish, ethers } from "ethers";
|
||||
|
||||
/**
|
||||
@@ -54,12 +54,12 @@ export class Erc721ClaimableWithConditions implements DetectableFeature {
|
||||
public conditions: DropClaimConditions<BaseClaimConditionERC721>;
|
||||
private contractWrapper: ContractWrapper<BaseClaimConditionERC721>;
|
||||
private erc721: Erc721;
|
||||
private storage: IStorage;
|
||||
private storage: ThirdwebStorage;
|
||||
|
||||
constructor(
|
||||
erc721: Erc721,
|
||||
contractWrapper: ContractWrapper<BaseClaimConditionERC721>,
|
||||
storage: IStorage,
|
||||
storage: ThirdwebStorage,
|
||||
) {
|
||||
this.erc721 = erc721;
|
||||
this.contractWrapper = contractWrapper;
|
||||
|
||||
@@ -2,7 +2,7 @@ import {
|
||||
detectContractFeature,
|
||||
hasFunction,
|
||||
} from "../../common/feature-detection";
|
||||
import { uploadOrExtractURIs } from "../../common/nft";
|
||||
import { getBaseUriFromBatch, uploadOrExtractURIs } from "../../common/nft";
|
||||
import {
|
||||
FEATURE_NFT_LAZY_MINTABLE,
|
||||
FEATURE_NFT_REVEALABLE,
|
||||
@@ -23,7 +23,7 @@ import { Erc721Claimable } from "./erc-721-claimable";
|
||||
import { Erc721ClaimableWithConditions } from "./erc-721-claimable-with-conditions";
|
||||
import type { IClaimableERC721 } from "@thirdweb-dev/contracts-js";
|
||||
import { TokensLazyMintedEvent } from "@thirdweb-dev/contracts-js/dist/declarations/src/LazyMint";
|
||||
import { IStorage } from "@thirdweb-dev/storage";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
import { ethers } from "ethers";
|
||||
|
||||
/**
|
||||
@@ -85,12 +85,12 @@ export class Erc721LazyMintable implements DetectableFeature {
|
||||
|
||||
private contractWrapper: ContractWrapper<BaseDropERC721>;
|
||||
private erc721: Erc721;
|
||||
private storage: IStorage;
|
||||
private storage: ThirdwebStorage;
|
||||
|
||||
constructor(
|
||||
erc721: Erc721,
|
||||
contractWrapper: ContractWrapper<BaseDropERC721>,
|
||||
storage: IStorage,
|
||||
storage: ThirdwebStorage,
|
||||
) {
|
||||
this.erc721 = erc721;
|
||||
this.contractWrapper = contractWrapper;
|
||||
@@ -138,20 +138,11 @@ export class Erc721LazyMintable implements DetectableFeature {
|
||||
metadatas,
|
||||
this.storage,
|
||||
startFileNumber.toNumber(),
|
||||
this.contractWrapper.readContract.address,
|
||||
await this.contractWrapper.getSigner()?.getAddress(),
|
||||
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 baseUri = getBaseUriFromBatch(batch);
|
||||
|
||||
const receipt = await this.contractWrapper.sendTransaction("lazyMint", [
|
||||
batch.length,
|
||||
baseUri.endsWith("/") ? baseUri : `${baseUri}/`,
|
||||
|
||||
@@ -9,7 +9,7 @@ import { Erc721 } from "./erc-721";
|
||||
import { Erc721BatchMintable } from "./erc-721-batch-mintable";
|
||||
import type { IMintableERC721, IMulticall } from "@thirdweb-dev/contracts-js";
|
||||
import { TransferEvent } from "@thirdweb-dev/contracts-js/dist/declarations/src/ITokenERC721";
|
||||
import { IStorage } from "@thirdweb-dev/storage";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
|
||||
/**
|
||||
* Mint ERC721 NFTs
|
||||
@@ -24,7 +24,7 @@ import { IStorage } from "@thirdweb-dev/storage";
|
||||
export class Erc721Mintable implements DetectableFeature {
|
||||
featureName = FEATURE_NFT_MINTABLE.name;
|
||||
private contractWrapper: ContractWrapper<IMintableERC721>;
|
||||
private storage: IStorage;
|
||||
private storage: ThirdwebStorage;
|
||||
private erc721: Erc721;
|
||||
|
||||
public batch: Erc721BatchMintable | undefined;
|
||||
@@ -32,7 +32,7 @@ export class Erc721Mintable implements DetectableFeature {
|
||||
constructor(
|
||||
erc721: Erc721,
|
||||
contractWrapper: ContractWrapper<IMintableERC721>,
|
||||
storage: IStorage,
|
||||
storage: ThirdwebStorage,
|
||||
) {
|
||||
this.erc721 = erc721;
|
||||
this.contractWrapper = contractWrapper;
|
||||
|
||||
@@ -10,7 +10,7 @@ import type {
|
||||
SignatureDrop,
|
||||
TokenERC721,
|
||||
} from "@thirdweb-dev/contracts-js";
|
||||
import { IStorage } from "@thirdweb-dev/storage";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
import { BigNumber, BigNumberish } from "ethers";
|
||||
|
||||
/**
|
||||
@@ -28,10 +28,10 @@ export class StandardErc721<
|
||||
> implements UpdateableNetwork
|
||||
{
|
||||
protected contractWrapper: ContractWrapper<T>;
|
||||
protected storage: IStorage;
|
||||
protected storage: ThirdwebStorage;
|
||||
public erc721: Erc721<T>;
|
||||
|
||||
constructor(contractWrapper: ContractWrapper<T>, storage: IStorage) {
|
||||
constructor(contractWrapper: ContractWrapper<T>, storage: ThirdwebStorage) {
|
||||
this.contractWrapper = contractWrapper;
|
||||
this.storage = storage;
|
||||
this.erc721 = new Erc721(this.contractWrapper, this.storage);
|
||||
|
||||
@@ -21,7 +21,7 @@ import type {
|
||||
TokenERC721,
|
||||
} from "@thirdweb-dev/contracts-js";
|
||||
import { TokensMintedWithSignatureEvent } from "@thirdweb-dev/contracts-js/dist/declarations/src/SignatureDrop";
|
||||
import { IStorage } from "@thirdweb-dev/storage";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
import { BigNumber, ethers } from "ethers";
|
||||
import invariant from "tiny-invariant";
|
||||
|
||||
@@ -34,11 +34,11 @@ export class Erc721WithQuantitySignatureMintable implements DetectableFeature {
|
||||
|
||||
private contractWrapper: ContractWrapper<SignatureMintERC721 | TokenERC721>;
|
||||
|
||||
private storage: IStorage;
|
||||
private storage: ThirdwebStorage;
|
||||
|
||||
constructor(
|
||||
contractWrapper: ContractWrapper<SignatureMintERC721 | TokenERC721>,
|
||||
storage: IStorage,
|
||||
storage: ThirdwebStorage,
|
||||
) {
|
||||
this.contractWrapper = contractWrapper;
|
||||
this.storage = storage;
|
||||
|
||||
@@ -52,7 +52,7 @@ import type {
|
||||
SignatureDrop,
|
||||
TokenERC721,
|
||||
} from "@thirdweb-dev/contracts-js";
|
||||
import { IStorage } from "@thirdweb-dev/storage";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
import { BigNumber, BigNumberish, constants } from "ethers";
|
||||
|
||||
/**
|
||||
@@ -81,9 +81,9 @@ export class Erc721<
|
||||
private lazyMintable: Erc721LazyMintable | undefined;
|
||||
private signatureMintable: Erc721WithQuantitySignatureMintable | undefined;
|
||||
protected contractWrapper: ContractWrapper<T>;
|
||||
protected storage: IStorage;
|
||||
protected storage: ThirdwebStorage;
|
||||
|
||||
constructor(contractWrapper: ContractWrapper<T>, storage: IStorage) {
|
||||
constructor(contractWrapper: ContractWrapper<T>, storage: ThirdwebStorage) {
|
||||
this.contractWrapper = contractWrapper;
|
||||
this.storage = storage;
|
||||
this.query = this.detectErc721Enumerable();
|
||||
|
||||
@@ -25,7 +25,7 @@ import { ContractWrapper } from "./contract-wrapper";
|
||||
import type { TWFactory } from "@thirdweb-dev/contracts-js";
|
||||
import TWFactoryAbi from "@thirdweb-dev/contracts-js/dist/abis/TWFactory.json";
|
||||
import { ProxyDeployedEvent } from "@thirdweb-dev/contracts-js/dist/declarations/src/TWFactory";
|
||||
import { IStorage } from "@thirdweb-dev/storage";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
import {
|
||||
BigNumber,
|
||||
constants,
|
||||
@@ -39,12 +39,12 @@ import { z } from "zod";
|
||||
* @internal
|
||||
*/
|
||||
export class ContractFactory extends ContractWrapper<TWFactory> {
|
||||
private storage: IStorage;
|
||||
private storage: ThirdwebStorage;
|
||||
|
||||
constructor(
|
||||
factoryAddr: string,
|
||||
network: NetworkOrSignerOrProvider,
|
||||
storage: IStorage,
|
||||
storage: ThirdwebStorage,
|
||||
options?: SDKOptions,
|
||||
) {
|
||||
super(network, factoryAddr, TWFactoryAbi, options);
|
||||
@@ -61,11 +61,7 @@ export class ContractFactory extends ContractWrapper<TWFactory> {
|
||||
const metadata = contract.schema.deploy.parse(contractMetadata);
|
||||
|
||||
// TODO: is there any special pre-processing we need to do before uploading?
|
||||
const contractURI = await this.storage.uploadMetadata(
|
||||
metadata,
|
||||
this.readContract.address,
|
||||
await this.getSigner()?.getAddress(),
|
||||
);
|
||||
const contractURI = await this.storage.upload(metadata);
|
||||
|
||||
const ABI = await contract.getAbi();
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ import { TransactionResult, TransactionResultWithId } from "../types";
|
||||
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 { IStorage } from "@thirdweb-dev/storage";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
import { BigNumber, BigNumberish, ethers, constants } from "ethers";
|
||||
import invariant from "tiny-invariant";
|
||||
|
||||
@@ -38,11 +38,11 @@ import invariant from "tiny-invariant";
|
||||
*/
|
||||
export class MarketplaceAuction {
|
||||
private contractWrapper: ContractWrapper<Marketplace>;
|
||||
private storage: IStorage;
|
||||
private storage: ThirdwebStorage;
|
||||
|
||||
constructor(
|
||||
contractWrapper: ContractWrapper<Marketplace>,
|
||||
storage: IStorage,
|
||||
storage: ThirdwebStorage,
|
||||
) {
|
||||
this.contractWrapper = contractWrapper;
|
||||
this.storage = storage;
|
||||
|
||||
@@ -36,7 +36,7 @@ import ERC165Abi from "@thirdweb-dev/contracts-js/dist/abis/IERC165.json";
|
||||
import ERC721Abi from "@thirdweb-dev/contracts-js/dist/abis/IERC721.json";
|
||||
import ERC1155Abi from "@thirdweb-dev/contracts-js/dist/abis/IERC1155.json";
|
||||
import { ListingAddedEvent } from "@thirdweb-dev/contracts-js/dist/declarations/src/Marketplace";
|
||||
import { IStorage } from "@thirdweb-dev/storage";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
import {
|
||||
BigNumber,
|
||||
BigNumberish,
|
||||
@@ -53,11 +53,11 @@ import invariant from "tiny-invariant";
|
||||
*/
|
||||
export class MarketplaceDirect {
|
||||
private contractWrapper: ContractWrapper<Marketplace>;
|
||||
private storage: IStorage;
|
||||
private storage: ThirdwebStorage;
|
||||
|
||||
constructor(
|
||||
contractWrapper: ContractWrapper<Marketplace>,
|
||||
storage: IStorage,
|
||||
storage: ThirdwebStorage,
|
||||
) {
|
||||
this.contractWrapper = contractWrapper;
|
||||
this.storage = storage;
|
||||
|
||||
@@ -39,7 +39,7 @@ import type {
|
||||
} from "./types";
|
||||
import { UserWallet } from "./wallet/UserWallet";
|
||||
import IThirdwebContractABI from "@thirdweb-dev/contracts-js/dist/abis/IThirdwebContract.json";
|
||||
import { IpfsStorage, RemoteStorage, IStorage } from "@thirdweb-dev/storage";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
import { Contract, ContractInterface, ethers, Signer } from "ethers";
|
||||
import invariant from "tiny-invariant";
|
||||
|
||||
@@ -72,7 +72,7 @@ export class ThirdwebSDK extends RPCConnectionHandler {
|
||||
signer: Signer,
|
||||
network?: ChainOrRpc,
|
||||
options: SDKOptions = {},
|
||||
storage: IStorage = new IpfsStorage(),
|
||||
storage: ThirdwebStorage = new ThirdwebStorage(),
|
||||
): ThirdwebSDK {
|
||||
const sdk = new ThirdwebSDK(network || signer, options, storage);
|
||||
sdk.updateSignerOrProvider(signer);
|
||||
@@ -103,7 +103,7 @@ export class ThirdwebSDK extends RPCConnectionHandler {
|
||||
privateKey: string,
|
||||
network: ChainOrRpc,
|
||||
options: SDKOptions = {},
|
||||
storage: IStorage = new IpfsStorage(),
|
||||
storage: ThirdwebStorage = new ThirdwebStorage(),
|
||||
): ThirdwebSDK {
|
||||
const signerOrProvider = getProviderForNetwork(network);
|
||||
const provider = Signer.isSigner(signerOrProvider)
|
||||
@@ -128,7 +128,7 @@ export class ThirdwebSDK extends RPCConnectionHandler {
|
||||
/**
|
||||
* Internal handler for uploading and downloading files
|
||||
*/
|
||||
private storageHandler: IStorage;
|
||||
private storageHandler: ThirdwebStorage;
|
||||
/**
|
||||
* New contract deployer
|
||||
*/
|
||||
@@ -140,7 +140,7 @@ export class ThirdwebSDK extends RPCConnectionHandler {
|
||||
/**
|
||||
* Upload and download files from IPFS or from your own storage service
|
||||
*/
|
||||
public storage: RemoteStorage;
|
||||
public storage: ThirdwebStorage;
|
||||
/**
|
||||
* Enable authentication with the connected wallet
|
||||
*/
|
||||
@@ -149,12 +149,12 @@ export class ThirdwebSDK extends RPCConnectionHandler {
|
||||
constructor(
|
||||
network: ChainOrRpc | SignerOrProvider,
|
||||
options: SDKOptions = {},
|
||||
storage: IStorage = new IpfsStorage(),
|
||||
storage: ThirdwebStorage = new ThirdwebStorage(),
|
||||
) {
|
||||
const signerOrProvider = getProviderForNetwork(network);
|
||||
super(signerOrProvider, options);
|
||||
this.storageHandler = storage;
|
||||
this.storage = new RemoteStorage(storage);
|
||||
this.storage = storage;
|
||||
this.wallet = new UserWallet(signerOrProvider, options);
|
||||
this.deployer = new ContractDeployer(signerOrProvider, options, storage);
|
||||
this.auth = new WalletAuthenticator(signerOrProvider, this.wallet, options);
|
||||
@@ -409,8 +409,8 @@ export class ThirdwebSDK extends RPCConnectionHandler {
|
||||
}
|
||||
|
||||
private updateContractSignerOrProvider() {
|
||||
this.wallet.connect(this.getSignerOrProvider());
|
||||
this.auth.updateSignerOrProvider(this.getSignerOrProvider());
|
||||
this.wallet.onNetworkUpdated(this.getSignerOrProvider());
|
||||
this.deployer.updateSignerOrProvider(this.getSignerOrProvider());
|
||||
this._publisher.updateSignerOrProvider(this.getSignerOrProvider());
|
||||
for (const [, contract] of this.contractCache) {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import type { CONTRACTS_MAP, PREBUILT_CONTRACTS_MAP } from "../contracts";
|
||||
import type { SmartContract } from "../contracts/smart-contract";
|
||||
import { FileOrBuffer } from "@thirdweb-dev/storage";
|
||||
import { BigNumber, BytesLike, CallOverrides, Signer, providers } from "ethers";
|
||||
|
||||
// --- utility types extracted from from ts-toolbelt --- //
|
||||
@@ -47,16 +46,6 @@ export type ValueOf<T> = T[keyof T];
|
||||
|
||||
export type SignerOrProvider = Signer | providers.Provider;
|
||||
|
||||
export type BufferOrStringWithName = {
|
||||
data: Buffer | string;
|
||||
name?: string;
|
||||
};
|
||||
|
||||
type JsonLiteral = boolean | null | number | string;
|
||||
type JsonLiteralOrFileOrBuffer = JsonLiteral | FileOrBuffer;
|
||||
export type Json = JsonLiteralOrFileOrBuffer | JsonObject | Json[];
|
||||
export type JsonObject = { [key: string]: Json };
|
||||
|
||||
type TransactionResultWithMetadata<T = unknown> = {
|
||||
receipt: providers.TransactionReceipt;
|
||||
data: () => Promise<T>;
|
||||
|
||||
@@ -11,9 +11,23 @@ import { RPCConnectionHandler } from "../classes/rpc-connection-handler";
|
||||
import { NetworkOrSignerOrProvider, TransactionResult } from "../types";
|
||||
import type { IERC20 } from "@thirdweb-dev/contracts-js";
|
||||
import ERC20Abi from "@thirdweb-dev/contracts-js/dist/abis/IERC20.json";
|
||||
import { ethers, BigNumber, providers } from "ethers";
|
||||
import { ethers, BigNumber, providers, Signer } from "ethers";
|
||||
import EventEmitter from "eventemitter3";
|
||||
import invariant from "tiny-invariant";
|
||||
|
||||
/**
|
||||
*
|
||||
* {@link UserWallet} events that you can subscribe to using `sdk.wallet.events`.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface UserWalletEvents {
|
||||
/**
|
||||
* Emitted when `sdk.wallet.connect()` is called.
|
||||
*/
|
||||
signerChanged: [Signer | undefined];
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect and Interact with a user wallet
|
||||
* @example
|
||||
@@ -25,22 +39,24 @@ import invariant from "tiny-invariant";
|
||||
export class UserWallet {
|
||||
private connection: RPCConnectionHandler;
|
||||
private options: SDKOptions;
|
||||
public events = new EventEmitter<UserWalletEvents>();
|
||||
|
||||
constructor(network: NetworkOrSignerOrProvider, options: SDKOptions) {
|
||||
this.connection = new RPCConnectionHandler(network, options);
|
||||
this.options = options;
|
||||
this.events = new EventEmitter();
|
||||
}
|
||||
|
||||
// TODO connect()
|
||||
// TODO disconnect()
|
||||
// TODO switchChain()
|
||||
// TODO event listener
|
||||
// TODO tokens()
|
||||
// TODO NFTs()
|
||||
|
||||
// TODO this will become the source of truth of the signer and have every contract read from it
|
||||
onNetworkUpdated(network: NetworkOrSignerOrProvider): void {
|
||||
// TODO separate signer and provider logics
|
||||
public connect(network: NetworkOrSignerOrProvider) {
|
||||
this.connection.updateSignerOrProvider(network);
|
||||
this.events.emit("signerChanged", this.connection.getSigner());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
import { providers, utils } from "ethers";
|
||||
|
||||
|
||||
// mostly copied from ethers.js directly but make it a StaticJsonRpcProvider
|
||||
export class StaticJsonRpcBatchProvider extends providers.StaticJsonRpcProvider {
|
||||
_pendingBatchAggregator: NodeJS.Timer | null;
|
||||
_pendingBatch: Array<{
|
||||
request: { method: string, params: Array<any>, id: number, jsonrpc: "2.0" },
|
||||
resolve: (result: any) => void,
|
||||
reject: (error: Error) => void
|
||||
}> | null;
|
||||
|
||||
constructor(url: string | utils.ConnectionInfo | undefined, network: providers.Networkish | undefined){
|
||||
super(url, network);
|
||||
this._pendingBatchAggregator = null;
|
||||
this._pendingBatch = null;
|
||||
}
|
||||
|
||||
send(method: string, params: Array<any>): Promise<any> {
|
||||
const request = {
|
||||
method: method,
|
||||
params: params,
|
||||
id: (this._nextId++),
|
||||
jsonrpc: "2.0"
|
||||
};
|
||||
|
||||
if (this._pendingBatch === null) {
|
||||
this._pendingBatch = [ ];
|
||||
}
|
||||
|
||||
const inflightRequest: any = { request, resolve: null, reject: null };
|
||||
|
||||
const promise = new Promise((resolve, reject) => {
|
||||
inflightRequest.resolve = resolve;
|
||||
inflightRequest.reject = reject;
|
||||
});
|
||||
|
||||
this._pendingBatch.push(inflightRequest);
|
||||
|
||||
if (!this._pendingBatchAggregator) {
|
||||
// Schedule batch for next event loop + short duration
|
||||
this._pendingBatchAggregator = setTimeout(() => {
|
||||
|
||||
// Get the current batch and clear it, so new requests
|
||||
// go into the next batch
|
||||
const batch = this._pendingBatch || [];
|
||||
this._pendingBatch = null;
|
||||
this._pendingBatchAggregator = null;
|
||||
|
||||
// Get the request as an array of requests
|
||||
const request_ = batch.map((inflight) => inflight.request);
|
||||
|
||||
this.emit("debug", {
|
||||
action: "requestBatch",
|
||||
request: utils.deepCopy(request),
|
||||
provider: this
|
||||
});
|
||||
|
||||
return utils.fetchJson(this.connection, JSON.stringify(request_)).then((result) => {
|
||||
this.emit("debug", {
|
||||
action: "response",
|
||||
request: request_,
|
||||
response: result,
|
||||
provider: this
|
||||
});
|
||||
|
||||
// For each result, feed it to the correct Promise, depending
|
||||
// on whether it was a success or error
|
||||
batch.forEach((inflightRequest_, index) => {
|
||||
const payload = result[index];
|
||||
if (payload.error) {
|
||||
const error = new Error(payload.error.message);
|
||||
(<any>error).code = payload.error.code;
|
||||
(<any>error).data = payload.error.data;
|
||||
inflightRequest_.reject(error);
|
||||
} else {
|
||||
inflightRequest_.resolve(payload.result);
|
||||
}
|
||||
});
|
||||
|
||||
}, (error) => {
|
||||
this.emit("debug", {
|
||||
action: "response",
|
||||
error: error,
|
||||
request: request,
|
||||
provider: this
|
||||
});
|
||||
|
||||
batch.forEach((inflightRequest_) => {
|
||||
inflightRequest_.reject(error);
|
||||
});
|
||||
});
|
||||
|
||||
}, 10);
|
||||
}
|
||||
|
||||
return promise;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { AddressSchema, BasisPointsSchema, JsonSchema } from "../../shared";
|
||||
import { FileBufferOrStringSchema } from "@thirdweb-dev/storage";
|
||||
import { AddressSchema, BasisPointsSchema } from "../../shared";
|
||||
import { FileOrBufferOrStringSchema, JsonSchema } from "@thirdweb-dev/storage";
|
||||
import { constants } from "ethers";
|
||||
import { z } from "zod";
|
||||
|
||||
@@ -9,7 +9,7 @@ import { z } from "zod";
|
||||
export const CommonContractSchema = z.object({
|
||||
name: z.string(),
|
||||
description: z.string().optional(),
|
||||
image: FileBufferOrStringSchema.optional(),
|
||||
image: FileOrBufferOrStringSchema.optional(),
|
||||
external_link: z.string().url().optional(),
|
||||
});
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { toSemver } from "../../common/index";
|
||||
import { AddressSchema, BigNumberishSchema, JsonSchema } from "../shared";
|
||||
import { AddressSchema, BigNumberishSchema } from "../shared";
|
||||
import {
|
||||
CommonContractOutputSchema,
|
||||
CommonContractSchema,
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
CommonTrustedForwarderSchema,
|
||||
MerkleSchema,
|
||||
} from "./common";
|
||||
import { FileBufferOrStringSchema } from "@thirdweb-dev/storage";
|
||||
import { FileOrBufferOrStringSchema, JsonSchema } from "@thirdweb-dev/storage";
|
||||
import { BigNumberish } from "ethers";
|
||||
import { z } from "zod";
|
||||
|
||||
@@ -145,8 +145,8 @@ export const ExtraPublishMetadataSchemaInput = z
|
||||
license: z.string().optional(),
|
||||
changelog: z.string().optional(),
|
||||
tags: z.array(z.string()).optional(),
|
||||
audit: FileBufferOrStringSchema.nullable().optional(),
|
||||
logo: FileBufferOrStringSchema.nullable().optional(),
|
||||
audit: FileOrBufferOrStringSchema.nullable().optional(),
|
||||
logo: FileOrBufferOrStringSchema.nullable().optional(),
|
||||
isDeployableViaFactory: z.boolean().optional(),
|
||||
factoryDeploymentData: FactoryDeploymentSchema.optional(),
|
||||
})
|
||||
@@ -190,7 +190,7 @@ export type FullPublishMetadata = z.infer<
|
||||
export const ProfileSchemaInput = z.object({
|
||||
name: z.string().optional(),
|
||||
bio: z.string().optional(),
|
||||
avatar: FileBufferOrStringSchema.nullable().optional(),
|
||||
avatar: FileOrBufferOrStringSchema.nullable().optional(),
|
||||
website: z.string().optional(),
|
||||
twitter: z.string().optional(),
|
||||
telegram: z.string().optional(),
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Json } from "../core/types";
|
||||
import { Json } from "@thirdweb-dev/storage";
|
||||
import { BigNumber, CallOverrides, utils } from "ethers";
|
||||
import { z } from "zod";
|
||||
|
||||
@@ -31,17 +31,6 @@ export const PercentSchema = z
|
||||
.max(100, "Cannot exeed 100%")
|
||||
.min(0, "Cannot be below 0%");
|
||||
|
||||
export const JsonLiteral = z.union([
|
||||
z.string(),
|
||||
z.number(),
|
||||
z.boolean(),
|
||||
z.null(),
|
||||
]);
|
||||
|
||||
export const JsonSchema: z.ZodSchema<Json> = z.lazy(() =>
|
||||
z.union([JsonLiteral, z.array(JsonSchema), z.record(JsonSchema)]),
|
||||
);
|
||||
export const JsonObjectSchema = z.record(JsonSchema);
|
||||
export const HexColor = z.union([
|
||||
z
|
||||
.string()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { BigNumberSchema, HexColor, JsonSchema } from "../../shared";
|
||||
import { BigNumberSchema, HexColor } from "../../shared";
|
||||
import { OptionalPropertiesInput } from "./properties";
|
||||
import { FileBufferOrStringSchema } from "@thirdweb-dev/storage";
|
||||
import { FileOrBufferOrStringSchema, JsonSchema } from "@thirdweb-dev/storage";
|
||||
import { z } from "zod";
|
||||
|
||||
/**
|
||||
@@ -10,8 +10,8 @@ export const CommonTokenInput = z
|
||||
.object({
|
||||
name: z.union([z.string(), z.number()]).optional(),
|
||||
description: z.string().nullable().optional(),
|
||||
image: FileBufferOrStringSchema.nullable().optional(),
|
||||
external_url: FileBufferOrStringSchema.nullable().optional(),
|
||||
image: FileOrBufferOrStringSchema.nullable().optional(),
|
||||
external_url: FileOrBufferOrStringSchema.nullable().optional(),
|
||||
})
|
||||
.catchall(z.lazy(() => JsonSchema));
|
||||
|
||||
@@ -29,7 +29,7 @@ export const CommonTokenOutput = CommonTokenInput.extend({
|
||||
* @internal
|
||||
*/
|
||||
export const CommonNFTInput = CommonTokenInput.extend({
|
||||
animation_url: FileBufferOrStringSchema.optional(),
|
||||
animation_url: FileOrBufferOrStringSchema.optional(),
|
||||
background_color: HexColor.optional(),
|
||||
properties: OptionalPropertiesInput,
|
||||
attributes: OptionalPropertiesInput,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { JsonObjectSchema } from "../../shared";
|
||||
import { JsonObjectSchema } from "@thirdweb-dev/storage";
|
||||
import { z } from "zod";
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { FileBufferOrString } from "@thirdweb-dev/storage";
|
||||
import { FileOrBufferOrString } from "@thirdweb-dev/storage";
|
||||
import type { BigNumberish, Bytes } from "ethers";
|
||||
|
||||
/**
|
||||
@@ -17,7 +17,7 @@ export interface NFTContractDeployMetadata {
|
||||
/**
|
||||
* Optional image for the contract
|
||||
*/
|
||||
image?: FileBufferOrString;
|
||||
image?: FileOrBufferOrString;
|
||||
/**
|
||||
* Optional url for the contract
|
||||
*/
|
||||
@@ -68,7 +68,7 @@ export interface TokenContractDeployMetadata {
|
||||
/**
|
||||
* Optional image for the contract
|
||||
*/
|
||||
image?: FileBufferOrString;
|
||||
image?: FileOrBufferOrString;
|
||||
/**
|
||||
* Optional url for the contract
|
||||
*/
|
||||
@@ -111,7 +111,7 @@ export interface MarketplaceContractDeployMetadata {
|
||||
/**
|
||||
* Optional image for the contract
|
||||
*/
|
||||
image?: FileBufferOrString;
|
||||
image?: FileOrBufferOrString;
|
||||
/**
|
||||
* Optional url for the contract
|
||||
*/
|
||||
@@ -146,7 +146,7 @@ export interface VoteContractDeployMetadata {
|
||||
/**
|
||||
* Optional image for the contract
|
||||
*/
|
||||
image?: FileBufferOrString;
|
||||
image?: FileOrBufferOrString;
|
||||
/**
|
||||
* Optional url for the contract
|
||||
*/
|
||||
@@ -209,7 +209,7 @@ export interface SplitContractDeployMetadata {
|
||||
/**
|
||||
* Optional image for the contract
|
||||
*/
|
||||
image?: FileBufferOrString;
|
||||
image?: FileOrBufferOrString;
|
||||
/**
|
||||
* Optional url for the contract
|
||||
*/
|
||||
@@ -240,7 +240,7 @@ export interface MultiwrapContractDeployMetadata {
|
||||
/**
|
||||
* Optional image for the contract
|
||||
*/
|
||||
image?: FileBufferOrString;
|
||||
image?: FileOrBufferOrString;
|
||||
/**
|
||||
* Optional url for the contract
|
||||
*/
|
||||
|
||||
@@ -13,9 +13,9 @@ export interface UploadProgressEvent {
|
||||
/**
|
||||
* Standardized return type for contract events that returns event arguments
|
||||
*/
|
||||
export type ContractEvent = {
|
||||
export type ContractEvent<TEvent = Record<string, any>> = {
|
||||
eventName: string;
|
||||
data: Record<string, unknown>;
|
||||
data: TEvent;
|
||||
// from ethers.providers.Log
|
||||
transaction: {
|
||||
blockNumber: number;
|
||||
|
||||
@@ -98,7 +98,7 @@ describe("Edition Contract", async () => {
|
||||
});
|
||||
|
||||
it("should mint with URI", async () => {
|
||||
const uri = await storage.uploadMetadata({
|
||||
const uri = await storage.upload({
|
||||
name: "Test1",
|
||||
});
|
||||
const tx = await bundleContract.mint({
|
||||
@@ -111,7 +111,7 @@ describe("Edition Contract", async () => {
|
||||
});
|
||||
|
||||
it("should mint batch with URI", async () => {
|
||||
const uri = await storage.uploadMetadata({
|
||||
const uri = await storage.upload({
|
||||
name: "Test1",
|
||||
});
|
||||
await bundleContract.mintBatch([
|
||||
@@ -241,14 +241,12 @@ describe("Edition Contract", async () => {
|
||||
{
|
||||
metadata: {
|
||||
name: "Test0",
|
||||
image: "ipfs://myownipfs0",
|
||||
},
|
||||
supply: 5,
|
||||
},
|
||||
{
|
||||
metadata: {
|
||||
name: "Test1",
|
||||
image: "ipfs://myownipfs1",
|
||||
},
|
||||
supply: 5,
|
||||
},
|
||||
@@ -258,7 +256,6 @@ describe("Edition Contract", async () => {
|
||||
let i = 0;
|
||||
nfts.forEach((nft) => {
|
||||
expect(nft.metadata.name).to.be.equal(`Test${i}`);
|
||||
expect(nft.metadata.image).to.be.equal(`ipfs://myownipfs${i}`);
|
||||
i++;
|
||||
});
|
||||
});
|
||||
|
||||
@@ -42,7 +42,7 @@ import {
|
||||
TWRegistry__factory,
|
||||
VoteERC20__factory,
|
||||
} from "@thirdweb-dev/contracts-js";
|
||||
import { IStorage } from "@thirdweb-dev/storage";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
import { ethers } from "ethers";
|
||||
import hardhat from "hardhat";
|
||||
|
||||
@@ -59,7 +59,7 @@ let sdk: ThirdwebSDK;
|
||||
const ipfsGatewayUrl = DEFAULT_IPFS_GATEWAY;
|
||||
let signer: SignerWithAddress;
|
||||
let signers: SignerWithAddress[];
|
||||
let storage: IStorage;
|
||||
let storage: ThirdwebStorage;
|
||||
let implementations: { [key in ContractType]?: string };
|
||||
|
||||
const fastForwardTime = async (timeInSeconds: number): Promise<void> => {
|
||||
@@ -221,7 +221,7 @@ export const mochaHooks = {
|
||||
// eslint-disable-next-line turbo/no-undeclared-env-vars
|
||||
process.env.contractPublisherAddress = contractPublisher.address;
|
||||
|
||||
storage = new MockStorage();
|
||||
storage = MockStorage();
|
||||
sdk = new ThirdwebSDK(
|
||||
signer,
|
||||
{
|
||||
|
||||
@@ -1,170 +1,14 @@
|
||||
import { NotFoundError } from "../../src";
|
||||
import { JsonObject } from "../../src/core/types";
|
||||
import {
|
||||
FileOrBuffer,
|
||||
isBufferInstance,
|
||||
isFileInstance,
|
||||
IStorage,
|
||||
UploadResult,
|
||||
MockUploader,
|
||||
MockDownloader,
|
||||
ThirdwebStorage,
|
||||
} from "@thirdweb-dev/storage";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
|
||||
export class MockStorage implements IStorage {
|
||||
private objects: { [key: string]: any } = {};
|
||||
private folders: { [cid: string]: { [id: string]: any } } = {};
|
||||
export function MockStorage(): ThirdwebStorage {
|
||||
// Store mapping of URIs to files/objects
|
||||
const storage = {};
|
||||
|
||||
public async upload(
|
||||
data: string | FileOrBuffer,
|
||||
_contractAddress?: string,
|
||||
_signerAddress?: string,
|
||||
): Promise<string> {
|
||||
const uuid = uuidv4();
|
||||
let serializedData = "";
|
||||
|
||||
if (isFileInstance(data)) {
|
||||
serializedData = await data.text();
|
||||
} else if (isBufferInstance(data)) {
|
||||
serializedData = data.toString();
|
||||
} else if (typeof data === "string") {
|
||||
serializedData = data;
|
||||
}
|
||||
|
||||
const key = `mock://${uuid}`;
|
||||
this.objects[uuid] = serializedData;
|
||||
return Promise.resolve(key);
|
||||
}
|
||||
|
||||
public async uploadBatch(
|
||||
files: (string | FileOrBuffer)[],
|
||||
fileStartNumber?: number,
|
||||
_contractAddress?: string,
|
||||
_signerAddress?: string,
|
||||
): Promise<UploadResult> {
|
||||
const cid = uuidv4();
|
||||
const uris: string[] = [];
|
||||
this.folders[cid] = {};
|
||||
|
||||
let index = fileStartNumber ? fileStartNumber : 0;
|
||||
for (const file of files) {
|
||||
let contents: string;
|
||||
if (isFileInstance(file)) {
|
||||
contents = await file.text();
|
||||
} else if (isBufferInstance(file)) {
|
||||
contents = file.toString();
|
||||
} else if (typeof file === "string") {
|
||||
contents = file;
|
||||
} else {
|
||||
contents = isBufferInstance(file.data)
|
||||
? file.data.toString()
|
||||
: file.data;
|
||||
const name = file.name ? file.name : `file_${index}`;
|
||||
this.folders.cid[name] = contents;
|
||||
continue;
|
||||
}
|
||||
this.folders[cid][index.toString()] = contents;
|
||||
uris.push(`${cid}/${index}`);
|
||||
index += 1;
|
||||
}
|
||||
|
||||
return Promise.resolve({
|
||||
baseUri: `mock://${cid}`,
|
||||
uris,
|
||||
});
|
||||
}
|
||||
|
||||
public async getUploadToken(_contractAddress: string): Promise<string> {
|
||||
return Promise.resolve("mock-token");
|
||||
}
|
||||
|
||||
private _get(hash: string): string {
|
||||
hash = hash.replace("mock://", "").replace("fake://", "");
|
||||
const split = hash.split("/");
|
||||
if (split.length === 1) {
|
||||
if (hash in this.objects) {
|
||||
return this.objects[hash];
|
||||
} else {
|
||||
throw new NotFoundError(hash);
|
||||
}
|
||||
}
|
||||
const [cid, index] = split;
|
||||
if (!(cid in this.folders)) {
|
||||
throw new NotFoundError(cid);
|
||||
}
|
||||
if (!(index in this.folders[cid])) {
|
||||
throw new NotFoundError(`${cid}/${index}`);
|
||||
}
|
||||
return this.folders[cid][index.toString()];
|
||||
}
|
||||
|
||||
get(hash: string): Promise<Record<string, any>> {
|
||||
return Promise.resolve(JSON.parse(this._get(hash)));
|
||||
}
|
||||
|
||||
getRaw(hash: string): Promise<string> {
|
||||
return Promise.resolve(this._get(hash));
|
||||
}
|
||||
|
||||
public async uploadMetadata(
|
||||
metadata: JsonObject,
|
||||
contractAddress?: string,
|
||||
_signerAddress?: string,
|
||||
): Promise<string> {
|
||||
// since there's only single object, always use the first index
|
||||
const { uris } = await this.uploadMetadataBatch(
|
||||
[metadata],
|
||||
0,
|
||||
contractAddress,
|
||||
);
|
||||
|
||||
return uris[0];
|
||||
}
|
||||
|
||||
public async uploadMetadataBatch(
|
||||
metadatas: JsonObject[],
|
||||
fileStartNumber?: number,
|
||||
contractAddress?: string,
|
||||
signerAddress?: string,
|
||||
): Promise<UploadResult> {
|
||||
await this.batchUploadProperties(metadatas);
|
||||
|
||||
const metadataToUpload: string[] = metadatas.map((m: any) =>
|
||||
JSON.stringify(m),
|
||||
);
|
||||
|
||||
const { baseUri: cid } = await this.uploadBatch(
|
||||
metadataToUpload,
|
||||
fileStartNumber,
|
||||
contractAddress,
|
||||
signerAddress,
|
||||
);
|
||||
const baseUri = `${cid}/`;
|
||||
return {
|
||||
uris: metadataToUpload.map((_, i) => `${baseUri}${i}`),
|
||||
baseUri,
|
||||
};
|
||||
}
|
||||
|
||||
private async uploadProperties(object: Record<string, any>): Promise<void> {
|
||||
const keys = Object.keys(object).sort();
|
||||
for (const key in keys) {
|
||||
const val = object[keys[key]];
|
||||
const shouldUpload = isFileInstance(val) || isBufferInstance(val);
|
||||
if (shouldUpload) {
|
||||
object[keys[key]] = await this.upload(val);
|
||||
}
|
||||
|
||||
if (typeof val === "object") {
|
||||
await this.uploadProperties(val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async batchUploadProperties(metadatas: JsonObject[]): Promise<any> {
|
||||
for (const file of metadatas) {
|
||||
if (typeof file === "string") {
|
||||
continue;
|
||||
}
|
||||
await this.uploadProperties(file);
|
||||
}
|
||||
}
|
||||
const uploader = new MockUploader(storage);
|
||||
const downloader = new MockDownloader(storage);
|
||||
return new ThirdwebStorage(uploader, downloader);
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ describe("NFT Drop Contract", async () => {
|
||||
});
|
||||
|
||||
it("should lazy mint with URI", async () => {
|
||||
const uri = await storage.uploadMetadata({
|
||||
const uri = await storage.upload({
|
||||
name: "Test1",
|
||||
});
|
||||
await dropContract.createBatch([uri]);
|
||||
|
||||
@@ -102,7 +102,7 @@ describe("NFT Contract", async () => {
|
||||
});
|
||||
|
||||
it("should mint with URI", async () => {
|
||||
const uri = await storage.uploadMetadata({
|
||||
const uri = await storage.upload({
|
||||
name: "Test1",
|
||||
});
|
||||
await nftContract.mint(uri);
|
||||
@@ -112,7 +112,7 @@ describe("NFT Contract", async () => {
|
||||
});
|
||||
|
||||
it("should mint batch with URI", async () => {
|
||||
const uri = await storage.uploadMetadata({
|
||||
const uri = await storage.upload({
|
||||
name: "Test1",
|
||||
});
|
||||
await nftContract.mintBatch([uri]);
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
DropERC721__factory,
|
||||
TokenERC721__factory,
|
||||
} from "@thirdweb-dev/contracts-js";
|
||||
import { IpfsStorage } from "@thirdweb-dev/storage";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
import { expect } from "chai";
|
||||
import { ethers } from "ethers";
|
||||
import { readFileSync } from "fs";
|
||||
@@ -21,7 +21,7 @@ global.fetch = require("cross-fetch");
|
||||
|
||||
export const uploadContractMetadata = async (
|
||||
contractName: string,
|
||||
storage: IpfsStorage,
|
||||
storage: ThirdwebStorage,
|
||||
) => {
|
||||
const buildinfo = JSON.parse(
|
||||
readFileSync("test/test_abis/hardhat-build-info.json", "utf-8"),
|
||||
@@ -29,14 +29,18 @@ export const uploadContractMetadata = async (
|
||||
const info =
|
||||
buildinfo.output.contracts[`contracts/${contractName}.sol`][contractName];
|
||||
const bytecode = `0x${info.evm.bytecode.object}`;
|
||||
const metadataUri = await storage.uploadSingle(info.metadata);
|
||||
const bytecodeUri = await storage.uploadSingle(bytecode);
|
||||
const metadataUri = await storage.upload(info.metadata, {
|
||||
uploadWithoutDirectory: true,
|
||||
});
|
||||
const bytecodeUri = await storage.upload(bytecode, {
|
||||
uploadWithoutDirectory: true,
|
||||
});
|
||||
const model = {
|
||||
name: contractName,
|
||||
metadataUri: `ipfs://${metadataUri}`,
|
||||
bytecodeUri: `ipfs://${bytecodeUri}`,
|
||||
};
|
||||
return await storage.uploadMetadata(model);
|
||||
return await storage.upload(model);
|
||||
};
|
||||
|
||||
describe("Publishing", async () => {
|
||||
|
||||
@@ -180,7 +180,7 @@ describe("Signature drop tests", async () => {
|
||||
});
|
||||
|
||||
it("should mint with URI", async () => {
|
||||
const uri = await storage.uploadMetadata({
|
||||
const uri = await storage.upload({
|
||||
name: "Test1",
|
||||
});
|
||||
const toSign = {
|
||||
@@ -194,10 +194,10 @@ describe("Signature drop tests", async () => {
|
||||
});
|
||||
|
||||
it("should mint batch with URI", async () => {
|
||||
const uri1 = await storage.uploadMetadata({
|
||||
const uri1 = await storage.upload({
|
||||
name: "Test1",
|
||||
});
|
||||
const uri2 = await storage.uploadMetadata({
|
||||
const uri2 = await storage.upload({
|
||||
name: "Test2",
|
||||
});
|
||||
const toSign1 = {
|
||||
|
||||
@@ -169,7 +169,7 @@ describe("Edition sig minting", async () => {
|
||||
});
|
||||
|
||||
it("should mint with URI", async () => {
|
||||
const uri = await storage.uploadMetadata({
|
||||
const uri = await storage.upload({
|
||||
name: "Test1",
|
||||
});
|
||||
const toSign = {
|
||||
@@ -183,10 +183,10 @@ describe("Edition sig minting", async () => {
|
||||
});
|
||||
|
||||
it("should mint batch with URI", async () => {
|
||||
const uri1 = await storage.uploadMetadata({
|
||||
const uri1 = await storage.upload({
|
||||
name: "Test1",
|
||||
});
|
||||
const uri2 = await storage.uploadMetadata({
|
||||
const uri2 = await storage.upload({
|
||||
name: "Test2",
|
||||
});
|
||||
const toSign1 = {
|
||||
|
||||
@@ -188,7 +188,7 @@ describe("NFT sig minting", async () => {
|
||||
});
|
||||
|
||||
it("should mint with URI", async () => {
|
||||
const uri = await storage.uploadMetadata({
|
||||
const uri = await storage.upload({
|
||||
name: "Test1",
|
||||
});
|
||||
const toSign = {
|
||||
@@ -201,10 +201,10 @@ describe("NFT sig minting", async () => {
|
||||
});
|
||||
|
||||
it("should mint batch with URI", async () => {
|
||||
const uri1 = await storage.uploadMetadata({
|
||||
const uri1 = await storage.upload({
|
||||
name: "Test1",
|
||||
});
|
||||
const uri2 = await storage.uploadMetadata({
|
||||
const uri2 = await storage.upload({
|
||||
name: "Test2",
|
||||
});
|
||||
const toSign1 = {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { createSnapshot, Snapshot } from "../src/index";
|
||||
import { MockStorage } from "./mock/MockStorage";
|
||||
import { IStorage } from "@thirdweb-dev/storage";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
|
||||
const chai = require("chai");
|
||||
const deepEqualInAnyOrder = require("deep-equal-in-any-order");
|
||||
@@ -28,10 +28,10 @@ describe("Snapshots", async () => {
|
||||
maxClaimable: 0,
|
||||
}));
|
||||
|
||||
let storage: IStorage;
|
||||
let storage: ThirdwebStorage;
|
||||
|
||||
beforeEach(async () => {
|
||||
storage = new MockStorage();
|
||||
storage = MockStorage();
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
@@ -88,7 +88,7 @@ describe("Snapshots", async () => {
|
||||
});
|
||||
|
||||
it("should upload the snapshot to storage", async () => {
|
||||
const rawSnapshotJson = await storage.get(uri);
|
||||
const rawSnapshotJson = await storage.downloadJSON(uri);
|
||||
expect(rawSnapshotJson).to.deep.equalInAnyOrder(snapshot);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,5 +1,18 @@
|
||||
# @thirdweb-dev/solana
|
||||
|
||||
## 0.2.11
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#137](https://github.com/thirdweb-dev/js/pull/137) [`9c7e551`](https://github.com/thirdweb-dev/js/commit/9c7e5518fffa1c1628179fc69d524be7105d3a75) Thanks [@jnsdls](https://github.com/jnsdls)! - enforce creator value to include the creator at a minimum
|
||||
|
||||
- [#140](https://github.com/thirdweb-dev/js/pull/140) [`5a52585`](https://github.com/thirdweb-dev/js/commit/5a52585a7a2cd0041dfbb5124582a3c0403b285b) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Fixes for dashboard integration
|
||||
|
||||
- [#138](https://github.com/thirdweb-dev/js/pull/138) [`6b48b92`](https://github.com/thirdweb-dev/js/commit/6b48b9288b38b66c1177b19c00bfab9fee7c529d) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Optimize Drop creation flow, cleanup types
|
||||
|
||||
- Updated dependencies [[`57432d2`](https://github.com/thirdweb-dev/js/commit/57432d21c4c9e880a36c61f4988c60af61ac9d44)]:
|
||||
- @thirdweb-dev/storage@1.0.0
|
||||
|
||||
## 0.2.10
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@thirdweb-dev/solana",
|
||||
"version": "0.2.10",
|
||||
"version": "0.2.11",
|
||||
"description": "thirdweb Solana SDK",
|
||||
"main": "dist/thirdweb-dev-solana.cjs.js",
|
||||
"module": "dist/thirdweb-dev-solana.esm.js",
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { TransactionResult } from "../types/common";
|
||||
import {
|
||||
NFTDropClaimInput,
|
||||
NFTDropClaimSchema,
|
||||
NFTDropClaimOutput,
|
||||
NFTDropConditionsOutputSchema,
|
||||
NFTDropMetadataInput,
|
||||
} from "../types/contracts/nft-drop";
|
||||
import { Metaplex } from "@metaplex-foundation/js";
|
||||
import { PublicKey } from "@solana/web3.js";
|
||||
@@ -16,21 +15,21 @@ export class ClaimConditions {
|
||||
this.metaplex = metaplex;
|
||||
}
|
||||
|
||||
async get(): Promise<NFTDropClaimOutput> {
|
||||
async get(): Promise<NFTDropMetadataInput> {
|
||||
const candyMachine = await this.getCandyMachine();
|
||||
|
||||
return {
|
||||
price: BigInt(candyMachine.price.basisPoints.toNumber()),
|
||||
sellerFeeBasisPoints: BigInt(candyMachine.sellerFeeBasisPoints),
|
||||
itemsAvailable: BigInt(candyMachine.itemsAvailable.toNumber()),
|
||||
price: candyMachine.price.basisPoints.toNumber(),
|
||||
sellerFeeBasisPoints: candyMachine.sellerFeeBasisPoints,
|
||||
itemsAvailable: candyMachine.itemsAvailable.toNumber(),
|
||||
goLiveDate: candyMachine.goLiveDate
|
||||
? new Date(candyMachine.goLiveDate.toNumber() * 1000)
|
||||
: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
async set(metadata: NFTDropClaimInput): Promise<TransactionResult> {
|
||||
const parsed = NFTDropClaimSchema.parse(metadata);
|
||||
async set(metadata: NFTDropMetadataInput): Promise<TransactionResult> {
|
||||
const parsed = NFTDropConditionsOutputSchema.parse(metadata);
|
||||
|
||||
const result = await this.metaplex
|
||||
.candyMachines()
|
||||
|
||||
@@ -1,32 +1,40 @@
|
||||
import {
|
||||
NFTCollectionMetadataInput,
|
||||
NFTCollectionMetadataInputSchema,
|
||||
TokenMetadataInput,
|
||||
TokenMetadataInputSchema,
|
||||
} from "../types/contracts";
|
||||
import {
|
||||
NFTDropContractSchema,
|
||||
NFTDropMetadataInput,
|
||||
NFTDropConditionsOutputSchema,
|
||||
NFTDropContractInput,
|
||||
} from "../types/contracts/nft-drop";
|
||||
import { findMetadataPda, Metaplex, token } from "@metaplex-foundation/js";
|
||||
import { enforceCreator } from "./helpers/creators-helper";
|
||||
import {
|
||||
findMetadataPda,
|
||||
Metaplex,
|
||||
sol,
|
||||
toBigNumber,
|
||||
token,
|
||||
} from "@metaplex-foundation/js";
|
||||
import {
|
||||
createCreateMetadataAccountV2Instruction,
|
||||
DataV2,
|
||||
} from "@metaplex-foundation/mpl-token-metadata";
|
||||
import { Keypair, PublicKey } from "@solana/web3.js";
|
||||
import { IStorage } from "@thirdweb-dev/storage";
|
||||
import { Keypair } from "@solana/web3.js";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
|
||||
export class Deployer {
|
||||
private metaplex: Metaplex;
|
||||
private storage: IStorage;
|
||||
private storage: ThirdwebStorage;
|
||||
|
||||
constructor(metaplex: Metaplex, storage: IStorage) {
|
||||
constructor(metaplex: Metaplex, storage: ThirdwebStorage) {
|
||||
this.metaplex = metaplex;
|
||||
this.storage = storage;
|
||||
}
|
||||
|
||||
async createToken(tokenMetadata: TokenMetadataInput): Promise<string> {
|
||||
const tokenMetadataParsed = TokenMetadataInputSchema.parse(tokenMetadata);
|
||||
const uri = await this.storage.uploadMetadata(tokenMetadataParsed);
|
||||
const uri = await this.storage.upload(tokenMetadataParsed);
|
||||
const mint = Keypair.generate();
|
||||
const owner = this.metaplex.identity().publicKey;
|
||||
const mintTx = await this.metaplex
|
||||
@@ -46,13 +54,7 @@ export class Deployer {
|
||||
symbol: tokenMetadataParsed.symbol || "",
|
||||
sellerFeeBasisPoints: 0,
|
||||
uri,
|
||||
creators: [
|
||||
{
|
||||
address: owner,
|
||||
share: 100,
|
||||
verified: false,
|
||||
},
|
||||
],
|
||||
creators: enforceCreator([], this.metaplex.identity().publicKey),
|
||||
collection: null,
|
||||
uses: null,
|
||||
};
|
||||
@@ -77,53 +79,73 @@ export class Deployer {
|
||||
async createNftCollection(
|
||||
collectionMetadata: NFTCollectionMetadataInput,
|
||||
): Promise<string> {
|
||||
const uri = await this.storage.uploadMetadata(collectionMetadata);
|
||||
const parsed = NFTCollectionMetadataInputSchema.parse(collectionMetadata);
|
||||
const uri = await this.storage.upload(parsed);
|
||||
|
||||
const { nft: collectionNft } = await this.metaplex
|
||||
.nfts()
|
||||
.create({
|
||||
name: collectionMetadata.name,
|
||||
symbol: collectionMetadata.symbol,
|
||||
name: parsed.name,
|
||||
symbol: parsed.symbol,
|
||||
sellerFeeBasisPoints: 0,
|
||||
uri,
|
||||
isCollection: true,
|
||||
creators: collectionMetadata.creators?.map((creator) => ({
|
||||
address: new PublicKey(creator.address),
|
||||
share: creator.share,
|
||||
})),
|
||||
creators: enforceCreator(
|
||||
parsed.creators,
|
||||
this.metaplex.identity().publicKey,
|
||||
),
|
||||
})
|
||||
.run();
|
||||
return collectionNft.mint.address.toBase58();
|
||||
}
|
||||
|
||||
async createNftDrop(metadata: NFTDropMetadataInput): Promise<string> {
|
||||
const parsed = NFTDropContractSchema.parse(metadata);
|
||||
async createNftDrop(metadata: NFTDropContractInput): Promise<string> {
|
||||
const collectionInfo = NFTCollectionMetadataInputSchema.parse(metadata);
|
||||
const candyMachineInfo = NFTDropConditionsOutputSchema.parse(metadata);
|
||||
const uri = await this.storage.upload(collectionInfo);
|
||||
|
||||
// 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()
|
||||
const collectionMint = Keypair.generate();
|
||||
const collectionTx = await this.metaplex
|
||||
.nfts()
|
||||
.builders()
|
||||
.create({
|
||||
...parsed,
|
||||
collection: new PublicKey(collection),
|
||||
creators,
|
||||
})
|
||||
.run();
|
||||
useNewMint: collectionMint,
|
||||
name: collectionInfo.name,
|
||||
symbol: collectionInfo.symbol,
|
||||
sellerFeeBasisPoints: 0,
|
||||
uri,
|
||||
isCollection: true,
|
||||
creators: enforceCreator(
|
||||
collectionInfo.creators,
|
||||
this.metaplex.identity().publicKey,
|
||||
),
|
||||
});
|
||||
|
||||
return nftDrop.address.toBase58();
|
||||
const candyMachineKeypair = Keypair.generate();
|
||||
const candyMachineTx = await this.metaplex
|
||||
.candyMachines()
|
||||
.builders()
|
||||
.create({
|
||||
...candyMachineInfo,
|
||||
price: candyMachineInfo.price || sol(0),
|
||||
sellerFeeBasisPoints: candyMachineInfo.sellerFeeBasisPoints || 0,
|
||||
itemsAvailable: candyMachineInfo.itemsAvailable || toBigNumber(0),
|
||||
candyMachine: candyMachineKeypair,
|
||||
collection: collectionMint.publicKey,
|
||||
creators: enforceCreator(
|
||||
collectionInfo.creators,
|
||||
this.metaplex.identity().publicKey,
|
||||
),
|
||||
});
|
||||
|
||||
const result = await collectionTx
|
||||
.add(candyMachineTx)
|
||||
.sendAndConfirm(this.metaplex);
|
||||
|
||||
if (!result.response.signature) {
|
||||
throw new Error("Transaction failed");
|
||||
}
|
||||
|
||||
return candyMachineKeypair.publicKey.toBase58();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
import { NFTCollectionMetadataInput } from "../../types/contracts";
|
||||
import type { Creator } from "@metaplex-foundation/js";
|
||||
import { PublicKey } from "@solana/web3.js";
|
||||
|
||||
export function enforceCreator(
|
||||
creators: NFTCollectionMetadataInput["creators"] = [],
|
||||
owner: PublicKey,
|
||||
): Creator[] {
|
||||
if (creators.length === 0) {
|
||||
// If no creators are specified, we assume the owner is the creator
|
||||
creators = creators.concat({
|
||||
address: owner.toBase58(),
|
||||
share: 100,
|
||||
verified: true,
|
||||
});
|
||||
}
|
||||
return creators.map((creator) => ({
|
||||
...creator,
|
||||
verified: creator.verified || false,
|
||||
address: new PublicKey(creator.address),
|
||||
}));
|
||||
}
|
||||
@@ -52,7 +52,7 @@ export class NFTHelper {
|
||||
};
|
||||
}
|
||||
|
||||
async balanceOf(walletAddress: string, mintAddress: string): Promise<bigint> {
|
||||
async balanceOf(walletAddress: string, mintAddress: string): Promise<number> {
|
||||
const address = await getAssociatedTokenAddress(
|
||||
new PublicKey(mintAddress),
|
||||
new PublicKey(walletAddress),
|
||||
@@ -60,9 +60,9 @@ export class NFTHelper {
|
||||
|
||||
try {
|
||||
const account = await getAccount(this.connection, address);
|
||||
return account.amount;
|
||||
return Number(account.amount);
|
||||
} catch (e) {
|
||||
return BigInt(0);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import {
|
||||
CandyMachine,
|
||||
Metadata,
|
||||
Metaplex,
|
||||
TokenMetadataProgram,
|
||||
TokenProgram,
|
||||
} from "@metaplex-foundation/js";
|
||||
import { TokenStandard } from "@metaplex-foundation/mpl-token-metadata";
|
||||
import { PublicKey } from "@solana/web3.js";
|
||||
@@ -17,48 +17,46 @@ export class Registry {
|
||||
|
||||
public async getAccountType(address: string) {
|
||||
try {
|
||||
const metadata = await this.metaplex
|
||||
.nfts()
|
||||
.findByMint({ mintAddress: new PublicKey(address) })
|
||||
const candyMachine = await this.metaplex
|
||||
.candyMachines()
|
||||
.findByAddress({ address: new PublicKey(address) })
|
||||
.run();
|
||||
if (metadata) {
|
||||
if (metadata.collectionDetails) {
|
||||
return "nft-collection";
|
||||
} else {
|
||||
if (metadata.tokenStandard === TokenStandard.Fungible) {
|
||||
return "token";
|
||||
}
|
||||
}
|
||||
if (candyMachine) {
|
||||
return "nft-drop";
|
||||
}
|
||||
} catch (e) {
|
||||
try {
|
||||
const candyMachine = await this.metaplex
|
||||
.candyMachines()
|
||||
.findByAddress({ address: new PublicKey(address) })
|
||||
.run();
|
||||
if (candyMachine) {
|
||||
return "nft-drop";
|
||||
} catch (err) {
|
||||
// ignore and try next
|
||||
}
|
||||
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 (err) {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
throw new Error("Unknown account type");
|
||||
}
|
||||
|
||||
public async getAccountsForWallet(
|
||||
walletAddress: string,
|
||||
): Promise<WalletAccount[]> {
|
||||
const pubKeys = await this.getMetadataAddressesForWallet(walletAddress);
|
||||
const mints = await this.getOwnedTokenAccountsForWallet(walletAddress);
|
||||
const metadatas = await this.metaplex
|
||||
.nfts()
|
||||
.findAllByMintList({ mints: pubKeys })
|
||||
.findAllByMintList({ mints })
|
||||
.run();
|
||||
|
||||
const candyMachines = await this.metaplex
|
||||
.candyMachines()
|
||||
.findAllBy({
|
||||
type: "wallet",
|
||||
type: "authority",
|
||||
publicKey: new PublicKey(walletAddress),
|
||||
})
|
||||
.run();
|
||||
@@ -106,10 +104,10 @@ export class Registry {
|
||||
);
|
||||
}
|
||||
|
||||
public async getMetadataAddressesForWallet(walletAddress: string) {
|
||||
return await TokenMetadataProgram.metadataV1Accounts(this.metaplex)
|
||||
private async getOwnedTokenAccountsForWallet(walletAddress: string) {
|
||||
return await TokenProgram.tokenAccounts(this.metaplex)
|
||||
.selectMint()
|
||||
.whereCreator(1, new PublicKey(walletAddress))
|
||||
.whereOwner(new PublicKey(walletAddress))
|
||||
.getDataAsPublicKeys();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// import { getPayer } from "../utils/local-config";
|
||||
import { CurrencyValue, WalletSigner } from "../types/common";
|
||||
import { toCurrencyValue } from "../utils/token";
|
||||
import {
|
||||
|
||||
@@ -16,29 +16,30 @@ import {
|
||||
Metadata,
|
||||
} from "@metaplex-foundation/mpl-token-metadata";
|
||||
import { ConfirmedSignatureInfo, PublicKey } from "@solana/web3.js";
|
||||
import { IStorage } from "@thirdweb-dev/storage";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
|
||||
export class NFTCollection {
|
||||
private metaplex: Metaplex;
|
||||
private storage: IStorage;
|
||||
private storage: ThirdwebStorage;
|
||||
private nft: NFTHelper;
|
||||
collectionMintAddress: PublicKey;
|
||||
public publicKey: PublicKey;
|
||||
public accountType = "nft-collection" as const;
|
||||
|
||||
constructor(
|
||||
collectionMintAddress: string,
|
||||
metaplex: Metaplex,
|
||||
storage: IStorage,
|
||||
storage: ThirdwebStorage,
|
||||
) {
|
||||
this.storage = storage;
|
||||
this.metaplex = metaplex;
|
||||
this.nft = new NFTHelper(metaplex);
|
||||
this.collectionMintAddress = new PublicKey(collectionMintAddress);
|
||||
this.publicKey = new PublicKey(collectionMintAddress);
|
||||
}
|
||||
|
||||
async getMetadata(): Promise<NFTCollectionMetadata> {
|
||||
const metadata = await this.metaplex
|
||||
.nfts()
|
||||
.findByMint({ mintAddress: this.collectionMintAddress })
|
||||
.findByMint({ mintAddress: this.publicKey })
|
||||
.run();
|
||||
|
||||
return this.nft.toNFTMetadata(metadata);
|
||||
@@ -48,11 +49,16 @@ export class NFTCollection {
|
||||
return this.nft.get(mintAddress);
|
||||
}
|
||||
|
||||
async getAll(): Promise<string[]> {
|
||||
async getAll(): Promise<NFTMetadata[]> {
|
||||
const addresses = await this.getAllNFTAddresses();
|
||||
return await Promise.all(addresses.map((a) => this.get(a)));
|
||||
}
|
||||
|
||||
async getAllNFTAddresses(): Promise<string[]> {
|
||||
const allSignatures: ConfirmedSignatureInfo[] = [];
|
||||
// This returns the first 1000, so we need to loop through until we run out of signatures to get.
|
||||
let signatures = await this.metaplex.connection.getSignaturesForAddress(
|
||||
this.collectionMintAddress,
|
||||
this.publicKey,
|
||||
);
|
||||
|
||||
allSignatures.push(...signatures);
|
||||
@@ -61,7 +67,7 @@ export class NFTCollection {
|
||||
before: signatures[signatures.length - 1]?.signature,
|
||||
};
|
||||
signatures = await this.metaplex.connection.getSignaturesForAddress(
|
||||
this.collectionMintAddress,
|
||||
this.publicKey,
|
||||
options,
|
||||
);
|
||||
allSignatures.push(...signatures);
|
||||
@@ -131,12 +137,12 @@ export class NFTCollection {
|
||||
return Array.from(mintAddresses);
|
||||
}
|
||||
|
||||
async balance(mintAddress: string): Promise<bigint> {
|
||||
async balance(mintAddress: string): Promise<number> {
|
||||
const address = this.metaplex.identity().publicKey.toBase58();
|
||||
return this.balanceOf(address, mintAddress);
|
||||
}
|
||||
|
||||
async balanceOf(walletAddress: string, mintAddress: string): Promise<bigint> {
|
||||
async balanceOf(walletAddress: string, mintAddress: string): Promise<number> {
|
||||
return this.nft.balanceOf(walletAddress, mintAddress);
|
||||
}
|
||||
|
||||
@@ -205,15 +211,14 @@ export class NFTCollection {
|
||||
|
||||
// TODO add options param for initial/maximum supply
|
||||
async mintTo(to: string, metadata: NFTMetadataInput) {
|
||||
const uri = await this.storage.uploadMetadata(metadata);
|
||||
const uri = await this.storage.upload(metadata);
|
||||
const { nft } = await this.metaplex
|
||||
.nfts()
|
||||
.create({
|
||||
// useExistingMint: newMint,
|
||||
name: metadata.name || "",
|
||||
name: metadata.name?.toString() || "",
|
||||
uri,
|
||||
sellerFeeBasisPoints: 0,
|
||||
collection: this.collectionMintAddress,
|
||||
collection: this.publicKey,
|
||||
collectionAuthority: this.metaplex.identity(),
|
||||
tokenOwner: new PublicKey(to),
|
||||
// Always sets max supply to unlimited so editions can be minted
|
||||
|
||||
@@ -9,21 +9,26 @@ import {
|
||||
} from "../types/nft";
|
||||
import { Metaplex } from "@metaplex-foundation/js";
|
||||
import { PublicKey } from "@solana/web3.js";
|
||||
import { IStorage } from "@thirdweb-dev/storage";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
import invariant from "tiny-invariant";
|
||||
|
||||
export class NFTDrop {
|
||||
private metaplex: Metaplex;
|
||||
private storage: IStorage;
|
||||
private storage: ThirdwebStorage;
|
||||
private nft: NFTHelper;
|
||||
public dropMintAddress: PublicKey;
|
||||
public accountType = "nft-drop" as const;
|
||||
public publicKey: PublicKey;
|
||||
public claimConditions: ClaimConditions;
|
||||
|
||||
constructor(dropMintAddress: string, metaplex: Metaplex, storage: IStorage) {
|
||||
constructor(
|
||||
dropMintAddress: string,
|
||||
metaplex: Metaplex,
|
||||
storage: ThirdwebStorage,
|
||||
) {
|
||||
this.storage = storage;
|
||||
this.metaplex = metaplex;
|
||||
this.nft = new NFTHelper(metaplex);
|
||||
this.dropMintAddress = new PublicKey(dropMintAddress);
|
||||
this.publicKey = new PublicKey(dropMintAddress);
|
||||
this.claimConditions = new ClaimConditions(dropMintAddress, metaplex);
|
||||
}
|
||||
|
||||
@@ -44,45 +49,44 @@ export class NFTDrop {
|
||||
// TODO: Add pagination to get NFT functions
|
||||
async getAll(): Promise<NFTMetadata[]> {
|
||||
const info = await this.getCandyMachine();
|
||||
const nfts = await Promise.all(
|
||||
// TODO merge with getAllClaimed()
|
||||
return await Promise.all(
|
||||
info.items.map(async (item) => {
|
||||
const metadata = await this.storage.get(item.uri);
|
||||
const metadata = await this.storage.downloadJSON(item.uri);
|
||||
return { uri: item.uri, ...metadata };
|
||||
}),
|
||||
);
|
||||
|
||||
return nfts;
|
||||
}
|
||||
|
||||
async getAllClaimed(): Promise<NFTMetadata[]> {
|
||||
const nfts = await this.metaplex
|
||||
.candyMachines()
|
||||
.findMintedNfts({ candyMachine: this.dropMintAddress })
|
||||
.findMintedNfts({ candyMachine: this.publicKey })
|
||||
.run();
|
||||
|
||||
const metadatas = nfts.map((nft) => this.nft.toNFTMetadata(nft));
|
||||
return metadatas;
|
||||
return nfts.map((nft) => this.nft.toNFTMetadata(nft));
|
||||
}
|
||||
|
||||
async balance(mintAddress: string): Promise<bigint> {
|
||||
async balance(mintAddress: string): Promise<number> {
|
||||
const address = this.metaplex.identity().publicKey.toBase58();
|
||||
return this.balanceOf(address, mintAddress);
|
||||
}
|
||||
|
||||
async balanceOf(walletAddress: string, mintAddress: string): Promise<bigint> {
|
||||
async balanceOf(walletAddress: string, mintAddress: string): Promise<number> {
|
||||
return this.nft.balanceOf(walletAddress, mintAddress);
|
||||
}
|
||||
|
||||
async totalUnclaimedSupply(): Promise<bigint> {
|
||||
async totalUnclaimedSupply(): Promise<number> {
|
||||
const info = await this.getCandyMachine();
|
||||
return BigInt(
|
||||
Math.min(info.itemsLoaded.toNumber(), info.itemsRemaining.toNumber()),
|
||||
return Math.min(
|
||||
info.itemsLoaded.toNumber(),
|
||||
info.itemsRemaining.toNumber(),
|
||||
);
|
||||
}
|
||||
|
||||
async totalClaimedSupply(): Promise<bigint> {
|
||||
async totalClaimedSupply(): Promise<number> {
|
||||
const info = await this.getCandyMachine();
|
||||
return BigInt(info.itemsMinted.toNumber());
|
||||
return info.itemsMinted.toNumber();
|
||||
}
|
||||
|
||||
async transfer(
|
||||
@@ -96,9 +100,9 @@ export class NFTDrop {
|
||||
const parsedMetadatas = metadatas.map((metadata) =>
|
||||
CommonNFTInput.parse(metadata),
|
||||
);
|
||||
const upload = await this.storage.uploadMetadataBatch(parsedMetadatas);
|
||||
const items = upload.uris.map((uri, i) => ({
|
||||
name: parsedMetadatas[i].name || "",
|
||||
const uris = await this.storage.uploadBatch(parsedMetadatas);
|
||||
const items = uris.map((uri, i) => ({
|
||||
name: parsedMetadatas[i].name?.toString() || "",
|
||||
uri,
|
||||
}));
|
||||
|
||||
@@ -128,7 +132,7 @@ export class NFTDrop {
|
||||
private async getCandyMachine() {
|
||||
return this.metaplex
|
||||
.candyMachines()
|
||||
.findByAddress({ address: this.dropMintAddress })
|
||||
.findByAddress({ address: this.publicKey })
|
||||
.run();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,31 +16,36 @@ import {
|
||||
} from "@metaplex-foundation/js";
|
||||
import { getAccount, getAssociatedTokenAddress } from "@solana/spl-token";
|
||||
import { Connection, PublicKey } from "@solana/web3.js";
|
||||
import { IStorage } from "@thirdweb-dev/storage";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
|
||||
export class Token {
|
||||
private connection: Connection;
|
||||
private metaplex: Metaplex;
|
||||
private storage: IStorage;
|
||||
tokenMintAddress: PublicKey;
|
||||
private storage: ThirdwebStorage;
|
||||
public accountType = "token" as const;
|
||||
public publicKey: PublicKey;
|
||||
|
||||
constructor(tokenMintAddress: string, metaplex: Metaplex, storage: IStorage) {
|
||||
constructor(
|
||||
tokenMintAddress: string,
|
||||
metaplex: Metaplex,
|
||||
storage: ThirdwebStorage,
|
||||
) {
|
||||
this.storage = storage;
|
||||
this.metaplex = metaplex;
|
||||
this.connection = metaplex.connection;
|
||||
this.tokenMintAddress = new PublicKey(tokenMintAddress);
|
||||
this.publicKey = new PublicKey(tokenMintAddress);
|
||||
}
|
||||
|
||||
async getMint() {
|
||||
return await this.metaplex
|
||||
.tokens()
|
||||
.findMintByAddress({ address: this.tokenMintAddress })
|
||||
.findMintByAddress({ address: this.publicKey })
|
||||
.run(); // TODO abstract types away
|
||||
}
|
||||
|
||||
async getMetadata(): Promise<TokenMetadata> {
|
||||
const mint = await this.getMint();
|
||||
const addr = findMetadataPda(this.tokenMintAddress);
|
||||
const addr = findMetadataPda(this.publicKey);
|
||||
const account = await this.metaplex.rpc().getAccount(addr);
|
||||
const meta = toMetadata(toMetadataAccount(account));
|
||||
return {
|
||||
@@ -66,7 +71,7 @@ export class Token {
|
||||
.tokens()
|
||||
.mint({
|
||||
amount: token(amountParsed, info.decimals),
|
||||
mintAddress: this.tokenMintAddress,
|
||||
mintAddress: this.publicKey,
|
||||
})
|
||||
.run();
|
||||
return {
|
||||
@@ -82,7 +87,7 @@ export class Token {
|
||||
const result = await this.metaplex
|
||||
.tokens()
|
||||
.send({
|
||||
mintAddress: this.tokenMintAddress,
|
||||
mintAddress: this.publicKey,
|
||||
amount: token(amount, info.decimals),
|
||||
toOwner: new PublicKey(receiverAddress),
|
||||
})
|
||||
@@ -99,7 +104,7 @@ export class Token {
|
||||
async balanceOf(walletAddress: string): Promise<CurrencyValue> {
|
||||
const mint = await this.getMint();
|
||||
const addr = await getAssociatedTokenAddress(
|
||||
this.tokenMintAddress,
|
||||
this.publicKey,
|
||||
new PublicKey(walletAddress),
|
||||
);
|
||||
try {
|
||||
|
||||
@@ -12,4 +12,4 @@ export {
|
||||
TokenMetadataInputSchema,
|
||||
NFTCollectionMetadataInputSchema,
|
||||
} from "./types/contracts";
|
||||
export { NFTDropContractSchema } from "./types/contracts/nft-drop";
|
||||
export { NFTDropContractInputSchema } from "./types/contracts/nft-drop";
|
||||
|
||||
+12
-11
@@ -1,7 +1,6 @@
|
||||
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";
|
||||
import { NFTDrop } from "./contracts/nft-drop";
|
||||
import { Program } from "./contracts/program";
|
||||
@@ -16,17 +15,23 @@ import {
|
||||
setProvider,
|
||||
} from "@project-serum/anchor";
|
||||
import { Connection } from "@solana/web3.js";
|
||||
import { IpfsStorage, IStorage, PinataUploader } from "@thirdweb-dev/storage";
|
||||
import { IpfsUploader, ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
|
||||
export class ThirdwebSDK {
|
||||
static fromNetwork(network: Network, storage?: IStorage): ThirdwebSDK {
|
||||
return new ThirdwebSDK(new Connection(getUrlForNetwork(network)), storage);
|
||||
static fromNetwork(network: Network, storage?: ThirdwebStorage): ThirdwebSDK {
|
||||
return new ThirdwebSDK(
|
||||
new Connection(getUrlForNetwork(network), {
|
||||
disableRetryOnRateLimit: true,
|
||||
commitment: "confirmed",
|
||||
}),
|
||||
storage,
|
||||
);
|
||||
}
|
||||
|
||||
private connection: Connection;
|
||||
private metaplex: Metaplex;
|
||||
private anchorProvider: AnchorProvider;
|
||||
private storage: IStorage;
|
||||
private storage: ThirdwebStorage;
|
||||
|
||||
public registry: Registry;
|
||||
public deployer: Deployer;
|
||||
@@ -34,12 +39,8 @@ export class ThirdwebSDK {
|
||||
|
||||
constructor(
|
||||
connection: Connection,
|
||||
storage: IStorage = new IpfsStorage(
|
||||
DEFAULT_IPFS_GATEWAY,
|
||||
new PinataUploader(),
|
||||
{
|
||||
appendGatewayUrl: true,
|
||||
},
|
||||
storage: ThirdwebStorage = new ThirdwebStorage(
|
||||
new IpfsUploader({ uploadWithGatewayUrl: true }),
|
||||
),
|
||||
) {
|
||||
this.connection = connection;
|
||||
|
||||
@@ -2,11 +2,11 @@ import { ThirdwebSDK } from "../sdk";
|
||||
import { Network } from "../types/index";
|
||||
import { getPayer } from "./local-config";
|
||||
import { KeypairSigner } from "@metaplex-foundation/js";
|
||||
import { IpfsStorage, IStorage } from "@thirdweb-dev/storage";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
|
||||
export function createThirdwebSDK(
|
||||
network: Network,
|
||||
storage: IStorage = new IpfsStorage(),
|
||||
storage: ThirdwebStorage = new ThirdwebStorage(),
|
||||
): ThirdwebSDK {
|
||||
const payer = getPayer();
|
||||
const signer: KeypairSigner = {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user