Compare commits
26
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0f0125acf2 | ||
|
|
894cbefc63 | ||
|
|
73dc026ff9 | ||
|
|
af32007b9f | ||
|
|
5fba324ac1 | ||
|
|
bff433e121 | ||
|
|
68c8e3c4d7 | ||
|
|
4e8ff58208 | ||
|
|
d0d5a3c23d | ||
|
|
9eaa21d09a | ||
|
|
557429b5cf | ||
|
|
96d2dfd25d | ||
|
|
5862c558fb | ||
|
|
e913e0daa9 | ||
|
|
f7a74a3b7c | ||
|
|
73883f56d0 | ||
|
|
e47085af92 | ||
|
|
f525e2cc23 | ||
|
|
af85389dc0 | ||
|
|
ccb4ce8d00 | ||
|
|
4cdd0bd634 | ||
|
|
ea95c5f609 | ||
|
|
699a2b16fb | ||
|
|
a8267f912d | ||
|
|
c673e39f23 | ||
|
|
08d04fe4dc |
@@ -13,7 +13,7 @@ concurrency:
|
||||
jobs:
|
||||
build:
|
||||
name: Build and Test
|
||||
# timeout-minutes: 15
|
||||
timeout-minutes: 30
|
||||
runs-on: ubuntu-latest-32
|
||||
# To use Remote Caching, uncomment the next lines and follow the steps below.
|
||||
env:
|
||||
|
||||
@@ -12,7 +12,7 @@ concurrency:
|
||||
|
||||
jobs:
|
||||
integration-tests:
|
||||
timeout-minutes: 60
|
||||
timeout-minutes: 30
|
||||
runs-on: ubuntu-latest-32
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
@@ -13,8 +13,7 @@ concurrency:
|
||||
jobs:
|
||||
lint:
|
||||
name: Lint
|
||||
# timeout-minutes: 15
|
||||
# try out buildjet to speed up build and test
|
||||
timeout-minutes: 15
|
||||
runs-on: ubuntu-latest
|
||||
# To use Remote Caching, uncomment the next lines and follow the steps below.
|
||||
env:
|
||||
|
||||
@@ -41,6 +41,7 @@ jobs:
|
||||
|
||||
deploy:
|
||||
runs-on: ubuntu-latest-32
|
||||
timeout-minutes: 30
|
||||
needs: deploy-check
|
||||
if: needs.deploy-check.outputs.triggered == 'true'
|
||||
steps:
|
||||
|
||||
@@ -14,6 +14,7 @@ concurrency:
|
||||
jobs:
|
||||
release:
|
||||
name: Release
|
||||
timeout-minutes: 30
|
||||
runs-on: ubuntu-latest-32
|
||||
steps:
|
||||
- name: Checkout branch
|
||||
|
||||
@@ -5,6 +5,7 @@ on: [pull_request]
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest-32
|
||||
timeout-minutes: 30
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
+1
-1
@@ -36,7 +36,7 @@
|
||||
"@changesets/cli": "^2.24.3",
|
||||
"@manypkg/cli": "^0.19.1",
|
||||
"@manypkg/get-packages": "^1.1.3",
|
||||
"@trivago/prettier-plugin-sort-imports": "^3.3.0",
|
||||
"@trivago/prettier-plugin-sort-imports": "^4.0.0",
|
||||
"eslint": "^8.21.0",
|
||||
"postinstall-postinstall": "^2.1.0",
|
||||
"prettier": "^2.7.1",
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# @thirdweb-dev/auth
|
||||
|
||||
## 2.0.40
|
||||
|
||||
## 2.0.39
|
||||
|
||||
## 2.0.38
|
||||
|
||||
## 2.0.37
|
||||
|
||||
## 2.0.36
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@thirdweb-dev/auth",
|
||||
"version": "2.0.37",
|
||||
"version": "2.0.40",
|
||||
"exports": {
|
||||
"./next": {
|
||||
"module": "./next/dist/thirdweb-dev-auth-next.esm.js",
|
||||
|
||||
@@ -1,5 +1,32 @@
|
||||
# thirdweb
|
||||
|
||||
## 0.9.9
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#483](https://github.com/thirdweb-dev/js/pull/483) [`4e8ff58`](https://github.com/thirdweb-dev/js/commit/4e8ff582084c3d201314e6c5dff5b79f15eb6292) Thanks [@jnsdls](https://github.com/jnsdls)! - `thirdweb create` with usage of `forge` should now function correctly in windows CMD shell
|
||||
|
||||
- Updated dependencies [[`5fba324`](https://github.com/thirdweb-dev/js/commit/5fba324ac17ab02bc8f13d82a232bc5c6970c8e5), [`894cbef`](https://github.com/thirdweb-dev/js/commit/894cbefc6361f23fc528a6c6819f5c71793d46e4), [`557429b`](https://github.com/thirdweb-dev/js/commit/557429b5cfb3af2983ee01cf7d12d41ee0557593), [`73dc026`](https://github.com/thirdweb-dev/js/commit/73dc026ff9d0ac9099bd5a9a9cab8fdbfc0ae723), [`9eaa21d`](https://github.com/thirdweb-dev/js/commit/9eaa21d09ab9c700aea61a2a25f8ca9859d20857), [`68c8e3c`](https://github.com/thirdweb-dev/js/commit/68c8e3c4d79f5d56dc4414241bcca0d88285fcca), [`bff433e`](https://github.com/thirdweb-dev/js/commit/bff433e12150dc029e33a578219c8437a510da99)]:
|
||||
- @thirdweb-dev/sdk@3.6.10
|
||||
|
||||
## 0.9.8
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`e913e0d`](https://github.com/thirdweb-dev/js/commit/e913e0daa9ece9a6274f7ffa4e66bdcbf32e6ada), [`5862c55`](https://github.com/thirdweb-dev/js/commit/5862c558fb48604b5aca4defd1ccc06fc3536358), [`73883f5`](https://github.com/thirdweb-dev/js/commit/73883f56d0ed0a35ace8b98b96caa782443be22e), [`f7a74a3`](https://github.com/thirdweb-dev/js/commit/f7a74a3b7c489ada5b1ec435632a326334a33c9b)]:
|
||||
- @thirdweb-dev/sdk@3.6.9
|
||||
|
||||
## 0.9.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#454](https://github.com/thirdweb-dev/js/pull/454) [`c673e39`](https://github.com/thirdweb-dev/js/commit/c673e39f23ef082097d73d62910580e8fad400a0) Thanks [@jnsdls](https://github.com/jnsdls)! - upgraded dependencies
|
||||
|
||||
- [#461](https://github.com/thirdweb-dev/js/pull/461) [`af85389`](https://github.com/thirdweb-dev/js/commit/af85389dc07fb163b8f9d70093eb1b691e47561a) Thanks [@nachoiacovino](https://github.com/nachoiacovino)! - Fix deprecated links
|
||||
|
||||
- Updated dependencies [[`c673e39`](https://github.com/thirdweb-dev/js/commit/c673e39f23ef082097d73d62910580e8fad400a0), [`ea95c5f`](https://github.com/thirdweb-dev/js/commit/ea95c5f609e306e333ee0f73f7920503358ca848), [`699a2b1`](https://github.com/thirdweb-dev/js/commit/699a2b16fb991c474ec57db8f178e2601d631f39), [`4cdd0bd`](https://github.com/thirdweb-dev/js/commit/4cdd0bd6348494a256d7c6a2bdf8f7b5c20f6877), [`a8267f9`](https://github.com/thirdweb-dev/js/commit/a8267f912df84c58d3fe3f47b90bd474f73c84ca)]:
|
||||
- @thirdweb-dev/sdk@3.6.8
|
||||
|
||||
## 0.9.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -62,7 +62,7 @@ Deploying released contracts give deployers access to automatic SDKs to integrat
|
||||
npx thirdweb@latest detect
|
||||
```
|
||||
|
||||
As you're developing your contracts, you may want to implement [Extensions](https://portal.thirdweb.com/extensions) to unlock functionality on the SDKs (ie. nft minting with automatic upload to IPFS) and the dashboard (ie. generated UI to manage permissions). This command will show what extensions were detected on your contract, unlocking the corresponding functionality on the SDKs and dashboard.
|
||||
As you're developing your contracts, you may want to implement [Extensions](https://portal.thirdweb.com/contractkit) to unlock functionality on the SDKs (ie. nft minting with automatic upload to IPFS) and the dashboard (ie. generated UI to manage permissions). This command will show what extensions were detected on your contract, unlocking the corresponding functionality on the SDKs and dashboard.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "thirdweb",
|
||||
"main": "dist/cli/index.js",
|
||||
"version": "0.9.6",
|
||||
"version": "0.9.9",
|
||||
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/cli",
|
||||
"author": "thirdweb eng <[email protected]>",
|
||||
"license": "Apache-2.0",
|
||||
@@ -18,12 +18,12 @@
|
||||
"@types/prompts": "^2.0.14",
|
||||
"@types/rimraf": "^3.0.0",
|
||||
"@types/tar": "^6.1.2",
|
||||
"@types/update-notifier": "^5.1.0",
|
||||
"@types/update-notifier": "^6.0.1",
|
||||
"@types/validate-npm-package-name": "^4.0.0",
|
||||
"hardhat": "^2.9.3",
|
||||
"jest": "^28",
|
||||
"jest": "^29",
|
||||
"prettier": "^2.7.1",
|
||||
"ts-jest": "^28.0.8",
|
||||
"ts-jest": "^29.0.3",
|
||||
"ts-node": "^10.7.0",
|
||||
"tsup": "^6.2.3",
|
||||
"typescript": "^4.7.4"
|
||||
@@ -74,4 +74,4 @@
|
||||
"sourcemap": true,
|
||||
"clean": true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,13 +72,13 @@ export async function detectExtensions(options: any) {
|
||||
const enabledFeatures: Feature[] = features.enabledFeatures.map(
|
||||
(feature) => ({
|
||||
name: feature.name,
|
||||
reference: `https://portal.thirdweb.com/extensions/${feature.name.toLowerCase()}`,
|
||||
reference: `https://portal.thirdweb.com/interfaces/${feature.name.toLowerCase()}`,
|
||||
}),
|
||||
);
|
||||
const suggestedFeatures: Feature[] = features.suggestedFeatures.map(
|
||||
(feature) => ({
|
||||
name: feature.name,
|
||||
reference: `https://portal.thirdweb.com/extensions/${feature.name.toLowerCase()}`,
|
||||
reference: `https://portal.thirdweb.com/interfaces/${feature.name.toLowerCase()}`,
|
||||
}),
|
||||
);
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/* eslint-disable import/no-extraneous-dependencies */
|
||||
import { runCommand } from "./run-command";
|
||||
import { rm, writeFile } from "node:fs/promises";
|
||||
|
||||
/**
|
||||
* Spawn a package manager installation with either Yarn or NPM.
|
||||
@@ -8,12 +9,12 @@ import { runCommand } from "./run-command";
|
||||
*/
|
||||
export async function submodules(): Promise<void> {
|
||||
// Remove script folder because it clashes with contract templates (and not needed with deploy)
|
||||
await runCommand("rm", ["-rf", "script"]);
|
||||
await rm("script", { recursive: true, force: true });
|
||||
|
||||
// Reset and recursively add forge submodules
|
||||
await runCommand("rm", ["-rf", ".gitmodules"]);
|
||||
await runCommand("rm", ["-rf", "lib"]);
|
||||
await runCommand("touch", [".gitmodules"]);
|
||||
await rm(".gitmodules", { recursive: true, force: true });
|
||||
await rm("lib", { recursive: true, force: true });
|
||||
await writeFile(".gitmodules", "");
|
||||
await runCommand("git", [
|
||||
"submodule",
|
||||
"add",
|
||||
|
||||
@@ -1,5 +1,15 @@
|
||||
# @thirdweb-dev/contracts-js
|
||||
|
||||
## 1.2.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#463](https://github.com/thirdweb-dev/js/pull/463) [`557429b`](https://github.com/thirdweb-dev/js/commit/557429b5cfb3af2983ee01cf7d12d41ee0557593) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - PackVRF contract
|
||||
|
||||
- [#311](https://github.com/thirdweb-dev/js/pull/311) [`9eaa21d`](https://github.com/thirdweb-dev/js/commit/9eaa21d09ab9c700aea61a2a25f8ca9859d20857) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Update to v3.3.0 of contracts package
|
||||
|
||||
- [#311](https://github.com/thirdweb-dev/js/pull/311) [`9eaa21d`](https://github.com/thirdweb-dev/js/commit/9eaa21d09ab9c700aea61a2a25f8ca9859d20857) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Update to 3.1.10 of contracts package
|
||||
|
||||
## 1.2.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"main": "dist/thirdweb-dev-contracts-js-factories-TWMultichainRegistryLogic__factory.cjs.js",
|
||||
"module": "dist/thirdweb-dev-contracts-js-factories-TWMultichainRegistryLogic__factory.esm.js"
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"main": "dist/thirdweb-dev-contracts-js-factories-TWMultichainRegistryRouter__factory.cjs.js",
|
||||
"module": "dist/thirdweb-dev-contracts-js-factories-TWMultichainRegistryRouter__factory.esm.js"
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"main": "dist/thirdweb-dev-contracts-js-factories-TWMultichainRegistryStorage__factory.cjs.js",
|
||||
"module": "dist/thirdweb-dev-contracts-js-factories-TWMultichainRegistryStorage__factory.esm.js"
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"main": "dist/thirdweb-dev-contracts-js-factories-TWRegistryLegacy__factory.cjs.js",
|
||||
"module": "dist/thirdweb-dev-contracts-js-factories-TWRegistryLegacy__factory.esm.js"
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@thirdweb-dev/contracts-js",
|
||||
"version": "1.2.2",
|
||||
"version": "1.2.3",
|
||||
"main": "dist/thirdweb-dev-contracts-js.cjs.js",
|
||||
"module": "dist/thirdweb-dev-contracts-js.esm.js",
|
||||
"license": "Apache-2.0",
|
||||
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/contracts-js",
|
||||
"dependencies": {
|
||||
"@thirdweb-dev/contracts": "3.2.8-0"
|
||||
"@thirdweb-dev/contracts": "3.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/preset-env": "^7.18.10",
|
||||
|
||||
@@ -1,5 +1,17 @@
|
||||
# @thirdweb-dev/react
|
||||
|
||||
## 3.6.10
|
||||
|
||||
## 3.6.9
|
||||
|
||||
## 3.6.8
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#454](https://github.com/thirdweb-dev/js/pull/454) [`c673e39`](https://github.com/thirdweb-dev/js/commit/c673e39f23ef082097d73d62910580e8fad400a0) Thanks [@jnsdls](https://github.com/jnsdls)! - upgraded dependencies
|
||||
|
||||
- [#422](https://github.com/thirdweb-dev/js/pull/422) [`08d04fe`](https://github.com/thirdweb-dev/js/commit/08d04fe4dc74ba0d12625d7d4004c23f7dc330a2) Thanks [@yehia67](https://github.com/yehia67)! - Add `btnTitle` prop to the `ConnectWallet` component. Now you can set customized string or child component to the connect button.
|
||||
|
||||
## 3.6.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@thirdweb-dev/react",
|
||||
"version": "3.6.7",
|
||||
"version": "3.6.10",
|
||||
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/react",
|
||||
"author": "thirdweb eng <[email protected]>",
|
||||
"license": "Apache-2.0",
|
||||
@@ -97,19 +97,19 @@
|
||||
"dependencies": {
|
||||
"@emotion/react": "^11.10.0",
|
||||
"@emotion/styled": "^11.10.0",
|
||||
"@gnosis.pm/safe-core-sdk": "^3.1.1",
|
||||
"@gnosis.pm/safe-ethers-adapters": "0.1.0-alpha.11",
|
||||
"@gnosis.pm/safe-ethers-lib": "^1.6.1",
|
||||
"@react-icons/all-files": "^4.1.0",
|
||||
"@safe-global/safe-core-sdk": "^3.2.0",
|
||||
"@safe-global/safe-ethers-adapters": "0.1.0-alpha.13",
|
||||
"@safe-global/safe-ethers-lib": "^1.7.0",
|
||||
"@solana/wallet-adapter-phantom": "^0.9.17",
|
||||
"@tanstack/react-query": "^4.0.10",
|
||||
"@zag-js/menu": "^0.1.16",
|
||||
"@zag-js/react": "^0.1.18",
|
||||
"@zag-js/menu": "^0.3.0",
|
||||
"@zag-js/react": "^0.3.1",
|
||||
"buffer": "^6.0.3",
|
||||
"color": "^4.2.3",
|
||||
"copy-to-clipboard": "^3.3.2",
|
||||
"detect-browser": "^5.3.0",
|
||||
"magic-sdk": "^8.1.1",
|
||||
"magic-sdk": "^10.1.0",
|
||||
"mime": "^3.0.0",
|
||||
"react-cool-dimensions": "^2.0.7",
|
||||
"tiny-invariant": "^1.2.0",
|
||||
|
||||
@@ -63,6 +63,7 @@ interface ConnectWalletProps extends ThemeProviderProps {
|
||||
loginOptional?: boolean;
|
||||
};
|
||||
className?: string;
|
||||
btnTitle?: JSX.Element | string;
|
||||
}
|
||||
|
||||
let connecting = false;
|
||||
@@ -120,6 +121,7 @@ const chainIdToCurrencyMap: Record<
|
||||
export const ConnectWallet: React.FC<ConnectWalletProps> = ({
|
||||
auth,
|
||||
className,
|
||||
btnTitle,
|
||||
...themeProps
|
||||
}) => {
|
||||
const id = useId();
|
||||
@@ -248,7 +250,9 @@ export const ConnectWallet: React.FC<ConnectWalletProps> = ({
|
||||
</span>
|
||||
)
|
||||
) : (
|
||||
<span style={{ whiteSpace: "nowrap" }}>Connect Wallet</span>
|
||||
<span style={{ whiteSpace: "nowrap" }}>
|
||||
{btnTitle || "Connect Wallet"}
|
||||
</span>
|
||||
)}
|
||||
</Button>
|
||||
|
||||
|
||||
@@ -98,9 +98,9 @@ export class GnosisSafeConnector extends Connector {
|
||||
invariant(serverUrl, "Chain not supported");
|
||||
|
||||
const [safeEthersAdapters, safeCoreSdk, safeEthersLib] = await Promise.all([
|
||||
import("@gnosis.pm/safe-ethers-adapters"),
|
||||
import("@gnosis.pm/safe-core-sdk"),
|
||||
import("@gnosis.pm/safe-ethers-lib"),
|
||||
import("@safe-global/safe-ethers-adapters"),
|
||||
import("@safe-global/safe-core-sdk"),
|
||||
import("@safe-global/safe-ethers-lib"),
|
||||
]);
|
||||
|
||||
const ethAdapter = new safeEthersLib.default({
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,5 +1,52 @@
|
||||
# @thirdweb-dev/sdk
|
||||
|
||||
## 3.6.10
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#498](https://github.com/thirdweb-dev/js/pull/498) [`5fba324`](https://github.com/thirdweb-dev/js/commit/5fba324ac17ab02bc8f13d82a232bc5c6970c8e5) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Fix claim condition detection on legacy contracts
|
||||
|
||||
- [#502](https://github.com/thirdweb-dev/js/pull/502) [`894cbef`](https://github.com/thirdweb-dev/js/commit/894cbefc6361f23fc528a6c6819f5c71793d46e4) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Add intermediate event for contract deploy transactions before confirmation
|
||||
|
||||
- [#463](https://github.com/thirdweb-dev/js/pull/463) [`557429b`](https://github.com/thirdweb-dev/js/commit/557429b5cfb3af2983ee01cf7d12d41ee0557593) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - PackVRF integration
|
||||
|
||||
- [#500](https://github.com/thirdweb-dev/js/pull/500) [`73dc026`](https://github.com/thirdweb-dev/js/commit/73dc026ff9d0ac9099bd5a9a9cab8fdbfc0ae723) Thanks [@retocrooman](https://github.com/retocrooman)! - Getting the domain separator is not a required function. If an error occurs there, the permit it self will not be possible. Therefore, error handling should be performed appropriately.
|
||||
|
||||
- [#311](https://github.com/thirdweb-dev/js/pull/311) [`9eaa21d`](https://github.com/thirdweb-dev/js/commit/9eaa21d09ab9c700aea61a2a25f8ca9859d20857) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Add support for multi chain registry
|
||||
|
||||
- [#491](https://github.com/thirdweb-dev/js/pull/491) [`68c8e3c`](https://github.com/thirdweb-dev/js/commit/68c8e3c4d79f5d56dc4414241bcca0d88285fcca) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Use multichain registry to resolve contracts that cannot be resolved via IPFS
|
||||
|
||||
- [#494](https://github.com/thirdweb-dev/js/pull/494) [`bff433e`](https://github.com/thirdweb-dev/js/commit/bff433e12150dc029e33a578219c8437a510da99) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Fix deploy transaction listener for all deploy types `sdk.deployer.addDeployListener()`
|
||||
|
||||
- Updated dependencies [[`557429b`](https://github.com/thirdweb-dev/js/commit/557429b5cfb3af2983ee01cf7d12d41ee0557593), [`9eaa21d`](https://github.com/thirdweb-dev/js/commit/9eaa21d09ab9c700aea61a2a25f8ca9859d20857), [`9eaa21d`](https://github.com/thirdweb-dev/js/commit/9eaa21d09ab9c700aea61a2a25f8ca9859d20857)]:
|
||||
- @thirdweb-dev/contracts-js@1.2.3
|
||||
|
||||
## 3.6.9
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#465](https://github.com/thirdweb-dev/js/pull/465) [`e913e0d`](https://github.com/thirdweb-dev/js/commit/e913e0daa9ece9a6274f7ffa4e66bdcbf32e6ada) Thanks [@adam-maj](https://github.com/adam-maj)! - Fix passing options to factory
|
||||
|
||||
- [`5862c55`](https://github.com/thirdweb-dev/js/commit/5862c558fb48604b5aca4defd1ccc06fc3536358) Thanks [@kumaryash90](https://github.com/kumaryash90)! - update forwarder address
|
||||
|
||||
- [#466](https://github.com/thirdweb-dev/js/pull/466) [`73883f5`](https://github.com/thirdweb-dev/js/commit/73883f56d0ed0a35ace8b98b96caa782443be22e) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - workaround for signing typed data with magic.link signers
|
||||
|
||||
- [#468](https://github.com/thirdweb-dev/js/pull/468) [`f7a74a3`](https://github.com/thirdweb-dev/js/commit/f7a74a3b7c489ada5b1ec435632a326334a33c9b) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Expose `sdk.wallet.signTypedData()`
|
||||
|
||||
## 3.6.8
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#454](https://github.com/thirdweb-dev/js/pull/454) [`c673e39`](https://github.com/thirdweb-dev/js/commit/c673e39f23ef082097d73d62910580e8fad400a0) Thanks [@jnsdls](https://github.com/jnsdls)! - upgraded dependencies
|
||||
|
||||
- [#421](https://github.com/thirdweb-dev/js/pull/421) [`ea95c5f`](https://github.com/thirdweb-dev/js/commit/ea95c5f609e306e333ee0f73f7920503358ca848) Thanks [@yehia67](https://github.com/yehia67)! - Add new utilities to parse/format currencies. `toEther` & `toWei` as default 18 decimal and `toUnits` & `toDisplayValue` in case of different numbers of decimals.
|
||||
|
||||
- [#456](https://github.com/thirdweb-dev/js/pull/456) [`699a2b1`](https://github.com/thirdweb-dev/js/commit/699a2b16fb991c474ec57db8f178e2601d631f39) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - [SOL] Fix claiming drops with custom currencies
|
||||
|
||||
- [#458](https://github.com/thirdweb-dev/js/pull/458) [`4cdd0bd`](https://github.com/thirdweb-dev/js/commit/4cdd0bd6348494a256d7c6a2bdf8f7b5c20f6877) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Fixes for latest wallet package integration
|
||||
|
||||
- [#453](https://github.com/thirdweb-dev/js/pull/453) [`a8267f9`](https://github.com/thirdweb-dev/js/commit/a8267f912df84c58d3fe3f47b90bd474f73c84ca) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - [SOL] Drop fixes for large batch uploads
|
||||
|
||||
## 3.6.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@thirdweb-dev/sdk",
|
||||
"version": "3.6.7",
|
||||
"version": "3.6.10",
|
||||
"description": "The main thirdweb SDK.",
|
||||
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/sdk",
|
||||
"license": "Apache-2.0",
|
||||
@@ -62,7 +62,7 @@
|
||||
"build": "tsc && preconstruct build",
|
||||
"test:evm:all": "SWC_NODE_PROJECT=./tsconfig.test.json mocha --config './test/evm/.mocharc.json' --timeout 30000 --parallel './test/evm/**/*.test.ts'",
|
||||
"test:evm": "make test-evm",
|
||||
"test:evm:single": "SWC_NODE_PROJECT=./tsconfig.test.json mocha --config './test/evm/.mocharc.json' --timeout 30000",
|
||||
"test:evm:single": "SWC_NODE_PROJECT=./tsconfig.test.json mocha --config './test/evm/.mocharc.json' --timeout 90000",
|
||||
"node:solana:start": "DEBUG='amman:(info|error|debug)' amman start --forceClone",
|
||||
"node:solana:stop": "amman stop",
|
||||
"test:solana:all": "SWC_NODE_PROJECT=./tsconfig.test.json mocha --config './test/solana/.mocharc.json' --timeout 30000 --parallel './test/solana/**/*.test.ts'",
|
||||
@@ -85,7 +85,7 @@
|
||||
"@types/deep-equal-in-any-order": "^1.0.1",
|
||||
"@types/mocha": "^10.0.0",
|
||||
"@types/node": "^18.0.0",
|
||||
"@types/uuid": "^8.3.3",
|
||||
"@types/uuid": "^9.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.33.0",
|
||||
"@typescript-eslint/parser": "^5.33.0",
|
||||
"chai": "^4.3.6",
|
||||
@@ -146,4 +146,4 @@
|
||||
"url": "https://github.com/thirdweb-dev/js/issues"
|
||||
},
|
||||
"author": "thirdweb eng <[email protected]>"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
export const DEFAULT_API_KEY =
|
||||
"c6634ad2d97b74baf15ff556016830c251050e6c36b9da508ce3ec80095d3dc1";
|
||||
|
||||
export function getRpcUrl(network: string) {
|
||||
return `https://${network}.rpc.thirdweb.com/${DEFAULT_API_KEY}`;
|
||||
export function getRpcUrl(network: string, apikey = DEFAULT_API_KEY) {
|
||||
return `https://${network}.rpc.thirdweb.com/${apikey}`;
|
||||
}
|
||||
|
||||
@@ -192,3 +192,19 @@ export async function normalizeAmount(
|
||||
const decimals = await contractWrapper.readContract.decimals();
|
||||
return utils.parseUnits(AmountSchema.parse(amount), decimals);
|
||||
}
|
||||
|
||||
export function toEther(amount: BigNumberish): string {
|
||||
return utils.formatEther(amount);
|
||||
}
|
||||
|
||||
export function toWei(amount: Amount): BigNumber {
|
||||
return utils.parseEther(AmountSchema.parse(amount));
|
||||
}
|
||||
|
||||
export function toUnits(amount: Amount, decimals: BigNumberish): BigNumber {
|
||||
return utils.parseUnits(AmountSchema.parse(amount), decimals);
|
||||
}
|
||||
|
||||
export function toDisplayValue(amount: BigNumberish, decimals: BigNumberish): string {
|
||||
return utils.formatUnits(amount, decimals);
|
||||
}
|
||||
|
||||
@@ -286,7 +286,7 @@ export class TransactionError extends Error {
|
||||
raw: string,
|
||||
functionInfo: FunctionInfo | undefined,
|
||||
) {
|
||||
let builtErrorMsg = "Contract transaction failed\n\n";
|
||||
let builtErrorMsg = "\n| Contract transaction failed |\n\n";
|
||||
builtErrorMsg += `Message: ${reason}`;
|
||||
builtErrorMsg += "\n\n| Transaction info |\n";
|
||||
builtErrorMsg += withSpaces("from", from);
|
||||
@@ -320,9 +320,6 @@ export class TransactionError extends Error {
|
||||
// ignore if can't parse URL
|
||||
}
|
||||
builtErrorMsg += "\n\n";
|
||||
builtErrorMsg +=
|
||||
"Need help with this error? Join our community: https://discord.gg/thirdweb";
|
||||
builtErrorMsg += "\n\n\n\n";
|
||||
builtErrorMsg += "| Raw error |";
|
||||
builtErrorMsg += "\n\n";
|
||||
builtErrorMsg += raw;
|
||||
@@ -401,9 +398,19 @@ export async function convertToTWError(
|
||||
// not sure what this is, just throw it back
|
||||
raw = error.toString();
|
||||
}
|
||||
const reason =
|
||||
let reason =
|
||||
parseMessageParts(/.*?"message[^a-zA-Z0-9]*([^"\\]*).*?/, raw) ||
|
||||
parseMessageParts(/.*?"reason[^a-zA-Z0-9]*([^"\\]*).*?/, raw);
|
||||
if (reason && reason.toLowerCase().includes("cannot estimate gas")) {
|
||||
// the error might be in the next reason block
|
||||
const nextReason = parseMessageParts(
|
||||
/.*?"reason[^a-zA-Z0-9]*([^"\\]*).*?/,
|
||||
raw.slice(raw.indexOf(reason) + reason.length),
|
||||
);
|
||||
if (nextReason) {
|
||||
reason = nextReason;
|
||||
}
|
||||
}
|
||||
const data = parseMessageParts(/.*?"data[^a-zA-Z0-9]*([^"\\]*).*?/, raw);
|
||||
const rpcUrl = parseMessageParts(/.*?"url[^a-zA-Z0-9]*([^"\\]*).*?/, raw);
|
||||
let from = parseMessageParts(/.*?"from[^a-zA-Z0-9]*([^"\\]*).*?/, raw);
|
||||
|
||||
@@ -13,16 +13,14 @@ import {
|
||||
AbiFunction,
|
||||
AbiSchema,
|
||||
AbiTypeSchema,
|
||||
ContractInfoSchema,
|
||||
ContractSource,
|
||||
FullPublishMetadata,
|
||||
FullPublishMetadataSchemaOutput,
|
||||
PreDeployMetadata,
|
||||
PreDeployMetadataFetched,
|
||||
PreDeployMetadataFetchedSchema,
|
||||
PublishedMetadata,
|
||||
} from "../schema/contracts/custom";
|
||||
import { ExtensionNotImplementedError } from "./error";
|
||||
import { fetchContractMetadata } from "./metadata-resolver";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
import bs58 from "bs58";
|
||||
import { BaseContract, BigNumber, ethers } from "ethers";
|
||||
@@ -411,109 +409,6 @@ function isHexStrict(hex: string | number) {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @param address
|
||||
* @param provider
|
||||
* @param storage
|
||||
*/
|
||||
export async function fetchContractMetadataFromAddress(
|
||||
address: string,
|
||||
provider: ethers.providers.Provider,
|
||||
storage: ThirdwebStorage,
|
||||
) {
|
||||
const compilerMetadataUri = await resolveContractUriFromAddress(
|
||||
address,
|
||||
provider,
|
||||
);
|
||||
if (!compilerMetadataUri) {
|
||||
throw new Error(`Could not resolve metadata for contract at ${address}`);
|
||||
}
|
||||
return await fetchContractMetadata(compilerMetadataUri, storage);
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @param compilerMetadataUri
|
||||
* @param storage
|
||||
*/
|
||||
export async function fetchContractMetadata(
|
||||
compilerMetadataUri: string,
|
||||
storage: ThirdwebStorage,
|
||||
): Promise<PublishedMetadata> {
|
||||
const metadata = await storage.downloadJSON(compilerMetadataUri);
|
||||
if (!metadata || !metadata.output) {
|
||||
throw new Error(
|
||||
`Could not resolve metadata for contract at ${compilerMetadataUri}`,
|
||||
);
|
||||
}
|
||||
const abi = AbiSchema.parse(metadata.output.abi);
|
||||
const compilationTarget = metadata.settings.compilationTarget;
|
||||
const targets = Object.keys(compilationTarget);
|
||||
const name = compilationTarget[targets[0]];
|
||||
const info = ContractInfoSchema.parse({
|
||||
title: metadata.output.devdoc.title,
|
||||
author: metadata.output.devdoc.author,
|
||||
details: metadata.output.devdoc.detail,
|
||||
notice: metadata.output.userdoc.notice,
|
||||
});
|
||||
const licenses: string[] = [
|
||||
...new Set(
|
||||
Object.entries(metadata.sources).map(([, src]) => (src as any).license),
|
||||
),
|
||||
];
|
||||
return {
|
||||
name,
|
||||
abi,
|
||||
metadata,
|
||||
info,
|
||||
licenses,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @param publishedMetadata
|
||||
* @param storage
|
||||
*/
|
||||
export async function fetchSourceFilesFromMetadata(
|
||||
publishedMetadata: PublishedMetadata,
|
||||
storage: ThirdwebStorage,
|
||||
): Promise<ContractSource[]> {
|
||||
return await Promise.all(
|
||||
Object.entries(publishedMetadata.metadata.sources).map(
|
||||
async ([path, info]) => {
|
||||
const urls = (info as any).urls as string[];
|
||||
const ipfsLink = urls
|
||||
? urls.find((url) => url.includes("ipfs"))
|
||||
: undefined;
|
||||
if (ipfsLink) {
|
||||
const ipfsHash = ipfsLink.split("ipfs/")[1];
|
||||
// 5 sec timeout for sources that haven't been uploaded to ipfs
|
||||
const timeout = new Promise<string>((_r, rej) =>
|
||||
setTimeout(() => rej("timeout"), 5000),
|
||||
);
|
||||
const source = await Promise.race([
|
||||
(await storage.download(`ipfs://${ipfsHash}`)).text(),
|
||||
timeout,
|
||||
]);
|
||||
return {
|
||||
filename: path,
|
||||
source,
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
filename: path,
|
||||
source:
|
||||
(info as any).content ||
|
||||
"Could not find source for this contract",
|
||||
};
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @param publishMetadataUri
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
export * from "./error";
|
||||
export * from "./snapshots";
|
||||
export * from "./role";
|
||||
export * from "./metadata-resolver";
|
||||
export * from "./feature-detection";
|
||||
export * from "./version-checker";
|
||||
// export helpful util
|
||||
|
||||
@@ -0,0 +1,188 @@
|
||||
import { ThirdwebSDK } from "../core";
|
||||
import {
|
||||
Abi,
|
||||
PublishedMetadata,
|
||||
AbiSchema,
|
||||
ContractInfoSchema,
|
||||
ContractSource,
|
||||
} from "../schema";
|
||||
import { resolveContractUriFromAddress } from "./feature-detection";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
import { providers } from "ethers";
|
||||
|
||||
// Internal static cache
|
||||
const metadataCache: Record<string, PublishedMetadata> = {};
|
||||
// polygonSDK to fetch metadata from the multichain registry
|
||||
const polygonSDK = new ThirdwebSDK("polygon");
|
||||
|
||||
function getCacheKey(address: string, chainId: number) {
|
||||
return `${address}-${chainId}`;
|
||||
}
|
||||
|
||||
function putInCache(
|
||||
address: string,
|
||||
chainId: number,
|
||||
metadata: PublishedMetadata,
|
||||
) {
|
||||
metadataCache[getCacheKey(address, chainId)] = metadata;
|
||||
}
|
||||
|
||||
function getFromCache(address: string, chainId: number) {
|
||||
return metadataCache[getCacheKey(address, chainId)];
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @param address
|
||||
* @param provider
|
||||
* @param storage
|
||||
*/
|
||||
export async function fetchContractMetadataFromAddress(
|
||||
address: string,
|
||||
provider: providers.Provider,
|
||||
storage: ThirdwebStorage,
|
||||
) {
|
||||
const chainId = (await provider.getNetwork()).chainId;
|
||||
const cached = getFromCache(address, chainId);
|
||||
if (cached) {
|
||||
return cached;
|
||||
}
|
||||
let metadata: PublishedMetadata | undefined;
|
||||
try {
|
||||
const compilerMetadataUri = await resolveContractUriFromAddress(
|
||||
address,
|
||||
provider,
|
||||
);
|
||||
if (!compilerMetadataUri) {
|
||||
throw new Error(`Could not resolve metadata for contract at ${address}`);
|
||||
}
|
||||
metadata = await fetchContractMetadata(compilerMetadataUri, storage);
|
||||
} catch (e) {
|
||||
try {
|
||||
// try from multichain registry
|
||||
const importedUri =
|
||||
await polygonSDK.multiChainRegistry.getContractMetadataURI(
|
||||
chainId,
|
||||
address,
|
||||
);
|
||||
metadata = await fetchContractMetadata(importedUri, storage);
|
||||
} catch (err) {
|
||||
throw new Error(`Could not resolve metadata for contract at ${address}`);
|
||||
}
|
||||
}
|
||||
if (!metadata) {
|
||||
throw new Error(`Could not resolve metadata for contract at ${address}`);
|
||||
}
|
||||
putInCache(address, chainId, metadata);
|
||||
return metadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @param address
|
||||
* @param provider
|
||||
* @param storage
|
||||
* @returns
|
||||
*/
|
||||
export async function fetchAbiFromAddress(
|
||||
address: string,
|
||||
provider: providers.Provider,
|
||||
storage: ThirdwebStorage,
|
||||
): Promise<Abi | undefined> {
|
||||
try {
|
||||
const metadata = await fetchContractMetadataFromAddress(
|
||||
address,
|
||||
provider,
|
||||
storage,
|
||||
);
|
||||
if (metadata && metadata.abi) {
|
||||
return metadata.abi;
|
||||
}
|
||||
} catch (e) {
|
||||
// ignore and return undefined
|
||||
// will fallback to embedded ABIs for prebuilts
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @param compilerMetadataUri
|
||||
* @param storage
|
||||
*/
|
||||
export async function fetchContractMetadata(
|
||||
compilerMetadataUri: string,
|
||||
storage: ThirdwebStorage,
|
||||
): Promise<PublishedMetadata> {
|
||||
const metadata = await storage.downloadJSON(compilerMetadataUri);
|
||||
if (!metadata || !metadata.output) {
|
||||
throw new Error(
|
||||
`Could not resolve metadata for contract at ${compilerMetadataUri}`,
|
||||
);
|
||||
}
|
||||
const abi = AbiSchema.parse(metadata.output.abi);
|
||||
const compilationTarget = metadata.settings.compilationTarget;
|
||||
const targets = Object.keys(compilationTarget);
|
||||
const name = compilationTarget[targets[0]];
|
||||
const info = ContractInfoSchema.parse({
|
||||
title: metadata.output.devdoc.title,
|
||||
author: metadata.output.devdoc.author,
|
||||
details: metadata.output.devdoc.detail,
|
||||
notice: metadata.output.userdoc.notice,
|
||||
});
|
||||
const licenses: string[] = [
|
||||
...new Set(
|
||||
Object.entries(metadata.sources).map(([, src]) => (src as any).license),
|
||||
),
|
||||
];
|
||||
return {
|
||||
name,
|
||||
abi,
|
||||
metadata,
|
||||
info,
|
||||
licenses,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @param publishedMetadata
|
||||
* @param storage
|
||||
*/
|
||||
export async function fetchSourceFilesFromMetadata(
|
||||
publishedMetadata: PublishedMetadata,
|
||||
storage: ThirdwebStorage,
|
||||
): Promise<ContractSource[]> {
|
||||
return await Promise.all(
|
||||
Object.entries(publishedMetadata.metadata.sources).map(
|
||||
async ([path, info]) => {
|
||||
const urls = (info as any).urls as string[];
|
||||
const ipfsLink = urls
|
||||
? urls.find((url) => url.includes("ipfs"))
|
||||
: undefined;
|
||||
if (ipfsLink) {
|
||||
const ipfsHash = ipfsLink.split("ipfs/")[1];
|
||||
// 3 sec timeout for sources that haven't been uploaded to ipfs
|
||||
const timeout = new Promise<string>((_r, rej) =>
|
||||
setTimeout(() => rej("timeout"), 3000),
|
||||
);
|
||||
const source = await Promise.race([
|
||||
(await storage.download(`ipfs://${ipfsHash}`)).text(),
|
||||
timeout,
|
||||
]);
|
||||
return {
|
||||
filename: path,
|
||||
source,
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
filename: path,
|
||||
source:
|
||||
(info as any).content ||
|
||||
"Could not find source for this contract",
|
||||
};
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -71,7 +71,14 @@ async function getDomainSeperator(signer: Signer, contractAddress: string) {
|
||||
try {
|
||||
return await contract.DOMAIN_SEPARATOR();
|
||||
} catch (err) {
|
||||
return await contract.getDomainSeperator();
|
||||
try {
|
||||
return await contract.getDomainSeperator();
|
||||
} catch (err2) {
|
||||
console.error(
|
||||
'Error getting domain separator',
|
||||
err2
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -49,6 +49,7 @@ export async function signTypedDataInternal(
|
||||
);
|
||||
|
||||
let signature = "";
|
||||
const signerAddress = (await signer.getAddress()).toLowerCase();
|
||||
|
||||
// an indirect way for accessing walletconnect's underlying provider
|
||||
if ((provider as any)?.provider?.isWalletConnect) {
|
||||
@@ -66,11 +67,19 @@ export async function signTypedDataInternal(
|
||||
} catch (err: any) {
|
||||
if (err?.message?.includes("Method eth_signTypedData_v4 not supported")) {
|
||||
signature = await provider.send("eth_signTypedData", [
|
||||
(await signer.getAddress()).toLowerCase(),
|
||||
signerAddress,
|
||||
JSON.stringify(payload),
|
||||
]);
|
||||
} else {
|
||||
throw err;
|
||||
// magic.link signer only supports this way
|
||||
try {
|
||||
await provider.send("eth_signTypedData_v4", [
|
||||
signerAddress,
|
||||
JSON.stringify(payload),
|
||||
]);
|
||||
} catch (finalErr: any) {
|
||||
throw finalErr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ export const OZ_DEFENDER_FORWARDER_ADDRESS =
|
||||
const TWRegistry_address = "0x7c487845f98938Bb955B1D5AD069d9a30e4131fd";
|
||||
const TWFactory_address = "0x5DBC7B840baa9daBcBe9D2492E45D7244B54A2A0";
|
||||
const ContractPublisher_address = "0x664244560eBa21Bf82d7150C791bE1AbcD5B4cd7"; // Polygon only
|
||||
const MultichainRegistry_address = "0xcdAD8FA86e18538aC207872E8ff3536501431B73"; // Polygon only
|
||||
|
||||
/**
|
||||
* @internal
|
||||
@@ -92,7 +93,7 @@ export const CONTRACT_ADDRESSES: Record<
|
||||
twBYOCRegistry: "0x3E6eE864f850F5e5A98bc950B68E181Cf4010F23",
|
||||
},
|
||||
[ChainId.Arbitrum]: {
|
||||
openzeppelinForwarder: OZ_DEFENDER_FORWARDER_ADDRESS,
|
||||
openzeppelinForwarder: "0x8cbc8B5d71702032904750A66AEfE8B603eBC538",
|
||||
openzeppelinForwarderEOA: "0x4f247c69184ad61036EC2Bb3213b69F10FbEDe1F",
|
||||
biconomyForwarder: "0xfe0fa3C06d03bDC7fb49c892BbB39113B534fB57",
|
||||
twFactory: "0xd24b3de085CFd8c54b94feAD08a7962D343E6DE0",
|
||||
@@ -109,7 +110,7 @@ export const CONTRACT_ADDRESSES: Record<
|
||||
twBYOCRegistry: constants.AddressZero,
|
||||
},
|
||||
[ChainId.Optimism]: {
|
||||
openzeppelinForwarder: OZ_DEFENDER_FORWARDER_ADDRESS,
|
||||
openzeppelinForwarder: "0x8cbc8B5d71702032904750A66AEfE8B603eBC538",
|
||||
openzeppelinForwarderEOA: "0x7e80648EB2071E26937F9D42A513ccf4815fc702",
|
||||
biconomyForwarder: "0xefba8a2a82ec1fb1273806174f5e28fbb917cf95",
|
||||
twFactory: "0xd24b3de085CFd8c54b94feAD08a7962D343E6DE0",
|
||||
@@ -291,6 +292,19 @@ export function getContractPublisherAddress() {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export function getMultichainRegistryAddress() {
|
||||
// eslint-disable-next-line turbo/no-undeclared-env-vars
|
||||
if (process.env.multiChainRegistryAddress) {
|
||||
// eslint-disable-next-line turbo/no-undeclared-env-vars
|
||||
return process.env.multiChainRegistryAddress as string;
|
||||
} else {
|
||||
return MultichainRegistry_address;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param chainId - chain id
|
||||
|
||||
@@ -53,6 +53,7 @@ import {
|
||||
FEATURE_ROYALTY,
|
||||
FEATURE_APPURI,
|
||||
FEATURE_GASLESS,
|
||||
FEATURE_PACK_VRF,
|
||||
} from "./thirdweb-features";
|
||||
|
||||
/**
|
||||
@@ -106,7 +107,8 @@ export type Feature =
|
||||
| typeof FEATURE_METADATA
|
||||
| typeof FEATURE_APPURI
|
||||
| typeof FEATURE_OWNER
|
||||
| typeof FEATURE_GASLESS;
|
||||
| typeof FEATURE_GASLESS
|
||||
| typeof FEATURE_PACK_VRF;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
@@ -135,4 +137,5 @@ export const SUPPORTED_FEATURES: Record<string, Feature> = {
|
||||
[FEATURE_APPURI.name]: FEATURE_APPURI,
|
||||
[FEATURE_OWNER.name]: FEATURE_OWNER,
|
||||
[FEATURE_GASLESS.name]: FEATURE_GASLESS,
|
||||
[FEATURE_PACK_VRF.name]: FEATURE_PACK_VRF,
|
||||
};
|
||||
|
||||
@@ -176,3 +176,16 @@ export const NATIVE_TOKENS: Record<
|
||||
export function getNativeTokenByChainId(chainId: ChainId): NativeToken {
|
||||
return NATIVE_TOKENS[chainId as SUPPORTED_CHAIN_ID];
|
||||
}
|
||||
|
||||
export const LINK_TOKEN_ADDRESS: Record<number, string> = {
|
||||
[ChainId.Mainnet]: "0x514910771AF9Ca656af840dff83E8264EcF986CA",
|
||||
[ChainId.Goerli]: "0x326C977E6efc84E512bB9C30f76E30c160eD06FB",
|
||||
[ChainId.BinanceSmartChainMainnet]:
|
||||
"0x404460C6A5EdE2D891e8297795264fDe62ADBB75",
|
||||
[ChainId.Polygon]: "0xb0897686c545045aFc77CF20eC7A532E3120E0F1",
|
||||
[ChainId.Mumbai]: "0x326C977E6efc84E512bB9C30f76E30c160eD06FB",
|
||||
[ChainId.Avalanche]: "0x5947BB275c521040051D82396192181b413227A3",
|
||||
[ChainId.AvalancheFujiTestnet]: "0x0b9d5D9136855f6FEc3c0993feE6E9CE8a297846",
|
||||
[ChainId.Fantom]: "0x6F43FF82CCA38001B6699a8AC47A2d0E66939407",
|
||||
[ChainId.FantomTestnet]: "0xfaFedb041c0DD4fA2Dc0d87a6B0979Ee6FA7af5F",
|
||||
};
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import DropERC1155_V2Abi from "@thirdweb-dev/contracts-js/dist/abis/DropERC1155_V2.json";
|
||||
import IBurnableERC1155Abi from "@thirdweb-dev/contracts-js/dist/abis/IBurnableERC1155.json";
|
||||
import IClaimableERC1155 from "@thirdweb-dev/contracts-js/dist/abis/IClaimableERC1155.json";
|
||||
import DelayedRevealAbi from "@thirdweb-dev/contracts-js/dist/abis/IDelayedReveal.json";
|
||||
import IDropMultiPhase1155 from "@thirdweb-dev/contracts-js/dist/abis/IDrop1155.json";
|
||||
import DropERC1155_V2Abi from "@thirdweb-dev/contracts-js/dist/abis/IDropERC1155_V2.json";
|
||||
import IDropSinglePhase1155 from "@thirdweb-dev/contracts-js/dist/abis/IDropSinglePhase1155.json";
|
||||
import IDropSinglePhase1155_V1 from "@thirdweb-dev/contracts-js/dist/abis/IDropSinglePhase1155_V1.json";
|
||||
import Erc1155Abi from "@thirdweb-dev/contracts-js/dist/abis/IERC1155.json";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import DropERC20_V2Abi from "@thirdweb-dev/contracts-js/dist/abis/DropERC20_V2.json";
|
||||
import IBurnableERC20Abi from "@thirdweb-dev/contracts-js/dist/abis/IBurnableERC20.json";
|
||||
import IDrop from "@thirdweb-dev/contracts-js/dist/abis/IDrop.json";
|
||||
import DropERC20_V2Abi from "@thirdweb-dev/contracts-js/dist/abis/IDropERC20_V2.json";
|
||||
import IDropSinglePhase from "@thirdweb-dev/contracts-js/dist/abis/IDropSinglePhase.json";
|
||||
import IDropSinglePhaseV1 from "@thirdweb-dev/contracts-js/dist/abis/IDropSinglePhase_V1.json";
|
||||
import ERC20Abi from "@thirdweb-dev/contracts-js/dist/abis/IERC20.json";
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import DropERC721_V3Abi from "@thirdweb-dev/contracts-js/dist/abis/DropERC721_V3.json";
|
||||
import IBurnableERC721Abi from "@thirdweb-dev/contracts-js/dist/abis/IBurnableERC721.json";
|
||||
import IClaimableERC721 from "@thirdweb-dev/contracts-js/dist/abis/IClaimableERC721.json";
|
||||
import DelayedRevealAbi from "@thirdweb-dev/contracts-js/dist/abis/IDelayedReveal.json";
|
||||
import IDrop from "@thirdweb-dev/contracts-js/dist/abis/IDrop.json";
|
||||
import DropERC721_V3Abi from "@thirdweb-dev/contracts-js/dist/abis/IDropERC721_V3.json";
|
||||
import IDropSinglePhase from "@thirdweb-dev/contracts-js/dist/abis/IDropSinglePhase.json";
|
||||
import IDropSinglePhaseV1 from "@thirdweb-dev/contracts-js/dist/abis/IDropSinglePhase_V1.json";
|
||||
import Erc721Abi from "@thirdweb-dev/contracts-js/dist/abis/IERC721.json";
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import IERC2771ContextAbi from "@thirdweb-dev/contracts-js/dist/abis/ERC2771Context.json";
|
||||
import IAppURI from "@thirdweb-dev/contracts-js/dist/abis/IAppURI.json";
|
||||
import IContractMetadataAbi from "@thirdweb-dev/contracts-js/dist/abis/IContractMetadata.json";
|
||||
import IPackVRFAbi from "@thirdweb-dev/contracts-js/dist/abis/IPackVRFDirect.json";
|
||||
import IPermissionsAbi from "@thirdweb-dev/contracts-js/dist/abis/IPermissions.json";
|
||||
import IPermissionsEnumerableAbi from "@thirdweb-dev/contracts-js/dist/abis/IPermissionsEnumerable.json";
|
||||
import IThirdwebPlatformFeeAbi from "@thirdweb-dev/contracts-js/dist/abis/IPlatformFee.json";
|
||||
@@ -109,3 +110,14 @@ export const FEATURE_GASLESS = {
|
||||
abis: [IERC2771ContextAbi],
|
||||
features: {},
|
||||
} as const;
|
||||
|
||||
export const FEATURE_PACK_VRF = {
|
||||
name: "PackVRF",
|
||||
namespace: "pack.vrf",
|
||||
docLinks: {
|
||||
sdk: "sdk.packvrf",
|
||||
contracts: "IPackVRFDirect",
|
||||
},
|
||||
abis: [IPackVRFAbi],
|
||||
features: {},
|
||||
} as const;
|
||||
|
||||
@@ -168,3 +168,21 @@ export function getReadOnlyProvider(network: string, chainId?: number) {
|
||||
return ethers.getDefaultProvider(network);
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const ChainIdToName: Record<number, string> = {
|
||||
[ChainId.Polygon]: "polygon",
|
||||
[ChainId.Mumbai]: "mumbai",
|
||||
[ChainId.Goerli]: "goerli",
|
||||
[ChainId.Mainnet]: "mainnet",
|
||||
[ChainId.Optimism]: "optimism",
|
||||
[ChainId.OptimismGoerli]: "optimism-goerli",
|
||||
[ChainId.Arbitrum]: "arbitrum",
|
||||
[ChainId.ArbitrumGoerli]: "arbitrum-goerli",
|
||||
[ChainId.Fantom]: "fantom",
|
||||
[ChainId.FantomTestnet]: "fantom-testnet",
|
||||
[ChainId.Avalanche]: "avalanche",
|
||||
[ChainId.AvalancheFujiTestnet]: "avalanche-fuji",
|
||||
[ChainId.BinanceSmartChainMainnet]: "binance",
|
||||
[ChainId.BinanceSmartChainTestnet]: "binance-testnet",
|
||||
};
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
import { getPrebuiltInfo } from "../common/legacy";
|
||||
import { fetchAbiFromAddress } from "../common/metadata-resolver";
|
||||
import { ALL_ROLES } from "../common/role";
|
||||
import { getSignerAndProvider } from "../core/classes/rpc-connection-handler";
|
||||
import type {
|
||||
@@ -19,10 +20,10 @@ import {
|
||||
TokenErc721ContractSchema,
|
||||
VoteContractSchema,
|
||||
} from "../schema";
|
||||
import { CustomContractSchema } from "../schema/contracts/custom";
|
||||
import { Abi, CustomContractSchema } from "../schema/contracts/custom";
|
||||
import { DropErc20ContractSchema } from "../schema/contracts/drop-erc20";
|
||||
import { MultiwrapContractSchema } from "../schema/contracts/multiwrap";
|
||||
import type { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
import { ethers } from "ethers";
|
||||
|
||||
type InitalizeParams = [
|
||||
@@ -42,7 +43,7 @@ export const EditionDropInitializer = {
|
||||
) => {
|
||||
const [, provider] = getSignerAndProvider(network, options);
|
||||
const [abi, contract, _network] = await Promise.all([
|
||||
await EditionDropInitializer.getAbi(address, provider),
|
||||
await EditionDropInitializer.getAbi(address, provider, storage),
|
||||
import("./prebuilt-implementations/edition-drop"),
|
||||
provider.getNetwork(),
|
||||
]);
|
||||
@@ -56,7 +57,16 @@ export const EditionDropInitializer = {
|
||||
_network.chainId,
|
||||
);
|
||||
},
|
||||
getAbi: async (address: string, provider: ethers.providers.Provider) => {
|
||||
getAbi: async (
|
||||
address: string,
|
||||
provider: ethers.providers.Provider,
|
||||
storage: ThirdwebStorage,
|
||||
) => {
|
||||
const abi = await fetchAbiFromAddress(address, provider, storage);
|
||||
if (abi) {
|
||||
return abi;
|
||||
}
|
||||
// Deprecated - only needed for backwards compatibility with non-released contracts - should remove in v4
|
||||
const contractInfo = await getContractInfo(address, provider);
|
||||
return !contractInfo || contractInfo.version > 2
|
||||
? (await import("@thirdweb-dev/contracts-js/dist/abis/DropERC1155.json"))
|
||||
@@ -79,7 +89,7 @@ export const EditionInitializer = {
|
||||
) => {
|
||||
const [, provider] = getSignerAndProvider(network, options);
|
||||
const [abi, contract, _network] = await Promise.all([
|
||||
EditionInitializer.getAbi(address, provider),
|
||||
EditionInitializer.getAbi(address, provider, storage),
|
||||
import("./prebuilt-implementations/edition"),
|
||||
provider.getNetwork(),
|
||||
]);
|
||||
@@ -93,7 +103,16 @@ export const EditionInitializer = {
|
||||
_network.chainId,
|
||||
);
|
||||
},
|
||||
getAbi: async (address: string, provider: ethers.providers.Provider) => {
|
||||
getAbi: async (
|
||||
address: string,
|
||||
provider: ethers.providers.Provider,
|
||||
storage: ThirdwebStorage,
|
||||
) => {
|
||||
const abi = await fetchAbiFromAddress(address, provider, storage);
|
||||
if (abi) {
|
||||
return abi;
|
||||
}
|
||||
// Deprecated - only needed for backwards compatibility with non-released contracts - should remove in v4
|
||||
return (
|
||||
await import("@thirdweb-dev/contracts-js/dist/abis/TokenERC1155.json")
|
||||
).default;
|
||||
@@ -110,7 +129,7 @@ export const MarketplaceInitializer = {
|
||||
) => {
|
||||
const [, provider] = getSignerAndProvider(network, options);
|
||||
const [abi, contract, _network] = await Promise.all([
|
||||
MarketplaceInitializer.getAbi(address, provider),
|
||||
MarketplaceInitializer.getAbi(address, provider, storage),
|
||||
import("./prebuilt-implementations/marketplace"),
|
||||
provider.getNetwork(),
|
||||
]);
|
||||
@@ -124,7 +143,16 @@ export const MarketplaceInitializer = {
|
||||
_network.chainId,
|
||||
);
|
||||
},
|
||||
getAbi: async (address: string, provider: ethers.providers.Provider) => {
|
||||
getAbi: async (
|
||||
address: string,
|
||||
provider: ethers.providers.Provider,
|
||||
storage: ThirdwebStorage,
|
||||
) => {
|
||||
const abi = await fetchAbiFromAddress(address, provider, storage);
|
||||
if (abi) {
|
||||
return abi;
|
||||
}
|
||||
// Deprecated - only needed for backwards compatibility with non-released contracts - should remove in v4
|
||||
return (
|
||||
await import("@thirdweb-dev/contracts-js/dist/abis/Marketplace.json")
|
||||
).default;
|
||||
@@ -141,7 +169,7 @@ export const MultiwrapInitializer = {
|
||||
) => {
|
||||
const [, provider] = getSignerAndProvider(network, options);
|
||||
const [abi, contract, _network] = await Promise.all([
|
||||
MultiwrapInitializer.getAbi(address, provider),
|
||||
MultiwrapInitializer.getAbi(address, provider, storage),
|
||||
import("./prebuilt-implementations/multiwrap"),
|
||||
provider.getNetwork(),
|
||||
]);
|
||||
@@ -155,7 +183,16 @@ export const MultiwrapInitializer = {
|
||||
_network.chainId,
|
||||
);
|
||||
},
|
||||
getAbi: async (address: string, provider: ethers.providers.Provider) => {
|
||||
getAbi: async (
|
||||
address: string,
|
||||
provider: ethers.providers.Provider,
|
||||
storage: ThirdwebStorage,
|
||||
) => {
|
||||
const abi = await fetchAbiFromAddress(address, provider, storage);
|
||||
if (abi) {
|
||||
return abi;
|
||||
}
|
||||
// Deprecated - only needed for backwards compatibility with non-released contracts - should remove in v4
|
||||
return (await import("@thirdweb-dev/contracts-js/dist/abis/Multiwrap.json"))
|
||||
.default;
|
||||
},
|
||||
@@ -172,7 +209,7 @@ export const NFTCollectionInitializer = {
|
||||
) => {
|
||||
const [, provider] = getSignerAndProvider(network, options);
|
||||
const [abi, contract, _network] = await Promise.all([
|
||||
NFTCollectionInitializer.getAbi(address, provider),
|
||||
NFTCollectionInitializer.getAbi(address, provider, storage),
|
||||
import("./prebuilt-implementations/nft-collection"),
|
||||
provider.getNetwork(),
|
||||
]);
|
||||
@@ -186,7 +223,16 @@ export const NFTCollectionInitializer = {
|
||||
_network.chainId,
|
||||
);
|
||||
},
|
||||
getAbi: async (address: string, provider: ethers.providers.Provider) => {
|
||||
getAbi: async (
|
||||
address: string,
|
||||
provider: ethers.providers.Provider,
|
||||
storage: ThirdwebStorage,
|
||||
) => {
|
||||
const abi = await fetchAbiFromAddress(address, provider, storage);
|
||||
if (abi) {
|
||||
return abi;
|
||||
}
|
||||
// Deprecated - only needed for backwards compatibility with non-released contracts - should remove in v4
|
||||
return (
|
||||
await import("@thirdweb-dev/contracts-js/dist/abis/TokenERC721.json")
|
||||
).default;
|
||||
@@ -203,7 +249,7 @@ export const NFTDropInitializer = {
|
||||
) => {
|
||||
const [, provider] = getSignerAndProvider(network, options);
|
||||
const [abi, contract, _network] = await Promise.all([
|
||||
NFTDropInitializer.getAbi(address, provider),
|
||||
NFTDropInitializer.getAbi(address, provider, storage),
|
||||
import("./prebuilt-implementations/nft-drop"),
|
||||
provider.getNetwork(),
|
||||
]);
|
||||
@@ -217,7 +263,16 @@ export const NFTDropInitializer = {
|
||||
_network.chainId,
|
||||
);
|
||||
},
|
||||
getAbi: async (address: string, provider: ethers.providers.Provider) => {
|
||||
getAbi: async (
|
||||
address: string,
|
||||
provider: ethers.providers.Provider,
|
||||
storage: ThirdwebStorage,
|
||||
) => {
|
||||
const abi = await fetchAbiFromAddress(address, provider, storage);
|
||||
if (abi) {
|
||||
return abi;
|
||||
}
|
||||
// Deprecated - only needed for backwards compatibility with non-released contracts - should remove in v4
|
||||
const contractInfo = await getContractInfo(address, provider);
|
||||
return !contractInfo || contractInfo.version > 3
|
||||
? (await import("@thirdweb-dev/contracts-js/dist/abis/DropERC721.json"))
|
||||
@@ -241,7 +296,7 @@ export const PackInitializer = {
|
||||
) => {
|
||||
const [, provider] = getSignerAndProvider(network, options);
|
||||
const [abi, contract, _network] = await Promise.all([
|
||||
PackInitializer.getAbi(address, provider),
|
||||
PackInitializer.getAbi(address, provider, storage),
|
||||
import("./prebuilt-implementations/pack"),
|
||||
provider.getNetwork(),
|
||||
]);
|
||||
@@ -255,7 +310,16 @@ export const PackInitializer = {
|
||||
_network.chainId,
|
||||
);
|
||||
},
|
||||
getAbi: async (address: string, provider: ethers.providers.Provider) => {
|
||||
getAbi: async (
|
||||
address: string,
|
||||
provider: ethers.providers.Provider,
|
||||
storage: ThirdwebStorage,
|
||||
): Promise<Abi> => {
|
||||
const abi = await fetchAbiFromAddress(address, provider, storage);
|
||||
if (abi) {
|
||||
return abi;
|
||||
}
|
||||
// Deprecated - only needed for backwards compatibility with non-released contracts - should remove in v4
|
||||
return (await import("@thirdweb-dev/contracts-js/dist/abis/Pack.json"))
|
||||
.default;
|
||||
},
|
||||
@@ -272,7 +336,7 @@ export const SignatureDropInitializer = {
|
||||
) => {
|
||||
const [, provider] = getSignerAndProvider(network, options);
|
||||
const [abi, contract, _network] = await Promise.all([
|
||||
SignatureDropInitializer.getAbi(address, provider),
|
||||
SignatureDropInitializer.getAbi(address, provider, storage),
|
||||
import("./prebuilt-implementations/signature-drop"),
|
||||
provider.getNetwork(),
|
||||
]);
|
||||
@@ -286,7 +350,16 @@ export const SignatureDropInitializer = {
|
||||
_network.chainId,
|
||||
);
|
||||
},
|
||||
getAbi: async (address: string, provider: ethers.providers.Provider) => {
|
||||
getAbi: async (
|
||||
address: string,
|
||||
provider: ethers.providers.Provider,
|
||||
storage: ThirdwebStorage,
|
||||
) => {
|
||||
const abi = await fetchAbiFromAddress(address, provider, storage);
|
||||
if (abi) {
|
||||
return abi;
|
||||
}
|
||||
// Deprecated - only needed for backwards compatibility with non-released contracts - should remove in v4
|
||||
const contractInfo = await getContractInfo(address, provider);
|
||||
return !contractInfo || contractInfo.version > 4
|
||||
? (
|
||||
@@ -313,7 +386,7 @@ export const SplitInitializer = {
|
||||
) => {
|
||||
const [, provider] = getSignerAndProvider(network, options);
|
||||
const [abi, contract, _network] = await Promise.all([
|
||||
SplitInitializer.getAbi(address, provider),
|
||||
SplitInitializer.getAbi(address, provider, storage),
|
||||
import("./prebuilt-implementations/split"),
|
||||
provider.getNetwork(),
|
||||
]);
|
||||
@@ -327,7 +400,16 @@ export const SplitInitializer = {
|
||||
_network.chainId,
|
||||
);
|
||||
},
|
||||
getAbi: async (address: string, provider: ethers.providers.Provider) => {
|
||||
getAbi: async (
|
||||
address: string,
|
||||
provider: ethers.providers.Provider,
|
||||
storage: ThirdwebStorage,
|
||||
) => {
|
||||
const abi = await fetchAbiFromAddress(address, provider, storage);
|
||||
if (abi) {
|
||||
return abi;
|
||||
}
|
||||
// Deprecated - only needed for backwards compatibility with non-released contracts - should remove in v4
|
||||
return (await import("@thirdweb-dev/contracts-js/dist/abis/Split.json"))
|
||||
.default;
|
||||
},
|
||||
@@ -344,7 +426,7 @@ export const TokenDropInitializer = {
|
||||
) => {
|
||||
const [, provider] = getSignerAndProvider(network, options);
|
||||
const [abi, contract, _network] = await Promise.all([
|
||||
TokenDropInitializer.getAbi(address, provider),
|
||||
TokenDropInitializer.getAbi(address, provider, storage),
|
||||
import("./prebuilt-implementations/token-drop"),
|
||||
provider.getNetwork(),
|
||||
]);
|
||||
@@ -358,7 +440,16 @@ export const TokenDropInitializer = {
|
||||
_network.chainId,
|
||||
);
|
||||
},
|
||||
getAbi: async (address: string, provider: ethers.providers.Provider) => {
|
||||
getAbi: async (
|
||||
address: string,
|
||||
provider: ethers.providers.Provider,
|
||||
storage: ThirdwebStorage,
|
||||
) => {
|
||||
const abi = await fetchAbiFromAddress(address, provider, storage);
|
||||
if (abi) {
|
||||
return abi;
|
||||
}
|
||||
// Deprecated - only needed for backwards compatibility with non-released contracts - should remove in v4
|
||||
const contractInfo = await getContractInfo(address, provider);
|
||||
return !contractInfo || contractInfo.version > 2
|
||||
? (await import("@thirdweb-dev/contracts-js/dist/abis/DropERC20.json"))
|
||||
@@ -378,7 +469,7 @@ export const TokenInitializer = {
|
||||
) => {
|
||||
const [, provider] = getSignerAndProvider(network, options);
|
||||
const [abi, contract, _network] = await Promise.all([
|
||||
TokenInitializer.getAbi(address, provider),
|
||||
TokenInitializer.getAbi(address, provider, storage),
|
||||
import("./prebuilt-implementations/token"),
|
||||
provider.getNetwork(),
|
||||
]);
|
||||
@@ -392,7 +483,16 @@ export const TokenInitializer = {
|
||||
_network.chainId,
|
||||
);
|
||||
},
|
||||
getAbi: async (address: string, provider: ethers.providers.Provider) => {
|
||||
getAbi: async (
|
||||
address: string,
|
||||
provider: ethers.providers.Provider,
|
||||
storage: ThirdwebStorage,
|
||||
) => {
|
||||
const abi = await fetchAbiFromAddress(address, provider, storage);
|
||||
if (abi) {
|
||||
return abi;
|
||||
}
|
||||
// Deprecated - only needed for backwards compatibility with non-released contracts - should remove in v4
|
||||
return (
|
||||
await import("@thirdweb-dev/contracts-js/dist/abis/TokenERC20.json")
|
||||
).default;
|
||||
@@ -410,7 +510,7 @@ export const VoteInitializer = {
|
||||
) => {
|
||||
const [, provider] = getSignerAndProvider(network, options);
|
||||
const [abi, contract, _network] = await Promise.all([
|
||||
VoteInitializer.getAbi(address, provider),
|
||||
VoteInitializer.getAbi(address, provider, storage),
|
||||
import("./prebuilt-implementations/vote"),
|
||||
provider.getNetwork(),
|
||||
]);
|
||||
@@ -424,7 +524,16 @@ export const VoteInitializer = {
|
||||
_network.chainId,
|
||||
);
|
||||
},
|
||||
getAbi: async (address: string, provider: ethers.providers.Provider) => {
|
||||
getAbi: async (
|
||||
address: string,
|
||||
provider: ethers.providers.Provider,
|
||||
storage: ThirdwebStorage,
|
||||
) => {
|
||||
const abi = await fetchAbiFromAddress(address, provider, storage);
|
||||
if (abi) {
|
||||
return abi;
|
||||
}
|
||||
// Deprecated - only needed for backwards compatibility with non-released contracts - should remove in v4
|
||||
return (await import("@thirdweb-dev/contracts-js/dist/abis/VoteERC20.json"))
|
||||
.default;
|
||||
},
|
||||
@@ -463,7 +572,7 @@ export const PREBUILT_CONTRACTS_MAP = {
|
||||
const SmartContract = {
|
||||
name: "SmartContract" as const,
|
||||
contractType: "custom" as const,
|
||||
schema: CustomContractSchema,
|
||||
schema: {},
|
||||
roles: ALL_ROLES,
|
||||
};
|
||||
|
||||
|
||||
@@ -23,10 +23,10 @@ import {
|
||||
TransactionResultWithId,
|
||||
} from "../../core/types";
|
||||
import { PaperCheckout } from "../../integrations/thirdweb-checkout";
|
||||
import { Abi } from "../../schema/contracts/custom";
|
||||
import { DropErc1155ContractSchema } from "../../schema/contracts/drop-erc1155";
|
||||
import { SDKOptions } from "../../schema/sdk-options";
|
||||
import { PrebuiltEditionDrop } from "../../types/eips";
|
||||
import type ABI from "@thirdweb-dev/contracts-js/dist/abis/DropERC1155.json";
|
||||
import { ThirdwebStorage, UploadProgressEvent } from "@thirdweb-dev/storage";
|
||||
import { BigNumber, BigNumberish, CallOverrides, constants } from "ethers";
|
||||
|
||||
@@ -47,7 +47,7 @@ import { BigNumber, BigNumberish, CallOverrides, constants } from "ethers";
|
||||
export class EditionDrop extends StandardErc1155<PrebuiltEditionDrop> {
|
||||
private static contractRoles = ["admin", "minter", "transfer"] as const;
|
||||
|
||||
public abi: typeof ABI;
|
||||
public abi: Abi;
|
||||
public sales: ContractPrimarySale<PrebuiltEditionDrop>;
|
||||
public platformFees: ContractPlatformFee<PrebuiltEditionDrop>;
|
||||
public encoder: ContractEncoder<PrebuiltEditionDrop>;
|
||||
@@ -124,7 +124,7 @@ export class EditionDrop extends StandardErc1155<PrebuiltEditionDrop> {
|
||||
address: string,
|
||||
storage: ThirdwebStorage,
|
||||
options: SDKOptions = {},
|
||||
abi: typeof ABI,
|
||||
abi: Abi,
|
||||
chainId: number,
|
||||
contractWrapper = new ContractWrapper<PrebuiltEditionDrop>(
|
||||
network,
|
||||
|
||||
@@ -20,11 +20,11 @@ import {
|
||||
TransactionResult,
|
||||
TransactionResultWithId,
|
||||
} from "../../core/types";
|
||||
import { Abi } from "../../schema/contracts/custom";
|
||||
import { TokenErc1155ContractSchema } from "../../schema/contracts/token-erc1155";
|
||||
import { SDKOptions } from "../../schema/sdk-options";
|
||||
import { EditionMetadataOrUri } from "../../schema/tokens/edition";
|
||||
import type { TokenERC1155 } from "@thirdweb-dev/contracts-js";
|
||||
import type ABI from "@thirdweb-dev/contracts-js/dist/abis/TokenERC1155.json";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
import { BigNumber, BigNumberish, CallOverrides, constants } from "ethers";
|
||||
|
||||
@@ -45,7 +45,7 @@ import { BigNumber, BigNumberish, CallOverrides, constants } from "ethers";
|
||||
export class Edition extends StandardErc1155<TokenERC1155> {
|
||||
static contractRoles = ["admin", "minter", "transfer"] as const;
|
||||
|
||||
public abi: typeof ABI;
|
||||
public abi: Abi;
|
||||
public metadata: ContractMetadata<
|
||||
TokenERC1155,
|
||||
typeof TokenErc1155ContractSchema
|
||||
@@ -104,7 +104,7 @@ export class Edition extends StandardErc1155<TokenERC1155> {
|
||||
address: string,
|
||||
storage: ThirdwebStorage,
|
||||
options: SDKOptions = {},
|
||||
abi: typeof ABI,
|
||||
abi: Abi,
|
||||
chainId: number,
|
||||
contractWrapper = new ContractWrapper<TokenERC1155>(
|
||||
network,
|
||||
|
||||
@@ -17,6 +17,7 @@ import { MarketplaceDirect } from "../../core/classes/marketplace-direct";
|
||||
import { UpdateableNetwork } from "../../core/interfaces/contract";
|
||||
import { NetworkOrSignerOrProvider, TransactionResult } from "../../core/types";
|
||||
import { ListingType } from "../../enums";
|
||||
import { Abi } from "../../schema/contracts/custom";
|
||||
import { MarketplaceContractSchema } from "../../schema/contracts/marketplace";
|
||||
import { SDKOptions } from "../../schema/sdk-options";
|
||||
import { Price } from "../../types/currency";
|
||||
@@ -24,7 +25,6 @@ import { AuctionListing, DirectListing, Offer } from "../../types/marketplace";
|
||||
import { MarketplaceFilter } from "../../types/marketplace/MarketPlaceFilter";
|
||||
import { UnmappedOffer } from "../../types/marketplace/UnmappedOffer";
|
||||
import type { Marketplace as MarketplaceContract } from "@thirdweb-dev/contracts-js";
|
||||
import type ABI from "@thirdweb-dev/contracts-js/dist/abis/Marketplace.json";
|
||||
import { NewOfferEventObject } from "@thirdweb-dev/contracts-js/dist/declarations/src/Marketplace";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
import { BigNumber, BigNumberish, CallOverrides, constants } from "ethers";
|
||||
@@ -47,7 +47,7 @@ import invariant from "tiny-invariant";
|
||||
export class Marketplace implements UpdateableNetwork {
|
||||
static contractRoles = ["admin", "lister", "asset"] as const;
|
||||
|
||||
public abi: typeof ABI;
|
||||
public abi: Abi;
|
||||
private contractWrapper: ContractWrapper<MarketplaceContract>;
|
||||
private storage: ThirdwebStorage;
|
||||
|
||||
@@ -148,7 +148,7 @@ export class Marketplace implements UpdateableNetwork {
|
||||
address: string,
|
||||
storage: ThirdwebStorage,
|
||||
options: SDKOptions = {},
|
||||
abi: typeof ABI,
|
||||
abi: Abi,
|
||||
chainId: number,
|
||||
contractWrapper = new ContractWrapper<MarketplaceContract>(
|
||||
network,
|
||||
|
||||
@@ -20,7 +20,7 @@ import {
|
||||
TransactionResult,
|
||||
TransactionResultWithId,
|
||||
} from "../../core/types";
|
||||
import { SDKOptions } from "../../schema";
|
||||
import { Abi, SDKOptions } from "../../schema";
|
||||
import { MultiwrapContractSchema } from "../../schema/contracts/multiwrap";
|
||||
import {
|
||||
ERC1155Wrappable,
|
||||
@@ -30,7 +30,6 @@ import {
|
||||
WrappedTokens,
|
||||
} from "../../types/multiwrap";
|
||||
import type { Multiwrap as MultiwrapContract } from "@thirdweb-dev/contracts-js";
|
||||
import type ABI from "@thirdweb-dev/contracts-js/dist/abis/Multiwrap.json";
|
||||
import {
|
||||
ITokenBundle,
|
||||
TokensWrappedEvent,
|
||||
@@ -61,7 +60,7 @@ export class Multiwrap extends StandardErc721<MultiwrapContract> {
|
||||
"asset",
|
||||
] as const;
|
||||
|
||||
public abi: typeof ABI;
|
||||
public abi: Abi;
|
||||
public encoder: ContractEncoder<MultiwrapContract>;
|
||||
public estimator: GasCostEstimator<MultiwrapContract>;
|
||||
public metadata: ContractMetadata<
|
||||
@@ -102,7 +101,7 @@ export class Multiwrap extends StandardErc721<MultiwrapContract> {
|
||||
address: string,
|
||||
storage: ThirdwebStorage,
|
||||
options: SDKOptions = {},
|
||||
abi: typeof ABI,
|
||||
abi: Abi,
|
||||
chainId: number,
|
||||
contractWrapper = new ContractWrapper<MultiwrapContract>(
|
||||
network,
|
||||
|
||||
@@ -19,10 +19,10 @@ import type {
|
||||
TransactionResult,
|
||||
TransactionResultWithId,
|
||||
} from "../../core/types";
|
||||
import { Abi } from "../../schema/contracts/custom";
|
||||
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 { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
import { BigNumberish, CallOverrides, constants } from "ethers";
|
||||
|
||||
@@ -43,7 +43,7 @@ import { BigNumberish, CallOverrides, constants } from "ethers";
|
||||
export class NFTCollection extends StandardErc721<TokenERC721> {
|
||||
static contractRoles = ["admin", "minter", "transfer"] as const;
|
||||
|
||||
public abi: typeof ABI;
|
||||
public abi: Abi;
|
||||
public metadata: ContractMetadata<
|
||||
TokenERC721,
|
||||
typeof TokenErc721ContractSchema
|
||||
@@ -107,7 +107,7 @@ export class NFTCollection extends StandardErc721<TokenERC721> {
|
||||
address: string,
|
||||
storage: ThirdwebStorage,
|
||||
options: SDKOptions = {},
|
||||
abi: typeof ABI,
|
||||
abi: Abi,
|
||||
chainId: number,
|
||||
contractWrapper = new ContractWrapper<TokenERC721>(
|
||||
network,
|
||||
|
||||
@@ -27,11 +27,11 @@ import {
|
||||
TransactionResultWithId,
|
||||
} from "../../core/types";
|
||||
import { PaperCheckout } from "../../integrations/thirdweb-checkout";
|
||||
import { Abi } from "../../schema/contracts/custom";
|
||||
import { DropErc721ContractSchema } from "../../schema/contracts/drop-erc721";
|
||||
import { SDKOptions } from "../../schema/sdk-options";
|
||||
import { PrebuiltNFTDrop } from "../../types/eips";
|
||||
import { UploadProgressEvent } from "../../types/events";
|
||||
import type ABI from "@thirdweb-dev/contracts-js/dist/abis/DropERC721.json";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
import { BigNumber, BigNumberish, CallOverrides, constants } from "ethers";
|
||||
|
||||
@@ -52,7 +52,7 @@ import { BigNumber, BigNumberish, CallOverrides, constants } from "ethers";
|
||||
export class NFTDrop extends StandardErc721<PrebuiltNFTDrop> {
|
||||
static contractRoles = ["admin", "minter", "transfer"] as const;
|
||||
|
||||
public abi: typeof ABI;
|
||||
public abi: Abi;
|
||||
public encoder: ContractEncoder<PrebuiltNFTDrop>;
|
||||
public estimator: GasCostEstimator<PrebuiltNFTDrop>;
|
||||
public metadata: ContractMetadata<
|
||||
@@ -161,7 +161,7 @@ export class NFTDrop extends StandardErc721<PrebuiltNFTDrop> {
|
||||
address: string,
|
||||
storage: ThirdwebStorage,
|
||||
options: SDKOptions = {},
|
||||
abi: typeof ABI,
|
||||
abi: Abi,
|
||||
chainId: number,
|
||||
contractWrapper = new ContractWrapper<PrebuiltNFTDrop>(
|
||||
network,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { QueryAllParams } from "../../../core/schema/QueryParams";
|
||||
import { NFT } from "../../../core/schema/nft";
|
||||
import { assertEnabled, detectContractFeature } from "../../common";
|
||||
import {
|
||||
fetchCurrencyMetadata,
|
||||
hasERC20Allowance,
|
||||
@@ -8,6 +9,7 @@ import {
|
||||
import { isTokenApprovedForTransfer } from "../../common/marketplace";
|
||||
import { uploadOrExtractURI } from "../../common/nft";
|
||||
import { getRoleHash } from "../../common/role";
|
||||
import { FEATURE_PACK_VRF } from "../../constants/thirdweb-features";
|
||||
import { ContractEncoder } from "../../core/classes/contract-encoder";
|
||||
import { ContractEvents } from "../../core/classes/contract-events";
|
||||
import { ContractInterceptor } from "../../core/classes/contract-interceptor";
|
||||
@@ -19,10 +21,12 @@ import { ContractWrapper } from "../../core/classes/contract-wrapper";
|
||||
import { Erc1155 } from "../../core/classes/erc-1155";
|
||||
import { StandardErc1155 } from "../../core/classes/erc-1155-standard";
|
||||
import { GasCostEstimator } from "../../core/classes/gas-cost-estimator";
|
||||
import { PackVRF } from "../../core/classes/pack-vrf";
|
||||
import {
|
||||
NetworkOrSignerOrProvider,
|
||||
TransactionResultWithId,
|
||||
} from "../../core/types";
|
||||
import { Abi } from "../../schema";
|
||||
import { PackContractSchema } from "../../schema/contracts/packs";
|
||||
import { SDKOptions } from "../../schema/sdk-options";
|
||||
import {
|
||||
@@ -32,8 +36,10 @@ import {
|
||||
PackRewardsOutput,
|
||||
PackRewardsOutputSchema,
|
||||
} from "../../schema/tokens/pack";
|
||||
import type { Pack as PackContract } from "@thirdweb-dev/contracts-js";
|
||||
import type ABI from "@thirdweb-dev/contracts-js/dist/abis/Pack.json";
|
||||
import type {
|
||||
IPackVRFDirect,
|
||||
Pack as PackContract,
|
||||
} from "@thirdweb-dev/contracts-js";
|
||||
import { PackUpdatedEvent } from "@thirdweb-dev/contracts-js/dist/declarations/src/IPack";
|
||||
import {
|
||||
ITokenBundle,
|
||||
@@ -60,7 +66,7 @@ import { BigNumber, BigNumberish, CallOverrides, ethers } from "ethers";
|
||||
export class Pack extends StandardErc1155<PackContract> {
|
||||
static contractRoles = ["admin", "minter", "asset", "transfer"] as const;
|
||||
|
||||
public abi: typeof ABI;
|
||||
public abi: Abi;
|
||||
public metadata: ContractMetadata<PackContract, typeof PackContractSchema>;
|
||||
public roles: ContractRoles<PackContract, typeof Pack.contractRoles[number]>;
|
||||
public encoder: ContractEncoder<PackContract>;
|
||||
@@ -92,12 +98,21 @@ export class Pack extends StandardErc1155<PackContract> {
|
||||
public erc1155: Erc1155<PackContract>;
|
||||
public owner: ContractOwner<PackContract>;
|
||||
|
||||
private _vrf?: PackVRF;
|
||||
|
||||
/**
|
||||
* If enabled in the contract, use the Chainlink VRF functionality to open packs
|
||||
*/
|
||||
get vrf(): PackVRF {
|
||||
return assertEnabled(this._vrf, FEATURE_PACK_VRF);
|
||||
}
|
||||
|
||||
constructor(
|
||||
network: NetworkOrSignerOrProvider,
|
||||
address: string,
|
||||
storage: ThirdwebStorage,
|
||||
options: SDKOptions = {},
|
||||
abi: typeof ABI,
|
||||
abi: Abi,
|
||||
chainId: number,
|
||||
contractWrapper = new ContractWrapper<PackContract>(
|
||||
network,
|
||||
@@ -131,6 +146,7 @@ export class Pack extends StandardErc1155<PackContract> {
|
||||
this.events = new ContractEvents(this.contractWrapper);
|
||||
this.interceptor = new ContractInterceptor(this.contractWrapper);
|
||||
this.owner = new ContractOwner(this.contractWrapper);
|
||||
this._vrf = this.detectVrf();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -138,6 +154,7 @@ export class Pack extends StandardErc1155<PackContract> {
|
||||
*/
|
||||
onNetworkUpdated(network: NetworkOrSignerOrProvider): void {
|
||||
this.contractWrapper.updateSignerOrProvider(network);
|
||||
this._vrf?.onNetworkUpdated(network);
|
||||
}
|
||||
|
||||
getAddress(): string {
|
||||
@@ -536,6 +553,11 @@ export class Pack extends StandardErc1155<PackContract> {
|
||||
tokenId: BigNumberish,
|
||||
amount: BigNumberish = 1,
|
||||
): Promise<PackRewards> {
|
||||
if (this._vrf) {
|
||||
throw new Error(
|
||||
"This contract is using Chainlink VRF, use `contract.vrf.open()` or `contract.vrf.openAndClaim()` instead",
|
||||
);
|
||||
}
|
||||
const receipt = await this.contractWrapper.sendTransaction(
|
||||
"openPack",
|
||||
[tokenId, amount],
|
||||
@@ -716,4 +738,19 @@ export class Pack extends StandardErc1155<PackContract> {
|
||||
): Promise<any> {
|
||||
return this.contractWrapper.call(functionName, ...args);
|
||||
}
|
||||
|
||||
private detectVrf() {
|
||||
if (
|
||||
detectContractFeature<IPackVRFDirect>(this.contractWrapper, "PackVRF")
|
||||
) {
|
||||
return new PackVRF(
|
||||
this.contractWrapper.getSignerOrProvider(),
|
||||
this.contractWrapper.readContract.address,
|
||||
this.storage,
|
||||
this.contractWrapper.options,
|
||||
this.chainId,
|
||||
);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,11 +28,11 @@ import {
|
||||
TransactionResultWithId,
|
||||
} from "../../core/types";
|
||||
import { PaperCheckout } from "../../integrations/thirdweb-checkout";
|
||||
import { Abi } from "../../schema/contracts/custom";
|
||||
import { DropErc721ContractSchema } from "../../schema/contracts/drop-erc721";
|
||||
import { SDKOptions } from "../../schema/sdk-options";
|
||||
import { ClaimOptions, UploadProgressEvent } from "../../types";
|
||||
import type { SignatureDrop as SignatureDropContract } from "@thirdweb-dev/contracts-js";
|
||||
import type ABI from "@thirdweb-dev/contracts-js/dist/abis/SignatureDrop.json";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
import { BigNumber, BigNumberish, CallOverrides, constants } from "ethers";
|
||||
|
||||
@@ -55,7 +55,7 @@ import { BigNumber, BigNumberish, CallOverrides, constants } from "ethers";
|
||||
export class SignatureDrop extends StandardErc721<SignatureDropContract> {
|
||||
static contractRoles = ["admin", "minter", "transfer"] as const;
|
||||
|
||||
public abi: typeof ABI;
|
||||
public abi: Abi;
|
||||
public erc721: Erc721<SignatureDropContract>;
|
||||
public owner: ContractOwner<SignatureDropContract>;
|
||||
public encoder: ContractEncoder<SignatureDropContract>;
|
||||
@@ -171,7 +171,7 @@ export class SignatureDrop extends StandardErc721<SignatureDropContract> {
|
||||
address: string,
|
||||
storage: ThirdwebStorage,
|
||||
options: SDKOptions = {},
|
||||
abi: typeof ABI,
|
||||
abi: Abi,
|
||||
chainId: number,
|
||||
contractWrapper = new ContractWrapper<SignatureDropContract>(
|
||||
network,
|
||||
|
||||
@@ -8,6 +8,7 @@ import { ContractWrapper } from "../../core/classes/contract-wrapper";
|
||||
import { GasCostEstimator } from "../../core/classes/gas-cost-estimator";
|
||||
import { UpdateableNetwork } from "../../core/interfaces/contract";
|
||||
import { NetworkOrSignerOrProvider, TransactionResult } from "../../core/types";
|
||||
import { Abi } from "../../schema/contracts/custom";
|
||||
import { SplitsContractSchema } from "../../schema/contracts/splits";
|
||||
import { SDKOptions } from "../../schema/sdk-options";
|
||||
import { SplitRecipient } from "../../types/SplitRecipient";
|
||||
@@ -17,7 +18,6 @@ import type {
|
||||
Split as SplitContract,
|
||||
} 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 { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
import { BigNumber, CallOverrides, Contract } from "ethers";
|
||||
|
||||
@@ -41,7 +41,7 @@ export class Split implements UpdateableNetwork {
|
||||
private contractWrapper: ContractWrapper<SplitContract>;
|
||||
private storage: ThirdwebStorage;
|
||||
|
||||
public abi: typeof ABI;
|
||||
public abi: Abi;
|
||||
public metadata: ContractMetadata<SplitContract, typeof SplitsContractSchema>;
|
||||
public encoder: ContractEncoder<SplitContract>;
|
||||
public estimator: GasCostEstimator<SplitContract>;
|
||||
@@ -65,7 +65,7 @@ export class Split implements UpdateableNetwork {
|
||||
address: string,
|
||||
storage: ThirdwebStorage,
|
||||
options: SDKOptions = {},
|
||||
abi: typeof ABI,
|
||||
abi: Abi,
|
||||
chainId: number,
|
||||
contractWrapper = new ContractWrapper<SplitContract>(
|
||||
network,
|
||||
|
||||
@@ -11,11 +11,11 @@ import { DropClaimConditions } from "../../core/classes/drop-claim-conditions";
|
||||
import { StandardErc20 } from "../../core/classes/erc-20-standard";
|
||||
import { GasCostEstimator } from "../../core/classes/gas-cost-estimator";
|
||||
import { NetworkOrSignerOrProvider, TransactionResult } from "../../core/types";
|
||||
import { Abi } from "../../schema/contracts/custom";
|
||||
import { DropErc20ContractSchema } from "../../schema/contracts/drop-erc20";
|
||||
import { SDKOptions } from "../../schema/sdk-options";
|
||||
import { Amount, CurrencyValue } from "../../types";
|
||||
import { PrebuiltTokenDrop } from "../../types/eips";
|
||||
import type ABI from "@thirdweb-dev/contracts-js/dist/abis/DropERC20.json";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
import { CallOverrides, constants } from "ethers";
|
||||
|
||||
@@ -35,7 +35,7 @@ import { CallOverrides, constants } from "ethers";
|
||||
export class TokenDrop extends StandardErc20<PrebuiltTokenDrop> {
|
||||
static contractRoles = ["admin", "transfer"] as const;
|
||||
|
||||
public abi: typeof ABI;
|
||||
public abi: Abi;
|
||||
public metadata: ContractMetadata<
|
||||
PrebuiltTokenDrop,
|
||||
typeof DropErc20ContractSchema
|
||||
@@ -82,7 +82,7 @@ export class TokenDrop extends StandardErc20<PrebuiltTokenDrop> {
|
||||
address: string,
|
||||
storage: ThirdwebStorage,
|
||||
options: SDKOptions = {},
|
||||
abi: typeof ABI,
|
||||
abi: Abi,
|
||||
chainId: number,
|
||||
contractWrapper = new ContractWrapper<PrebuiltTokenDrop>(
|
||||
network,
|
||||
|
||||
@@ -12,12 +12,12 @@ import { Erc20SignatureMintable } from "../../core/classes/erc-20-signature-mint
|
||||
import { StandardErc20 } from "../../core/classes/erc-20-standard";
|
||||
import { GasCostEstimator } from "../../core/classes/gas-cost-estimator";
|
||||
import { NetworkOrSignerOrProvider, TransactionResult } from "../../core/types";
|
||||
import { Abi } from "../../schema/contracts/custom";
|
||||
import { TokenErc20ContractSchema } from "../../schema/contracts/token-erc20";
|
||||
import { SDKOptions } from "../../schema/sdk-options";
|
||||
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 { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
import { CallOverrides, constants } from "ethers";
|
||||
|
||||
@@ -38,7 +38,7 @@ import { CallOverrides, constants } from "ethers";
|
||||
export class Token extends StandardErc20<TokenERC20> {
|
||||
static contractRoles = ["admin", "minter", "transfer"] as const;
|
||||
|
||||
public abi: typeof ABI;
|
||||
public abi: Abi;
|
||||
public metadata: ContractMetadata<
|
||||
TokenERC20,
|
||||
typeof TokenErc20ContractSchema
|
||||
@@ -74,7 +74,7 @@ export class Token extends StandardErc20<TokenERC20> {
|
||||
address: string,
|
||||
storage: ThirdwebStorage,
|
||||
options: SDKOptions = {},
|
||||
abi: typeof ABI,
|
||||
abi: Abi,
|
||||
chainId: number,
|
||||
contractWrapper = new ContractWrapper<TokenERC20>(
|
||||
network,
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
TransactionResultWithId,
|
||||
} from "../../core/types";
|
||||
import { VoteType } from "../../enums";
|
||||
import { Abi } from "../../schema";
|
||||
import { VoteContractSchema } from "../../schema/contracts/vote";
|
||||
import { SDKOptions } from "../../schema/sdk-options";
|
||||
import { CurrencyValue } from "../../types/currency";
|
||||
@@ -26,7 +27,6 @@ import {
|
||||
} from "../../types/vote";
|
||||
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 { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
import {
|
||||
@@ -55,7 +55,7 @@ export class Vote implements UpdateableNetwork {
|
||||
private contractWrapper: ContractWrapper<VoteERC20>;
|
||||
private storage: ThirdwebStorage;
|
||||
|
||||
public abi: typeof ABI;
|
||||
public abi: Abi;
|
||||
public metadata: ContractMetadata<VoteERC20, typeof VoteContractSchema>;
|
||||
public encoder: ContractEncoder<VoteERC20>;
|
||||
public estimator: GasCostEstimator<VoteERC20>;
|
||||
@@ -75,7 +75,7 @@ export class Vote implements UpdateableNetwork {
|
||||
address: string,
|
||||
storage: ThirdwebStorage,
|
||||
options: SDKOptions = {},
|
||||
abi: typeof ABI,
|
||||
abi: Abi,
|
||||
chainId: number,
|
||||
contractWrapper = new ContractWrapper<VoteERC20>(
|
||||
network,
|
||||
|
||||
@@ -4,7 +4,7 @@ import {
|
||||
fetchExtendedReleaseMetadata,
|
||||
fetchPreDeployMetadata,
|
||||
} from "../../common/index";
|
||||
import { ChainId } from "../../constants";
|
||||
import { ChainId, EventType } from "../../constants";
|
||||
import { getContractAddressByChainId } from "../../constants/addresses";
|
||||
import {
|
||||
EditionDropInitializer,
|
||||
@@ -24,6 +24,7 @@ import {
|
||||
} from "../../contracts";
|
||||
import { FactoryDeploymentSchema } from "../../schema/contracts/custom";
|
||||
import { SDKOptions } from "../../schema/sdk-options";
|
||||
import { DeployEvent, DeployEvents } from "../../types";
|
||||
import {
|
||||
MarketplaceContractDeployMetadata,
|
||||
MultiwrapContractDeployMetadata,
|
||||
@@ -39,7 +40,6 @@ import {
|
||||
PrebuiltContractType,
|
||||
} from "../types";
|
||||
import { ContractFactory } from "./factory";
|
||||
import { FactoryEvents } from "./factory-events";
|
||||
import { ContractRegistry } from "./registry";
|
||||
import { RPCConnectionHandler } from "./rpc-connection-handler";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
@@ -50,6 +50,7 @@ import {
|
||||
ContractInterface,
|
||||
ethers,
|
||||
} from "ethers";
|
||||
import { EventEmitter } from "eventemitter3";
|
||||
import invariant from "tiny-invariant";
|
||||
import { z } from "zod";
|
||||
|
||||
@@ -68,8 +69,16 @@ export class ContractDeployer extends RPCConnectionHandler {
|
||||
* should never be accessed directly, use {@link ContractDeployer.getRegistry} instead
|
||||
*/
|
||||
private _registry: Promise<ContractRegistry> | undefined;
|
||||
public events: FactoryEvents | undefined;
|
||||
private storage: ThirdwebStorage;
|
||||
private events: EventEmitter<DeployEvents>;
|
||||
private transactionListener = (event: any) => {
|
||||
if (event.status === "submitted") {
|
||||
this.events.emit("contractDeployed", {
|
||||
status: "submitted",
|
||||
transactionHash: event.transactionHash,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
constructor(
|
||||
network: NetworkOrSignerOrProvider,
|
||||
@@ -78,7 +87,7 @@ export class ContractDeployer extends RPCConnectionHandler {
|
||||
) {
|
||||
super(network, options);
|
||||
this.storage = storage;
|
||||
|
||||
this.events = new EventEmitter();
|
||||
// Initialize factory and registry (we don't need to make these calls async)
|
||||
this.getFactory();
|
||||
this.getRegistry();
|
||||
@@ -427,7 +436,15 @@ export class ContractDeployer extends RPCConnectionHandler {
|
||||
} catch (e) {
|
||||
parsedVersion = undefined;
|
||||
}
|
||||
return await factory.deploy(contractType, parsedMetadata, parsedVersion);
|
||||
factory.on(EventType.Transaction, this.transactionListener);
|
||||
const deployedAddress = await factory.deploy(
|
||||
contractType,
|
||||
parsedMetadata,
|
||||
this.events,
|
||||
parsedVersion,
|
||||
);
|
||||
factory.off(EventType.Transaction, this.transactionListener);
|
||||
return deployedAddress;
|
||||
}
|
||||
|
||||
//
|
||||
@@ -515,14 +532,18 @@ export class ContractDeployer extends RPCConnectionHandler {
|
||||
factoryAddress,
|
||||
this.getSignerOrProvider(),
|
||||
this.storage,
|
||||
{},
|
||||
this.options,
|
||||
);
|
||||
return await proxyFactory.deployProxyByImplementation(
|
||||
proxyFactory.on(EventType.Transaction, this.transactionListener);
|
||||
const deployedAddress = await proxyFactory.deployProxyByImplementation(
|
||||
implementationAddress,
|
||||
implementationAbi,
|
||||
initializerFunction,
|
||||
initializerArgs,
|
||||
this.events,
|
||||
);
|
||||
proxyFactory.off(EventType.Transaction, this.transactionListener);
|
||||
return deployedAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -602,8 +623,6 @@ export class ContractDeployer extends RPCConnectionHandler {
|
||||
this.storage,
|
||||
this.options,
|
||||
);
|
||||
this.events = new FactoryEvents(factory);
|
||||
|
||||
return factory;
|
||||
}));
|
||||
}
|
||||
@@ -800,8 +819,39 @@ export class ContractDeployer extends RPCConnectionHandler {
|
||||
const deployer = await new ethers.ContractFactory(abi, bytecode)
|
||||
.connect(signer)
|
||||
.deploy(...constructorParams);
|
||||
this.events.emit("contractDeployed", {
|
||||
status: "submitted",
|
||||
transactionHash: deployer.deployTransaction.hash,
|
||||
});
|
||||
const deployedContract = await deployer.deployed();
|
||||
// TODO parse transaction receipt
|
||||
this.events.emit("contractDeployed", {
|
||||
status: "completed",
|
||||
contractAddress: deployedContract.address,
|
||||
transactionHash: deployedContract.deployTransaction.hash,
|
||||
});
|
||||
return deployedContract.address;
|
||||
}
|
||||
|
||||
/**
|
||||
* Listen to all deploy transactions from this deployer
|
||||
* @param listener the listener to add
|
||||
*/
|
||||
public addDeployListener(listener: (event: DeployEvent) => void) {
|
||||
this.events.on("contractDeployed", listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a deploy listener
|
||||
* @param listener the listener to remove
|
||||
*/
|
||||
public removeDeployListener(listener: (event: DeployEvent) => void) {
|
||||
this.events.off("contractDeployed", listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all deploy listeners
|
||||
*/
|
||||
public removeAllDeployListeners() {
|
||||
this.events.removeAllListeners("contractDeployed");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,7 +85,11 @@ export class ContractEvents<TContract extends BaseContract> {
|
||||
const parsedLog =
|
||||
this.contractWrapper.readContract.interface.parseLog(log);
|
||||
listener(
|
||||
this.toContractEvent(parsedLog.eventFragment, parsedLog.args, log),
|
||||
this.toContractEvent<TEvent>(
|
||||
parsedLog.eventFragment,
|
||||
parsedLog.args,
|
||||
log,
|
||||
),
|
||||
);
|
||||
};
|
||||
|
||||
@@ -333,15 +337,31 @@ export class ContractEvents<TContract extends BaseContract> {
|
||||
const results: Record<string, any> = {};
|
||||
event.inputs.forEach((param, index) => {
|
||||
if (Array.isArray(args[index])) {
|
||||
const obj: Record<string, unknown> = {};
|
||||
const components = param.components;
|
||||
if (components) {
|
||||
const arr = args[index];
|
||||
for (let i = 0; i < components.length; i++) {
|
||||
const name = components[i].name;
|
||||
obj[name] = arr[i];
|
||||
if (param.type === "tuple[]") {
|
||||
// tuple[]
|
||||
const objArray: Record<string, unknown>[] = [];
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
const tuple = arr[i];
|
||||
const obj: Record<string, unknown> = {};
|
||||
for (let j = 0; j < components.length; j++) {
|
||||
const name = components[j].name;
|
||||
obj[name] = tuple[j];
|
||||
}
|
||||
objArray.push(obj);
|
||||
}
|
||||
results[param.name] = objArray;
|
||||
} else {
|
||||
// simple tuple
|
||||
const obj: Record<string, unknown> = {};
|
||||
for (let i = 0; i < components.length; i++) {
|
||||
const name = components[i].name;
|
||||
obj[name] = arr[i];
|
||||
}
|
||||
results[param.name] = obj;
|
||||
}
|
||||
results[param.name] = obj;
|
||||
}
|
||||
} else {
|
||||
results[param.name] = args[index];
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import {
|
||||
detectContractFeature,
|
||||
ExtensionNotImplementedError,
|
||||
fetchContractMetadataFromAddress,
|
||||
hasFunction,
|
||||
fetchContractMetadataFromAddress,
|
||||
} from "../../common";
|
||||
import { FEATURE_METADATA } from "../../constants/thirdweb-features";
|
||||
import { DetectableFeature } from "../interfaces/DetectableFeature";
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
fetchRawPredeployMetadata,
|
||||
fetchSourceFilesFromMetadata,
|
||||
resolveContractUriFromAddress,
|
||||
} from "../../common/feature-detection";
|
||||
} from "../../common";
|
||||
import { isIncrementalVersion } from "../../common/version-checker";
|
||||
import { getContractPublisherAddress } from "../../constants";
|
||||
import {
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
import { ContractEvents } from "./contract-events";
|
||||
import { ContractWrapper } from "./contract-wrapper";
|
||||
import type { TWFactory } from "@thirdweb-dev/contracts-js";
|
||||
import { ProxyDeployedEvent } from "@thirdweb-dev/contracts-js/dist/declarations/src/TWFactory";
|
||||
|
||||
interface DeployEvent {
|
||||
transactionHash: string;
|
||||
contractAddress: string;
|
||||
}
|
||||
|
||||
export class FactoryEvents extends ContractEvents<TWFactory> {
|
||||
constructor(contractWrapper: ContractWrapper<TWFactory>) {
|
||||
super(contractWrapper);
|
||||
}
|
||||
|
||||
public addDeployListener(listener: (event: DeployEvent) => void) {
|
||||
this.addTransactionListener(async (event) => {
|
||||
if (!event.transactionHash) {
|
||||
return;
|
||||
}
|
||||
|
||||
const receipt = await this.contractWrapper
|
||||
.getProvider()
|
||||
.getTransactionReceipt(event.transactionHash);
|
||||
const events = this.contractWrapper.parseLogs<ProxyDeployedEvent>(
|
||||
"ProxyDeployed",
|
||||
receipt.logs,
|
||||
);
|
||||
|
||||
if (events.length > 0) {
|
||||
listener({
|
||||
...event,
|
||||
contractAddress: events[0].args.proxy,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,7 @@ import {
|
||||
VoteInitializer,
|
||||
} from "../../contracts";
|
||||
import { SDKOptions } from "../../schema/sdk-options";
|
||||
import { DeployEvents } from "../../types";
|
||||
import {
|
||||
DeploySchemaForPrebuiltContractType,
|
||||
NetworkOrSignerOrProvider,
|
||||
@@ -36,6 +37,7 @@ import {
|
||||
ContractInterface,
|
||||
ethers,
|
||||
} from "ethers";
|
||||
import { EventEmitter } from "eventemitter3";
|
||||
import { z } from "zod";
|
||||
|
||||
/**
|
||||
@@ -75,6 +77,7 @@ export class ContractFactory extends ContractWrapper<TWFactory> {
|
||||
contractMetadata: z.input<
|
||||
DeploySchemaForPrebuiltContractType<TContractType>
|
||||
>,
|
||||
eventEmitter: EventEmitter<DeployEvents>,
|
||||
version?: number,
|
||||
): Promise<string> {
|
||||
const contract = PREBUILT_CONTRACTS_MAP[contractType];
|
||||
@@ -98,6 +101,7 @@ export class ContractFactory extends ContractWrapper<TWFactory> {
|
||||
const ABI = await contract.getAbi(
|
||||
implementationAddress,
|
||||
this.getProvider(),
|
||||
this.storage,
|
||||
);
|
||||
|
||||
const encodedFunc = Contract.getInterface(ABI).encodeFunctionData(
|
||||
@@ -121,7 +125,14 @@ export class ContractFactory extends ContractWrapper<TWFactory> {
|
||||
throw new Error("No ProxyDeployed event found");
|
||||
}
|
||||
|
||||
return events[0].args.proxy;
|
||||
const contractAddress = events[0].args.proxy;
|
||||
eventEmitter.emit("contractDeployed", {
|
||||
status: "completed",
|
||||
contractAddress,
|
||||
transactionHash: receipt.transactionHash,
|
||||
});
|
||||
|
||||
return contractAddress;
|
||||
}
|
||||
|
||||
// TODO once IContractFactory is implemented, this can be probably be moved to its own class
|
||||
@@ -130,6 +141,7 @@ export class ContractFactory extends ContractWrapper<TWFactory> {
|
||||
implementationAbi: ContractInterface,
|
||||
initializerFunction: string,
|
||||
initializerArgs: any[],
|
||||
eventEmitter: EventEmitter<DeployEvents>,
|
||||
): Promise<string> {
|
||||
const encodedFunc = Contract.getInterface(
|
||||
implementationAbi,
|
||||
@@ -150,7 +162,14 @@ export class ContractFactory extends ContractWrapper<TWFactory> {
|
||||
throw new Error("No ProxyDeployed event found");
|
||||
}
|
||||
|
||||
return events[0].args.proxy;
|
||||
const contractAddress = events[0].args.proxy;
|
||||
eventEmitter.emit("contractDeployed", {
|
||||
status: "completed",
|
||||
contractAddress,
|
||||
transactionHash: receipt.transactionHash,
|
||||
});
|
||||
|
||||
return contractAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
import { NetworkOrSignerOrProvider, TransactionResult } from "..";
|
||||
import { getMultichainRegistryAddress } from "../../constants/addresses";
|
||||
import { PublishedMetadata } from "../../schema/contracts/custom";
|
||||
import { SDKOptions } from "../../schema/sdk-options";
|
||||
import { AddContractInput, ContractInput, DeployedContract } from "../../types";
|
||||
import { ContractWrapper } from "./contract-wrapper";
|
||||
import type {
|
||||
TWMultichainRegistryRouter,
|
||||
TWMultichainRegistryLogic,
|
||||
} from "@thirdweb-dev/contracts-js";
|
||||
import TWRegistryABI from "@thirdweb-dev/contracts-js/dist/abis/TWMultichainRegistryLogic.json";
|
||||
import TWRegistryRouterABI from "@thirdweb-dev/contracts-js/dist/abis/TWMultichainRegistryRouter.json";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
import { constants, utils } from "ethers";
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export class MultichainRegistry {
|
||||
private registryLogic: ContractWrapper<TWMultichainRegistryLogic>;
|
||||
private registryRouter: ContractWrapper<TWMultichainRegistryRouter>;
|
||||
private storage: ThirdwebStorage;
|
||||
|
||||
constructor(
|
||||
network: NetworkOrSignerOrProvider,
|
||||
storage: ThirdwebStorage,
|
||||
options: SDKOptions = {},
|
||||
) {
|
||||
this.storage = storage;
|
||||
this.registryLogic = new ContractWrapper<TWMultichainRegistryLogic>(
|
||||
network,
|
||||
getMultichainRegistryAddress(),
|
||||
TWRegistryABI,
|
||||
options,
|
||||
);
|
||||
|
||||
this.registryRouter = new ContractWrapper<TWMultichainRegistryRouter>(
|
||||
network,
|
||||
getMultichainRegistryAddress(),
|
||||
TWRegistryRouterABI,
|
||||
options,
|
||||
);
|
||||
}
|
||||
|
||||
public async updateSigner(signer: NetworkOrSignerOrProvider) {
|
||||
this.registryLogic.updateSignerOrProvider(signer);
|
||||
this.registryRouter.updateSignerOrProvider(signer);
|
||||
}
|
||||
|
||||
public async getContractMetadataURI(
|
||||
chainId: number,
|
||||
address: string,
|
||||
): Promise<string> {
|
||||
return await this.registryLogic.readContract.getMetadataUri(
|
||||
chainId,
|
||||
address,
|
||||
);
|
||||
}
|
||||
|
||||
public async getContractMetadata(
|
||||
chainId: number,
|
||||
address: string,
|
||||
): Promise<PublishedMetadata> {
|
||||
const uri = await this.getContractMetadataURI(chainId, address);
|
||||
if (!uri) {
|
||||
throw new Error(
|
||||
`No metadata URI found for contract ${address} on chain ${chainId}`,
|
||||
);
|
||||
}
|
||||
// TODO define the metadata JSON schema
|
||||
return await this.storage.downloadJSON<PublishedMetadata>(uri);
|
||||
}
|
||||
|
||||
public async getContractAddresses(
|
||||
walletAddress: string,
|
||||
): Promise<DeployedContract[]> {
|
||||
return (await this.registryLogic.readContract.getAll(walletAddress))
|
||||
.filter(
|
||||
(result) =>
|
||||
utils.isAddress(result.deploymentAddress) &&
|
||||
result.deploymentAddress.toLowerCase() !== constants.AddressZero,
|
||||
)
|
||||
.map((result) => ({
|
||||
address: result.deploymentAddress,
|
||||
chainId: result.chainId.toNumber(),
|
||||
}));
|
||||
}
|
||||
|
||||
public async addContract(
|
||||
contract: AddContractInput,
|
||||
): Promise<TransactionResult> {
|
||||
return await this.addContracts([contract]);
|
||||
}
|
||||
|
||||
public async addContracts(
|
||||
contracts: AddContractInput[],
|
||||
): Promise<TransactionResult> {
|
||||
const deployerAddress = await this.registryRouter.getSignerAddress();
|
||||
const encoded: string[] = [];
|
||||
contracts.forEach((contact) => {
|
||||
encoded.push(
|
||||
this.registryLogic.readContract.interface.encodeFunctionData("add", [
|
||||
deployerAddress,
|
||||
contact.address,
|
||||
contact.chainId,
|
||||
contact.metadataURI || "",
|
||||
]),
|
||||
);
|
||||
});
|
||||
|
||||
return {
|
||||
receipt: await this.registryRouter.multiCall(encoded),
|
||||
};
|
||||
}
|
||||
|
||||
public async removeContract(
|
||||
contract: ContractInput,
|
||||
): Promise<TransactionResult> {
|
||||
return await this.removeContracts([contract]);
|
||||
}
|
||||
|
||||
public async removeContracts(
|
||||
contracts: ContractInput[],
|
||||
): Promise<TransactionResult> {
|
||||
const deployerAddress = await this.registryRouter.getSignerAddress();
|
||||
const encoded: string[] = [];
|
||||
contracts.forEach((contract) => {
|
||||
encoded.push(
|
||||
this.registryLogic.readContract.interface.encodeFunctionData("remove", [
|
||||
deployerAddress,
|
||||
contract.address,
|
||||
contract.chainId,
|
||||
]),
|
||||
);
|
||||
});
|
||||
|
||||
return {
|
||||
receipt: await this.registryRouter.multiCall(encoded),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,278 @@
|
||||
import {
|
||||
ContractEvents,
|
||||
NetworkOrSignerOrProvider,
|
||||
TransactionResultWithId,
|
||||
} from "..";
|
||||
import { fetchCurrencyMetadata } from "../../common";
|
||||
import { LINK_TOKEN_ADDRESS } from "../../constants";
|
||||
import { FEATURE_PACK_VRF } from "../../constants/thirdweb-features";
|
||||
import { PackRewards, SDKOptions } from "../../schema";
|
||||
import { Amount, CurrencyValue } from "../../types";
|
||||
import { DetectableFeature } from "../interfaces/DetectableFeature";
|
||||
import { UpdateableNetwork } from "../interfaces/contract";
|
||||
import { ContractWrapper } from "./contract-wrapper";
|
||||
import { Erc20 } from "./erc-20";
|
||||
import type { ERC20, PackVRFDirect } from "@thirdweb-dev/contracts-js";
|
||||
import ERC20Abi from "@thirdweb-dev/contracts-js/dist/abis/ERC20.json";
|
||||
import IPackAbi from "@thirdweb-dev/contracts-js/dist/abis/IPackVRFDirect.json";
|
||||
import {
|
||||
ITokenBundle,
|
||||
PackOpenedEvent,
|
||||
PackOpenedEventObject,
|
||||
PackOpenRequestedEvent,
|
||||
} from "@thirdweb-dev/contracts-js/dist/declarations/src/IPackVRFDirect";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
import { BigNumber, BigNumberish, ethers } from "ethers";
|
||||
|
||||
export class PackVRF implements UpdateableNetwork, DetectableFeature {
|
||||
featureName = FEATURE_PACK_VRF.name;
|
||||
private contractWrapper: ContractWrapper<PackVRFDirect>;
|
||||
private storage: ThirdwebStorage;
|
||||
public chainId: number;
|
||||
private events: ContractEvents<PackVRFDirect>;
|
||||
|
||||
constructor(
|
||||
network: NetworkOrSignerOrProvider,
|
||||
address: string,
|
||||
storage: ThirdwebStorage,
|
||||
options: SDKOptions,
|
||||
chainId: number,
|
||||
contractWrapper: ContractWrapper<PackVRFDirect> = new ContractWrapper(
|
||||
network,
|
||||
address,
|
||||
IPackAbi,
|
||||
options,
|
||||
),
|
||||
) {
|
||||
this.contractWrapper = contractWrapper;
|
||||
this.storage = storage;
|
||||
this.chainId = chainId;
|
||||
this.events = new ContractEvents(this.contractWrapper);
|
||||
}
|
||||
|
||||
onNetworkUpdated(network: NetworkOrSignerOrProvider): void {
|
||||
this.contractWrapper.updateSignerOrProvider(network);
|
||||
}
|
||||
|
||||
getAddress(): string {
|
||||
return this.contractWrapper.readContract.address;
|
||||
}
|
||||
|
||||
/**
|
||||
* Open a pack using Chainlink VRFs random number generation
|
||||
* This will return a transaction result with the requestId of the open request, NOT the contents of the pack
|
||||
* To get the contents of the pack, you must call claimRewards once the VRF request has been fulfilled
|
||||
* You can use the canClaimRewards method to check if the VRF request has been fulfilled
|
||||
* @param tokenId
|
||||
* @param amount
|
||||
* @returns
|
||||
*/
|
||||
public async open(
|
||||
tokenId: BigNumberish,
|
||||
amount: BigNumberish = 1,
|
||||
): Promise<TransactionResultWithId> {
|
||||
const receipt = await this.contractWrapper.sendTransaction(
|
||||
"openPack",
|
||||
[tokenId, amount],
|
||||
{
|
||||
// Higher gas limit for opening packs
|
||||
gasLimit: 500000,
|
||||
},
|
||||
);
|
||||
let id = BigNumber.from(0);
|
||||
try {
|
||||
const event = this.contractWrapper.parseLogs<PackOpenRequestedEvent>(
|
||||
"PackOpenRequested",
|
||||
receipt?.logs,
|
||||
);
|
||||
id = event[0].args.requestId;
|
||||
} catch (e) {}
|
||||
|
||||
return {
|
||||
receipt,
|
||||
id,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Claim the rewards from a pack that has been opened
|
||||
* This will return the contents of the pack
|
||||
* Make sure to check if the VRF request has been fulfilled using canClaimRewards() before calling this method
|
||||
* @returns the random rewards from opening a pack
|
||||
*/
|
||||
public async claimRewards(): Promise<PackRewards> {
|
||||
const receipt = await this.contractWrapper.sendTransaction(
|
||||
"claimRewards",
|
||||
[],
|
||||
{
|
||||
// Higher gas limit for opening packs
|
||||
gasLimit: 500000,
|
||||
},
|
||||
);
|
||||
const event = this.contractWrapper.parseLogs<PackOpenedEvent>(
|
||||
"PackOpened",
|
||||
receipt?.logs,
|
||||
);
|
||||
if (event.length === 0) {
|
||||
throw new Error("PackOpened event not found");
|
||||
}
|
||||
const rewards = event[0].args.rewardUnitsDistributed;
|
||||
|
||||
return this.parseRewards(rewards);
|
||||
}
|
||||
|
||||
private async parseRewards(
|
||||
rewards: ITokenBundle.TokenStructOutput[],
|
||||
): Promise<PackRewards> {
|
||||
const erc20Rewards: PackRewards["erc20Rewards"] = [];
|
||||
const erc721Rewards: PackRewards["erc721Rewards"] = [];
|
||||
const erc1155Rewards: PackRewards["erc1155Rewards"] = [];
|
||||
|
||||
for (const reward of rewards) {
|
||||
switch (reward.tokenType) {
|
||||
case 0: {
|
||||
const tokenMetadata = await fetchCurrencyMetadata(
|
||||
this.contractWrapper.getProvider(),
|
||||
reward.assetContract,
|
||||
);
|
||||
erc20Rewards.push({
|
||||
contractAddress: reward.assetContract,
|
||||
quantityPerReward: ethers.utils
|
||||
.formatUnits(reward.totalAmount, tokenMetadata.decimals)
|
||||
.toString(),
|
||||
});
|
||||
break;
|
||||
}
|
||||
case 1: {
|
||||
erc721Rewards.push({
|
||||
contractAddress: reward.assetContract,
|
||||
tokenId: reward.tokenId.toString(),
|
||||
});
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
erc1155Rewards.push({
|
||||
contractAddress: reward.assetContract,
|
||||
tokenId: reward.tokenId.toString(),
|
||||
quantityPerReward: reward.totalAmount.toString(),
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
erc20Rewards,
|
||||
erc721Rewards,
|
||||
erc1155Rewards,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup a listener that will be called everytime a pack is opened in this contract
|
||||
* @param callback the listener to call when a pack is opened
|
||||
* @returns a unsubscribe function to cleanup the listener
|
||||
*/
|
||||
public async addPackOpenEventListener(
|
||||
callback: (
|
||||
packId: string,
|
||||
openerAddress: string,
|
||||
rewards: PackRewards,
|
||||
) => void,
|
||||
) {
|
||||
return this.events.addEventListener<PackOpenedEventObject>(
|
||||
"PackOpened",
|
||||
async (event) => {
|
||||
callback(
|
||||
event.data.packId.toString(),
|
||||
event.data.opener,
|
||||
await this.parseRewards(event.data.rewardUnitsDistributed),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the connected address can claim rewards after opening a pack
|
||||
* @param claimerAddress Optional: the address to check if they can claim rewards, defaults to the connected address
|
||||
* @returns whether the connected address can claim rewards after opening a pack
|
||||
*/
|
||||
public async canClaimRewards(claimerAddress?: string): Promise<boolean> {
|
||||
const address =
|
||||
claimerAddress || (await this.contractWrapper.getSignerAddress());
|
||||
return await this.contractWrapper.readContract.canClaimRewards(address);
|
||||
}
|
||||
|
||||
/**
|
||||
* Open a pack and claim the rewards in one transaction.
|
||||
* This function will only start the flow of opening a pack, the rewards will be granted automatically to the connected address after VRF request is fulfilled
|
||||
* @param packId The id of the pack to open
|
||||
* @param amount Optional: the amount of packs to open, defaults to 1
|
||||
* @param gasLimit Optional: the gas limit to use for the VRF callback transaction, defaults to 500000
|
||||
* @returns
|
||||
*/
|
||||
public async openAndClaim(
|
||||
packId: BigNumberish,
|
||||
amount: BigNumberish = 1,
|
||||
gasLimit: BigNumberish = 500000,
|
||||
): Promise<TransactionResultWithId> {
|
||||
const receipt = await this.contractWrapper.sendTransaction(
|
||||
"openPackAndClaimRewards",
|
||||
[packId, amount, gasLimit],
|
||||
{
|
||||
// Higher gas limit for opening packs
|
||||
gasLimit: BigNumber.from(500000),
|
||||
},
|
||||
);
|
||||
let id = BigNumber.from(0);
|
||||
try {
|
||||
const event = this.contractWrapper.parseLogs<PackOpenRequestedEvent>(
|
||||
"PackOpenRequested",
|
||||
receipt?.logs,
|
||||
);
|
||||
id = event[0].args.requestId;
|
||||
} catch (e) {}
|
||||
|
||||
return {
|
||||
receipt,
|
||||
id,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the balance of LINK in the contract
|
||||
* @returns the balance of LINK in the contract
|
||||
*/
|
||||
public async getLinkBalance(): Promise<CurrencyValue> {
|
||||
return this.getLinkContract().balanceOf(
|
||||
this.contractWrapper.readContract.address,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transfer LINK to this contract
|
||||
* @param amount the amount of LINK to transfer to the contract
|
||||
*/
|
||||
public async transferLink(amount: Amount) {
|
||||
await this.getLinkContract().transfer(
|
||||
this.contractWrapper.readContract.address,
|
||||
amount,
|
||||
);
|
||||
}
|
||||
|
||||
private getLinkContract(): Erc20 {
|
||||
const linkAddress = LINK_TOKEN_ADDRESS[this.chainId];
|
||||
if (!linkAddress) {
|
||||
throw new Error(
|
||||
`No LINK token address found for chainId ${this.chainId}`,
|
||||
);
|
||||
}
|
||||
const contract = new ContractWrapper<ERC20>(
|
||||
this.contractWrapper.getSignerOrProvider(),
|
||||
linkAddress,
|
||||
ERC20Abi,
|
||||
this.contractWrapper.options,
|
||||
);
|
||||
return new Erc20(contract, this.storage, this.chainId);
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@ import { WalletAuthenticator } from "./auth/wallet-authenticator";
|
||||
import type { ContractMetadata } from "./classes";
|
||||
import { ContractDeployer } from "./classes/contract-deployer";
|
||||
import { ContractPublisher } from "./classes/contract-publisher";
|
||||
import { MultichainRegistry } from "./classes/multichain-registry";
|
||||
import {
|
||||
getSignerAndProvider,
|
||||
RPCConnectionHandler,
|
||||
@@ -162,6 +163,10 @@ export class ThirdwebSDK extends RPCConnectionHandler {
|
||||
* New contract deployer
|
||||
*/
|
||||
public deployer: ContractDeployer;
|
||||
/**
|
||||
* The registry of deployed contracts
|
||||
*/
|
||||
public multiChainRegistry: MultichainRegistry;
|
||||
/**
|
||||
* Interact with the connected wallet
|
||||
*/
|
||||
@@ -187,6 +192,11 @@ export class ThirdwebSDK extends RPCConnectionHandler {
|
||||
this.wallet = new UserWallet(signerOrProvider, options);
|
||||
this.deployer = new ContractDeployer(signerOrProvider, options, storage);
|
||||
this.auth = new WalletAuthenticator(signerOrProvider, this.wallet, options);
|
||||
this.multiChainRegistry = new MultichainRegistry(
|
||||
signerOrProvider,
|
||||
this.storageHandler,
|
||||
this.options,
|
||||
);
|
||||
this._publisher = new ContractPublisher(
|
||||
signerOrProvider,
|
||||
this.options,
|
||||
@@ -425,7 +435,7 @@ export class ThirdwebSDK extends RPCConnectionHandler {
|
||||
if (!contractTypeOrABI || contractTypeOrABI === "custom") {
|
||||
const resolvedContractType = await this.resolveContractType(address);
|
||||
if (resolvedContractType === "custom") {
|
||||
// if it's a custom contract we gotta fetch the compilet metadata
|
||||
// if it's a custom contract we gotta fetch the compiler metadata
|
||||
try {
|
||||
const publisher = this.getPublisher();
|
||||
const metadata = await publisher.fetchCompilerMetadataFromAddress(
|
||||
@@ -439,7 +449,7 @@ export class ThirdwebSDK extends RPCConnectionHandler {
|
||||
// otherwise if it's a prebuilt contract we can just use the contract type
|
||||
const contractAbi = await PREBUILT_CONTRACTS_MAP[
|
||||
resolvedContractType
|
||||
].getAbi(address, this.getProvider());
|
||||
].getAbi(address, this.getProvider(), this.storage);
|
||||
newContract = await this.getContractFromAbi(address, contractAbi);
|
||||
}
|
||||
}
|
||||
@@ -515,6 +525,7 @@ export class ThirdwebSDK extends RPCConnectionHandler {
|
||||
* ```
|
||||
*/
|
||||
public async getContractList(walletAddress: string) {
|
||||
// TODO - this only reads from the current registry chain, not the multichain registry
|
||||
const addresses =
|
||||
(await (
|
||||
await this.deployer.getRegistry()
|
||||
@@ -574,6 +585,7 @@ export class ThirdwebSDK extends RPCConnectionHandler {
|
||||
this.auth.updateSignerOrProvider(this.getSignerOrProvider());
|
||||
this.deployer.updateSignerOrProvider(this.getSignerOrProvider());
|
||||
this._publisher.updateSignerOrProvider(this.getSignerOrProvider());
|
||||
this.multiChainRegistry.updateSigner(this.getSignerOrProvider());
|
||||
for (const [, contract] of this.contractCache) {
|
||||
contract.onNetworkUpdated(this.getSignerOrProvider());
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
isNativeToken,
|
||||
normalizePriceValue,
|
||||
} from "../../common/currency";
|
||||
import { EIP712Domain, signTypedDataInternal } from "../../common/sign";
|
||||
import { NATIVE_TOKEN_ADDRESS } from "../../constants";
|
||||
import { SDKOptions } from "../../schema";
|
||||
import { Amount, CurrencyValue } from "../../types";
|
||||
@@ -11,7 +12,7 @@ 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, Signer } from "ethers";
|
||||
import { ethers, BigNumber, providers, Signer, TypedDataField } from "ethers";
|
||||
import EventEmitter from "eventemitter3";
|
||||
import invariant from "tiny-invariant";
|
||||
|
||||
@@ -183,6 +184,42 @@ export class UserWallet {
|
||||
return await signer.signMessage(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sign a typed data structure (EIP712) with the connected wallet private key
|
||||
* @param domain - the domain as EIP712 standard
|
||||
* @param types - the strcuture and data types as defined by the EIP712 standard
|
||||
* @param message - the data to sign
|
||||
* @returns the payload and its associated signature
|
||||
*
|
||||
* @example
|
||||
* ```javascript
|
||||
* // This is the message to be signed
|
||||
* // Now we can sign the message with the connected wallet
|
||||
* const { payload, signature } = await sdk.wallet.signTypedData(
|
||||
* {
|
||||
name: "MyEIP721Domain",
|
||||
version: "1",
|
||||
chainId: 1,
|
||||
verifyingContract: "0x...",
|
||||
},
|
||||
{ MyStruct: [ { name: "to", type: "address" }, { name: "quantity", type: "uint256" } ] },
|
||||
{ to: "0x...", quantity: 1 },
|
||||
* );
|
||||
* ```
|
||||
*/
|
||||
public async signTypedData(
|
||||
domain: EIP712Domain,
|
||||
types: Record<string, Array<TypedDataField>>,
|
||||
message: Record<string, any>,
|
||||
): Promise<{ payload: any; signature: string }> {
|
||||
return await signTypedDataInternal(
|
||||
this.requireWallet(),
|
||||
domain,
|
||||
types,
|
||||
message,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Recover the signing address from a signed message
|
||||
* @param message - the original message that was signed
|
||||
|
||||
@@ -5,14 +5,13 @@ export type { ContractType, NetworkOrSignerOrProvider } from "./core/types";
|
||||
|
||||
export type { Role } from "./common/role";
|
||||
|
||||
export { CommonContractSchema } from "./schema/contracts/common";
|
||||
export * from "./schema/contracts/custom";
|
||||
export * from "./schema/contracts/common/claim-conditions";
|
||||
export * from "./schema/tokens/common/properties";
|
||||
export * from "./constants/chains";
|
||||
export * from "./schema/tokens/token";
|
||||
export * from "./schema/tokens/edition";
|
||||
export * from "./schema/contracts/common";
|
||||
export * from "./schema/contracts/custom";
|
||||
export * from "./schema/auth";
|
||||
export type {
|
||||
SDKOptions,
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
import { BytesLikeSchema } from "../../../../core/schema/shared";
|
||||
import { AddressSchema } from "../../shared";
|
||||
import { z } from "zod";
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export const PluginMapInput = z.object({
|
||||
functionSelector: BytesLikeSchema,
|
||||
functionSignature: z.string(),
|
||||
pluginAddress: AddressSchema,
|
||||
});
|
||||
@@ -1,3 +1,4 @@
|
||||
export * from "./custom";
|
||||
export * from "./common";
|
||||
export * from "./drop-erc721";
|
||||
export * from "./drop-erc1155";
|
||||
@@ -8,4 +9,3 @@ export * from "./token-erc20";
|
||||
export * from "./token-erc721";
|
||||
export * from "./token-erc1155";
|
||||
export * from "./vote";
|
||||
export * from "./custom";
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
export interface DeployEvent {
|
||||
status: "submitted" | "completed";
|
||||
transactionHash: string;
|
||||
contractAddress?: string;
|
||||
}
|
||||
|
||||
export interface DeployEvents {
|
||||
contractDeployed: [DeployEvent];
|
||||
}
|
||||
@@ -1 +1,2 @@
|
||||
export * from "./deploy-metadata";
|
||||
export * from "./deploy-events";
|
||||
|
||||
@@ -8,3 +8,4 @@ export * from "./SplitRecipient";
|
||||
export * from "./deploy";
|
||||
export * from "./events";
|
||||
export * from "./multiwrap";
|
||||
export * from "./registry";
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
import { PluginMapInput } from "../schema/contracts/common/plugins";
|
||||
import { z } from "zod";
|
||||
|
||||
/**
|
||||
* Input model to pass a list of extension-addresses + function-selectors
|
||||
* @public
|
||||
*/
|
||||
export type Plugin = z.input<typeof PluginMapInput>;
|
||||
@@ -0,0 +1,13 @@
|
||||
export type ContractInput = {
|
||||
address: string;
|
||||
chainId: number;
|
||||
};
|
||||
|
||||
export type AddContractInput = ContractInput & {
|
||||
metadataURI?: string;
|
||||
};
|
||||
|
||||
export type DeployedContract = {
|
||||
address: string;
|
||||
chainId: number;
|
||||
};
|
||||
@@ -9,6 +9,7 @@ export type {
|
||||
} from "./core/schema/nft";
|
||||
|
||||
export type { CurrencyValue, TokenMetadata } from "./core/schema/token";
|
||||
export { getRpcUrl } from "./core/constants/urls";
|
||||
|
||||
export type { QueryAllParams } from "./core/schema/QueryParams";
|
||||
|
||||
|
||||
@@ -64,9 +64,7 @@ export class ClaimConditions {
|
||||
const goLiveDate = candyMachine.goLiveDate
|
||||
? new Date(candyMachine.goLiveDate.toNumber() * 1000)
|
||||
: null;
|
||||
const isWithinGoLiveDate = candyMachine.goLiveDate
|
||||
? candyMachine.goLiveDate.toNumber() * 1000 >= Date.now()
|
||||
: true;
|
||||
const isWithinGoLiveDate = goLiveDate ? goLiveDate <= new Date() : true;
|
||||
const maxClaimable =
|
||||
candyMachine.endSettings && candyMachine.endSettings.endSettingType === 1
|
||||
? candyMachine.endSettings.number.toNumber()
|
||||
@@ -155,7 +153,7 @@ export class ClaimConditions {
|
||||
const parsed = NFTDropUpdateableConditionsInputSchema.parse(metadata);
|
||||
|
||||
// recipients
|
||||
const wallet: PublicKey | undefined = parsed.primarySaleRecipient
|
||||
let wallet: PublicKey | undefined = parsed.primarySaleRecipient
|
||||
? new PublicKey(parsed.primarySaleRecipient)
|
||||
: undefined;
|
||||
const sellerFeeBasisPoints = parsed.sellerFeeBasisPoints;
|
||||
@@ -165,12 +163,19 @@ export class ClaimConditions {
|
||||
? sol(Number(parsed.price))
|
||||
: undefined;
|
||||
let tokenMint: PublicKey | undefined = undefined;
|
||||
if (parsed.currencyAddress && parsed.price) {
|
||||
if (parsed.currencyAddress) {
|
||||
const fetchedToken = await this.metaplex
|
||||
.tokens()
|
||||
.findMintByAddress({ address: new PublicKey(parsed.currencyAddress) });
|
||||
price = token(Number(parsed.price), fetchedToken.decimals);
|
||||
price = token(Number(parsed.price || 0), fetchedToken.decimals);
|
||||
tokenMint = fetchedToken.address;
|
||||
wallet = this.metaplex
|
||||
.tokens()
|
||||
.pdas()
|
||||
.associatedTokenAccount({
|
||||
mint: tokenMint,
|
||||
owner: wallet ? wallet : this.metaplex.identity().publicKey,
|
||||
});
|
||||
}
|
||||
|
||||
// dates
|
||||
|
||||
@@ -440,9 +440,9 @@ export class NFTDrop {
|
||||
* console.log("Claimed NFT at address", claimedAddresses[0]);
|
||||
* ```
|
||||
*/
|
||||
async claim(quantity: Amount): Promise<string[]> {
|
||||
async claim(amount: Amount): Promise<string[]> {
|
||||
const address = this.metaplex.identity().publicKey.toBase58();
|
||||
return this.claimTo(address, quantity);
|
||||
return this.claimTo(address, amount);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -462,7 +462,7 @@ export class NFTDrop {
|
||||
const candyMachine = await this.getCandyMachine();
|
||||
await this.claimConditions.assertCanClaimable(Number(amount));
|
||||
const builders = await Promise.all(
|
||||
[...Array(amount).keys()].map(async () => {
|
||||
[...Array(Number(amount)).keys()].map(async () => {
|
||||
return await this.metaplex
|
||||
.candyMachinesV2()
|
||||
.builders()
|
||||
|
||||
@@ -29,28 +29,43 @@ export async function sendMultipartTransaction(
|
||||
// make the connected wallet sign all transactions
|
||||
const signedTx = await metaplex.identity().signAllTransactions(txns);
|
||||
|
||||
// send the signed transactions
|
||||
let signatures = [];
|
||||
for (const tx of signedTx) {
|
||||
const signature = await metaplex.connection.sendRawTransaction(
|
||||
tx.serialize(),
|
||||
// send the signed transactions in batches
|
||||
const batchSize = 200; // sends 200 raw tx in parallel
|
||||
const batches = [];
|
||||
for (let i = 0; i < signedTx.length; i += batchSize) {
|
||||
batches.push(signedTx.slice(i, i + batchSize));
|
||||
}
|
||||
let signatures: string[][] = [];
|
||||
for (const txs of batches) {
|
||||
const signature = await Promise.all(
|
||||
txs.map((tx) => metaplex.connection.sendRawTransaction(tx.serialize())),
|
||||
);
|
||||
signatures.push(signature);
|
||||
}
|
||||
|
||||
// wait for confirmations in parallel
|
||||
const confirmations = await Promise.all(
|
||||
signatures.map((sig) => {
|
||||
return metaplex.rpc().confirmTransaction(sig, {
|
||||
blockhash: block.blockhash,
|
||||
lastValidBlockHeight: block.lastValidBlockHeight,
|
||||
});
|
||||
}),
|
||||
);
|
||||
// wait for confirmations in parallel batches
|
||||
let confirmations = [];
|
||||
for (let i = 0; i < signatures.length; i += 1) {
|
||||
await delay(1000); // add delay to avoid rate limiting
|
||||
const sigs = signatures[i];
|
||||
const confirmationBatch = await Promise.all(
|
||||
sigs.map((sig) => {
|
||||
return metaplex.rpc().confirmTransaction(sig, {
|
||||
blockhash: block.blockhash,
|
||||
lastValidBlockHeight: block.lastValidBlockHeight,
|
||||
});
|
||||
}),
|
||||
);
|
||||
confirmations.push(...confirmationBatch);
|
||||
}
|
||||
|
||||
if (confirmations.length === 0) {
|
||||
throw new Error("Transaction failed");
|
||||
}
|
||||
|
||||
return signatures.map((signature) => ({ signature }));
|
||||
return signatures.flatMap((sig) => sig).map((signature) => ({ signature }));
|
||||
}
|
||||
|
||||
async function delay(delayMs: number) {
|
||||
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import {
|
||||
Abi,
|
||||
ChainId,
|
||||
CONTRACTS_MAP,
|
||||
ContractType,
|
||||
@@ -18,6 +19,7 @@ import {
|
||||
TokenInitializer,
|
||||
VoteInitializer,
|
||||
} from "../../src/evm";
|
||||
import { Plugin } from "../../src/evm/types/plugins";
|
||||
import { MockStorage } from "./mock/MockStorage";
|
||||
import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers";
|
||||
import {
|
||||
@@ -44,10 +46,19 @@ import {
|
||||
TWFactory__factory,
|
||||
TWRegistry,
|
||||
TWRegistry__factory,
|
||||
TWMultichainRegistryRouter,
|
||||
TWMultichainRegistryRouter__factory,
|
||||
TWMultichainRegistryLogic,
|
||||
TWMultichainRegistryLogic__factory,
|
||||
PluginMap,
|
||||
PluginMap__factory,
|
||||
VoteERC20__factory,
|
||||
TWProxy__factory,
|
||||
TWProxy,
|
||||
} from "@thirdweb-dev/contracts-js";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
import { ethers } from "ethers";
|
||||
import { FormatTypes, FunctionFragment, Interface } from "ethers/lib/utils";
|
||||
import hardhat from "hardhat";
|
||||
|
||||
// it's there, trust me bro
|
||||
@@ -227,6 +238,10 @@ export const mochaHooks = {
|
||||
process.env.factoryAddress = thirdwebFactoryDeployer.address;
|
||||
// eslint-disable-next-line turbo/no-undeclared-env-vars
|
||||
process.env.contractPublisherAddress = contractPublisher.address;
|
||||
// eslint-disable-next-line turbo/no-undeclared-env-vars
|
||||
process.env.multiChainRegistryAddress = await setupMultichainRegistry(
|
||||
trustedForwarderAddress,
|
||||
);
|
||||
|
||||
storage = MockStorage();
|
||||
sdk = new ThirdwebSDK(
|
||||
@@ -241,6 +256,64 @@ export const mochaHooks = {
|
||||
},
|
||||
};
|
||||
|
||||
const generatePluginFunctions = (
|
||||
pluginAddress: string,
|
||||
pluginAbi: Abi,
|
||||
): Plugin[] => {
|
||||
const pluginInterface = new ethers.utils.Interface(pluginAbi);
|
||||
const pluginFunctions: Plugin[] = [];
|
||||
// TODO - filter out common functions like _msgSender(), contractType(), etc.
|
||||
for (const fnFragment of Object.values(pluginInterface.functions)) {
|
||||
const fn = pluginInterface.getFunction(fnFragment.name);
|
||||
pluginFunctions.push({
|
||||
functionSelector: pluginInterface.getSighash(fn),
|
||||
functionSignature:
|
||||
fn.name + "(" + fn.inputs.map((i) => i.type).join(",") + ")",
|
||||
pluginAddress,
|
||||
});
|
||||
}
|
||||
return pluginFunctions;
|
||||
};
|
||||
|
||||
// Setup multichain registry for tests
|
||||
async function setupMultichainRegistry(
|
||||
trustedForwarderAddress: string,
|
||||
): Promise<string> {
|
||||
const multichainRegistryLogicDeployer = (await new ethers.ContractFactory(
|
||||
TWMultichainRegistryLogic__factory.abi,
|
||||
TWMultichainRegistryLogic__factory.bytecode,
|
||||
)
|
||||
.connect(signer)
|
||||
.deploy()) as TWMultichainRegistryLogic;
|
||||
const multichainRegistryLogic =
|
||||
await multichainRegistryLogicDeployer.deployed();
|
||||
|
||||
const plugins: Plugin[] = generatePluginFunctions(
|
||||
multichainRegistryLogic.address,
|
||||
TWMultichainRegistryLogic__factory.abi,
|
||||
);
|
||||
|
||||
const pluginMapDeployer = (await new ethers.ContractFactory(
|
||||
PluginMap__factory.abi,
|
||||
PluginMap__factory.bytecode,
|
||||
)
|
||||
.connect(signer)
|
||||
.deploy(plugins)) as PluginMap;
|
||||
const pluginMap = await pluginMapDeployer.deployed();
|
||||
const multichainRegistryRouterDeployer = (await new ethers.ContractFactory(
|
||||
TWMultichainRegistryRouter__factory.abi,
|
||||
TWMultichainRegistryRouter__factory.bytecode,
|
||||
)
|
||||
.connect(signer)
|
||||
.deploy(pluginMap.address, [
|
||||
trustedForwarderAddress,
|
||||
])) as TWMultichainRegistryRouter;
|
||||
const multichainRegistryRouter =
|
||||
await multichainRegistryRouterDeployer.deployed();
|
||||
|
||||
return multichainRegistryRouter.address;
|
||||
}
|
||||
|
||||
export {
|
||||
ipfsGatewayUrl,
|
||||
sdk,
|
||||
|
||||
@@ -8,6 +8,10 @@ import {
|
||||
import { sdk, signers } from "./before-setup";
|
||||
import { AddressZero } from "@ethersproject/constants";
|
||||
import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers";
|
||||
import {
|
||||
MockContractPublisher__factory,
|
||||
TWFactory__factory,
|
||||
} from "@thirdweb-dev/contracts-js";
|
||||
import { expect } from "chai";
|
||||
import { ethers } from "ethers";
|
||||
|
||||
@@ -92,17 +96,25 @@ describe("Events", async () => {
|
||||
|
||||
it("Should emit deploy events", async () => {
|
||||
// eslint-disable-next-line no-unused-expressions
|
||||
expect(sdk.deployer.events).to.not.be.undefined;
|
||||
let txAddress = "";
|
||||
sdk.deployer.events?.addDeployListener((event) => {
|
||||
txAddress = event.contractAddress;
|
||||
const txAddress: string[] = [];
|
||||
sdk.deployer.addDeployListener((event) => {
|
||||
if (event.status === "completed") {
|
||||
txAddress.push(event.contractAddress || "");
|
||||
}
|
||||
});
|
||||
const address = await sdk.deployer.deployMarketplace({
|
||||
name: "Marketplace",
|
||||
});
|
||||
const secondAddress = await sdk.deployer.deployContractWithAbi(
|
||||
MockContractPublisher__factory.abi,
|
||||
MockContractPublisher__factory.bytecode,
|
||||
[],
|
||||
);
|
||||
// Wait for the async listener to get called
|
||||
await new Promise((res) => setTimeout(res, 2000));
|
||||
expect(txAddress).to.equal(address);
|
||||
sdk.deployer.removeAllDeployListeners();
|
||||
expect(txAddress[0]).to.equal(address);
|
||||
expect(txAddress[1]).to.equal(secondAddress);
|
||||
});
|
||||
|
||||
it("should emit Contract events", async () => {
|
||||
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
TokenERC721__factory,
|
||||
} from "@thirdweb-dev/contracts-js";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
import { assert, expect } from "chai";
|
||||
import { expect } from "chai";
|
||||
import { ethers } from "ethers";
|
||||
import { readFileSync } from "fs";
|
||||
import invariant from "tiny-invariant";
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { getMultichainRegistryAddress } from "../../src/evm";
|
||||
import { MultichainRegistry } from "../../src/evm/core/classes/multichain-registry";
|
||||
import { ContractRegistry } from "../../src/evm/core/classes/registry";
|
||||
import { sdk, signers } from "./before-setup";
|
||||
import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers";
|
||||
@@ -6,6 +8,8 @@ import { expect } from "chai";
|
||||
describe("Contract Registry", () => {
|
||||
let registry: ContractRegistry;
|
||||
|
||||
let multichainRegistry: MultichainRegistry;
|
||||
|
||||
let adminWallet: SignerWithAddress;
|
||||
|
||||
let address: string;
|
||||
@@ -14,6 +18,38 @@ describe("Contract Registry", () => {
|
||||
[adminWallet] = signers;
|
||||
});
|
||||
|
||||
it("multichain registry", async () => {
|
||||
sdk.updateSignerOrProvider(adminWallet);
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
multichainRegistry = sdk.multiChainRegistry;
|
||||
console.log(
|
||||
"multichain registry address: ",
|
||||
getMultichainRegistryAddress(),
|
||||
);
|
||||
|
||||
address = await sdk.deployer.deployNFTCollection({
|
||||
name: "Test1",
|
||||
primary_sale_recipient: adminWallet.address,
|
||||
});
|
||||
|
||||
const chainId: number = await adminWallet.getChainId();
|
||||
const metadataURI: string = "ipfs://metadata";
|
||||
await multichainRegistry.addContract({
|
||||
address,
|
||||
chainId,
|
||||
metadataURI,
|
||||
});
|
||||
|
||||
let uri = await multichainRegistry.getContractMetadataURI(chainId, address);
|
||||
expect(uri).to.equal(metadataURI);
|
||||
|
||||
let contracts = await multichainRegistry.getContractAddresses(
|
||||
adminWallet.address,
|
||||
);
|
||||
|
||||
expect(contracts[0].address).to.equal(address);
|
||||
});
|
||||
|
||||
it("should allow adding and removing contracts", async () => {
|
||||
sdk.updateSignerOrProvider(adminWallet);
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
|
||||
@@ -189,7 +189,7 @@ describe("Signature drop tests (v5)", async () => {
|
||||
name: "Test1",
|
||||
});
|
||||
const toSign = {
|
||||
to: adminWallet.address,
|
||||
to: samWallet.address,
|
||||
metadata: uri,
|
||||
quantity: 1,
|
||||
};
|
||||
@@ -207,12 +207,12 @@ describe("Signature drop tests (v5)", async () => {
|
||||
name: "Test2",
|
||||
});
|
||||
const toSign1 = {
|
||||
to: adminWallet.address,
|
||||
to: samWallet.address,
|
||||
metadata: uri1,
|
||||
quantity: 1,
|
||||
};
|
||||
const toSign2 = {
|
||||
to: adminWallet.address,
|
||||
to: samWallet.address,
|
||||
metadata: uri2,
|
||||
quantity: 1,
|
||||
};
|
||||
|
||||
@@ -195,12 +195,12 @@ describe("Edition sig minting", async () => {
|
||||
name: "Test2",
|
||||
});
|
||||
const toSign1 = {
|
||||
to: adminWallet.address,
|
||||
to: samWallet.address,
|
||||
metadata: uri1,
|
||||
quantity: 10,
|
||||
};
|
||||
const toSign2 = {
|
||||
to: adminWallet.address,
|
||||
to: samWallet.address,
|
||||
metadata: uri2,
|
||||
quantity: 10,
|
||||
};
|
||||
@@ -236,7 +236,7 @@ describe("Edition sig minting", async () => {
|
||||
it("should mint additional supply", async () => {
|
||||
const tx = await editionContract.signature.mint(v1);
|
||||
const additional = await editionContract.signature.generateFromTokenId({
|
||||
to: adminWallet.address,
|
||||
to: samWallet.address,
|
||||
tokenId: tx.id,
|
||||
quantity: 100,
|
||||
});
|
||||
|
||||
@@ -46,6 +46,7 @@ describe("NFT sig minting", async () => {
|
||||
currencyAddress: NATIVE_TOKEN_ADDRESS,
|
||||
metadata: {
|
||||
name: "OUCH VOUCH",
|
||||
background_color: "ff0000",
|
||||
},
|
||||
price: "1",
|
||||
to: samWallet.address,
|
||||
@@ -195,7 +196,7 @@ describe("NFT sig minting", async () => {
|
||||
name: "Test1",
|
||||
});
|
||||
const toSign = {
|
||||
to: adminWallet.address,
|
||||
to: samWallet.address,
|
||||
metadata: uri,
|
||||
};
|
||||
const payload = await nftContract.signature.generate(toSign);
|
||||
@@ -212,11 +213,11 @@ describe("NFT sig minting", async () => {
|
||||
name: "Test2",
|
||||
});
|
||||
const toSign1 = {
|
||||
to: adminWallet.address,
|
||||
to: samWallet.address,
|
||||
metadata: uri1,
|
||||
};
|
||||
const toSign2 = {
|
||||
to: adminWallet.address,
|
||||
to: samWallet.address,
|
||||
metadata: uri2,
|
||||
};
|
||||
const payloads = await nftContract.signature.generateBatch([
|
||||
|
||||
@@ -56,4 +56,25 @@ describe("Wallet", async () => {
|
||||
const address = sdk.wallet.recoverAddress(message, signature);
|
||||
expect(address).to.eq(adminWallet.address);
|
||||
});
|
||||
|
||||
it("should sign typed data and recover address", async () => {
|
||||
sdk.updateSignerOrProvider(adminWallet);
|
||||
const { payload, signature } = await sdk.wallet.signTypedData(
|
||||
{
|
||||
name: "MyEIP721Domain",
|
||||
version: "1",
|
||||
chainId: 1,
|
||||
verifyingContract: adminWallet.address,
|
||||
},
|
||||
{
|
||||
MyStruct: [
|
||||
{ name: "to", type: "address" },
|
||||
{ name: "quantity", type: "uint256" },
|
||||
],
|
||||
},
|
||||
{ to: adminWallet.address, quantity: 1 },
|
||||
);
|
||||
expect(payload !== undefined).to.eq(true);
|
||||
expect(signature !== undefined).to.eq(true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -149,16 +149,13 @@ describe("NFTCollection", async () => {
|
||||
expect(creators[0].share).to.equal(100);
|
||||
|
||||
const newCreator = Keypair.generate().publicKey.toBase58();
|
||||
await collection.updateCreators(
|
||||
[
|
||||
{
|
||||
address: sdk.wallet.getAddress() as string,
|
||||
share: 75,
|
||||
},
|
||||
{ address: newCreator, share: 25 },
|
||||
],
|
||||
true,
|
||||
);
|
||||
await collection.updateCreators([
|
||||
{
|
||||
address: sdk.wallet.getAddress() as string,
|
||||
share: 75,
|
||||
},
|
||||
{ address: newCreator, share: 25 },
|
||||
]);
|
||||
|
||||
creators = await collection.getCreators();
|
||||
expect(creators.length).to.equal(2);
|
||||
@@ -167,16 +164,17 @@ describe("NFTCollection", async () => {
|
||||
expect(creators[1].address).to.equal(newCreator);
|
||||
expect(creators[1].share).to.equal(25);
|
||||
|
||||
const all = await collection.getAll();
|
||||
for (const nft of all) {
|
||||
// @ts-ignore
|
||||
const creatorsOfNft = await collection.nft.creatorsOf(nft.metadata.id);
|
||||
expect(creatorsOfNft.length).to.equal(2);
|
||||
expect(creatorsOfNft[0].address).to.equal(sdk.wallet.getAddress());
|
||||
expect(creatorsOfNft[0].share).to.equal(75);
|
||||
expect(creatorsOfNft[1].address).to.equal(newCreator);
|
||||
expect(creatorsOfNft[1].share).to.equal(25);
|
||||
}
|
||||
// TODO (adam): fix this test
|
||||
// const all = await collection.getAll();
|
||||
// for (const nft of all) {
|
||||
// // @ts-ignore
|
||||
// const creatorsOfNft = await collection.nft.creatorsOf(nft.metadata.id);
|
||||
// expect(creatorsOfNft.length).to.equal(2);
|
||||
// expect(creatorsOfNft[0].address).to.equal(sdk.wallet.getAddress());
|
||||
// expect(creatorsOfNft[0].share).to.equal(75);
|
||||
// expect(creatorsOfNft[1].address).to.equal(newCreator);
|
||||
// expect(creatorsOfNft[1].share).to.equal(25);
|
||||
// }
|
||||
});
|
||||
|
||||
it("Should update royalty", async () => {
|
||||
@@ -189,13 +187,13 @@ describe("NFTCollection", async () => {
|
||||
expect(nft.isMutable).to.equal(true);
|
||||
expect(nft.sellerFeeBasisPoints).to.equal(0);
|
||||
|
||||
await collection.updateRoyalty(100, true);
|
||||
await collection.updateRoyalty(100);
|
||||
|
||||
royalty = await collection.getRoyalty();
|
||||
expect(royalty).to.equal(100);
|
||||
|
||||
// @ts-ignore
|
||||
nft = await collection.nft.getRaw(mintAddress);
|
||||
expect(nft.sellerFeeBasisPoints).to.equal(100);
|
||||
// TODO (adam): fix this test
|
||||
// nft = await collection.nft.getRaw(mintAddress);
|
||||
// expect(nft.sellerFeeBasisPoints).to.equal(100);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
import { NFTDrop, Token } from "../../src/solana";
|
||||
import { sdk } from "./before-setup";
|
||||
import { Keypair, PublicKey } from "@solana/web3.js";
|
||||
import { expect } from "chai";
|
||||
|
||||
describe("NFTDrop ClaimConditions", async () => {
|
||||
let drop: NFTDrop;
|
||||
let token: Token;
|
||||
let primarySaleRecipient: string;
|
||||
|
||||
before(async () => {
|
||||
const address = await sdk.deployer.createNftDrop({
|
||||
name: "NFT Drop #1",
|
||||
totalSupply: 5,
|
||||
});
|
||||
const addressToken = await sdk.deployer.createToken({
|
||||
name: "Token #1",
|
||||
initialSupply: 100,
|
||||
});
|
||||
drop = await sdk.getNFTDrop(address);
|
||||
token = await sdk.getToken(addressToken);
|
||||
primarySaleRecipient = Keypair.generate().publicKey.toBase58();
|
||||
await token.mintTo(primarySaleRecipient, 1); // only needed to create the token account / TODO expose utility for it
|
||||
});
|
||||
|
||||
it("should set claim conditions with custom currency and claim", async () => {
|
||||
const metadatas = [...Array(5)].map((_, i) => ({
|
||||
name: `NFT #${i + 1}`,
|
||||
description: `This is the #${i + 1} NFT`,
|
||||
}));
|
||||
await drop.lazyMint(metadatas);
|
||||
expect((await token.balance()).displayValue).to.eq("100.000000000");
|
||||
expect((await token.balanceOf(primarySaleRecipient)).displayValue).to.eq(
|
||||
"1.000000000",
|
||||
);
|
||||
|
||||
await drop.claimConditions.set({
|
||||
maxClaimable: 3,
|
||||
currencyAddress: token.publicKey.toBase58(),
|
||||
price: 1,
|
||||
primarySaleRecipient,
|
||||
});
|
||||
const addresses = await drop.claim(2);
|
||||
expect(addresses.length).to.eq(2);
|
||||
expect((await token.balance()).displayValue).to.eq("98.000000000");
|
||||
expect((await token.balanceOf(primarySaleRecipient)).displayValue).to.eq(
|
||||
"3.000000000",
|
||||
);
|
||||
expect(await drop.balance(addresses[0])).to.eq(1);
|
||||
expect(await drop.balance(addresses[1])).to.eq(1);
|
||||
});
|
||||
});
|
||||
@@ -1,5 +1,33 @@
|
||||
# @thirdweb-dev/unity-js-bridge
|
||||
|
||||
## 0.1.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`5fba324`](https://github.com/thirdweb-dev/js/commit/5fba324ac17ab02bc8f13d82a232bc5c6970c8e5), [`894cbef`](https://github.com/thirdweb-dev/js/commit/894cbefc6361f23fc528a6c6819f5c71793d46e4), [`557429b`](https://github.com/thirdweb-dev/js/commit/557429b5cfb3af2983ee01cf7d12d41ee0557593), [`73dc026`](https://github.com/thirdweb-dev/js/commit/73dc026ff9d0ac9099bd5a9a9cab8fdbfc0ae723), [`9eaa21d`](https://github.com/thirdweb-dev/js/commit/9eaa21d09ab9c700aea61a2a25f8ca9859d20857), [`68c8e3c`](https://github.com/thirdweb-dev/js/commit/68c8e3c4d79f5d56dc4414241bcca0d88285fcca), [`bff433e`](https://github.com/thirdweb-dev/js/commit/bff433e12150dc029e33a578219c8437a510da99)]:
|
||||
- @thirdweb-dev/sdk@3.6.10
|
||||
|
||||
## 0.1.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`e913e0d`](https://github.com/thirdweb-dev/js/commit/e913e0daa9ece9a6274f7ffa4e66bdcbf32e6ada), [`5862c55`](https://github.com/thirdweb-dev/js/commit/5862c558fb48604b5aca4defd1ccc06fc3536358), [`73883f5`](https://github.com/thirdweb-dev/js/commit/73883f56d0ed0a35ace8b98b96caa782443be22e), [`f7a74a3`](https://github.com/thirdweb-dev/js/commit/f7a74a3b7c489ada5b1ec435632a326334a33c9b)]:
|
||||
- @thirdweb-dev/sdk@3.6.9
|
||||
|
||||
## 0.1.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#457](https://github.com/thirdweb-dev/js/pull/457) [`ccb4ce8`](https://github.com/thirdweb-dev/js/commit/ccb4ce8d00f50ff34426a0a5e58c7243e409f706) Thanks [@jnsdls](https://github.com/jnsdls)! - Support for new magic link connector
|
||||
|
||||
- [#454](https://github.com/thirdweb-dev/js/pull/454) [`c673e39`](https://github.com/thirdweb-dev/js/commit/c673e39f23ef082097d73d62910580e8fad400a0) Thanks [@jnsdls](https://github.com/jnsdls)! - upgraded dependencies
|
||||
|
||||
- [#458](https://github.com/thirdweb-dev/js/pull/458) [`4cdd0bd`](https://github.com/thirdweb-dev/js/commit/4cdd0bd6348494a256d7c6a2bdf8f7b5c20f6877) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Fixes for latest wallet package integration
|
||||
|
||||
- Updated dependencies [[`ccb4ce8`](https://github.com/thirdweb-dev/js/commit/ccb4ce8d00f50ff34426a0a5e58c7243e409f706), [`c673e39`](https://github.com/thirdweb-dev/js/commit/c673e39f23ef082097d73d62910580e8fad400a0), [`ea95c5f`](https://github.com/thirdweb-dev/js/commit/ea95c5f609e306e333ee0f73f7920503358ca848), [`699a2b1`](https://github.com/thirdweb-dev/js/commit/699a2b16fb991c474ec57db8f178e2601d631f39), [`4cdd0bd`](https://github.com/thirdweb-dev/js/commit/4cdd0bd6348494a256d7c6a2bdf8f7b5c20f6877), [`a8267f9`](https://github.com/thirdweb-dev/js/commit/a8267f912df84c58d3fe3f47b90bd474f73c84ca)]:
|
||||
- @thirdweb-dev/wallets@0.1.1
|
||||
- @thirdweb-dev/sdk@3.6.8
|
||||
|
||||
## 0.1.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@thirdweb-dev/unity-js-bridge",
|
||||
"version": "0.1.3",
|
||||
"version": "0.1.6",
|
||||
"main": "dist/thirdweb-unity-bridge.js",
|
||||
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/unity-js-bridge",
|
||||
"license": "Apache-2.0",
|
||||
@@ -12,14 +12,14 @@
|
||||
"dist/"
|
||||
],
|
||||
"dependencies": {
|
||||
"@thirdweb-dev/pay": "*",
|
||||
"@thirdweb-dev/sdk": "*",
|
||||
"@thirdweb-dev/storage": "*",
|
||||
"@thirdweb-dev/wallets": "*",
|
||||
"@thirdweb-dev/pay": "*",
|
||||
"ethers": "^5.7.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"esbuild": "^0.15.16",
|
||||
"esbuild": "0.15.17",
|
||||
"node-stdlib-browser": "^1.2.0",
|
||||
"typescript": "^4.7.4"
|
||||
},
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
/// --- Thirdweb Brige ---
|
||||
import { CoinbasePayIntegration, FundWalletOptions } from "@thirdweb-dev/pay";
|
||||
import { ChainOrRpc, ThirdwebSDK } from "@thirdweb-dev/sdk/evm";
|
||||
import { ChainOrRpc, ThirdwebSDK, getRpcUrl } from "@thirdweb-dev/sdk";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
import {
|
||||
CoinbaseWallet,
|
||||
MetaMask,
|
||||
WalletConnect,
|
||||
InjectedWallet,
|
||||
MagicAuthWallet,
|
||||
} from "@thirdweb-dev/wallets";
|
||||
import type { AbstractWallet } from "@thirdweb-dev/wallets/dist/declarations/src/wallets/base";
|
||||
import { BigNumber } from "ethers";
|
||||
@@ -18,6 +19,8 @@ declare global {
|
||||
}
|
||||
}
|
||||
|
||||
const API_KEY =
|
||||
"339d65590ba0fa79e4c8be0af33d64eda709e13652acb02c6be63f5a1fbef9c3";
|
||||
const SEPARATOR = "/";
|
||||
const SUB_SEPARATOR = "#";
|
||||
|
||||
@@ -41,6 +44,7 @@ const WALLETS = [
|
||||
InjectedWallet,
|
||||
WalletConnect,
|
||||
CoinbaseWallet,
|
||||
MagicAuthWallet,
|
||||
] as const;
|
||||
|
||||
type PossibleWallet = typeof WALLETS[number]["id"];
|
||||
@@ -83,18 +87,19 @@ class ThirdwebBridge implements TWBridge {
|
||||
console.debug("thirdwebSDK initialization:", chain, options);
|
||||
const sdkOptions = JSON.parse(options);
|
||||
const storage =
|
||||
sdkOptions && sdkOptions.ipfsGatewayUrl
|
||||
sdkOptions?.storage && sdkOptions?.storage?.ipfsGatewayUrl
|
||||
? new ThirdwebStorage({
|
||||
gatewayUrls: {
|
||||
"ipfs://": [sdkOptions.ipfsGatewayUrl],
|
||||
"ipfs://": [sdkOptions.storage.ipfsGatewayUrl],
|
||||
},
|
||||
})
|
||||
: new ThirdwebStorage();
|
||||
|
||||
this.activeSDK = new ThirdwebSDK(chain, sdkOptions, storage);
|
||||
const rpcUrl = chain.startsWith("http") ? chain : getRpcUrl(chain, API_KEY);
|
||||
this.activeSDK = new ThirdwebSDK(rpcUrl, sdkOptions, storage);
|
||||
for (let wallet of WALLETS) {
|
||||
const walletInstance = new wallet({
|
||||
appName: sdkOptions.appName || "thirdweb powered dApp",
|
||||
appName: sdkOptions.wallet?.appName || "thirdweb powered dApp",
|
||||
...sdkOptions.wallet?.extras,
|
||||
});
|
||||
walletInstance.on("connect", async () =>
|
||||
this.updateSDKSigner(await walletInstance.getSigner()),
|
||||
|
||||
@@ -1,5 +1,15 @@
|
||||
# @thirdweb-dev/wallets
|
||||
|
||||
## 0.1.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#457](https://github.com/thirdweb-dev/js/pull/457) [`ccb4ce8`](https://github.com/thirdweb-dev/js/commit/ccb4ce8d00f50ff34426a0a5e58c7243e409f706) Thanks [@jnsdls](https://github.com/jnsdls)! - Support for new magic link connector
|
||||
|
||||
- [#454](https://github.com/thirdweb-dev/js/pull/454) [`c673e39`](https://github.com/thirdweb-dev/js/commit/c673e39f23ef082097d73d62910580e8fad400a0) Thanks [@jnsdls](https://github.com/jnsdls)! - upgraded dependencies
|
||||
|
||||
- [#458](https://github.com/thirdweb-dev/js/pull/458) [`4cdd0bd`](https://github.com/thirdweb-dev/js/commit/4cdd0bd6348494a256d7c6a2bdf8f7b5c20f6877) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Fixes for latest wallet package integration
|
||||
|
||||
## 0.1.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"main": "dist/thirdweb-dev-wallets-connectors-magic.cjs.js",
|
||||
"module": "dist/thirdweb-dev-wallets-connectors-magic.esm.js"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@thirdweb-dev/wallets",
|
||||
"version": "0.1.0",
|
||||
"version": "0.1.1",
|
||||
"main": "dist/thirdweb-dev-wallets.cjs.js",
|
||||
"module": "dist/thirdweb-dev-wallets.esm.js",
|
||||
"exports": {
|
||||
@@ -20,6 +20,14 @@
|
||||
"module": "./wallets/metamask/dist/thirdweb-dev-wallets-wallets-metamask.esm.js",
|
||||
"default": "./wallets/metamask/dist/thirdweb-dev-wallets-wallets-metamask.cjs.js"
|
||||
},
|
||||
"./wallets/magic-auth": {
|
||||
"module": "./wallets/magic-auth/dist/thirdweb-dev-wallets-wallets-magic-auth.esm.js",
|
||||
"default": "./wallets/magic-auth/dist/thirdweb-dev-wallets-wallets-magic-auth.cjs.js"
|
||||
},
|
||||
"./connectors/magic": {
|
||||
"module": "./connectors/magic/dist/thirdweb-dev-wallets-connectors-magic.esm.js",
|
||||
"default": "./connectors/magic/dist/thirdweb-dev-wallets-connectors-magic.cjs.js"
|
||||
},
|
||||
"./wallets/wallet-connect": {
|
||||
"module": "./wallets/wallet-connect/dist/thirdweb-dev-wallets-wallets-wallet-connect.esm.js",
|
||||
"default": "./wallets/wallet-connect/dist/thirdweb-dev-wallets-wallets-wallet-connect.cjs.js"
|
||||
@@ -60,7 +68,7 @@
|
||||
"preconstruct": {
|
||||
"entrypoints": [
|
||||
"index.ts",
|
||||
"connectors/**",
|
||||
"connectors/*/index.ts",
|
||||
"wallets/**"
|
||||
],
|
||||
"___experimentalFlags_WILL_CHANGE_IN_PATCH": {
|
||||
@@ -71,12 +79,17 @@
|
||||
"sideEffects": false,
|
||||
"dependencies": {
|
||||
"@coinbase/wallet-sdk": "^3.6.0",
|
||||
"@wagmi/core": "^0.7.6",
|
||||
"@magic-ext/connect": "^3.1.0",
|
||||
"@magic-ext/oauth": "^4.1.0",
|
||||
"@magic-sdk/provider": "^10.1.0",
|
||||
"@wagmi/chains": "^0.1.0",
|
||||
"@wagmi/core": "^0.8.2",
|
||||
"@walletconnect/ethereum-provider": "^1.7.8",
|
||||
"abitype": "^0.2.5",
|
||||
"ethers": "^5.7.2",
|
||||
"eventemitter3": "^5.0.0",
|
||||
"localforage": "^1.10.0"
|
||||
"localforage": "^1.10.0",
|
||||
"magic-sdk": "^10.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@preconstruct/cli": "^2.2.1",
|
||||
|
||||
@@ -158,7 +158,8 @@ export class CoinbaseWalletConnector extends Connector<
|
||||
: chain_.id === walletExtensionChainId,
|
||||
) || this.chains[0];
|
||||
const chainId = this.options.chainId || chain?.id;
|
||||
const jsonRpcUrl = this.options.jsonRpcUrl || chain?.rpcUrls.default;
|
||||
const jsonRpcUrl =
|
||||
this.options.jsonRpcUrl || chain?.rpcUrls.default.http[0];
|
||||
|
||||
this.#provider = this.#client.makeWeb3Provider(jsonRpcUrl, chainId);
|
||||
}
|
||||
@@ -199,7 +200,8 @@ export class CoinbaseWalletConnector extends Connector<
|
||||
id: chainId,
|
||||
name: `Chain ${id}`,
|
||||
network: `${id}`,
|
||||
rpcUrls: { default: "" },
|
||||
nativeCurrency: { name: "Ether", decimals: 18, symbol: "ETH" },
|
||||
rpcUrls: { default: { http: [""] } },
|
||||
}
|
||||
);
|
||||
} catch (error) {
|
||||
@@ -218,7 +220,10 @@ export class CoinbaseWalletConnector extends Connector<
|
||||
chainId: id,
|
||||
chainName: chain.name,
|
||||
nativeCurrency: chain.nativeCurrency,
|
||||
rpcUrls: [chain.rpcUrls.public ?? chain.rpcUrls.default],
|
||||
rpcUrls: [
|
||||
chain.rpcUrls.public?.http[0] ??
|
||||
chain.rpcUrls.default.http[0],
|
||||
],
|
||||
blockExplorerUrls: this.getBlockExplorerUrls(chain),
|
||||
},
|
||||
],
|
||||
|
||||
@@ -18,13 +18,16 @@ import type { Address } from "abitype";
|
||||
import { providers } from "ethers";
|
||||
import { getAddress, hexValue } from "ethers/lib/utils.js";
|
||||
|
||||
declare interface Window {
|
||||
ethereum: Ethereum;
|
||||
}
|
||||
|
||||
export type InjectedConnectorOptions = {
|
||||
/** Name of connector */
|
||||
name?: string | ((detectedName: string | string[]) => string);
|
||||
/**
|
||||
* [EIP-1193](https://eips.ethereum.org/EIPS/eip-1193) Ethereum Provider to target
|
||||
*
|
||||
* @default
|
||||
* () => typeof window !== 'undefined' ? window.ethereum : undefined
|
||||
*/
|
||||
getProvider?: () => Ethereum | undefined;
|
||||
/**
|
||||
* MetaMask 10.9.3 emits disconnect event when chain is changed.
|
||||
* This flag prevents the `"disconnect"` event from being emitted upon switching chains. See [GitHub issue](https://github.com/MetaMask/metamask-extension/issues/13375#issuecomment-1027663334) for more info.
|
||||
@@ -38,16 +41,19 @@ export type InjectedConnectorOptions = {
|
||||
shimDisconnect?: boolean;
|
||||
};
|
||||
|
||||
type ConnectorOptions = InjectedConnectorOptions &
|
||||
Required<Pick<InjectedConnectorOptions, "getProvider">>;
|
||||
|
||||
export class InjectedConnector extends Connector<
|
||||
Window["ethereum"],
|
||||
InjectedConnectorOptions | undefined,
|
||||
Ethereum,
|
||||
ConnectorOptions,
|
||||
providers.JsonRpcSigner
|
||||
> {
|
||||
readonly id: string;
|
||||
readonly name: string;
|
||||
readonly ready = typeof window !== "undefined" && !!window.ethereum;
|
||||
readonly ready: boolean;
|
||||
|
||||
#provider?: Window["ethereum"];
|
||||
#provider?: Ethereum;
|
||||
#switchingChains?: boolean;
|
||||
|
||||
protected shimDisconnectKey = "injected.shimDisconnect";
|
||||
@@ -62,28 +68,34 @@ export class InjectedConnector extends Connector<
|
||||
const options = {
|
||||
shimDisconnect: true,
|
||||
shimChainChangedDisconnect: true,
|
||||
getProvider: () =>
|
||||
typeof window !== "undefined"
|
||||
? (window.ethereum as Ethereum)
|
||||
: undefined,
|
||||
...options_,
|
||||
};
|
||||
super({ chains, options });
|
||||
|
||||
let name = "Injected";
|
||||
const overrideName = options.name;
|
||||
if (typeof overrideName === "string") {
|
||||
name = overrideName;
|
||||
} else if (typeof window !== "undefined") {
|
||||
const detectedName = getInjectedName(window.ethereum as Ethereum);
|
||||
if (overrideName) {
|
||||
name = overrideName(detectedName);
|
||||
const provider = options.getProvider();
|
||||
if (typeof options.name === "string") {
|
||||
this.name = options.name;
|
||||
} else if (provider) {
|
||||
const detectedName = getInjectedName(provider as Ethereum);
|
||||
if (options.name) {
|
||||
this.name = options.name(detectedName);
|
||||
} else {
|
||||
name =
|
||||
typeof detectedName === "string"
|
||||
? detectedName
|
||||
: (detectedName[0] as string);
|
||||
if (typeof detectedName === "string") {
|
||||
this.name = detectedName;
|
||||
} else {
|
||||
this.name = detectedName[0] as string;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.name = "Injected";
|
||||
}
|
||||
|
||||
this.id = "injected";
|
||||
this.name = name;
|
||||
this.ready = !!provider;
|
||||
}
|
||||
|
||||
async connect({ chainId }: { chainId?: number } = {}) {
|
||||
@@ -115,8 +127,8 @@ export class InjectedConnector extends Connector<
|
||||
}
|
||||
|
||||
// Add shim to storage signalling wallet is connected
|
||||
if (this.options?.shimDisconnect) {
|
||||
await getConnectorStorage().setItem(this.shimDisconnectKey, true);
|
||||
if (this.options.shimDisconnect) {
|
||||
await getConnectorStorage()?.setItem(this.shimDisconnectKey, true);
|
||||
}
|
||||
|
||||
return { account, chain: { id, unsupported }, provider };
|
||||
@@ -142,8 +154,8 @@ export class InjectedConnector extends Connector<
|
||||
provider.removeListener("disconnect", this.onDisconnect);
|
||||
|
||||
// Remove shim signalling wallet is disconnected
|
||||
if (this.options?.shimDisconnect) {
|
||||
await getConnectorStorage().removeItem(this.shimDisconnectKey);
|
||||
if (this.options.shimDisconnect) {
|
||||
await getConnectorStorage()?.removeItem(this.shimDisconnectKey);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,8 +180,9 @@ export class InjectedConnector extends Connector<
|
||||
}
|
||||
|
||||
async getProvider() {
|
||||
if (typeof window !== "undefined" && !!window.ethereum) {
|
||||
this.#provider = window.ethereum as Ethereum;
|
||||
const provider = this.options.getProvider();
|
||||
if (provider) {
|
||||
this.#provider = provider;
|
||||
}
|
||||
return this.#provider as Ethereum;
|
||||
}
|
||||
@@ -188,9 +201,9 @@ export class InjectedConnector extends Connector<
|
||||
async isAuthorized() {
|
||||
try {
|
||||
if (
|
||||
this.options?.shimDisconnect &&
|
||||
this.options.shimDisconnect &&
|
||||
// If shim does not exist in storage, wallet is disconnected
|
||||
!(await getConnectorStorage().getItem(this.shimDisconnectKey))
|
||||
!(await getConnectorStorage()?.getItem(this.shimDisconnectKey))
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
@@ -207,7 +220,7 @@ export class InjectedConnector extends Connector<
|
||||
}
|
||||
|
||||
async switchChain(chainId: number) {
|
||||
if (this.options?.shimChainChangedDisconnect) {
|
||||
if (this.options.shimChainChangedDisconnect) {
|
||||
this.#switchingChains = true;
|
||||
}
|
||||
|
||||
@@ -227,7 +240,8 @@ export class InjectedConnector extends Connector<
|
||||
id: chainId,
|
||||
name: `Chain ${id}`,
|
||||
network: `${id}`,
|
||||
rpcUrls: { default: "" },
|
||||
nativeCurrency: { name: "Ether", decimals: 18, symbol: "ETH" },
|
||||
rpcUrls: { default: { http: [""] } },
|
||||
}
|
||||
);
|
||||
} catch (error) {
|
||||
@@ -252,7 +266,11 @@ export class InjectedConnector extends Connector<
|
||||
chainId: id,
|
||||
chainName: chain.name,
|
||||
nativeCurrency: chain.nativeCurrency,
|
||||
rpcUrls: [chain.rpcUrls.public ?? chain.rpcUrls.default],
|
||||
rpcUrls: [
|
||||
chain.rpcUrls.public?.http[0] ??
|
||||
chain.rpcUrls.default.http[0] ??
|
||||
"",
|
||||
],
|
||||
blockExplorerUrls: this.getBlockExplorerUrls(chain),
|
||||
},
|
||||
],
|
||||
@@ -322,14 +340,14 @@ export class InjectedConnector extends Connector<
|
||||
// We need this as MetaMask can emit the "disconnect" event
|
||||
// upon switching chains. This workaround ensures that the
|
||||
// user currently isn't in the process of switching chains.
|
||||
if (this.options?.shimChainChangedDisconnect && this.#switchingChains) {
|
||||
if (this.options.shimChainChangedDisconnect && this.#switchingChains) {
|
||||
this.#switchingChains = false;
|
||||
return;
|
||||
}
|
||||
|
||||
this.emit("disconnect");
|
||||
// Remove shim signalling wallet is disconnected
|
||||
if (this.options?.shimDisconnect) {
|
||||
if (this.options.shimDisconnect) {
|
||||
await getConnectorStorage().removeItem(this.shimDisconnectKey);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -0,0 +1,181 @@
|
||||
import { MagicConnector, MagicOptions } from "./magicConnector";
|
||||
import { OAuthExtension, OAuthProvider } from "@magic-ext/oauth";
|
||||
import {
|
||||
InstanceWithExtensions,
|
||||
MagicSDKAdditionalConfiguration,
|
||||
SDKBase,
|
||||
} from "@magic-sdk/provider";
|
||||
import {
|
||||
Chain,
|
||||
ChainNotConfiguredError,
|
||||
normalizeChainId,
|
||||
UserRejectedRequestError,
|
||||
} from "@wagmi/core";
|
||||
import { getAddress } from "ethers/lib/utils.js";
|
||||
import { Magic } from "magic-sdk";
|
||||
|
||||
export interface MagicAuthOptions extends MagicOptions {
|
||||
enableEmailLogin?: boolean;
|
||||
enableSMSLogin?: boolean;
|
||||
oauthOptions?: {
|
||||
providers: OAuthProvider[];
|
||||
callbackUrl?: string;
|
||||
};
|
||||
magicSdkConfiguration?: MagicSDKAdditionalConfiguration<
|
||||
string,
|
||||
OAuthExtension[]
|
||||
>;
|
||||
}
|
||||
|
||||
export class MagicAuthConnector extends MagicConnector {
|
||||
magicSDK?: InstanceWithExtensions<SDKBase, OAuthExtension[]>;
|
||||
|
||||
magicSdkConfiguration: MagicSDKAdditionalConfiguration<
|
||||
string,
|
||||
OAuthExtension[]
|
||||
>;
|
||||
|
||||
enableSMSLogin: boolean;
|
||||
|
||||
enableEmailLogin: boolean;
|
||||
|
||||
oauthProviders: OAuthProvider[];
|
||||
|
||||
oauthCallbackUrl?: string;
|
||||
|
||||
constructor(config: { chains?: Chain[]; options: MagicAuthOptions }) {
|
||||
super(config);
|
||||
this.magicSdkConfiguration = config.options.magicSdkConfiguration || {};
|
||||
this.oauthProviders = config.options.oauthOptions?.providers || [];
|
||||
this.oauthCallbackUrl = config.options.oauthOptions?.callbackUrl;
|
||||
this.enableSMSLogin = config.options.enableSMSLogin || false;
|
||||
this.enableEmailLogin = config.options.enableEmailLogin || false;
|
||||
}
|
||||
|
||||
async connect({ chainId }: { chainId?: number }) {
|
||||
// for a specific chainId we will overwrite the magicSDKConfiguration
|
||||
if (chainId) {
|
||||
const chain = this.chains.find((c) => c.id === chainId);
|
||||
if (chain) {
|
||||
this.magicSdkConfiguration = {
|
||||
...this.magicSdkConfiguration,
|
||||
network: {
|
||||
chainId: chain.id,
|
||||
rpcUrl: chain.rpcUrls.default.http[0],
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
try {
|
||||
const provider = await this.getProvider();
|
||||
|
||||
if (provider.on) {
|
||||
provider.on("accountsChanged", this.onAccountsChanged);
|
||||
provider.on("chainChanged", this.onChainChanged);
|
||||
provider.on("disconnect", this.onDisconnect);
|
||||
}
|
||||
|
||||
// Check if there is a user logged in
|
||||
const isAuthenticated = await this.isAuthorized();
|
||||
|
||||
// Check if we have a chainId, in case of error just assign 0 for legacy
|
||||
let chainId_: number;
|
||||
try {
|
||||
chainId_ = await this.getChainId();
|
||||
} catch (e) {
|
||||
chainId_ = 0;
|
||||
}
|
||||
|
||||
// if there is a user logged in, return the user
|
||||
if (isAuthenticated) {
|
||||
return {
|
||||
provider,
|
||||
chain: {
|
||||
id: chainId_,
|
||||
unsupported: false,
|
||||
},
|
||||
account: getAddress(await this.getAccount()),
|
||||
};
|
||||
}
|
||||
|
||||
// open the modal and process the magic login steps
|
||||
if (!this.isModalOpen) {
|
||||
const output = await this.getUserDetailsByForm(
|
||||
this.enableSMSLogin,
|
||||
this.enableEmailLogin,
|
||||
this.oauthProviders,
|
||||
);
|
||||
const magic = this.getMagicSDK();
|
||||
|
||||
// LOGIN WITH MAGIC LINK WITH OAUTH PROVIDER
|
||||
if (output.oauthProvider) {
|
||||
await magic.oauth.loginWithRedirect({
|
||||
provider: output.oauthProvider,
|
||||
redirectURI: this.oauthCallbackUrl || window.location.href,
|
||||
});
|
||||
}
|
||||
|
||||
// LOGIN WITH MAGIC LINK WITH EMAIL
|
||||
if (output.email) {
|
||||
await magic.auth.loginWithMagicLink({
|
||||
email: output.email,
|
||||
});
|
||||
}
|
||||
|
||||
// LOGIN WITH MAGIC LINK WITH PHONE NUMBER
|
||||
if (output.phoneNumber) {
|
||||
await magic.auth.loginWithSMS({
|
||||
phoneNumber: output.phoneNumber,
|
||||
});
|
||||
}
|
||||
|
||||
const signer = await this.getSigner();
|
||||
const account = await signer.getAddress();
|
||||
|
||||
return {
|
||||
account: getAddress(account),
|
||||
chain: {
|
||||
id: chainId_,
|
||||
unsupported: false,
|
||||
},
|
||||
provider,
|
||||
};
|
||||
}
|
||||
throw new UserRejectedRequestError("User rejected request");
|
||||
} catch (error) {
|
||||
throw new UserRejectedRequestError("Something went wrong");
|
||||
}
|
||||
}
|
||||
|
||||
async getChainId(): Promise<number> {
|
||||
const networkOptions = this.magicSdkConfiguration?.network;
|
||||
if (typeof networkOptions === "object") {
|
||||
const chainID = networkOptions.chainId;
|
||||
if (chainID) {
|
||||
return normalizeChainId(chainID);
|
||||
}
|
||||
}
|
||||
throw new Error("Chain ID is not defined");
|
||||
}
|
||||
|
||||
getMagicSDK(): InstanceWithExtensions<SDKBase, OAuthExtension[]> {
|
||||
if (!this.magicSDK) {
|
||||
this.magicSDK = new Magic(this.magicOptions.apiKey, {
|
||||
...this.magicSdkConfiguration,
|
||||
extensions: [new OAuthExtension()],
|
||||
});
|
||||
return this.magicSDK;
|
||||
}
|
||||
return this.magicSDK;
|
||||
}
|
||||
|
||||
async switchChain(chainId: number): Promise<Chain> {
|
||||
// check if the chain is supported
|
||||
const chain = this.chains.find((c) => c.id === chainId);
|
||||
if (!chain) {
|
||||
throw new ChainNotConfiguredError({ chainId, connectorId: this.id });
|
||||
}
|
||||
await this.connect({ chainId });
|
||||
return chain;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
import { MagicConnector, MagicOptions } from "./magicConnector";
|
||||
import { ConnectExtension } from "@magic-ext/connect";
|
||||
import {
|
||||
InstanceWithExtensions,
|
||||
MagicSDKAdditionalConfiguration,
|
||||
SDKBase,
|
||||
} from "@magic-sdk/provider";
|
||||
import {
|
||||
Chain,
|
||||
ChainNotConfiguredError,
|
||||
normalizeChainId,
|
||||
UserRejectedRequestError,
|
||||
} from "@wagmi/core";
|
||||
import { getAddress } from "ethers/lib/utils.js";
|
||||
import { Magic } from "magic-sdk";
|
||||
|
||||
interface MagicConnectOptions extends MagicOptions {
|
||||
magicSdkConfiguration?: MagicSDKAdditionalConfiguration<
|
||||
string,
|
||||
ConnectExtension[]
|
||||
>;
|
||||
}
|
||||
|
||||
const CONNECT_TIME_KEY = "wagmi-magic-connector.connect.time";
|
||||
const CONNECT_DURATION = 604800000; // 7 days in milliseconds
|
||||
|
||||
export class MagicConnectConnector extends MagicConnector {
|
||||
magicSDK?: InstanceWithExtensions<SDKBase, ConnectExtension[]>;
|
||||
|
||||
magicSdkConfiguration: MagicSDKAdditionalConfiguration<
|
||||
string,
|
||||
ConnectExtension[]
|
||||
>;
|
||||
|
||||
constructor(config: { chains?: Chain[]; options: MagicConnectOptions }) {
|
||||
super(config);
|
||||
this.magicSdkConfiguration = config.options.magicSdkConfiguration || {};
|
||||
}
|
||||
|
||||
async connect({ chainId }: { chainId?: number }) {
|
||||
// for a specific chainId we will overwrite the magicSDKConfiguration
|
||||
if (chainId) {
|
||||
const chain = this.chains.find((c) => c.id === chainId);
|
||||
if (chain) {
|
||||
this.magicSdkConfiguration = {
|
||||
...this.magicSdkConfiguration,
|
||||
network: {
|
||||
chainId: chain.id,
|
||||
rpcUrl: chain.rpcUrls.default.http[0],
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
try {
|
||||
const provider = await this.getProvider();
|
||||
|
||||
if (provider.on) {
|
||||
provider.on("accountsChanged", this.onAccountsChanged);
|
||||
provider.on("chainChanged", this.onChainChanged);
|
||||
provider.on("disconnect", this.onDisconnect);
|
||||
}
|
||||
|
||||
// Check if there is a user logged in
|
||||
const isAuthenticated = await this.isAuthorized();
|
||||
|
||||
// Check if we have a chainId, in case of error just assign 0 for legacy
|
||||
let chainId_: number;
|
||||
try {
|
||||
chainId_ = await this.getChainId();
|
||||
} catch (e) {
|
||||
chainId_ = 0;
|
||||
}
|
||||
|
||||
// if there is a user logged in, return the user
|
||||
if (isAuthenticated) {
|
||||
return {
|
||||
provider,
|
||||
chain: {
|
||||
id: chainId_,
|
||||
unsupported: false,
|
||||
},
|
||||
account: await this.getAccount(),
|
||||
};
|
||||
}
|
||||
|
||||
// open the modal and process the magic login steps
|
||||
if (!this.isModalOpen) {
|
||||
const output = await this.getUserDetailsByForm(false, true, []);
|
||||
const magic = this.getMagicSDK();
|
||||
|
||||
// LOGIN WITH MAGIC LINK WITH EMAIL
|
||||
if (output.email) {
|
||||
await magic.auth.loginWithEmailOTP({
|
||||
email: output.email,
|
||||
});
|
||||
|
||||
const signer = await this.getSigner();
|
||||
const account = await signer.getAddress();
|
||||
|
||||
// As we have no way to know if a user is connected to Magic Connect we store a connect timestamp
|
||||
// in local storage
|
||||
window.localStorage.setItem(
|
||||
CONNECT_TIME_KEY,
|
||||
String(new Date().getTime()),
|
||||
);
|
||||
|
||||
return {
|
||||
account: getAddress(account),
|
||||
chain: {
|
||||
id: chainId_,
|
||||
unsupported: false,
|
||||
},
|
||||
provider,
|
||||
};
|
||||
}
|
||||
}
|
||||
throw new UserRejectedRequestError("User rejected request");
|
||||
} catch (error) {
|
||||
throw new UserRejectedRequestError("Something went wrong");
|
||||
}
|
||||
}
|
||||
|
||||
async getChainId(): Promise<number> {
|
||||
const networkOptions = this.magicSdkConfiguration?.network;
|
||||
if (typeof networkOptions === "object") {
|
||||
const chainID = networkOptions.chainId;
|
||||
if (chainID) {
|
||||
return normalizeChainId(chainID);
|
||||
}
|
||||
}
|
||||
throw new Error("Chain ID is not defined");
|
||||
}
|
||||
|
||||
getMagicSDK(): InstanceWithExtensions<SDKBase, ConnectExtension[]> {
|
||||
if (!this.magicSDK) {
|
||||
this.magicSDK = new Magic(this.magicOptions.apiKey, {
|
||||
...this.magicSdkConfiguration,
|
||||
extensions: [new ConnectExtension()],
|
||||
});
|
||||
return this.magicSDK;
|
||||
}
|
||||
return this.magicSDK;
|
||||
}
|
||||
|
||||
// Overrides isAuthorized because Connect opens overlay whenever we interact with one of its methods.
|
||||
// Moreover, there is currently no proper way to know if a user is currently logged in to Magic Connect.
|
||||
// So we use a local storage state to handle this information.
|
||||
// TODO Once connect API grows, integrate it
|
||||
async isAuthorized() {
|
||||
if (localStorage.getItem(CONNECT_TIME_KEY) === null) {
|
||||
return false;
|
||||
}
|
||||
return (
|
||||
parseInt(window.localStorage.getItem(CONNECT_TIME_KEY) || "") +
|
||||
CONNECT_DURATION >
|
||||
new Date().getTime()
|
||||
);
|
||||
}
|
||||
|
||||
// Overrides disconnect because there is currently no proper way to disconnect a user from Magic
|
||||
// Connect.
|
||||
// So we use a local storage state to handle this information.
|
||||
async disconnect(): Promise<void> {
|
||||
window.localStorage.removeItem(CONNECT_TIME_KEY);
|
||||
}
|
||||
|
||||
async switchChain(chainId: number): Promise<Chain> {
|
||||
// check if the chain is supported
|
||||
const chain = this.chains.find((c) => c.id === chainId);
|
||||
if (!chain) {
|
||||
throw new ChainNotConfiguredError({ chainId, connectorId: this.id });
|
||||
}
|
||||
await this.connect({ chainId });
|
||||
return chain;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
import { createModal } from "../modal/view";
|
||||
import { ConnectExtension } from "@magic-ext/connect";
|
||||
import { OAuthExtension, OAuthProvider } from "@magic-ext/oauth";
|
||||
import { InstanceWithExtensions, SDKBase } from "@magic-sdk/provider";
|
||||
import { RPCProviderModule } from "@magic-sdk/provider/dist/types/modules/rpc-provider";
|
||||
import { Chain, Connector, normalizeChainId } from "@wagmi/core";
|
||||
import { providers, Signer } from "ethers";
|
||||
import { getAddress } from "ethers/lib/utils";
|
||||
import type { AbstractProvider } from "web3-core";
|
||||
|
||||
const IS_SERVER = typeof window === "undefined";
|
||||
|
||||
export interface MagicOptions {
|
||||
apiKey: string;
|
||||
accentColor?: string;
|
||||
isDarkMode?: boolean;
|
||||
customLogo?: string;
|
||||
customHeaderText?: string;
|
||||
}
|
||||
|
||||
interface UserDetails {
|
||||
email: string;
|
||||
phoneNumber: string;
|
||||
oauthProvider: OAuthProvider;
|
||||
}
|
||||
|
||||
export abstract class MagicConnector extends Connector<
|
||||
RPCProviderModule & AbstractProvider
|
||||
> {
|
||||
ready = !IS_SERVER;
|
||||
|
||||
readonly id = "magic";
|
||||
|
||||
readonly name = "Magic";
|
||||
|
||||
provider?: RPCProviderModule & AbstractProvider;
|
||||
|
||||
isModalOpen = false;
|
||||
|
||||
magicOptions: MagicOptions;
|
||||
|
||||
protected constructor(config: { chains?: Chain[]; options: MagicOptions }) {
|
||||
super(config);
|
||||
this.magicOptions = config.options;
|
||||
}
|
||||
|
||||
async getAccount() {
|
||||
const provider = new providers.Web3Provider(
|
||||
(await this.getProvider()) as unknown as providers.ExternalProvider,
|
||||
);
|
||||
const signer = provider.getSigner();
|
||||
const account = await signer.getAddress();
|
||||
return getAddress(account);
|
||||
}
|
||||
|
||||
async getUserDetailsByForm(
|
||||
enableSMSLogin: boolean,
|
||||
enableEmailLogin: boolean,
|
||||
oauthProviders: OAuthProvider[],
|
||||
): Promise<UserDetails> {
|
||||
const output: UserDetails = (await createModal({
|
||||
accentColor: this.magicOptions.accentColor,
|
||||
isDarkMode: this.magicOptions.isDarkMode,
|
||||
customLogo: this.magicOptions.customLogo,
|
||||
customHeaderText: this.magicOptions.customHeaderText,
|
||||
enableSMSLogin: enableSMSLogin,
|
||||
enableEmailLogin: enableEmailLogin || true,
|
||||
oauthProviders,
|
||||
})) as UserDetails;
|
||||
|
||||
this.isModalOpen = false;
|
||||
return output;
|
||||
}
|
||||
|
||||
async getProvider() {
|
||||
if (this.provider) {
|
||||
return this.provider;
|
||||
}
|
||||
const magic = this.getMagicSDK();
|
||||
this.provider = magic.rpcProvider;
|
||||
return this.provider;
|
||||
}
|
||||
|
||||
async getSigner(): Promise<Signer> {
|
||||
const provider = new providers.Web3Provider(
|
||||
(await this.getProvider()) as unknown as providers.ExternalProvider,
|
||||
);
|
||||
return provider.getSigner();
|
||||
}
|
||||
|
||||
async isAuthorized() {
|
||||
const magic = this.getMagicSDK();
|
||||
try {
|
||||
return await magic.user.isLoggedIn();
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
protected onAccountsChanged(accounts: string[]): void {
|
||||
if (accounts.length === 0) {
|
||||
this.emit("disconnect");
|
||||
} else {
|
||||
this.emit("change", { account: getAddress(accounts[0]) });
|
||||
}
|
||||
}
|
||||
|
||||
protected onChainChanged(chainId: string | number): void {
|
||||
const id = normalizeChainId(chainId);
|
||||
const unsupported = this.isChainUnsupported(id);
|
||||
this.emit("change", { chain: { id, unsupported } });
|
||||
}
|
||||
|
||||
protected onDisconnect(): void {
|
||||
this.emit("disconnect");
|
||||
}
|
||||
|
||||
async disconnect(): Promise<void> {
|
||||
const magic = this.getMagicSDK();
|
||||
await magic.user.logout();
|
||||
}
|
||||
|
||||
abstract getMagicSDK():
|
||||
| InstanceWithExtensions<SDKBase, OAuthExtension[]>
|
||||
| InstanceWithExtensions<SDKBase, ConnectExtension[]>;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user