Compare commits

...
Author SHA1 Message Date
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
4e5eb13b82 Version Packages (#1266)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-06-29 19:11:03 -07:00
2b11353909 [SDK] remove ethers/lib/utils dependency (#1277)
Co-authored-by: Jonas Daniels <[email protected]>
2023-06-30 01:10:47 +00:00
Manan TankandGitHub 5882091eab [chains] Fix RPC override issue (#1276) 2023-06-29 22:27:59 +00:00
Mariano FuentesandGitHub 8fc5f204b0 Fix bugs with cli deploy (#1274) 2023-06-29 18:11:25 +00:00
nkrishangandGitHub c953571588 mass rename smart-wallet -> account (#1271) 2023-06-29 12:26:48 +00:00
Manan TankandGitHub edcd22d612 [react-core] Add autoConnectTimeout (#1273) 2023-06-29 12:19:43 +00:00
iketwandGitHub 0d5503aae9 [RN] Only handle link if coinbase in supportedWallets (#1270) 2023-06-28 21:56:12 +00:00
Manan TankandGitHub 998e521733 [wallets, react] expose advancedOptions in paper (#1269) 2023-06-28 21:08:34 +00:00
FirekeeperandGitHub be606dd3c9 [paper] set signer null on disconnect (#1267) 2023-06-28 17:26:35 +00:00
d794b8f9f4 update: Add zerion to useInstalledWallets (#1210)
Co-authored-by: Jonas Daniels <[email protected]>
2023-06-28 16:50:36 +00:00
YashandGitHub 21b29490d4 [SDK] Tests for new publish+deploy flow (#1244) 2023-06-28 16:23:36 +00:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
7b1d62b323 Version Packages (#1262)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-06-28 08:07:57 -07:00
Manan TankandGitHub e78da6b36c [wallets] Wallet connect v2 corrections (#1265) 2023-06-28 13:34:42 +00:00
ed711c8cb2 [Wallets] Use min number of chains if user passes more than 50 chains (#1263)
Co-authored-by: Manan Tank <[email protected]>
2023-06-28 12:04:28 +00:00
05a7495a13 [Wallets] handle passing RPC urls to SmartWallet config (#1255)
Co-authored-by: Jonas Daniels <[email protected]>
2023-06-28 07:20:04 +00:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
11bb053246 Version Packages (#1261)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-06-27 22:16:09 -07:00
efb3546ec8 [Wallets] Fix. Respect dev passed chains. (#1259)
Co-authored-by: Manan Tank <[email protected]>
2023-06-28 05:07:24 +00:00
90 changed files with 4676 additions and 3161 deletions
+21
View File
@@ -1,5 +1,26 @@
# @thirdweb-dev/auth
## 3.2.7
### Patch Changes
- Updated dependencies [[`2b113539`](https://github.com/thirdweb-dev/js/commit/2b113539098384f910b3c4d54e1fde9d35a6f053), [`be606dd3`](https://github.com/thirdweb-dev/js/commit/be606dd3c93c1514834c1d970e864d7f949a07ab), [`998e5217`](https://github.com/thirdweb-dev/js/commit/998e521733efd6a67e42f9bf0beab5fbdccf08ae)]:
- @thirdweb-dev/wallets@1.0.3
## 3.2.6
### Patch Changes
- Updated dependencies [[`ed711c8c`](https://github.com/thirdweb-dev/js/commit/ed711c8cb2ed6a0deb1b2a5eeec06df1d4edc5e8), [`05a7495a`](https://github.com/thirdweb-dev/js/commit/05a7495a132aabab5f48abbdb80a468ff6f65df8), [`e78da6b3`](https://github.com/thirdweb-dev/js/commit/e78da6b36cfa6d954234820563201bf760186ed1)]:
- @thirdweb-dev/wallets@1.0.2
## 3.2.5
### Patch Changes
- Updated dependencies [[`efb3546e`](https://github.com/thirdweb-dev/js/commit/efb3546ec8268156be00301af28b9d83ecd5ab08)]:
- @thirdweb-dev/wallets@1.0.1
## 3.2.4
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/auth",
"version": "3.2.4",
"version": "3.2.7",
"main": "dist/thirdweb-dev-auth.cjs.js",
"module": "dist/thirdweb-dev-auth.esm.js",
"browser": {
+6
View File
@@ -1,5 +1,11 @@
# @thirdweb-dev/chains
## 0.1.26
### Patch Changes
- [#1276](https://github.com/thirdweb-dev/js/pull/1276) [`5882091e`](https://github.com/thirdweb-dev/js/commit/5882091eab65978009a5a5305701f121851b10ad) Thanks [@MananTank](https://github.com/MananTank)! - Do not change order or RPCs in `getChainRPCs` to Fix RPC override issue
## 0.1.25
### Patch Changes
+1 -1
View File
@@ -10,9 +10,9 @@ export default {
},
"rpc": [
"https://ethereum.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://eth-mainnet.g.alchemy.com/v2/${ALCHEMY_API_KEY}",
"https://mainnet.infura.io/v3/${INFURA_API_KEY}",
"wss://mainnet.infura.io/ws/v3/${INFURA_API_KEY}",
"https://eth-mainnet.g.alchemy.com/v2/${ALCHEMY_API_KEY}",
"https://api.mycryptoapi.com/eth",
"https://cloudflare-eth.com",
"https://ethereum.publicnode.com"
+1 -1
View File
@@ -4,8 +4,8 @@ export default {
"chain": "ETH",
"rpc": [
"https://optimism.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://opt-mainnet.g.alchemy.com/v2/${ALCHEMY_API_KEY}",
"https://optimism-mainnet.infura.io/v3/${INFURA_API_KEY}",
"https://opt-mainnet.g.alchemy.com/v2/${ALCHEMY_API_KEY}",
"https://mainnet.optimism.io/"
],
"faucets": [],
+1 -1
View File
@@ -10,8 +10,8 @@ export default {
},
"rpc": [
"https://polygon.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://polygon-mainnet.g.alchemy.com/v2/${ALCHEMY_API_KEY}",
"https://polygon-mainnet.infura.io/v3/${INFURA_API_KEY}",
"https://polygon-mainnet.g.alchemy.com/v2/${ALCHEMY_API_KEY}",
"https://polygon-rpc.com/",
"https://rpc-mainnet.matic.network",
"https://matic-mainnet.chainstacklabs.com",
+1 -1
View File
@@ -4,8 +4,8 @@ export default {
"chain": "ETH",
"rpc": [
"https://optimism-goerli.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://opt-goerli.g.alchemy.com/v2/${ALCHEMY_API_KEY}",
"https://optimism-goerli.infura.io/v3/${INFURA_API_KEY}",
"https://opt-goerli.g.alchemy.com/v2/${ALCHEMY_API_KEY}",
"https://goerli.optimism.io/"
],
"faucets": [
+1 -1
View File
@@ -13,8 +13,8 @@ export default {
},
"rpc": [
"https://arbitrum-goerli.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://arb-goerli.g.alchemy.com/v2/${ALCHEMY_API_KEY}",
"https://arbitrum-goerli.infura.io/v3/${INFURA_API_KEY}",
"https://arb-goerli.g.alchemy.com/v2/${ALCHEMY_API_KEY}",
"https://goerli-rollup.arbitrum.io/rpc/"
],
"faucets": [],
+1 -1
View File
@@ -5,9 +5,9 @@ export default {
"chain": "ETH",
"rpc": [
"https://goerli.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://eth-goerli.g.alchemy.com/v2/${ALCHEMY_API_KEY}",
"https://goerli.infura.io/v3/${INFURA_API_KEY}",
"wss://goerli.infura.io/v3/${INFURA_API_KEY}",
"https://eth-goerli.g.alchemy.com/v2/${ALCHEMY_API_KEY}",
"https://rpc.goerli.mudit.blog/",
"https://ethereum-goerli.publicnode.com"
],
+3 -3
View File
@@ -5,10 +5,10 @@ export default {
"chain": "ETH",
"rpc": [
"https://linea-testnet.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://rpc.goerli.linea.build",
"wss://rpc.goerli.linea.build",
"https://linea-goerli.infura.io/v3/${INFURA_API_KEY}",
"wss://linea-goerli.infura.io/v3/${INFURA_API_KEY}"
"wss://linea-goerli.infura.io/v3/${INFURA_API_KEY}",
"https://rpc.goerli.linea.build",
"wss://rpc.goerli.linea.build"
],
"faucets": [
"https://faucetlink.to/goerli"
+1 -1
View File
@@ -11,8 +11,8 @@ export default {
},
"rpc": [
"https://mumbai.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://polygon-mumbai.g.alchemy.com/v2/${ALCHEMY_API_KEY}",
"https://polygon-mumbai.infura.io/v3/${INFURA_API_KEY}",
"https://polygon-mumbai.g.alchemy.com/v2/${ALCHEMY_API_KEY}",
"https://matic-mumbai.chainstacklabs.com",
"https://rpc-mumbai.maticvigil.com",
"https://matic-testnet-archive-rpc.bwarelabs.com",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/chains",
"version": "0.1.25",
"version": "0.1.26",
"main": "dist/thirdweb-dev-chains.cjs.js",
"module": "dist/thirdweb-dev-chains.esm.js",
"browser": {
@@ -80,6 +80,33 @@ chains = chains
};
});
/**
* Sort RPCs in chain
* @param {Chain} chain
*/
function sortRPCs(chain) {
const thirdwebRPCs = [];
const alchemyRPCs = [];
const infuraRPCs = [];
const otherRPCs = [];
chain.rpc.forEach((rpc) => {
if (rpc.includes("${THIRDWEB_API_KEY}")) {
thirdwebRPCs.push(rpc);
} else if (rpc.includes("${ALCHEMY_API_KEY}")) {
alchemyRPCs.push(rpc);
} else if (rpc.includes("${INFURA_API_KEY}")) {
infuraRPCs.push(rpc);
} else {
otherRPCs.push(rpc);
}
});
chain.rpc = [...thirdwebRPCs, ...infuraRPCs, ...alchemyRPCs, ...otherRPCs];
}
chains.forEach(sortRPCs);
const imports = [];
const exports = [];
const exportNames = [];
+30 -44
View File
@@ -28,59 +28,45 @@ export function getChainRPCs(
...options,
};
const modeChains = chain.rpc.filter((rpc) => {
if (rpc.startsWith("http") && mode === "http") {
return true;
}
if (rpc.startsWith("ws") && mode === "ws") {
return true;
const processedRPCs: string[] = [];
chain.rpc.forEach((rpc) => {
// exclude RPC if mode mismatch
if (mode === "http" && !rpc.startsWith("http")) {
return;
}
return false;
if (mode === "ws" && !rpc.startsWith("ws")) {
return;
}
// Replace API_KEY placeholder with value
if (thirdwebApiKey && rpc.includes("${THIRDWEB_API_KEY}")) {
processedRPCs.push(rpc.replace("${THIRDWEB_API_KEY}", thirdwebApiKey));
} else if (infuraApiKey && rpc.includes("${INFURA_API_KEY}")) {
processedRPCs.push(rpc.replace("${INFURA_API_KEY}", infuraApiKey));
} else if (alchemyApiKey && rpc.includes("${ALCHEMY_API_KEY}")) {
processedRPCs.push(rpc.replace("${ALCHEMY_API_KEY}", alchemyApiKey));
}
// exclude RPCs with unknown placeholder
else if (rpc.includes("${")) {
return;
}
// add as is
else {
processedRPCs.push(rpc);
}
});
const thirdwebRPC = modeChains
.filter((rpc) => {
return rpc.includes("${THIRDWEB_API_KEY}") && thirdwebApiKey;
})
.map((rpc) =>
thirdwebApiKey ? rpc.replace("${THIRDWEB_API_KEY}", thirdwebApiKey) : rpc,
);
const alchemyRPC = modeChains
.filter((rpc) => {
return rpc.includes("${ALCHEMY_API_KEY}") && alchemyApiKey;
})
.map((rpc) =>
alchemyApiKey ? rpc.replace("${ALCHEMY_API_KEY}", alchemyApiKey) : rpc,
);
const infuraRPC = modeChains
.filter((rpc) => {
return rpc.includes("${INFURA_API_KEY}") && infuraApiKey;
})
.map((rpc) =>
infuraApiKey ? rpc.replace("${INFURA_API_KEY}", infuraApiKey) : rpc,
);
const allOtherRpcs = modeChains.filter((rpc) => {
return !rpc.includes("${");
});
const orderedRPCs = [
...thirdwebRPC,
...infuraRPC,
...alchemyRPC,
...allOtherRpcs,
];
if (orderedRPCs.length === 0) {
if (processedRPCs.length === 0) {
throw new Error(
`No RPC available for chainId "${chain.chainId}" with mode ${mode}`,
);
}
return orderedRPCs;
return processedRPCs;
}
/**
+10
View File
@@ -1,5 +1,15 @@
# thirdweb
## 0.10.28
### Patch Changes
- [#1274](https://github.com/thirdweb-dev/js/pull/1274) [`8fc5f204`](https://github.com/thirdweb-dev/js/commit/8fc5f204b00733089c02beee92c86b0f1fcac271) Thanks [@Marfuen](https://github.com/Marfuen)! - Added all detections for deploy command and some minor bug fixes
- Updated dependencies [[`2b113539`](https://github.com/thirdweb-dev/js/commit/2b113539098384f910b3c4d54e1fde9d35a6f053), [`c9535715`](https://github.com/thirdweb-dev/js/commit/c95357158819abd42d4b0900ecc2fa40fcb957f8), [`5882091e`](https://github.com/thirdweb-dev/js/commit/5882091eab65978009a5a5305701f121851b10ad)]:
- @thirdweb-dev/sdk@3.10.27
- @thirdweb-dev/chains@0.1.26
## 0.10.27
### Patch Changes
+1 -1
View File
@@ -2,7 +2,7 @@ import { CREATE_MESSAGES } from "../constants/constants";
import { prepareEnvironment } from "@gmrchk/cli-testing-library";
// this creates an app, can take some time that's fine
jest.setTimeout(120_000);
jest.setTimeout(240_000);
describe("npx thirdweb detect", () => {
it("should detect ERC721Base extensions", async () => {
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "thirdweb",
"main": "dist/cli/index.js",
"version": "0.10.27",
"version": "0.10.28",
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/cli",
"author": "thirdweb eng <[email protected]>",
"license": "Apache-2.0",
@@ -1,27 +1,28 @@
import inquirer from "inquirer";
import { info } from "../helpers/logger";
import { FrameworkType, PackageManagerType } from "../types/ProjectType";
import inquirer from "inquirer";
import NextDetector from "./frameworks/next";
import CRADetector from "./frameworks/cra";
import RemixDetector from "./frameworks/remix";
import GatsbyDetector from "./frameworks/gatsby";
import VueDetector from "./frameworks/vue";
import ReactNativeCLIDetector from "./frameworks/reactNativeCli";
import DjangoDetector from "./frameworks/django";
import ExpoDetector from "./frameworks/expo";
import FoundryDetector from "./frameworks/foundry";
import HardhatDetector from "./frameworks/hardhat";
import { FrameworkDetector } from "./detector";
import TruffleDetector from "./frameworks/truffle";
import BrownieDetector from "./frameworks/brownie";
import FastAPIDetector from "./frameworks/fastAPI";
import FlaskDetector from "./frameworks/flask";
import PopulusDetector from "./frameworks/populus";
import FastifyDetector from "./frameworks/fastify";
import CRADetector from "./frameworks/cra";
import DjangoDetector from "./frameworks/django";
import EchoDetector from "./frameworks/echo";
import ExpoDetector from "./frameworks/expo";
import FastAPIDetector from "./frameworks/fastAPI";
import FastifyDetector from "./frameworks/fastify";
import FiberDetector from "./frameworks/fiber";
import FlaskDetector from "./frameworks/flask";
import FoundryDetector from "./frameworks/foundry";
import GatsbyDetector from "./frameworks/gatsby";
import GinDetector from "./frameworks/gin";
import HardhatDetector from "./frameworks/hardhat";
import NextDetector from "./frameworks/next";
import PopulusDetector from "./frameworks/populus";
import ReactNativeCLIDetector from "./frameworks/reactNativeCli";
import RemixDetector from "./frameworks/remix";
import RevelDetector from "./frameworks/revel";
import TruffleDetector from "./frameworks/truffle";
import ViteDetector from "./frameworks/vite";
import VueDetector from "./frameworks/vue";
import ZenjectDetector from "./frameworks/zenject";
export default async function detect(
@@ -50,6 +51,7 @@ export default async function detect(
new RevelDetector(),
new TruffleDetector(),
new VueDetector(),
new ViteDetector(),
new ZenjectDetector(),
];
@@ -1,13 +1,12 @@
import inquirer from "inquirer";
import { info } from "../helpers/logger";
import { LibraryType, PackageManagerType } from "../types/ProjectType";
import { LibraryDetector } from "./detector";
import inquirer from "inquirer";
import ReactDetector from "./libraries/react";
import ExpressDetector from "./libraries/express";
import ReactNativeDetector from "./libraries/reactNative";
import ViteDetector from "./libraries/vite";
import Web3PyDetector from "./libraries/web3py";
import PlayMakerDetector from "./libraries/playmaker";
import ReactDetector from "./libraries/react";
import ReactNativeDetector from "./libraries/reactNative";
import Web3PyDetector from "./libraries/web3py";
export default async function detect(
path: string,
@@ -19,7 +18,6 @@ export default async function detect(
new ExpressDetector(),
new ReactDetector(),
new ReactNativeDetector(),
new ViteDetector(),
new Web3PyDetector(),
new PlayMakerDetector(),
];
@@ -1,10 +1,10 @@
import { LibraryDetector } from "../detector";
import { existsSync, readFileSync } from "fs";
import { LibraryType } from "../../types/ProjectType";
import { parsePackageJson } from "../../../lib/utils";
import { FrameworkType } from "../../types/ProjectType";
import { FrameworkDetector } from "../detector";
export default class ViteDetector implements LibraryDetector {
public libraryType: LibraryType = "vite";
export default class ViteDetector implements FrameworkDetector {
public frameworkType: FrameworkType = "vite";
public matches(path: string): boolean {
const packageJsonPath = path + "/package.json";
@@ -16,7 +16,7 @@ export default class ViteDetector implements LibraryDetector {
const packageJson = readFileSync(packageJsonPath);
const { dependencies, devDependencies } = parsePackageJson(packageJson);
const viteDependencyExists =
dependencies["vite"] || devDependencies["vite"] || false;
!!dependencies["vite"] || !!devDependencies["vite"] || false;
return viteDependencyExists;
}
@@ -56,6 +56,7 @@ export type FrameworkType =
| "revel"
| "fiber"
| "zenject"
| "vite"
| "none";
export type LibraryType =
| "react"
+46 -19
View File
@@ -1,30 +1,57 @@
import NPMDetector from "../core/detection/npm";
import ViteDetector from "../core/detection/vite";
import YarnDetector from "../core/detection/yarn";
import { ThirdwebStorage } from "@thirdweb-dev/storage";
import detectFramework from "../core/detection/detectFramework";
import detectPackageManager from "../core/detection/detectPackageManager";
import { logger } from "../core/helpers/logger";
import { runCommand } from "../create/helpers/run-command";
import { upload } from "../storage/command";
import { ThirdwebStorage } from "@thirdweb-dev/storage";
export async function deployApp(distPath = "dist", projectPath = ".") {
const storage = new ThirdwebStorage();
const supportedFrameworks = [new ViteDetector()];
const detectedPackageManager = await detectPackageManager(projectPath, {});
const detectedFramework = await detectFramework(projectPath, {}, detectedPackageManager);
const possibleProjects = supportedFrameworks
.filter((detector) => detector.matches(projectPath))
.map((detector) => detector.projectType);
const hasYarn = new YarnDetector().matches(".");
const hasNPM = new NPMDetector().matches(".");
if (possibleProjects.length === 0) {
if (detectedFramework === "none") {
throw new Error("No supported project detected");
}
switch (detectedFramework) {
case "next":
distPath = "out";
break;
case "cra":
distPath = "build";
break;
default:
break;
}
logger.info(`Detected project type: ${detectedFramework}`);
logger.info(`Detected package manager: ${detectedPackageManager}`);
logger.info(`distPath: ${distPath}`);
logger.info(`projectPath: ${projectPath}`);
try {
if (hasYarn) {
await runCommand("yarn", ["build"]);
} else if (hasNPM) {
await runCommand("npm", ["build"]);
switch (detectedPackageManager) {
case "yarn":
await runCommand("yarn", ["build"], true);
if (detectedFramework === "next") {
await runCommand("yarn", ["export"], true);
}
break;
case "npm":
await runCommand("npm", ["run", "build"], true);
if (detectedFramework === "next") {
await runCommand("npm", ["run", "export"], true);
}
break;
case "pnpm":
await runCommand("pnpm", ["build"], true);
if (detectedFramework === "next") {
await runCommand("pnpm", ["export"], true);
}
break;
default:
throw new Error("No supported package manager detected");
}
} catch (err) {
console.error("Can't build project");
@@ -33,9 +60,9 @@ export async function deployApp(distPath = "dist", projectPath = ".") {
try {
const uri = await upload(storage, distPath);
return `https://thirdweb.com/app/deploy/${uri.replace("ipfs://", "")}`;
return `${uri.replace("ipfs://", "https://ipfs-public.thirdwebcdn.com/ipfs/")}`;
} catch (err) {
console.error("Can't upload project");
console.error("Can't upload project", err);
return Promise.reject("Can't upload project");
}
}
+1 -1
View File
@@ -1,7 +1,7 @@
import { spinner } from "../core/helpers/logger";
import { BufferOrStringWithName, ThirdwebStorage } from "@thirdweb-dev/storage";
import fs from "fs";
import path from "path";
import { spinner } from "../core/helpers/logger";
function recurseFiles(
uploadPath: string,
+27
View File
@@ -1,5 +1,32 @@
# @thirdweb-dev/react-core
## 3.14.7
### Patch Changes
- [#1271](https://github.com/thirdweb-dev/js/pull/1271) [`c9535715`](https://github.com/thirdweb-dev/js/commit/c95357158819abd42d4b0900ecc2fa40fcb957f8) Thanks [@nkrishang](https://github.com/nkrishang)! - Rename smart wallets to account
- [#1273](https://github.com/thirdweb-dev/js/pull/1273) [`edcd22d6`](https://github.com/thirdweb-dev/js/commit/edcd22d61236edb2832f9b2f9796e891d58cb145) Thanks [@MananTank](https://github.com/MananTank)! - Add `autoConnectTimeout` to `ThirdwebProvider` to timeout wallet autoconnect
- Updated dependencies [[`2b113539`](https://github.com/thirdweb-dev/js/commit/2b113539098384f910b3c4d54e1fde9d35a6f053), [`c9535715`](https://github.com/thirdweb-dev/js/commit/c95357158819abd42d4b0900ecc2fa40fcb957f8), [`be606dd3`](https://github.com/thirdweb-dev/js/commit/be606dd3c93c1514834c1d970e864d7f949a07ab), [`5882091e`](https://github.com/thirdweb-dev/js/commit/5882091eab65978009a5a5305701f121851b10ad), [`998e5217`](https://github.com/thirdweb-dev/js/commit/998e521733efd6a67e42f9bf0beab5fbdccf08ae)]:
- @thirdweb-dev/wallets@1.0.3
- @thirdweb-dev/sdk@3.10.27
- @thirdweb-dev/chains@0.1.26
## 3.14.6
### Patch Changes
- Updated dependencies [[`ed711c8c`](https://github.com/thirdweb-dev/js/commit/ed711c8cb2ed6a0deb1b2a5eeec06df1d4edc5e8), [`05a7495a`](https://github.com/thirdweb-dev/js/commit/05a7495a132aabab5f48abbdb80a468ff6f65df8), [`e78da6b3`](https://github.com/thirdweb-dev/js/commit/e78da6b36cfa6d954234820563201bf760186ed1)]:
- @thirdweb-dev/wallets@1.0.2
## 3.14.5
### Patch Changes
- Updated dependencies [[`efb3546e`](https://github.com/thirdweb-dev/js/commit/efb3546ec8268156be00301af28b9d83ecd5ab08)]:
- @thirdweb-dev/wallets@1.0.1
## 3.14.4
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/react-core",
"version": "3.14.4",
"version": "3.14.7",
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/react-core",
"author": "thirdweb eng <[email protected]>",
"license": "Apache-2.0",
@@ -61,6 +61,15 @@ export interface ThirdwebProviderCoreProps<TChains extends Chain[]>
* Whether or not to automatically switch to wallet's network to active chain
*/
autoSwitch?: boolean;
/**
* Timeout for auto-connecting wallet in milliseconds
*
* If wallet fails to connect in this time, it will stop trying to connect and user will have to manually connect
*
* @defaultValue 15000
*/
autoConnectTimeout?: number;
}
export const ThirdwebProviderCore = <TChains extends Chain[]>({
@@ -145,6 +154,7 @@ export const ThirdwebProviderCore = <TChains extends Chain[]>({
dAppMeta={dAppMeta}
activeChain={activeChainWithKey}
autoSwitch={props.autoSwitch}
autoConnectTimeout={props.autoConnectTimeout}
>
<ThirdwebSDKProviderWrapper
queryClient={props.queryClient}
@@ -101,12 +101,15 @@ export function ThirdwebWalletProvider(
dAppMeta?: DAppMetaData;
chains: Chain[];
autoSwitch?: boolean;
autoConnectTimeout?: number;
}>,
) {
const [signer, setSigner] = useState<Signer | undefined>(undefined);
const [connectionStatus, setConnectionStatus] =
useState<ConnectionStatus>("unknown");
const autoConnectTimeout = props.autoConnectTimeout || 15000;
const [activeWallet, setActiveWallet] = useState<
WalletInstance | undefined
>();
@@ -311,8 +314,14 @@ export function ThirdwebWalletProvider(
const personalWalletInstance = createWalletInstance(personalWalleObj);
try {
await personalWalletInstance.autoConnect(
personalWalletInfo.connectParams,
await timeoutPromise(
personalWalletInstance.autoConnect(
personalWalletInfo.connectParams,
),
{
ms: autoConnectTimeout,
message: "Failed to Auto connect. Auto connect timed out.",
},
);
} catch (e) {
console.error(e);
@@ -337,7 +346,10 @@ export function ThirdwebWalletProvider(
try {
setConnectionStatus("connecting");
await wallet.autoConnect(walletInfo.connectParams);
await timeoutPromise(wallet.autoConnect(walletInfo.connectParams), {
ms: autoConnectTimeout,
message: "Failed to Auto connect. Auto connect timed out.",
});
setConnectedWallet(wallet, walletInfo.connectParams, true);
} catch (e) {
console.error(e);
@@ -356,6 +368,7 @@ export function ThirdwebWalletProvider(
props.shouldAutoConnect,
activeWallet,
connectionStatus,
autoConnectTimeout,
]);
const connectWallet = useCallback(
@@ -496,3 +509,32 @@ async function saveLastConnectedWalletInfo(
console.error("Error saving the last connected wallet info", e);
}
}
/**
* Timeout a promise with a given Error message if the promise does not resolve in given time
*
* @param promise - Promise to track for timeout
* @param option - timeout options
* @returns
*/
function timeoutPromise<T>(
promise: Promise<T>,
option: { ms: number; message: string },
) {
return new Promise<T>((resolve, reject) => {
const timeoutId = setTimeout(() => {
reject(new Error(option.message));
}, option.ms);
promise.then(
(res) => {
clearTimeout(timeoutId);
resolve(res);
},
(err) => {
clearTimeout(timeoutId);
reject(err);
},
);
});
}
@@ -0,0 +1,185 @@
import {
RequiredParam,
requiredParamInvariant,
} from "../../../core/query-utils/required-param";
import { useSDKChainId } from "../../providers/thirdweb-sdk-provider";
import { WalletAddress } from "../../types";
import {
cacheKeys,
invalidateContractAndBalances,
} from "../../utils/cache-keys";
import { useQueryWithNetwork } from "../query-utils/useQueryWithNetwork";
import {
useMutation,
useQueryClient,
UseQueryResult,
} from "@tanstack/react-query";
import type { AccountEvent, SmartContract } from "@thirdweb-dev/sdk";
import type { BytesLike } from "ethers";
import invariant from "tiny-invariant";
/** **********************/
/** READ HOOKS **/
/** **********************/
/**
* Get all accounts
*
* @example
* ```javascript
* const { data: accounts, isLoading, error } = useAccounts(contract);
* ```
*
* @param contract - an instance of a account factory contract
* @returns a response object that includes an array of all accounts with their associated admin
* @twfeature AccountFactory
* @see {@link https://portal.thirdweb.com/react/react.useaccounts?utm_source=sdk | Documentation}
* @beta
*/
export function useAccounts(
contract: RequiredParam<SmartContract>,
): UseQueryResult<AccountEvent[]> {
const contractAddress = contract?.getAddress();
return useQueryWithNetwork(
cacheKeys.contract.accountFactory.getAll(contractAddress),
() => {
requiredParamInvariant(contract, "No Contract instance provided");
invariant(
contract.accountFactory.getAllAccounts,
"Contract instance does not support contract.accountFactory.getAllAccounts",
);
return contract.accountFactory.getAllAccounts();
},
{ enabled: !!contract },
);
}
/**
* Get all accounts associated with the provided address
*
* @example
* ```javascript
* const { data: accountsForAddress, isLoading, error } = useAccountsForAddress(contract, "{{account_address}}");
* ```
*
* @param contract - an instance of a account factory contract
* @returns a response object that includes an array of all accounts associated with the adress
* @twfeature AccountFactory
* @see {@link https://portal.thirdweb.com/react/react.useaccountsforaddress?utm_source=sdk | Documentation}
* @beta
*/
export function useAccountsForAddress(
contract: RequiredParam<SmartContract>,
address: RequiredParam<WalletAddress>,
): UseQueryResult<string[]> {
const contractAddress = contract?.getAddress();
return useQueryWithNetwork(
cacheKeys.contract.accountFactory.getAllForAddress(
contractAddress,
address,
),
() => {
requiredParamInvariant(contract, "No Contract instance provided");
invariant(
contract.accountFactory.getAssociatedAccounts,
"Contract instance does not support contract.accountFactory.getAssociatedAccounts",
);
return contract.accountFactory.getAssociatedAccounts(address || "");
},
{ enabled: !!contract },
);
}
/**
* Check if a account has been deployed for the given admin
*
* @example
* ```javascript
* const { data: isAccountDeployed, isLoading, error } = useIsAccountDeployed(contract);
* ```
*
* @param contract - an instance of a account factory contract
* @returns a boolean indicating if a account has been deployed for the given admin
* @twfeature AccountFactory
* @see {@link https://portal.thirdweb.com/react/react.useisaccountdeployed?utm_source=sdk | Documentation}
* @beta
*/
export function useIsAccountDeployed(
contract: RequiredParam<SmartContract>,
admin: RequiredParam<WalletAddress>,
extraData?: BytesLike,
): UseQueryResult<boolean> {
const contractAddress = contract?.getAddress();
return useQueryWithNetwork(
cacheKeys.contract.accountFactory.isAccountDeployed(contractAddress, admin),
() => {
requiredParamInvariant(contract, "No Contract instance provided");
invariant(
contract.accountFactory.isAccountDeployed,
"Contract instance does not support contract.accountFactory.getAllAccounts",
);
invariant(admin, "No account address provided");
return contract.accountFactory.isAccountDeployed(admin, extraData);
},
{ enabled: !!contract },
);
}
/** **********************/
/** WRITE HOOKS **/
/** **********************/
/**
* Create a account
*
* @example
* ```jsx
* const Component = () => {
* const { contract } = useContract("{{contract_address}}");
* const {
* mutate: createAccount,
* isLoading,
* error,
* } = useCreateAccount(contract);
*
* if (error) {
* console.error("failed to create account", error);
* }
*
* return (
* <button
* disabled={isLoading}
* onClick={() => createAccount("0x...")}
* >
* Create Account
* </button>
* );
* };
* ```
*
* @param contract - an instance of a account factory contract
* @returns a mutation object that can be used to create a account
* @twfeature AccountFactory
* @see {@link https://portal.thirdweb.com/react/react.usecreateaccount?utm_source=sdk | Documentation}
* @beta
*/
export function useCreateAccount(contract: RequiredParam<SmartContract>) {
const activeChainId = useSDKChainId();
const contractAddress = contract?.getAddress();
const queryClient = useQueryClient();
return useMutation(
async (admin: string, extraData?: BytesLike) => {
requiredParamInvariant(contract, "contract is undefined");
return contract.accountFactory.createAccount(admin, extraData);
},
{
onSettled: () =>
invalidateContractAndBalances(
queryClient,
contractAddress,
activeChainId,
),
},
);
}
@@ -27,32 +27,32 @@ import invariant from "tiny-invariant";
/** **********************/
/**
* Get all wallets
* Get all accounts
*
* @example
* ```javascript
* const { data: smartWallets, isLoading, error } = useSmartWalletSigners(contract);
* const { data: accounts, isLoading, error } = useAccountSigners(contract);
* ```
*
* @param contract - an instance of a smart wallet
* @returns a response object that includes an array of all signers of the provided smart wallet
* @twfeature SmartWallet
* @see {@link https://portal.thirdweb.com/react/react.usesmartwalletsigners?utm_source=sdk | Documentation}
* @param contract - an instance of a account
* @returns a response object that includes an array of all signers of the provided account
* @twfeature Account
* @see {@link https://portal.thirdweb.com/react/react.useaccountsigners?utm_source=sdk | Documentation}
* @beta
*/
export function useSmartWalletSigners(
export function useAccountSigners(
contract: RequiredParam<SmartContract>,
): UseQueryResult<SignerWithRestrictions[]> {
const contractAddress = contract?.getAddress();
return useQueryWithNetwork(
cacheKeys.contract.smartWallet.signers(contractAddress),
cacheKeys.contract.account.signers(contractAddress),
() => {
requiredParamInvariant(contract, "No Contract instance provided");
invariant(
contract.smartWallet.getSignersWithRestrictions,
"Contract instance does not support contract.smartWallet.getSignersWithRestrictions",
contract.account.getSignersWithRestrictions,
"Contract instance does not support contract.account.getSignersWithRestrictions",
);
return contract.smartWallet.getSignersWithRestrictions();
return contract.account.getSignersWithRestrictions();
},
{ enabled: !!contract },
);
@@ -62,40 +62,40 @@ export function useSmartWalletSigners(
/** WRITE HOOKS **/
/** **********************/
/**
* Set the wallet's entire snapshot of permissions
* Set the account's entire snapshot of permissions
*
* @example
* ```jsx
* const Component = () => {
* const { contract } = useContract("{{contract_address}}");
* const {
* mutate: setSmartWalletSigners,
* mutate: setAccountSigners,
* isLoading,
* error,
* } = useSetSmartWalletSigners(contract);
* } = useSetAccountSigners(contract);
*
* if (error) {
* console.error("failed to set smart wallet signers", error);
* console.error("failed to set account signers", error);
* }
*
* return (
* <button
* disabled={isLoading}
* onClick={() => setSmartWalletSigners("0x...")}
* onClick={() => setAccountSigners("0x...")}
* >
* Set Smart Wallet Signers
* Set Account Signers
* </button>
* );
* };
* ```
*
* @param contract - an instance of a smart wallet contract
* @returns a mutation object that can be used to set the smart wallet signers
* @twfeature SmartWallet
* @see {@link https://portal.thirdweb.com/react/react.usesetsmartwalletsigners?utm_source=sdk | Documentation}
* @param contract - an instance of a account contract
* @returns a mutation object that can be used to set the account signers
* @twfeature Account
* @see {@link https://portal.thirdweb.com/react/react.usesetaccountsigners?utm_source=sdk | Documentation}
* @beta
*/
export function useSetSmartWalletSigners(
export function useSetAccountSigners(
contract: RequiredParam<SmartContract>,
): UseMutationResult<
{ receipt: providers.TransactionReceipt },
@@ -111,7 +111,7 @@ export function useSetSmartWalletSigners(
async (permissionsSnapshot: SignerWithRestrictionsBatchInput) => {
requiredParamInvariant(contract, "contract is undefined");
return contract.smartWallet.setAccess(permissionsSnapshot);
return contract.account.setAccess(permissionsSnapshot);
},
{
onSettled: () =>
@@ -1,188 +0,0 @@
import {
RequiredParam,
requiredParamInvariant,
} from "../../../core/query-utils/required-param";
import { useSDKChainId } from "../../providers/thirdweb-sdk-provider";
import { WalletAddress } from "../../types";
import {
cacheKeys,
invalidateContractAndBalances,
} from "../../utils/cache-keys";
import { useQueryWithNetwork } from "../query-utils/useQueryWithNetwork";
import {
useMutation,
useQueryClient,
UseQueryResult,
} from "@tanstack/react-query";
import type { AccountEvent, SmartContract } from "@thirdweb-dev/sdk";
import type { BytesLike } from "ethers";
import invariant from "tiny-invariant";
/** **********************/
/** READ HOOKS **/
/** **********************/
/**
* Get all wallets
*
* @example
* ```javascript
* const { data: smartWallets, isLoading, error } = useSmartWallets(contract);
* ```
*
* @param contract - an instance of a smart wallet factory contract
* @returns a response object that includes an array of all smart wallets with their associated admin
* @twfeature SmartWalletFactory
* @see {@link https://portal.thirdweb.com/react/react.usesmartwallets?utm_source=sdk | Documentation}
* @beta
*/
export function useSmartWallets(
contract: RequiredParam<SmartContract>,
): UseQueryResult<AccountEvent[]> {
const contractAddress = contract?.getAddress();
return useQueryWithNetwork(
cacheKeys.contract.smartWalletFactory.getAll(contractAddress),
() => {
requiredParamInvariant(contract, "No Contract instance provided");
invariant(
contract.smartWalletFactory.getAllAccounts,
"Contract instance does not support contract.smartWalletFactory.getAllAccounts",
);
return contract.smartWalletFactory.getAllAccounts();
},
{ enabled: !!contract },
);
}
/**
* Get all wallets associated with the provided address
*
* @example
* ```javascript
* const { data: smartWalletsForAddress, isLoading, error } = useSmartWalletsForAddress(contract, "{{wallet_address}}");
* ```
*
* @param contract - an instance of a smart wallet factory contract
* @returns a response object that includes an array of all smart wallets associated with the adress
* @twfeature SmartWalletFactory
* @see {@link https://portal.thirdweb.com/react/react.usesmartwalletsforaddress?utm_source=sdk | Documentation}
* @beta
*/
export function useSmartWalletsForAddress(
contract: RequiredParam<SmartContract>,
address: RequiredParam<WalletAddress>,
): UseQueryResult<string[]> {
const contractAddress = contract?.getAddress();
return useQueryWithNetwork(
cacheKeys.contract.smartWalletFactory.getAllForAddress(
contractAddress,
address,
),
() => {
requiredParamInvariant(contract, "No Contract instance provided");
invariant(
contract.smartWalletFactory.getAssociatedWallets,
"Contract instance does not support contract.smartWalletFactory.getAssociatedWallets",
);
return contract.smartWalletFactory.getAssociatedWallets(address || "");
},
{ enabled: !!contract },
);
}
/**
* Check if a smart wallet has been deployed for the given admin
*
* @example
* ```javascript
* const { data: isSmartWalletDeployed, isLoading, error } = useIsSmartWalletDeployed(contract);
* ```
*
* @param contract - an instance of a smart wallet factory contract
* @returns a boolean indicating if a smart wallet has been deployed for the given admin
* @twfeature SmartWalletFactory
* @see {@link https://portal.thirdweb.com/react/react.useissmartwalletdeployed?utm_source=sdk | Documentation}
* @beta
*/
export function useIsSmartWalletDeployed(
contract: RequiredParam<SmartContract>,
admin: RequiredParam<WalletAddress>,
extraData?: BytesLike,
): UseQueryResult<boolean> {
const contractAddress = contract?.getAddress();
return useQueryWithNetwork(
cacheKeys.contract.smartWalletFactory.isSmartWalletDeployed(
contractAddress,
admin,
),
() => {
requiredParamInvariant(contract, "No Contract instance provided");
invariant(
contract.smartWalletFactory.isWalletDeployed,
"Contract instance does not support contract.smartWalletFactory.getAllWallets",
);
invariant(admin, "No wallet address provided");
return contract.smartWalletFactory.isWalletDeployed(admin, extraData);
},
{ enabled: !!contract },
);
}
/** **********************/
/** WRITE HOOKS **/
/** **********************/
/**
* Create a smart wallet
*
* @example
* ```jsx
* const Component = () => {
* const { contract } = useContract("{{contract_address}}");
* const {
* mutate: createSmartWallet,
* isLoading,
* error,
* } = useCreateSmartWallet(contract);
*
* if (error) {
* console.error("failed to create smart wallet", error);
* }
*
* return (
* <button
* disabled={isLoading}
* onClick={() => createSmartWallet("0x...")}
* >
* Create Smart Wallet
* </button>
* );
* };
* ```
*
* @param contract - an instance of a smart wallet factory contract
* @returns a mutation object that can be used to create a smart wallet
* @twfeature SmartWalletFactory
* @see {@link https://portal.thirdweb.com/react/react.usecreatesmartwallet?utm_source=sdk | Documentation}
* @beta
*/
export function useCreateSmartWallet(contract: RequiredParam<SmartContract>) {
const activeChainId = useSDKChainId();
const contractAddress = contract?.getAddress();
const queryClient = useQueryClient();
return useMutation(
async (admin: string, extraData?: BytesLike) => {
requiredParamInvariant(contract, "contract is undefined");
return contract.smartWalletFactory.createWallet(admin, extraData);
},
{
onSettled: () =>
invalidateContractAndBalances(
queryClient,
contractAddress,
activeChainId,
),
},
);
}
+8 -11
View File
@@ -194,19 +194,16 @@ export {
useBurnToken,
} from "./hooks/async/token";
// smart wallet factory
// account factory
export {
useIsSmartWalletDeployed,
useSmartWallets,
useCreateSmartWallet,
useSmartWalletsForAddress,
} from "./hooks/async/smart-wallet-factory";
useIsAccountDeployed,
useAccounts,
useCreateAccount,
useAccountsForAddress,
} from "./hooks/async/account-factory";
// smart wallet
export {
useSmartWalletSigners,
useSetSmartWalletSigners,
} from "./hooks/async/smart-wallet";
// account
export { useAccountSigners, useSetAccountSigners } from "./hooks/async/account";
// thirdweb hooks (work as long as at least `<ThirdwebSdkProvider>` is used)
+14 -13
View File
@@ -118,18 +118,23 @@ export const cacheKeys = {
overrides,
]),
smartWalletFactory: {
accountFactory: {
getAll: (contractAddress: RequiredParam<ContractAddress>) =>
createContractCacheKey(contractAddress, ["smartWalletFactory"]),
isSmartWalletDeployed: (contractAdress: RequiredParam<ContractAddress>, admin: RequiredParam<string>) =>
createContractCacheKey(contractAdress, ["isSmartWalletDeployed", admin]),
getAllForAddress: (contractAdress: RequiredParam<ContractAddress>, address: RequiredParam<string>) =>
createContractCacheKey(contractAddress, ["accountFactory"]),
isAccountDeployed: (
contractAdress: RequiredParam<ContractAddress>,
admin: RequiredParam<string>,
) => createContractCacheKey(contractAdress, ["isAccountDeployed", admin]),
getAllForAddress: (
contractAdress: RequiredParam<ContractAddress>,
address: RequiredParam<string>,
) =>
createContractCacheKey(contractAdress, ["getAllForAddress", address]),
},
smartWallet: {
account: {
signers: (contractAddress: RequiredParam<ContractAddress>) =>
createContractCacheKey(contractAddress, ["smartWallet", "signers"]),
createContractCacheKey(contractAddress, ["account", "signers"]),
},
app: {
@@ -198,12 +203,8 @@ export const cacheKeys = {
},
},
sharedMetadata: {
get: (
contractAddress: RequiredParam<ContractAddress>,
) =>
createContractCacheKey(
contractAddress
),
get: (contractAddress: RequiredParam<ContractAddress>) =>
createContractCacheKey(contractAddress),
},
drop: {
getAllUnclaimed: (
@@ -1,5 +1,11 @@
# @thirdweb-dev/react-native-compat
## 0.2.26
## 0.2.25
## 0.2.24
## 0.2.23
## 0.2.22
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@thirdweb-dev/react-native-compat",
"description": "Shims for Thirdweb in React Native Projects",
"version": "0.2.23",
"version": "0.2.26",
"author": "thirdweb eng <[email protected]>",
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/react-native-compat",
"license": "Apache-2.0",
+30
View File
@@ -1,5 +1,35 @@
# @thirdweb-dev/react-native
## 0.2.26
### Patch Changes
- [#1270](https://github.com/thirdweb-dev/js/pull/1270) [`0d5503aa`](https://github.com/thirdweb-dev/js/commit/0d5503aae90d9cd7a7adcabad19f7f45d9a37063) Thanks [@iketw](https://github.com/iketw)! - Only handle Coinbase callback URL if Coinbase is added as a supportedWallet
- [#1277](https://github.com/thirdweb-dev/js/pull/1277) [`2b113539`](https://github.com/thirdweb-dev/js/commit/2b113539098384f910b3c4d54e1fde9d35a6f053) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - remove ethers/lib/utils dependency
- Updated dependencies [[`2b113539`](https://github.com/thirdweb-dev/js/commit/2b113539098384f910b3c4d54e1fde9d35a6f053), [`c9535715`](https://github.com/thirdweb-dev/js/commit/c95357158819abd42d4b0900ecc2fa40fcb957f8), [`be606dd3`](https://github.com/thirdweb-dev/js/commit/be606dd3c93c1514834c1d970e864d7f949a07ab), [`edcd22d6`](https://github.com/thirdweb-dev/js/commit/edcd22d61236edb2832f9b2f9796e891d58cb145), [`5882091e`](https://github.com/thirdweb-dev/js/commit/5882091eab65978009a5a5305701f121851b10ad), [`998e5217`](https://github.com/thirdweb-dev/js/commit/998e521733efd6a67e42f9bf0beab5fbdccf08ae)]:
- @thirdweb-dev/wallets@1.0.3
- @thirdweb-dev/sdk@3.10.27
- @thirdweb-dev/react-core@3.14.7
- @thirdweb-dev/chains@0.1.26
## 0.2.25
### Patch Changes
- Updated dependencies [[`ed711c8c`](https://github.com/thirdweb-dev/js/commit/ed711c8cb2ed6a0deb1b2a5eeec06df1d4edc5e8), [`05a7495a`](https://github.com/thirdweb-dev/js/commit/05a7495a132aabab5f48abbdb80a468ff6f65df8), [`e78da6b3`](https://github.com/thirdweb-dev/js/commit/e78da6b36cfa6d954234820563201bf760186ed1)]:
- @thirdweb-dev/wallets@1.0.2
- @thirdweb-dev/react-core@3.14.6
## 0.2.24
### Patch Changes
- Updated dependencies [[`efb3546e`](https://github.com/thirdweb-dev/js/commit/efb3546ec8268156be00301af28b9d83ecd5ab08)]:
- @thirdweb-dev/wallets@1.0.1
- @thirdweb-dev/react-core@3.14.5
## 0.2.23
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/react-native",
"version": "0.2.23",
"version": "0.2.26",
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/react-native",
"author": "thirdweb eng <[email protected]>",
"license": "Apache-2.0",
@@ -65,7 +65,9 @@ export const ThirdwebProvider = <
theme,
...restProps
}: PropsWithChildren<ThirdwebProviderProps<TChains>>) => {
useCoinbaseWalletListener();
useCoinbaseWalletListener(
!!supportedWallets.find((w) => w.id === walletIds.coinbase),
);
const hasMagicConfig = useMemo(
() => !!supportedWallets.find((wc) => wc.id === walletIds.magicLink),
@@ -17,8 +17,7 @@ import {
WagmiConnector,
} from "@thirdweb-dev/wallets";
import type { Address } from "abitype";
import { providers } from "ethers";
import { getAddress, hexValue } from "ethers/lib/utils.js";
import { providers, utils } from "ethers";
export type CoinbaseWalletConnectorOptions = WalletMobileSDKProviderOptions &
ConfigurationParams & {
@@ -130,7 +129,7 @@ export class CoinbaseWalletConnector extends WagmiConnector<
throw new Error("No accounts found");
}
// return checksum address
return getAddress(accounts[0] as string);
return utils.getAddress(accounts[0] as string);
}
async getChainId() {
@@ -193,7 +192,7 @@ export class CoinbaseWalletConnector extends WagmiConnector<
async switchChain(chainId: number): Promise<Chain> {
const provider = await this.getProvider();
const id = hexValue(chainId);
const id = utils.hexValue(chainId);
try {
await provider.request({
@@ -279,7 +278,7 @@ export class CoinbaseWalletConnector extends WagmiConnector<
if (accounts.length === 0) {
this.emit("disconnect");
} else {
this.emit("change", { account: getAddress(accounts[0] as string) });
this.emit("change", { account: utils.getAddress(accounts[0] as string) });
}
};
@@ -13,7 +13,7 @@ import { useCoinbaseWalletListener } from "./useCoinbaseWalletListener";
*/
export function useCoinbaseWallet(callbackURL: string) {
const connect = useConnect();
useCoinbaseWalletListener();
useCoinbaseWalletListener(true);
return useCallback(
async (connectOptions?: { chainId?: number; callbackURL: string }) => {
@@ -7,12 +7,16 @@ import { Linking } from "react-native";
*
* This hook sets up the UniversalLinks listener.
*/
export function useCoinbaseWalletListener() {
export function useCoinbaseWalletListener(enable: boolean) {
useEffect(() => {
if (!enable) {
return;
}
const sub = Linking.addEventListener("url", ({ url }) => {
// @ts-ignore - Passing a URL object to handleResponse crashes the function
handleResponse(url);
});
return () => sub?.remove();
}, []);
}, [enable]);
}
+32
View File
@@ -1,5 +1,37 @@
# @thirdweb-dev/react
## 3.14.7
### Patch Changes
- [#1210](https://github.com/thirdweb-dev/js/pull/1210) [`d794b8f9`](https://github.com/thirdweb-dev/js/commit/d794b8f9f41dbd7e461ef07d0808c5b6d2f85515) Thanks [@konojunya](https://github.com/konojunya)! - Add zerion to useInstalledWallets
- [#1269](https://github.com/thirdweb-dev/js/pull/1269) [`998e5217`](https://github.com/thirdweb-dev/js/commit/998e521733efd6a67e42f9bf0beab5fbdccf08ae) Thanks [@MananTank](https://github.com/MananTank)! - - Expose `advancedOptions` and `styles` for paper wallet
- Allow using "+" in paper email
- Allow using "+" in magic email
- Update Paper SDK version
- Updated dependencies [[`2b113539`](https://github.com/thirdweb-dev/js/commit/2b113539098384f910b3c4d54e1fde9d35a6f053), [`c9535715`](https://github.com/thirdweb-dev/js/commit/c95357158819abd42d4b0900ecc2fa40fcb957f8), [`be606dd3`](https://github.com/thirdweb-dev/js/commit/be606dd3c93c1514834c1d970e864d7f949a07ab), [`edcd22d6`](https://github.com/thirdweb-dev/js/commit/edcd22d61236edb2832f9b2f9796e891d58cb145), [`5882091e`](https://github.com/thirdweb-dev/js/commit/5882091eab65978009a5a5305701f121851b10ad), [`998e5217`](https://github.com/thirdweb-dev/js/commit/998e521733efd6a67e42f9bf0beab5fbdccf08ae)]:
- @thirdweb-dev/wallets@1.0.3
- @thirdweb-dev/sdk@3.10.27
- @thirdweb-dev/react-core@3.14.7
- @thirdweb-dev/chains@0.1.26
## 3.14.6
### Patch Changes
- Updated dependencies [[`ed711c8c`](https://github.com/thirdweb-dev/js/commit/ed711c8cb2ed6a0deb1b2a5eeec06df1d4edc5e8), [`05a7495a`](https://github.com/thirdweb-dev/js/commit/05a7495a132aabab5f48abbdb80a468ff6f65df8), [`e78da6b3`](https://github.com/thirdweb-dev/js/commit/e78da6b36cfa6d954234820563201bf760186ed1)]:
- @thirdweb-dev/wallets@1.0.2
- @thirdweb-dev/react-core@3.14.6
## 3.14.5
### Patch Changes
- Updated dependencies [[`efb3546e`](https://github.com/thirdweb-dev/js/commit/efb3546ec8268156be00301af28b9d83ecd5ab08)]:
- @thirdweb-dev/wallets@1.0.1
- @thirdweb-dev/react-core@3.14.5
## 3.14.4
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/react",
"version": "3.14.4",
"version": "3.14.7",
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/react",
"author": "thirdweb eng <[email protected]>",
"license": "Apache-2.0",
@@ -1,5 +1,9 @@
import { useConnect, useWallet } from "@thirdweb-dev/react-core";
import { PaperWallet, walletIds } from "@thirdweb-dev/wallets";
import {
PaperWallet,
walletIds,
PaperWalletAdditionalOptions,
} from "@thirdweb-dev/wallets";
import { useCallback, useEffect } from "react";
import {
useQuery,
@@ -7,10 +11,12 @@ import {
useQueryClient,
} from "@tanstack/react-query";
type PaperConfig = Omit<PaperWalletAdditionalOptions, "chain" | "chains">;
export function usePaperWallet() {
const connect = useConnect();
return useCallback(
async (options: { chainId?: number; clientId: string; email?: string }) => {
async (options: { chainId?: number; email?: string } & PaperConfig) => {
const { paperWallet } = await import(
"../../../wallet/wallets/paperWallet"
);
@@ -3,22 +3,22 @@ import { assertWindowEthereum } from "@thirdweb-dev/wallets";
export function useInstalledWallets() {
let isMetamaskInstalled = false;
let isCoinbaseWalletInstalled = false;
let isTrustWalletInstalled: boolean | undefined = false;
let isZerionWalletInstalled = false;
let isTrustWalletInstalled = false;
if (assertWindowEthereum(globalThis.window)) {
isMetamaskInstalled = globalThis.window.ethereum?.isMetaMask;
isMetamaskInstalled = !!globalThis.window.ethereum?.isMetaMask;
isCoinbaseWalletInstalled =
globalThis.window.ethereum?.isCoinbaseWallet ||
globalThis.window.ethereum?.providers?.some((p) => p.isCoinbaseWallet) ||
false;
isTrustWalletInstalled = globalThis.window.ethereum?.isTrust;
!!globalThis.window.ethereum?.isCoinbaseWallet ||
!!globalThis.window.ethereum?.providers?.some((p) => p.isCoinbaseWallet);
isZerionWalletInstalled = !!globalThis.window.ethereum?.isZerion;
isTrustWalletInstalled = !!globalThis.window.ethereum?.isTrust;
}
const installedWallets = {
metamask: !!isMetamaskInstalled,
coinbaseWallet: !!isCoinbaseWalletInstalled,
trustWallet: !!isTrustWalletInstalled,
return {
metamask: isMetamaskInstalled,
coinbaseWallet: isCoinbaseWalletInstalled,
trustWallet: isTrustWalletInstalled,
zerionWallet: isZerionWalletInstalled,
};
return installedWallets;
}
@@ -79,7 +79,7 @@ const MagicSelectionUI: React.FC<
if (isEmail && isEmailEnabled) {
const emailRegex = /^([a-z0-9_\.-]+)@([\da-z\.-]+)\.([a-z\.]{2,})$/g;
const isValidEmail = emailRegex.test(input);
const isValidEmail = emailRegex.test(input.replace(/\+/g, ""));
if (!isValidEmail) {
return "Invalid email address";
}
@@ -1,4 +1,7 @@
import { PaperWallet } from "@thirdweb-dev/wallets";
import {
PaperWallet,
PaperWalletAdditionalOptions,
} from "@thirdweb-dev/wallets";
import {
WalletConfig,
WalletOptions,
@@ -11,7 +14,7 @@ import { Spinner } from "../../components/Spinner";
import { Flex } from "../../components/basic";
import { InputSelectionUI } from "./InputSelectionUI";
type PaperConfig = { clientId: string };
type PaperConfig = Omit<PaperWalletAdditionalOptions, "chain" | "chains">;
export const paperWallet = (config: PaperConfig): WalletConfig<PaperWallet> => {
return {
@@ -32,7 +35,8 @@ const PaperSelectionUI: React.FC<SelectUIProps<PaperWallet>> = (props) => {
placeholder="Enter your email address"
name="email"
type="email"
errorMessage={(input) => {
errorMessage={(_input) => {
const input = _input.replace(/\+/g, "");
const emailRegex = /^([a-z0-9_\.-]+)@([\da-z\.-]+)\.([a-z\.]{2,})$/g;
const isValidEmail = emailRegex.test(input);
if (!isValidEmail) {
+11
View File
@@ -1,5 +1,16 @@
# @thirdweb-dev/sdk
## 3.10.27
### Patch Changes
- [#1277](https://github.com/thirdweb-dev/js/pull/1277) [`2b113539`](https://github.com/thirdweb-dev/js/commit/2b113539098384f910b3c4d54e1fde9d35a6f053) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - remove ethers/lib/utils dependency
- [#1271](https://github.com/thirdweb-dev/js/pull/1271) [`c9535715`](https://github.com/thirdweb-dev/js/commit/c95357158819abd42d4b0900ecc2fa40fcb957f8) Thanks [@nkrishang](https://github.com/nkrishang)! - Rename smart wallets to account
- Updated dependencies [[`5882091e`](https://github.com/thirdweb-dev/js/commit/5882091eab65978009a5a5305701f121851b10ad)]:
- @thirdweb-dev/chains@0.1.26
## 3.10.26
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/sdk",
"version": "3.10.26",
"version": "3.10.27",
"description": "The main thirdweb SDK.",
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/sdk",
"license": "Apache-2.0",
@@ -1,5 +1,4 @@
import { BigNumber, providers, constants } from "ethers";
import { isAddress } from "ethers/lib/utils";
import { BigNumber, providers, constants, utils } from "ethers";
import { extractIPFSHashFromBytecode } from "./extractIPFSHashFromBytecode";
import { extractMinimalProxyImplementationAddress } from "./extractMinimalProxyImplementationAddress";
@@ -49,7 +48,7 @@ export async function resolveContractUriFromAddress(
);
const implementationAddress = `0x${proxyStorage.slice(-40)}`;
if (
isAddress(implementationAddress) &&
utils.isAddress(implementationAddress) &&
implementationAddress !== constants.AddressZero
) {
return await resolveContractUriFromAddress(
@@ -62,8 +62,8 @@ import {
FEATURE_ENGLISH_AUCTIONS,
FEATURE_OFFERS,
FEATURE_EXTENSION_ROUTER,
FEATURE_SMART_WALLET,
FEATURE_SMART_WALLET_FACTORY,
FEATURE_ACCOUNT,
FEATURE_ACCOUNT_FACTORY,
} from "./thirdweb-features";
/**
@@ -127,8 +127,8 @@ export type Feature =
| typeof FEATURE_DIRECT_LISTINGS
| typeof FEATURE_ENGLISH_AUCTIONS
| typeof FEATURE_OFFERS
| typeof FEATURE_SMART_WALLET
| typeof FEATURE_SMART_WALLET_FACTORY;
| typeof FEATURE_ACCOUNT
| typeof FEATURE_ACCOUNT_FACTORY;
export type Extension = Feature;
@@ -170,6 +170,6 @@ export const SUPPORTED_FEATURES: Record<string, Feature> = /* @__PURE__ */ {
[FEATURE_DIRECT_LISTINGS.name]: FEATURE_DIRECT_LISTINGS,
[FEATURE_ENGLISH_AUCTIONS.name]: FEATURE_ENGLISH_AUCTIONS,
[FEATURE_OFFERS.name]: FEATURE_OFFERS,
[FEATURE_SMART_WALLET_FACTORY.name]: FEATURE_SMART_WALLET_FACTORY,
[FEATURE_SMART_WALLET.name]: FEATURE_SMART_WALLET,
[FEATURE_ACCOUNT_FACTORY.name]: FEATURE_ACCOUNT_FACTORY,
[FEATURE_ACCOUNT.name]: FEATURE_ACCOUNT,
};
@@ -279,24 +279,24 @@ export const FEATURE_OFFERS = {
features: {},
} as const;
export const FEATURE_SMART_WALLET_FACTORY = {
name: "SmartWalletFactory",
namespace: "smartwallet.factory",
export const FEATURE_ACCOUNT_FACTORY = {
name: "AccountFactory",
namespace: "accountFactory",
docLinks: {
// TODO
sdk: "sdk.smartWalletFactory",
contracts: "smartWalletFactory",
sdk: "sdk.accountFactory",
contracts: "",
},
abis: [IAccountFactory],
features: {},
} as const;
export const FEATURE_SMART_WALLET = {
name: "SmartWallet",
namespace: "smartwallet",
export const FEATURE_ACCOUNT = {
name: "Account",
namespace: "account",
docLinks: {
// TODO
sdk: "",
sdk: "sdk.account",
contracts: "",
},
abis: [IAccountCore],
@@ -14,8 +14,8 @@ import {
FEATURE_PLATFORM_FEE,
FEATURE_PRIMARY_SALE,
FEATURE_ROYALTY,
FEATURE_SMART_WALLET_FACTORY,
FEATURE_SMART_WALLET,
FEATURE_ACCOUNT_FACTORY,
FEATURE_ACCOUNT,
} from "../constants/thirdweb-features";
import { Transaction } from "../core/classes/transactions";
import { ContractAppURI } from "../core/classes/contract-appuri";
@@ -54,7 +54,7 @@ import type {
EnglishAuctionsLogic,
OffersLogic,
IAccountFactory,
IAccountCore
IAccountCore,
} from "@thirdweb-dev/contracts-js";
import { ThirdwebStorage } from "@thirdweb-dev/storage";
import { BaseContract, CallOverrides } from "ethers";
@@ -66,8 +66,8 @@ import { ContractOwner } from "../core/classes/contract-owner";
import { MarketplaceV3DirectListings } from "../core/classes/marketplacev3-direct-listings";
import { MarketplaceV3EnglishAuctions } from "../core/classes/marketplacev3-english-auction";
import { MarketplaceV3Offers } from "../core/classes/marketplacev3-offers";
import { SmartWalletFactory } from "../core/classes/smart-wallet-factory";
import { SmartWallet } from "../core/classes/smart-wallet";
import { AccountFactory } from "../core/classes/account-factory";
import { Account } from "../core/classes/account";
/**
* Custom contract dynamic class with feature detection
@@ -299,46 +299,40 @@ export class SmartContract<
}
/**
* Smart Wallet Factory
* Account Factory
*
* @remarks Create smart wallets and fetch data about them.
* @remarks Create accounts and fetch data about them.
* @example
* ```javascript
*
* // Predict the address of the smart wallet that will be created for an admin.
* const deterministicAddress = await contract.smartWalletFactory.predictWalletAddress(admin, extraData);
* // Predict the address of the account that will be created for an admin.
* const deterministicAddress = await contract.accountFactory.predictAccountAddress(admin, extraData);
*
* // Create smart wallets
* const tx = await contract.smartWalletFactory.createWallet(admin, extraData);
* // Create accounts
* const tx = await contract.accountFactory.createAccount(admin, extraData);
* // the same as `deterministicAddress`
* const smartWalletAddress = tx.address;
* const accountAddress = tx.address;
*
* // Get all smart wallets created by the factory
* const allWallets = await contract.smartWalletFactory.getAllWallets();
* // Get all accounts created by the factory
* const allAccounts = await contract.accountFactory.getAllAccounts();
*
* // Get all smart wallets on which a signer has been given authority.
* const associatedWallets = await contract.smartWalletFactory.getAssociatedWallets(signer);
* // Get all accounts on which a signer has been given authority.
* const associatedAccounts = await contract.accountFactory.getAssociatedAccounts(signer);
*
* // Get all signers who have been given authority on a smart wallet.
* const associatedSigners = await contract.smartWalletFactory.getAssociatedSigners(smartWalletAddress);
* // Get all signers who have been given authority on a account.
* const associatedSigners = await contract.accountFactory.getAssociatedSigners(accountAddress);
*
* // Check whether a smart wallet has already been created for a given admin.
* const isWalletDeployed = await contract.smartWalletFactory.isWalletDeployed(admin, extraData);
* // Check whether a account has already been created for a given admin.
* const isAccountDeployed = await contract.accountFactory.isAccountDeployed(admin, extraData);
* ```
*/
get smartWalletFactory(): SmartWalletFactory<IAccountFactory> {
return assertEnabled(
this.detectSmartWalletFactory(),
FEATURE_SMART_WALLET_FACTORY,
);
get accountFactory(): AccountFactory<IAccountFactory> {
return assertEnabled(this.detectAccountFactory(), FEATURE_ACCOUNT_FACTORY);
}
// TODO documentation
get smartWallet(): SmartWallet<IAccountCore> {
return assertEnabled(
this.detectSmartWallet(),
FEATURE_SMART_WALLET,
)
get account(): Account<IAccountCore> {
return assertEnabled(this.detectAccount(), FEATURE_ACCOUNT);
}
private _chainId: number;
@@ -568,25 +562,28 @@ export class SmartContract<
return undefined;
}
// ========== Smart account features ==========
// ========== Account features ==========
private detectSmartWalletFactory() {
private detectAccountFactory() {
if (
detectContractFeature<IAccountFactory>(
this.contractWrapper,
FEATURE_SMART_WALLET_FACTORY.name,
FEATURE_ACCOUNT_FACTORY.name,
)
) {
return new SmartWalletFactory(this.contractWrapper);
return new AccountFactory(this.contractWrapper);
}
return undefined;
}
private detectSmartWallet() {
private detectAccount() {
if (
detectContractFeature<IAccountCore>(this.contractWrapper, FEATURE_SMART_WALLET.name)
detectContractFeature<IAccountCore>(
this.contractWrapper,
FEATURE_ACCOUNT.name,
)
) {
return new SmartWallet(this.contractWrapper);
return new Account(this.contractWrapper);
}
return undefined;
}
@@ -1,6 +1,6 @@
import type { IAccountFactory } from "@thirdweb-dev/contracts-js";
import { DetectableFeature } from "../interfaces/DetectableFeature";
import { FEATURE_SMART_WALLET_FACTORY } from "../../constants/thirdweb-features";
import { FEATURE_ACCOUNT_FACTORY } from "../../constants/thirdweb-features";
import { ContractEvents } from "./contract-events";
import { ContractWrapper } from "./contract-wrapper";
@@ -9,13 +9,13 @@ import { Transaction } from "./transactions";
import { TransactionResultWithAddress } from "../types";
import { type BytesLike, utils } from "ethers";
import { AccountCreatedEvent } from "@thirdweb-dev/contracts-js/dist/declarations/src/AccountFactory";
import type { AccountEvent } from "../../types/smart-wallet";
import type { AccountEvent } from "../../types/account";
import { isContractDeployed } from "../../common/any-evm-utils/isContractDeployed";
export class SmartWalletFactory<TContract extends IAccountFactory>
export class AccountFactory<TContract extends IAccountFactory>
implements DetectableFeature
{
featureName = FEATURE_SMART_WALLET_FACTORY.name;
featureName = FEATURE_ACCOUNT_FACTORY.name;
private contractWrapper: ContractWrapper<IAccountFactory>;
// utilities
@@ -36,19 +36,19 @@ export class SmartWalletFactory<TContract extends IAccountFactory>
*******************************/
/**
* Get the deterministic address of the smart wallet that will be created
* Get the deterministic address of the account that will be created
*
* @example
* ```javascript
* const walletAddress = await contract.smartWalletFactory.predictWalletAddress(admin);
* const accountAddress = await contract.accountFactory.predictAccountAddress(admin);
* ```
* @param admin - The admin of the smart wallet.
* @param extraData - (Optional) Extra data to be passed to the smart wallet on creation.
* @returns the deterministic address of the smart wallet that will be created for the given admin.
* @param admin - The admin of the account.
* @param extraData - (Optional) Extra data to be passed to the account on creation.
* @returns the deterministic address of the account that will be created for the given admin.
*
* @twfeature SmartWalletFactory
* @twfeature AccountFactory
*/
public async predictWalletAddress(
public async predictAccountAddress(
admin: string,
extraData?: BytesLike,
): Promise<string> {
@@ -60,48 +60,48 @@ export class SmartWalletFactory<TContract extends IAccountFactory>
}
/**
* Get all signers who have authority on the given smart wallet
* Get all signers who have authority on the given account
*
* @example
* ```javascript
* const allSigners = await contract.smartWalletFactory.getAssociatedSigners(admin);
* const allSigners = await contract.accountFactory.getAssociatedSigners(admin);
* ```
* @param wallet - The smart wallet address.
* @returns all signers who have authority on the given smart wallet.
* @param account - The account address.
* @returns all signers who have authority on the given account.
*
* @twfeature SmartWalletFactory
* @twfeature AccountFactory
*/
public async getAssociatedSigners(wallet: string): Promise<string[]> {
return this.contractWrapper.readContract.getSignersOfAccount(wallet);
public async getAssociatedSigners(account: string): Promise<string[]> {
return this.contractWrapper.readContract.getSignersOfAccount(account);
}
/**
* Get all wallets on which the given signer has authority
* Get all accounts on which the given signer has authority
*
* @example
* ```javascript
* const allWallets = await contract.smartWalletFactory.getAssociatedWallets(admin);
* const allAccounts = await contract.accountFactory.getAssociatedAccounts(admin);
* ```
* @param signer - The smart wallet address.
* @returns all wallets on which the given signer has authority.
* @param signer - The account address.
* @returns all accounts on which the given signer has authority.
*
* @twfeature SmartWalletFactory
* @twfeature AccountFactory
*/
public async getAssociatedWallets(signer: string): Promise<string[]> {
public async getAssociatedAccounts(signer: string): Promise<string[]> {
return this.contractWrapper.readContract.getAccountsOfSigner(signer);
}
/**
* Get all wallets
* Get all accounts
*
* @example
* ```javascript
* const allWallets = await contract.smartWalletFactory.getAllWallets();
* const allAccounts = await contract.accountFactory.getAllAccounts();
* ```
*
* @returns all wallets created via the smart wallet factory.
* @returns all accounts created via the account factory.
*
* @twfeature SmartWalletFactory
* @twfeature AccountFactory
*/
public async getAllAccounts(): Promise<AccountEvent[]> {
const allAccounts =
@@ -122,17 +122,17 @@ export class SmartWalletFactory<TContract extends IAccountFactory>
}
/**
* Check if a smart wallet has been deployed for the given admin
* Check if a account has been deployed for the given admin
*
* @param admin - The admin of the smart wallet.
* @param extraData - (Optional) Extra data to be passed to the smart wallet on creation.
* @returns whether the smart wallet has been deployed for the given admin.
* @param admin - The admin of the account.
* @param extraData - (Optional) Extra data to be passed to the account on creation.
* @returns whether the account has been deployed for the given admin.
*/
public async isWalletDeployed(
public async isAccountDeployed(
admin: string,
extraData?: BytesLike,
): Promise<boolean> {
const addr = await this.predictWalletAddress(admin, extraData);
const addr = await this.predictAccountAddress(admin, extraData);
return isContractDeployed(addr, this.contractWrapper.getProvider());
}
@@ -141,29 +141,29 @@ export class SmartWalletFactory<TContract extends IAccountFactory>
*******************************/
/**
* Create a smart wallet
* Create a account
*
* @remarks Create a smart wallet for an admin. The admin will have complete authority over the smart wallet.
* @remarks Create a account for an admin. The admin will have complete authority over the account.
*
* @param admin - The admin of the smart wallet.
* @param extraData - (Optional) Extra data to be passed to the smart wallet on creation.
* @param admin - The admin of the account.
* @param extraData - (Optional) Extra data to be passed to the account on creation.
*
* @example
* ```javascript
* const tx = await contract.smartWalletFactory.createWallet(admin, extraData);
* const tx = await contract.accountFactory.createAccount(admin, extraData);
* const receipt = tx.receipt();
* const smartWalletAddress = tx.address;
* const accountAddress = tx.address;
* ```
*
* @twfeature SmartWalletFactory
* @twfeature AccountFactory
*/
createWallet = /* @__PURE__ */ buildTransactionFunction(
createAccount = /* @__PURE__ */ buildTransactionFunction(
async (
walletAdmin: string,
accountAdmin: string,
extraData?: BytesLike,
): Promise<Transaction<TransactionResultWithAddress>> => {
if (await this.isWalletDeployed(walletAdmin, extraData)) {
throw new Error(`Wallet already deployed for admin: ${walletAdmin}`);
if (await this.isAccountDeployed(accountAdmin, extraData)) {
throw new Error(`Account already deployed for admin: ${accountAdmin}`);
}
let data: BytesLike = utils.toUtf8Bytes("");
@@ -174,7 +174,7 @@ export class SmartWalletFactory<TContract extends IAccountFactory>
return Transaction.fromContractWrapper({
contractWrapper: this.contractWrapper,
method: "createAccount",
args: [walletAdmin, data],
args: [accountAdmin, data],
parse: (receipt) => {
const event = this.contractWrapper.parseLogs<AccountCreatedEvent>(
"AccountCreated",
@@ -1,6 +1,6 @@
import { DetectableFeature } from "../interfaces/DetectableFeature";
import { ContractWrapper } from "./contract-wrapper";
import { FEATURE_SMART_WALLET } from "../../constants/thirdweb-features";
import { FEATURE_ACCOUNT } from "../../constants/thirdweb-features";
import { utils, BigNumber } from "ethers";
import { Transaction } from "./transactions";
@@ -27,10 +27,10 @@ import { AddressOrEns } from "../../schema";
import { resolveAddress } from "../../common";
import { Signer } from "ethers";
export class SmartWallet<TContract extends IAccountCore>
export class Account<TContract extends IAccountCore>
implements DetectableFeature
{
featureName = FEATURE_SMART_WALLET.name;
featureName = FEATURE_ACCOUNT.name;
private contractWrapper: ContractWrapper<IAccountCore>;
private emptyRole: string =
@@ -87,7 +87,7 @@ export class SmartWallet<TContract extends IAccountCore>
}
/**
* Generate and sign a payload to grant or revoke a signer's access to the smart wallet.
* Generate and sign a payload to grant or revoke a signer's access to the account.
*
* @param signer - The address of the signer
* @param roleAction - The address of the signer
@@ -137,7 +137,7 @@ export class SmartWallet<TContract extends IAccountCore>
}
/**
* Get the factory contract which deployed the smart wallet
* Get the factory contract which deployed the account
*
* @returns Returns the address of the factory
*
@@ -161,16 +161,16 @@ export class SmartWallet<TContract extends IAccountCore>
********************************/
/**
* Get whether a signer is an admin on the smart wallet.
* Get whether a signer is an admin on the account.
*
* @example
* ```javascript
* const isAdmin = await contract.smartWallet.isAdmin(signer);
* const isAdmin = await contract.account.isAdmin(signer);
* ```
* @param signer - The address of a signer of the smart wallet.
* @returns whether a signer is an admin on the smart wallet.
* @param signer - The address of a signer of the account.
* @returns whether a signer is an admin on the account.
*
* @twfeature SmartWallet
* @twfeature Account
*/
public async isAdmin(signerAddress: AddressOrEns): Promise<boolean> {
const resolvedSignerAddress = await resolveAddress(signerAddress);
@@ -180,16 +180,16 @@ export class SmartWallet<TContract extends IAccountCore>
}
/**
* Get the restrictions under which a given signer can use the smart wallet.
* Get the restrictions under which a given signer can use the account.
*
* @example
* ```javascript
* const restrictionsForSigner = await contract.smartWallet.getAccessRestrictions(signer);
* const restrictionsForSigner = await contract.account.getAccessRestrictions(signer);
* ```
* @param signer - The address of a signer of the smart wallet.
* @returns the restrictions under which a given signer can use the smart wallet.
* @param signer - The address of a signer of the account.
* @returns the restrictions under which a given signer can use the account.
*
* @twfeature SmartWallet
* @twfeature Account
*/
public async getAccessRestrictions(
signerAddress: AddressOrEns,
@@ -203,16 +203,16 @@ export class SmartWallet<TContract extends IAccountCore>
}
/**
* Get the deployer of the smart wallet
* @remarks Get the address of the EIP-4337 factory contract which deployed the smart wallet
* Get the deployer of the account
* @remarks Get the address of the EIP-4337 factory contract which deployed the account
*
* @example
* ```javascript
* const factoryAddress = await contract.smartWallet.getFactoryAddress();
* const factoryAddress = await contract.account.getFactoryAddress();
* ```
* @returns the address of the factory which deployed the smart wallet.
* @returns the address of the factory which deployed the account.
*
* @twfeature SmartWallet
* @twfeature Account
*/
public async getFactoryAddress(): Promise<string> {
return this.contractWrapper.readContract.factory();
@@ -223,11 +223,11 @@ export class SmartWallet<TContract extends IAccountCore>
*
* @example
* ```javascript
* const allSigners = await contract.smartWallet.getSignersWithRestrictions();
* const allSigners = await contract.account.getSignersWithRestrictions();
* ```
* @returns all signers (admin or non-admin) of the smart wallet, along with their access restrictions.
* @returns all signers (admin or non-admin) of the account, along with their access restrictions.
*
* @twfeature SmartWallet
* @twfeature Account
*/
public async getSignersWithRestrictions(): Promise<SignerWithRestrictions[]> {
// Get all associated signers.
@@ -250,19 +250,19 @@ export class SmartWallet<TContract extends IAccountCore>
********************************/
/**
* Grant an address admin access to the smart wallet
* Grant an address admin access to the account
*
* @remarks Grants an address admin access to the smart wallet. The admin will have complete authority over the smart wallet.
* @remarks Grants an address admin access to the account. The admin will have complete authority over the account.
*
* @param signer - The address to be granted admin access to the smart wallet.
* @param signer - The address to be granted admin access to the account.
*
* @example
* ```javascript
* const tx = await contract.smartWallet.grantAdminAccess(signer);
* const tx = await contract.account.grantAdminAccess(signer);
* const receipt = tx.receipt();
* ```
*
* @twfeature SmartWallet
* @twfeature Account
*/
grantAdminAccess = /* @__PURE__ */ buildTransactionFunction(
async (signerAddress: AddressOrEns): Promise<Transaction> => {
@@ -276,19 +276,19 @@ export class SmartWallet<TContract extends IAccountCore>
);
/**
* Revoke an address' admin access to the smart wallet
* Revoke an address' admin access to the account
*
* @remarks Revokes an address' admin access to the smart wallet.
* @remarks Revokes an address' admin access to the account.
*
* @param signer - The address of an admin of the smart wallet.
* @param signer - The address of an admin of the account.
*
* @example
* ```javascript
* const tx = await contract.smartWallet.revokeAdminAccess(signer);
* const tx = await contract.account.revokeAdminAccess(signer);
* const receipt = tx.receipt();
* ```
*
* @twfeature SmartWallet
* @twfeature Account
*/
revokeAdminAccess = /* @__PURE__ */ buildTransactionFunction(
async (signerAddress: AddressOrEns): Promise<Transaction> => {
@@ -302,20 +302,20 @@ export class SmartWallet<TContract extends IAccountCore>
);
/**
* Grant an address access to the smart wallet with restrictions
* Grant an address access to the account with restrictions
*
* @remarks Grants an address access to the smart wallet with certain restrictions.
* @remarks Grants an address access to the account with certain restrictions.
*
* @param signer - The address to be granted access to the smart wallet.
* @param restrictions - The restrictions to be applied to the signer's use of the smart wallet.
* @param signer - The address to be granted access to the account.
* @param restrictions - The restrictions to be applied to the signer's use of the account.
*
* @example
* ```javascript
* const tx = await contract.smartWallet.grantAccess(signer, restrictions);
* const tx = await contract.account.grantAccess(signer, restrictions);
* const receipt = tx.receipt();
* ```
*
* @twfeature SmartWallet
* @twfeature Account
*/
grantAccess = /* @__PURE__ */ buildTransactionFunction(
async (
@@ -395,20 +395,20 @@ export class SmartWallet<TContract extends IAccountCore>
);
/**
* Approve an address as a call target for a given signer on the smart wallet
* Approve an address as a call target for a given signer on the account
*
* @remarks Approves an address as a call target for a given signer on the smart wallet.
* @remarks Approves an address as a call target for a given signer on the account.
*
* @param signer - A signer with restricted access to the smart wallet.
* @param signer - A signer with restricted access to the account.
* @param target - The address to approve as a call target for the signer.
*
* @example
* ```javascript
* const tx = await contract.smartWallet.approveTargetForSigner(signer, target);
* const tx = await contract.account.approveTargetForSigner(signer, target);
* const receipt = tx.receipt();
* ```
*
* @twfeature SmartWallet
* @twfeature Account
*/
approveTargetForSigner = /* @__PURE__ */ buildTransactionFunction(
async (
@@ -444,20 +444,20 @@ export class SmartWallet<TContract extends IAccountCore>
);
/**
* Disapprove an address as a call target for a given signer on the smart wallet
* Disapprove an address as a call target for a given signer on the account
*
* @remarks Disapprove an address as a call target for a given signer on the smart wallet.
* @remarks Disapprove an address as a call target for a given signer on the account.
*
* @param signer - A signer with restricted access to the smart wallet.
* @param signer - A signer with restricted access to the account.
* @param target - The address to disapprove as a call target for the signer.
*
* @example
* ```javascript
* const tx = await contract.smartWallet.disapproveTargetForSigner(signer, target);
* const tx = await contract.account.disapproveTargetForSigner(signer, target);
* const receipt = tx.receipt();
* ```
*
* @twfeature SmartWallet
* @twfeature Account
*/
disapproveTargetForSigner = /* @__PURE__ */ buildTransactionFunction(
async (
@@ -496,20 +496,20 @@ export class SmartWallet<TContract extends IAccountCore>
);
/**
* Update the restrictions of an address for using the smart wallet
* Update the restrictions of an address for using the account
*
* @remarks Updates an address' access to the smart wallet.
* @remarks Updates an address' access to the account.
*
* @param signer - The address whose access to the smart wallet is to be updated.
* @param restrictions - The restrictions to be applied to the signer's use of the smart wallet.
* @param signer - The address whose access to the account is to be updated.
* @param restrictions - The restrictions to be applied to the signer's use of the account.
*
* @example
* ```javascript
* const tx = await contract.smartWallet.updateAccess(signer, restrictions);
* const tx = await contract.account.updateAccess(signer, restrictions);
* const receipt = tx.receipt();
* ```
*
* @twfeature SmartWallet
* @twfeature Account
*/
updateAccess = /* @__PURE__ */ buildTransactionFunction(
async (
@@ -550,19 +550,19 @@ export class SmartWallet<TContract extends IAccountCore>
);
/**
* Revoke a scoped access address to the smart wallet
* Revoke a scoped access address to the account
*
* @remarks Revokes an address' access to the smart wallet.
* @remarks Revokes an address' access to the account.
*
* @param signer - The address whose access to the smart wallet is to be revoked.
* @param signer - The address whose access to the account is to be revoked.
*
* @example
* ```javascript
* const tx = await contract.smartWallet.revokeAccess(signer);
* const tx = await contract.account.revokeAccess(signer);
* const receipt = tx.receipt();
* ```
*
* @twfeature SmartWallet
* @twfeature Account
*/
revokeAccess = /* @__PURE__ */ buildTransactionFunction(
async (signerAddress: AddressOrEns): Promise<Transaction> => {
@@ -600,19 +600,19 @@ export class SmartWallet<TContract extends IAccountCore>
);
/**
* Set the wallet's entire snapshot of permissions
* Set the account's entire snapshot of permissions
*
* @remarks Sets the wallet's entire snapshot of permissions.
* @remarks Sets the account's entire snapshot of permissions.
*
* @param permissionsSnapshot - the snapshot to set as the wallet's entire permission snapshot.
* @param permissionsSnapshot - the snapshot to set as the account's entire permission snapshot.
*
* @example
* ```javascript
* const tx = await contract.smartWallet.setAccess(permissionsSnapshot);
* const tx = await contract.account.setAccess(permissionsSnapshot);
* const receipt = tx.receipt();
* ```
*
* @twfeature SmartWallet
* @twfeature Account
*/
setAccess = /* @__PURE__ */ buildTransactionFunction(
async (
@@ -1,8 +1,7 @@
import { EventType } from "../../constants/events";
import type { ContractEvent, EventQueryOptions } from "../../types/events";
import { ContractWrapper } from "./contract-wrapper";
import type { BaseContract, Event, providers } from "ethers";
import type { EventFragment } from "ethers/lib/utils.js";
import type { BaseContract, Event, providers, utils } from "ethers";
import type { EventEmitter } from "eventemitter3";
/**
@@ -325,7 +324,7 @@ export class ContractEvents<TContract extends BaseContract> {
}
private toContractEvent<TEvent extends Record<string, any>>(
event: EventFragment,
event: utils.EventFragment,
args: ReadonlyArray<any>,
rawLog: providers.Log,
): ContractEvent<TEvent> {
@@ -44,7 +44,6 @@ import {
type Signer,
constants,
} from "ethers";
import { ConnectionInfo } from "ethers/lib/utils.js";
import invariant from "tiny-invariant";
import { CONTRACT_ADDRESSES } from "../../constants/addresses/CONTRACT_ADDRESSES";
import { getContractAddressByChainId } from "../../constants/addresses/getContractAddressByChainId";
@@ -471,7 +470,7 @@ export class ContractWrapper<
callOverrides: CallOverrides,
) {
const provider = this.getProvider() as providers.Provider & {
connection?: ConnectionInfo;
connection?: utils.ConnectionInfo;
};
// Get metadata for transaction to populate into error
+2 -2
View File
@@ -50,5 +50,5 @@ export * from "./contract-published-metadata";
export * from "./contract-owner";
export * from "./transactions";
export * from "./contract-appuri";
export * from "./smart-wallet";
export * from "./smart-wallet-factory";
export * from "./account";
export * from "./account-factory";
@@ -30,8 +30,6 @@ import {
constants,
} from "ethers";
import { BigNumber } from "ethers";
import { FormatTypes } from "ethers/lib/utils.js";
import type { ConnectionInfo } from "ethers/lib/utils.js";
import invariant from "tiny-invariant";
import EventEmitter from "eventemitter3";
import type { DeployEvents } from "../../types/deploy";
@@ -494,7 +492,7 @@ export class Transaction<
try {
// for dynamic contracts, add 30% to the gas limit to account for multiple delegate calls
const abi = JSON.parse(
this.contract.interface.format(FormatTypes.json) as string,
this.contract.interface.format("json") as string,
);
if (isRouterContract(abi)) {
overrides.gasLimit = overrides.gasLimit.mul(110).div(100);
@@ -663,7 +661,7 @@ export class Transaction<
*/
private async transactionError(error: any) {
const provider = this.provider as providers.Provider & {
connection?: ConnectionInfo;
connection?: utils.ConnectionInfo;
};
// Get metadata for transaction to populate into error
@@ -849,7 +847,7 @@ export class DeployTransaction extends TransactionContext {
*/
private async deployError(error: any) {
const provider = this.provider as providers.Provider & {
connection?: ConnectionInfo;
connection?: utils.ConnectionInfo;
};
// Get metadata for transaction to populate into error
+1 -1
View File
@@ -12,4 +12,4 @@ export * from "./multiwrap";
export * from "./registry";
export * from "./transactions";
export * from "./contract";
export * from "./smart-wallet";
export * from "./account";
@@ -4,8 +4,8 @@ import { assert, expect } from "chai";
import {
SignerWithRestrictions,
AccessRestrictions,
SmartWallet,
SmartWalletFactory,
Account,
AccountFactory,
SignerWithRestrictionsInput,
SignerWithRestrictionsBatchInput,
} from "../../src/evm";
@@ -23,9 +23,9 @@ import { IAccountCore, IAccountFactory } from "@thirdweb-dev/contracts-js";
global.fetch = /* @__PURE__ */ require("cross-fetch");
/* @__PURE__ */ describe("Smart wallets with wallet factory", function () {
let smartWalletFactory: SmartWalletFactory<IAccountFactory>;
let smartWallet: SmartWallet<IAccountCore>;
/* @__PURE__ */ describe("Accounts with account factory", function () {
let accountFactory: AccountFactory<IAccountFactory>;
let account: Account<IAccountCore>;
let adminWallet: SignerWithAddress;
@@ -66,64 +66,63 @@ global.fetch = /* @__PURE__ */ require("cross-fetch");
adminWallet,
[entrypoint.address],
);
smartWalletFactory = (await sdk.getContract(factoryAddress))
.smartWalletFactory;
accountFactory = (await sdk.getContract(factoryAddress)).accountFactory;
});
describe("Test: SmartWalletFactory actions", () => {
it("Should create a new wallet for the admin", async () => {
// Check before creating a smart wallet for `adminWallet`
const isDeployed = await smartWalletFactory.isWalletDeployed(
describe("Test: AccountFactory actions", () => {
it("Should create a new account for the admin", async () => {
// Check before creating a account for `adminWallet`
const isDeployed = await accountFactory.isAccountDeployed(
adminWallet.address,
);
assert.isFalse(
isDeployed,
"Smart wallet is should not be deployed for admin.",
"Account is should not be deployed for admin.",
);
const predictedAccountAddress: string =
await smartWalletFactory.predictWalletAddress(adminWallet.address);
await accountFactory.predictAccountAddress(adminWallet.address);
const allWalletsBefore = (await smartWalletFactory.getAllAccounts()).map(
(wallet) => wallet.account,
const allAccountsBefore = (await accountFactory.getAllAccounts()).map(
(wallet) => account.account,
);
assert.isFalse(
allWalletsBefore.includes(predictedAccountAddress),
allAccountsBefore.includes(predictedAccountAddress),
"Account should not be deployed for admin already.",
);
assert.isTrue(
(await smartWalletFactory.getAssociatedSigners(predictedAccountAddress))
(await accountFactory.getAssociatedSigners(predictedAccountAddress))
.length === 0,
"Wallet should have no associated signers.",
);
assert.isTrue(
(await smartWalletFactory.getAssociatedWallets(adminWallet.address))
(await accountFactory.getAssociatedAccounts(adminWallet.address))
.length === 0,
"Signer should have no associated wallets.",
"Signer should have no associated accounts.",
);
// Create smart wallet for `adminWallet`
const tx = await smartWalletFactory.createWallet(adminWallet.address);
// Create account for `adminWallet`
const tx = await accountFactory.createAccount(adminWallet.address);
// Checks after creating a smart wallet for `adminWallet`
// Checks after creating a account for `adminWallet`
assert.strictEqual(
utils.getAddress(tx.address),
utils.getAddress(predictedAccountAddress),
"Smart wallet address should match predicted address.",
"Account address should match predicted address.",
);
assert.isTrue(
await smartWalletFactory.isWalletDeployed(adminWallet.address),
"Smart wallet is should be deployed for admin.",
await accountFactory.isAccountDeployed(adminWallet.address),
"Account is should be deployed for admin.",
);
const allWalletsAfter = await smartWalletFactory.getAllAccounts();
const allAccountsAfter = await accountFactory.getAllAccounts();
assert.isTrue(
allWalletsAfter.length === 1,
"Only one wallet should be created on the factory.",
allAccountsAfter.length === 1,
"Only one account should be created on the factory.",
);
const { account, admin } = allWalletsAfter.filter(
const { account, admin } = allAccountsAfter.filter(
(wallet) =>
utils.getAddress(wallet.account) ===
utils.getAddress(predictedAccountAddress),
@@ -136,165 +135,161 @@ global.fetch = /* @__PURE__ */ require("cross-fetch");
assert.strictEqual(
utils.getAddress(admin),
utils.getAddress(adminWallet.address),
"Correct admin for smart wallet.",
"Correct admin for account.",
);
const associatedWallets = await smartWalletFactory.getAssociatedWallets(
const associatedAccounts = await accountFactory.getAssociatedAccounts(
admin,
);
const associatedSigner = await smartWalletFactory.getAssociatedSigners(
const associatedSigner = await accountFactory.getAssociatedSigners(
account,
);
assert.isTrue(
associatedWallets.length === 1,
associatedAccounts.length === 1,
"Wallet should have only the admin as an associated signer.",
);
assert.strictEqual(
utils.getAddress(account),
utils.getAddress(associatedWallets[0]),
utils.getAddress(associatedAccounts[0]),
"Wallet should have only the admin as an associated signer.",
);
assert.isTrue(
associatedSigner.length === 1,
"Signer should have only the created wallet as an associated wallet.",
"Signer should have only the created account as an associated account.",
);
assert.strictEqual(
utils.getAddress(admin),
utils.getAddress(associatedSigner[0]),
"Signer should have only the created wallet as an associated wallet.",
"Signer should have only the created account as an associated account.",
);
});
it("Should throw if creating wallet for an admin who already has one", async () => {
// Create a wallet for admin
await smartWalletFactory.createWallet(adminWallet.address);
it("Should throw if creating account for an admin who already has one", async () => {
// Create a account for admin
await accountFactory.createAccount(adminWallet.address);
// Try creating another wallet for the same admin
// Try creating another account for the same admin
try {
await smartWalletFactory.createWallet(adminWallet.address);
await accountFactory.createAccount(adminWallet.address);
expect.fail();
} catch (err: any) {
expect(err.message).to.equal(
`Wallet already deployed for admin: ${adminWallet.address}`,
`Account already deployed for admin: ${adminWallet.address}`,
);
}
});
});
describe("Test: SmartWallet actions", () => {
describe("Test: Account actions", () => {
beforeEach(async () => {
// Create a wallet for admin
const tx = await smartWalletFactory.createWallet(adminWallet.address);
const smartWalletAddress = tx.address;
// Create a account for admin
const tx = await accountFactory.createAccount(adminWallet.address);
const accountAddress = tx.address;
sdk.updateSignerOrProvider(adminWallet);
await mockUploadContractMetadata(
"smart-wallet",
smartWalletAddress,
accountAddress,
IAccountCoreAbi,
);
smartWallet = (await sdk.getContract(smartWalletAddress)).smartWallet;
account = (await sdk.getContract(accountAddress)).account;
});
it("Should be able to add another admin to the wallet.", async () => {
it("Should be able to add another admin to the account.", async () => {
assert.isFalse(
await smartWallet.isAdmin(signer1Wallet.address),
"New signer1 should not be an admin on the smart wallet.",
await account.isAdmin(signer1Wallet.address),
"New signer1 should not be an admin on the account.",
);
await smartWallet.grantAdminAccess(signer1Wallet.address);
await account.grantAdminAccess(signer1Wallet.address);
assert.isTrue(
await smartWallet.isAdmin(signer1Wallet.address),
"New signer1 should be an admin on the smart wallet.",
await account.isAdmin(signer1Wallet.address),
"New signer1 should be an admin on the account.",
);
const isAdmin = (
(await smartWallet.getSignersWithRestrictions()).find(
(await account.getSignersWithRestrictions()).find(
(result) =>
utils.getAddress(result.signer) ===
utils.getAddress(signer1Wallet.address),
) as SignerWithRestrictions
).isAdmin;
assert.isTrue(isAdmin, "New signer1 should be an admin on the wallet.");
assert.isTrue(isAdmin, "New signer1 should be an admin on the account.");
assert.isTrue(
(
await smartWalletFactory.getAssociatedSigners(
smartWallet.getAddress(),
)
await accountFactory.getAssociatedSigners(account.getAddress())
).includes(signer1Wallet.address),
"New signer1 is an associated signer of the wallet.",
"New signer1 is an associated signer of the account.",
);
assert.isTrue(
(
await smartWalletFactory.getAssociatedWallets(signer1Wallet.address)
).includes(smartWallet.getAddress()),
"Wallet is an associated wallet of the signer.",
await accountFactory.getAssociatedAccounts(signer1Wallet.address)
).includes(account.getAddress()),
"Wallet is an associated account of the signer.",
);
});
it("Should be able to remove an admin from the wallet.", async () => {
await smartWallet.grantAdminAccess(signer1Wallet.address);
it("Should be able to remove an admin from the account.", async () => {
await account.grantAdminAccess(signer1Wallet.address);
assert.isTrue(
await smartWallet.isAdmin(signer1Wallet.address),
"New signer1 should be an admin on the smart wallet.",
await account.isAdmin(signer1Wallet.address),
"New signer1 should be an admin on the account.",
);
await smartWallet.revokeAdminAccess(signer1Wallet.address);
await account.revokeAdminAccess(signer1Wallet.address);
assert.isFalse(
await smartWallet.isAdmin(signer1Wallet.address),
"New signer1 should not be an admin on the smart wallet.",
await account.isAdmin(signer1Wallet.address),
"New signer1 should not be an admin on the account.",
);
assert.isFalse(
(await smartWallet.getSignersWithRestrictions())
(await account.getSignersWithRestrictions())
.map((result) => utils.getAddress(result.signer))
.includes(signer1Wallet.address),
"New signer1 should not be an admin on the wallet.",
"New signer1 should not be an admin on the account.",
);
assert.isFalse(
(
await smartWalletFactory.getAssociatedSigners(
smartWallet.getAddress(),
)
await accountFactory.getAssociatedSigners(account.getAddress())
).includes(signer1Wallet.address),
"New signer1 is not an associated signer of the wallet.",
"New signer1 is not an associated signer of the account.",
);
assert.isFalse(
(
await smartWalletFactory.getAssociatedWallets(signer1Wallet.address)
).includes(smartWallet.getAddress()),
"Wallet is not an associated wallet of the signer.",
await accountFactory.getAssociatedAccounts(signer1Wallet.address)
).includes(account.getAddress()),
"Wallet is not an associated account of the signer.",
);
});
it("Should be able to grant restricted access to a new signer.", async () => {
const signersWithRestrictions =
await smartWallet.getSignersWithRestrictions();
await account.getSignersWithRestrictions();
assert.isFalse(
signersWithRestrictions
.map((result) => utils.getAddress(result.signer))
.includes(signer1Wallet.address),
"New signer should not already have access to the wallet.",
"New signer should not already have access to the account.",
);
// Grant access
await smartWallet.grantAccess(signer1Wallet.address, {
await account.grantAccess(signer1Wallet.address, {
nativeTokenLimitPerTransaction: "1",
approvedCallTargets: [adminWallet.address],
});
assert.isFalse(
await smartWallet.isAdmin(signer1Wallet.address),
"New signer1 should not be an admin on the smart wallet.",
await account.isAdmin(signer1Wallet.address),
"New signer1 should not be an admin on the account.",
);
const newSignerWithRestrictions =
await smartWallet.getSignersWithRestrictions();
await account.getSignersWithRestrictions();
const restrictions = newSignerWithRestrictions.find(
(result) =>
utils.getAddress(result.signer) ===
@@ -304,7 +299,7 @@ global.fetch = /* @__PURE__ */ require("cross-fetch");
newSignerWithRestrictions
.map((result) => utils.getAddress(result.signer))
.includes(signer1Wallet.address),
"New signer1 should be a signer on the wallet.",
"New signer1 should be a signer on the account.",
);
assert.strictEqual(
restrictions.nativeTokenLimitPerTransaction.toString(),
@@ -324,30 +319,28 @@ global.fetch = /* @__PURE__ */ require("cross-fetch");
assert.isTrue(
(
await smartWalletFactory.getAssociatedSigners(
smartWallet.getAddress(),
)
await accountFactory.getAssociatedSigners(account.getAddress())
).includes(signer1Wallet.address),
"New signer1 is an associated signer of the wallet.",
"New signer1 is an associated signer of the account.",
);
assert.isTrue(
(
await smartWalletFactory.getAssociatedWallets(signer1Wallet.address)
).includes(smartWallet.getAddress()),
"Wallet is an associated wallet of the signer.",
await accountFactory.getAssociatedAccounts(signer1Wallet.address)
).includes(account.getAddress()),
"Wallet is an associated account of the signer.",
);
});
it("Should not be able to grant restricted access to a signer who already has access.", async () => {
// Grant access to signer1
await smartWallet.grantAccess(signer1Wallet.address, {
await account.grantAccess(signer1Wallet.address, {
nativeTokenLimitPerTransaction: "1",
approvedCallTargets: [adminWallet.address],
});
// Try granting access to signer1 again
try {
await smartWallet.grantAccess(signer1Wallet.address, {
await account.grantAccess(signer1Wallet.address, {
approvedCallTargets: [adminWallet.address],
});
expect.fail();
@@ -358,57 +351,55 @@ global.fetch = /* @__PURE__ */ require("cross-fetch");
it("Should be able to revoke restricted access from an authorized signer.", async () => {
// Grant access to signer1
await smartWallet.grantAccess(signer1Wallet.address, {
await account.grantAccess(signer1Wallet.address, {
nativeTokenLimitPerTransaction: "1",
approvedCallTargets: [adminWallet.address],
});
// Revoke access
await smartWallet.revokeAccess(signer1Wallet.address);
await account.revokeAccess(signer1Wallet.address);
const signersWithRestrictions =
await smartWallet.getSignersWithRestrictions();
await account.getSignersWithRestrictions();
assert.isFalse(
signersWithRestrictions
.map((result) => utils.getAddress(result.signer))
.includes(signer1Wallet.address),
"New signer should not have access to the wallet.",
"New signer should not have access to the account.",
);
assert.isFalse(
await smartWallet.isAdmin(signer1Wallet.address),
"New signer1 should not be an admin on the smart wallet.",
await account.isAdmin(signer1Wallet.address),
"New signer1 should not be an admin on the account.",
);
assert.isFalse(
(
await smartWalletFactory.getAssociatedSigners(
smartWallet.getAddress(),
)
await accountFactory.getAssociatedSigners(account.getAddress())
).includes(signer1Wallet.address),
"New signer1 is not an associated signer of the wallet.",
"New signer1 is not an associated signer of the account.",
);
assert.isFalse(
(
await smartWalletFactory.getAssociatedWallets(signer1Wallet.address)
).includes(smartWallet.getAddress()),
"Wallet is not an associated wallet of the signer.",
await accountFactory.getAssociatedAccounts(signer1Wallet.address)
).includes(account.getAddress()),
"Wallet is not an associated account of the signer.",
);
});
it("Should not be able to revoke restricted access from a signer who doesn't have access.", async () => {
const signersWithRestrictions =
await smartWallet.getSignersWithRestrictions();
await account.getSignersWithRestrictions();
assert.isFalse(
signersWithRestrictions
.map((result) => utils.getAddress(result.signer))
.includes(signer1Wallet.address),
"New signer should not already have access to the wallet.",
"New signer should not already have access to the account.",
);
// Try revoking access from signer1
try {
await smartWallet.revokeAccess(signer1Wallet.address);
await account.revokeAccess(signer1Wallet.address);
expect.fail();
} catch (err: any) {
expect(err.message).to.equal(`Signer does not have any access`);
@@ -417,18 +408,17 @@ global.fetch = /* @__PURE__ */ require("cross-fetch");
it("Should be able to update access of an authorized signer.", async () => {
// Grant access
await smartWallet.grantAccess(signer1Wallet.address, {
await account.grantAccess(signer1Wallet.address, {
nativeTokenLimitPerTransaction: "1",
approvedCallTargets: [adminWallet.address],
});
assert.isFalse(
await smartWallet.isAdmin(signer1Wallet.address),
"New signer1 should not be an admin on the smart wallet.",
await account.isAdmin(signer1Wallet.address),
"New signer1 should not be an admin on the account.",
);
const signerWithRestrictions =
await smartWallet.getSignersWithRestrictions();
const signerWithRestrictions = await account.getSignersWithRestrictions();
const restrictions = signerWithRestrictions.find(
(result) =>
utils.getAddress(result.signer) ===
@@ -438,7 +428,7 @@ global.fetch = /* @__PURE__ */ require("cross-fetch");
signerWithRestrictions
.map((result) => utils.getAddress(result.signer))
.includes(signer1Wallet.address),
"New signer1 should be a signer on the wallet.",
"New signer1 should be a signer on the account.",
);
assert.strictEqual(
restrictions.nativeTokenLimitPerTransaction.toString(),
@@ -457,18 +447,18 @@ global.fetch = /* @__PURE__ */ require("cross-fetch");
);
// Update access
await smartWallet.updateAccess(signer1Wallet.address, {
await account.updateAccess(signer1Wallet.address, {
nativeTokenLimitPerTransaction: "3",
approvedCallTargets: [signer2Wallet.address],
});
assert.isFalse(
await smartWallet.isAdmin(signer1Wallet.address),
"New signer1 should not be an admin on the smart wallet.",
await account.isAdmin(signer1Wallet.address),
"New signer1 should not be an admin on the account.",
);
const newSignerWithRestrictions =
await smartWallet.getSignersWithRestrictions();
await account.getSignersWithRestrictions();
const newRestrictions = newSignerWithRestrictions.find(
(result) =>
utils.getAddress(result.signer) ===
@@ -478,7 +468,7 @@ global.fetch = /* @__PURE__ */ require("cross-fetch");
newSignerWithRestrictions
.map((result) => utils.getAddress(result.signer))
.includes(signer1Wallet.address),
"New signer1 should be a signer on the wallet.",
"New signer1 should be a signer on the account.",
);
assert.strictEqual(
newRestrictions.nativeTokenLimitPerTransaction.toString(),
@@ -499,17 +489,17 @@ global.fetch = /* @__PURE__ */ require("cross-fetch");
it("Should not be able to update access of a signer who doesn't have access.", async () => {
const signersWithRestrictions =
await smartWallet.getSignersWithRestrictions();
await account.getSignersWithRestrictions();
assert.isFalse(
signersWithRestrictions
.map((result) => utils.getAddress(result.signer))
.includes(signer1Wallet.address),
"New signer should not already have access to the wallet.",
"New signer should not already have access to the account.",
);
// Try updating access of signer1
try {
await smartWallet.updateAccess(signer1Wallet.address, {
await account.updateAccess(signer1Wallet.address, {
approvedCallTargets: [signer2Wallet.address],
});
expect.fail();
@@ -520,14 +510,12 @@ global.fetch = /* @__PURE__ */ require("cross-fetch");
it("Should be able to append an approved target for an authorized signer.", async () => {
// Grant access
await smartWallet.grantAccess(signer1Wallet.address, {
await account.grantAccess(signer1Wallet.address, {
nativeTokenLimitPerTransaction: "1",
approvedCallTargets: [adminWallet.address],
});
const restrictions = (
await smartWallet.getSignersWithRestrictions()
).find(
const restrictions = (await account.getSignersWithRestrictions()).find(
(result) =>
utils.getAddress(result.signer) ===
utils.getAddress(signer1Wallet.address),
@@ -544,14 +532,12 @@ global.fetch = /* @__PURE__ */ require("cross-fetch");
);
// Append approved target
await smartWallet.approveTargetForSigner(
await account.approveTargetForSigner(
signer1Wallet.address,
signer2Wallet.address,
);
const newRestrictions = (
await smartWallet.getSignersWithRestrictions()
).find(
const newRestrictions = (await account.getSignersWithRestrictions()).find(
(result) =>
utils.getAddress(result.signer) ===
utils.getAddress(signer1Wallet.address),
@@ -575,14 +561,12 @@ global.fetch = /* @__PURE__ */ require("cross-fetch");
it("Should be able to remove an approved target for an authorized signer.", async () => {
// Grant access
await smartWallet.grantAccess(signer1Wallet.address, {
await account.grantAccess(signer1Wallet.address, {
nativeTokenLimitPerTransaction: "1",
approvedCallTargets: [adminWallet.address, signer2Wallet.address],
});
const restrictions = (
await smartWallet.getSignersWithRestrictions()
).find(
const restrictions = (await account.getSignersWithRestrictions()).find(
(result) =>
utils.getAddress(result.signer) ===
utils.getAddress(signer1Wallet.address),
@@ -604,14 +588,12 @@ global.fetch = /* @__PURE__ */ require("cross-fetch");
);
// Remove approved target
await smartWallet.disapproveTargetForSigner(
await account.disapproveTargetForSigner(
signer1Wallet.address,
signer2Wallet.address,
);
const newRestrictions = (
await smartWallet.getSignersWithRestrictions()
).find(
const newRestrictions = (await account.getSignersWithRestrictions()).find(
(result) =>
utils.getAddress(result.signer) ===
utils.getAddress(signer1Wallet.address),
@@ -628,7 +610,7 @@ global.fetch = /* @__PURE__ */ require("cross-fetch");
);
});
it("Should be able to set access for the smart wallet in a batch.", async () => {
it("Should be able to set access for the account in a batch.", async () => {
/**
* All cases to test
*
@@ -661,7 +643,7 @@ global.fetch = /* @__PURE__ */ require("cross-fetch");
signersWithRestrictions.push(restrictionsSigner3);
// Removing existing admin
await smartWallet.grantAdminAccess(signer4Wallet.address);
await account.grantAdminAccess(signer4Wallet.address);
const restrictionsSigner4: SignerWithRestrictionsInput = {
signer: signer4Wallet.address,
@@ -679,7 +661,7 @@ global.fetch = /* @__PURE__ */ require("cross-fetch");
signersWithRestrictions.push(restrictionsSigner5);
// Removing an existing scoped signer
await smartWallet.grantAccess(signer6Wallet.address, {
await account.grantAccess(signer6Wallet.address, {
approvedCallTargets: [adminWallet.address],
});
@@ -692,7 +674,7 @@ global.fetch = /* @__PURE__ */ require("cross-fetch");
// Updating the restrictions of an existing scoped signer
await smartWallet.grantAccess(signer7Wallet.address, {
await account.grantAccess(signer7Wallet.address, {
approvedCallTargets: [adminWallet.address],
});
@@ -704,7 +686,7 @@ global.fetch = /* @__PURE__ */ require("cross-fetch");
signersWithRestrictions.push(restrictionsSigner7);
// Existing admin -> new scoped signer (demote)
await smartWallet.grantAdminAccess(signer8Wallet.address);
await account.grantAdminAccess(signer8Wallet.address);
const restrictionsSigner8: SignerWithRestrictionsInput = {
signer: signer8Wallet.address,
@@ -714,7 +696,7 @@ global.fetch = /* @__PURE__ */ require("cross-fetch");
signersWithRestrictions.push(restrictionsSigner8);
// Existing scoped signer -> new admin (promote)
await smartWallet.grantAccess(signer9Wallet.address, {
await account.grantAccess(signer9Wallet.address, {
approvedCallTargets: [adminWallet.address],
});
@@ -726,11 +708,11 @@ global.fetch = /* @__PURE__ */ require("cross-fetch");
signersWithRestrictions.push(restrictionsSigner9);
// Set access in batch
await smartWallet.setAccess(signersWithRestrictions);
await account.setAccess(signersWithRestrictions);
// Now checking if permissions are set correctly
const signersWithRestrictionsAfter =
await smartWallet.getSignersWithRestrictions();
await account.getSignersWithRestrictions();
for (const restrictions of signersWithRestrictionsAfter) {
switch (restrictions.signer) {
@@ -738,96 +720,96 @@ global.fetch = /* @__PURE__ */ require("cross-fetch");
case adminWallet.address:
assert.isTrue(
restrictions.isAdmin,
"Admin wallet should be an admin.",
"Admin account should be an admin.",
);
break;
// `signer3Wallet` should be an admin
case signer3Wallet.address:
assert.isTrue(
restrictions.isAdmin,
"Signer3 wallet should be an admin.",
"Signer3 account should be an admin.",
);
break;
// `signer4Wallet` should not be admin or scoped signer
case signer4Wallet.address:
assert.isFalse(
restrictions.isAdmin,
"Signer4 wallet should not be an admin.",
"Signer4 account should not be an admin.",
);
assert.strictEqual(
restrictions.restrictions.approvedCallTargets.length,
0,
"Signer4 wallet should not have any approved call targets.",
"Signer4 account should not have any approved call targets.",
);
break;
// `signer5Wallet` should be a scoped signer
case signer5Wallet.address:
assert.isFalse(
restrictions.isAdmin,
"Signer5 wallet should not be an admin.",
"Signer5 account should not be an admin.",
);
assert.strictEqual(
restrictions.restrictions.approvedCallTargets.length,
1,
"Signer5 wallet should have one approved call target.",
"Signer5 account should have one approved call target.",
);
assert.strictEqual(
restrictions.restrictions.approvedCallTargets[0],
adminWallet.address,
"Signer5 wallet should have the expected approved call targets.",
"Signer5 account should have the expected approved call targets.",
);
break;
// `signer6Wallet` should not be admin or scoped signer
case signer6Wallet.address:
assert.isFalse(
restrictions.isAdmin,
"Signer6 wallet should not be an admin.",
"Signer6 account should not be an admin.",
);
assert.strictEqual(
restrictions.restrictions.approvedCallTargets.length,
0,
"Signer6 wallet should not have any approved call targets.",
"Signer6 account should not have any approved call targets.",
);
break;
// `signer7Wallet` should be a scoped signer with its updated restrictions
case signer7Wallet.address:
assert.isFalse(
restrictions.isAdmin,
"Signer7 wallet should not be an admin.",
"Signer7 account should not be an admin.",
);
assert.strictEqual(
restrictions.restrictions.approvedCallTargets.length,
1,
"Signer7 wallet should have one approved call target.",
"Signer7 account should have one approved call target.",
);
assert.strictEqual(
restrictions.restrictions.approvedCallTargets[0],
signer1Wallet.address,
"Signer7 wallet should have the expected approved call targets.",
"Signer7 account should have the expected approved call targets.",
);
break;
// `signer8Wallet` should be a scoped signer
case signer8Wallet.address:
assert.isFalse(
restrictions.isAdmin,
"Signer8 wallet should not be an admin.",
"Signer8 account should not be an admin.",
);
assert.strictEqual(
restrictions.restrictions.approvedCallTargets.length,
1,
"Signer8 wallet should have one approved call target.",
"Signer8 account should have one approved call target.",
);
assert.strictEqual(
restrictions.restrictions.approvedCallTargets[0],
adminWallet.address,
"Signer8 wallet should have the expected approved call targets.",
"Signer8 account should have the expected approved call targets.",
);
break;
// `signer9Wallet` should be an admin
case signer9Wallet.address:
assert.isTrue(
restrictions.isAdmin,
"Signer9 wallet should be an admin.",
"Signer9 account should be an admin.",
);
break;
default:
+3
View File
@@ -82,6 +82,7 @@ let signers: SignerWithAddress[];
let storage: ThirdwebStorage;
let implementations: { [key in ContractType]?: string };
let mock_weth_address: string;
let thirdwebFactory: TWFactory;
const fastForwardTime = async (timeInSeconds: number): Promise<void> => {
const now = Math.floor(Date.now() / 1000);
@@ -133,6 +134,7 @@ export const mochaHooks = {
.connect(signer)
.deploy(trustedForwarderAddress, registry.address)) as TWFactory;
thirdwebFactory = await thirdwebFactoryDeployer.deployed();
const deployTxFactory = thirdwebFactoryDeployer.deployTransaction;
await deployTxFactory.wait();
const thirdwebRegistryAddress = await thirdwebFactoryDeployer.registry();
@@ -391,4 +393,5 @@ export {
storage,
implementations,
hardhatEthers,
thirdwebFactory,
};
+2 -2
View File
@@ -1,12 +1,12 @@
import { ThirdwebSDK } from "../../src/evm";
import { expectError, sdk } from "./before-setup";
import { expect } from "chai";
import { ethers } from "ethers";
import { Wallet } from "ethers";
describe("Error Handling", async () => {
it("should throw proper error on account with no balance", async () => {
const newSdk = ThirdwebSDK.fromSigner(
ethers.Wallet.createRandom().connect(sdk.getProvider()),
Wallet.createRandom().connect(sdk.getProvider()),
);
try {
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,85 @@
// from SplitWallet published at: ipfs://Qmc5cQotuEnCo71ZztJk69wc8kuKbGmuCARL2FyAojoi9r/
const abi = [
{
inputs: [],
stateMutability: "nonpayable",
type: "constructor",
},
{
inputs: [],
name: "Unauthorized",
type: "error",
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: "address",
name: "split",
type: "address",
},
{
indexed: false,
internalType: "uint256",
name: "amount",
type: "uint256",
},
],
name: "ReceiveETH",
type: "event",
},
{
inputs: [
{
internalType: "contract ERC20",
name: "token",
type: "address",
},
{
internalType: "uint256",
name: "amount",
type: "uint256",
},
],
name: "sendERC20ToMain",
outputs: [],
stateMutability: "payable",
type: "function",
},
{
inputs: [
{
internalType: "uint256",
name: "amount",
type: "uint256",
},
],
name: "sendETHToMain",
outputs: [],
stateMutability: "payable",
type: "function",
},
{
inputs: [],
name: "splitMain",
outputs: [
{
internalType: "contract ISplitMain",
name: "",
type: "address",
},
],
stateMutability: "view",
type: "function",
},
];
const bytecode =
"0x60a060405234801561001057600080fd5b5033606081901b60805261035461004c60003960008181604b0152818160bc015281816101080152818161013c015261018601526103546000f3fe6080604052600436106100345760003560e01c80630e769b2b146100395780637c1f3ffe14610089578063ab0ebff41461009e575b600080fd5b34801561004557600080fd5b5061006d7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b03909116815260200160405180910390f35b61009c6100973660046102d0565b6100b1565b005b61009c6100ac366004610306565b610131565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146100f9576040516282b42960e81b815260040160405180910390fd5b61012d6001600160a01b0383167f0000000000000000000000000000000000000000000000000000000000000000836101af565b5050565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614610179576040516282b42960e81b815260040160405180910390fd5b6101ac6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001682610233565b50565b600060405163a9059cbb60e01b81526001600160a01b03841660048201528260248201526000806044836000895af19150506101ea81610289565b61022d5760405162461bcd60e51b815260206004820152600f60248201526e1514905394d1915497d19052531151608a1b60448201526064015b60405180910390fd5b50505050565b600080600080600085875af19050806102845760405162461bcd60e51b815260206004820152601360248201527211551217d514905394d1915497d19052531151606a1b6044820152606401610224565b505050565b60003d8261029b57806000803e806000fd5b80602081146102b35780156102c457600092506102c9565b816000803e600051151592506102c9565b600192505b5050919050565b600080604083850312156102e2578182fd5b82356001600160a01b03811681146102f8578283fd5b946020939093013593505050565b600060208284031215610317578081fd5b503591905056fea26469706673582212208e095a368bcb2efb2a8afd9b560ad94441e926086a4bc92edf16c33900df798e64736f6c63430008040033";
const deployedBytecode =
"0x6080604052600436106100345760003560e01c80630e769b2b146100395780637c1f3ffe14610089578063ab0ebff41461009e575b600080fd5b34801561004557600080fd5b5061006d7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b03909116815260200160405180910390f35b61009c6100973660046102d0565b6100b1565b005b61009c6100ac366004610306565b610131565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146100f9576040516282b42960e81b815260040160405180910390fd5b61012d6001600160a01b0383167f0000000000000000000000000000000000000000000000000000000000000000836101af565b5050565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614610179576040516282b42960e81b815260040160405180910390fd5b6101ac6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001682610233565b50565b600060405163a9059cbb60e01b81526001600160a01b03841660048201528260248201526000806044836000895af19150506101ea81610289565b61022d5760405162461bcd60e51b815260206004820152600f60248201526e1514905394d1915497d19052531151608a1b60448201526064015b60405180910390fd5b50505050565b600080600080600085875af19050806102845760405162461bcd60e51b815260206004820152601360248201527211551217d514905394d1915497d19052531151606a1b6044820152606401610224565b505050565b60003d8261029b57806000803e806000fd5b80602081146102b35780156102c457600092506102c9565b816000803e600051151592506102c9565b600192505b5050919050565b600080604083850312156102e2578182fd5b82356001600160a01b03811681146102f8578283fd5b946020939093013593505050565b600060208284031215610317578081fd5b503591905056fea26469706673582212208e095a368bcb2efb2a8afd9b560ad94441e926086a4bc92edf16c33900df798e64736f6c63430008040033";
export { abi, bytecode, deployedBytecode };
File diff suppressed because one or more lines are too long
+9 -1
View File
@@ -20,5 +20,13 @@ export async function mockUploadWithCID(
file: FileOrBufferOrString,
options?: IpfsUploadBatchOptions | undefined,
) {
storage[cid] = file;
const [cidMain, id] = cid.split("/");
if (id) {
storage[cidMain] = {
[id]: file,
};
} else {
storage[cid] = file;
}
}
@@ -0,0 +1,360 @@
import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers";
import { jsonProvider, sdk, signers, thirdwebFactory } from "./before-setup";
import { expect, assert } from "chai";
import {
Forwarder__factory,
DropERC721__factory,
ERC721Base__factory,
} from "@thirdweb-dev/contracts-js";
import {
bytecode as SplitMainBytecode,
deployedBytecode as SplitMainDeployedBytecode,
abi as SplitMainAbi,
} from "./metadata/SplitMain";
import {
bytecode as SplitWalletBytecode,
deployedBytecode as SplitWalletDeployedBytecode,
abi as SplitWalletAbi,
} from "./metadata/SplitWallet";
import {
bytecode as TWCloneFactoryBytecode,
abi as TWCloneFactoryAbi,
} from "./metadata/TWCloneFactory";
import { mockUploadMetadataWithBytecode } from "./utils";
const extendedMetadataMock = {
name: "",
metadataUri: "",
bytecodeUri: "",
analytics: {},
version: "1.0.0",
displayName: "",
description: "",
changelog: "",
isDeployableViaFactory: false,
isDeployableViaProxy: false,
factoryDeploymentData: {
implementationAddresses: {},
implementationInitializerFunction: "initialize",
factoryAddresses: {},
},
constructorParams: {},
publisher: "",
customFactoryInput: {
factoryFunction: "",
customFactoryAddresses: [],
},
};
describe("New Publish Flow", async () => {
let adminWallet: SignerWithAddress,
samWallet: SignerWithAddress,
bobWallet: SignerWithAddress,
w4: SignerWithAddress;
beforeEach(async () => {
await jsonProvider.send("hardhat_reset", []);
[adminWallet, samWallet, bobWallet, , , , , w4] = signers;
});
before("Mock upload infra contracts", async () => {
// mock upload Forwarder
await mockUploadMetadataWithBytecode(
"Forwarder",
Forwarder__factory.abi,
Forwarder__factory.bytecode,
"",
{
...extendedMetadataMock,
deployType: "standard",
networksForDeployment: {
allNetworks: true,
networksEnabled: [],
},
},
"ipfs://Qmcu8FaqerUvQYb4qPg7PwkXa6dRtEe45LedLJPN42Jwqe/0",
// ^ we use actual publish uri as mock uri here, because this contract's uri is fetched from publisher by contractName
);
// mock upload TWCloneFactory
await mockUploadMetadataWithBytecode(
"Forwarder",
TWCloneFactoryAbi,
TWCloneFactoryBytecode,
"",
{
...extendedMetadataMock,
deployType: "standard",
networksForDeployment: {
allNetworks: true,
networksEnabled: [],
},
},
"ipfs://QmYfw13Zykqf9jAmJobNgYrEpatEF9waWcQPUHvJ7sctRb/0",
// ^ we use actual publish uri as mock uri here, because this contract's uri is fetched from publisher by contractName
);
});
describe("Deploy newly published contract", async () => {
it("should deploy regular contract", async () => {
// mock publish as a standard-deploy contract
const mockPublishUri = await mockUploadMetadataWithBytecode(
"ERC721Base",
ERC721Base__factory.abi,
ERC721Base__factory.bytecode,
"",
{
...extendedMetadataMock,
deployType: "standard",
networksForDeployment: {
allNetworks: true,
networksEnabled: [],
},
publisher: await adminWallet.getAddress(),
},
);
// deploy with mock uri
const admin = await adminWallet.getAddress();
const contractAddress = await sdk.deployer.deployContractFromUri(
mockPublishUri,
["Direct Deploy ERC721Base", "DDE", admin, 5],
);
const contract = await sdk.getContract(contractAddress);
const name = await contract.call("name");
const owner = await contract.call("owner");
expect(name).to.equal("Direct Deploy ERC721Base");
expect(owner).to.equal(admin);
});
it("should deploy with custom factory", async () => {
// mock publish SplitMain as a standard-deploy contract
const splitMainMockUri = await mockUploadMetadataWithBytecode(
"SplitMain",
SplitMainAbi,
SplitMainBytecode,
SplitMainDeployedBytecode,
{
...extendedMetadataMock,
deployType: "standard",
networksForDeployment: {
allNetworks: true,
networksEnabled: [],
},
publisher: await adminWallet.getAddress(),
},
);
// deploy custom-factory `SplitMain`, with mock uri
const splitMainAddress = await sdk.deployer.deployContractFromUri(
splitMainMockUri,
[],
);
// mock publish SplitWallet as a custom-factory-deploy contract
const splitWalletMockUri = await mockUploadMetadataWithBytecode(
"SplitWallet",
SplitWalletAbi,
SplitWalletBytecode,
SplitWalletDeployedBytecode,
{
...extendedMetadataMock,
deployType: "customFactory",
factoryDeploymentData: {
...extendedMetadataMock.factoryDeploymentData,
customFactoryInput: {
factoryFunction: "createSplit",
params: [],
customFactoryAddresses: {
"31337": splitMainAddress,
},
},
},
networksForDeployment: {
allNetworks: true,
networksEnabled: [],
},
publisher: await adminWallet.getAddress(),
},
);
// deploy contract via custom-factory, with mock uri
const splitWalletAddress = await sdk.deployer.deployContractFromUri(
splitWalletMockUri,
[
[await bobWallet.getAddress(), await adminWallet.getAddress()],
[500000, 500000],
0,
await adminWallet.getAddress(),
],
);
const splitContract = await sdk.getContract(splitWalletAddress);
const deployingAddress = await splitContract.call("splitMain");
expect(deployingAddress).to.equal(splitMainAddress);
});
});
describe("Deploy re-published contract", async () => {
it("should deploy with auto factory", async () => {
const mockPublisher = process.env.contractPublisherAddress;
process.env.contractPublisherAddress =
"0x664244560eBa21Bf82d7150C791bE1AbcD5B4cd7";
const adminAddress = await adminWallet.getAddress();
// mock publish as a autoFactory-deploy contract
const mockPublishUri = await mockUploadMetadataWithBytecode(
"DropERC721",
DropERC721__factory.abi,
DropERC721__factory.bytecode,
"",
{
...extendedMetadataMock,
deployType: "autoFactory",
networksForDeployment: {
allNetworks: true,
networksEnabled: [],
},
publisher: adminAddress,
},
);
// deploy via autoFactory, with mock uri
const contractAddress = await sdk.deployer.deployContractFromUri(
mockPublishUri,
[
adminAddress,
"DropERC721 Minimal Proxy",
"DME",
"",
[],
adminAddress,
adminAddress,
5,
5,
adminAddress,
],
);
const contract = await sdk.getContract(contractAddress);
const name = await contract.call("name");
const owner = await contract.call("owner");
const code = await sdk.getProvider().getCode(contractAddress);
expect(name).to.equal("DropERC721 Minimal Proxy");
expect(owner).to.equal(adminAddress);
assert(code.startsWith("0x363d3d373d3d3d363d73"));
process.env.contractPublisherAddress = mockPublisher;
});
});
describe("Deploy old contract without re-publishing", async () => {
it("should deploy regular contract", async () => {
// mock publish as old style -- omit deployType, networksForDeployment etc
const mockPublishUri = await mockUploadMetadataWithBytecode(
"ERC721Base",
ERC721Base__factory.abi,
ERC721Base__factory.bytecode,
"",
extendedMetadataMock,
);
// deploy with mock uri
const admin = await adminWallet.getAddress();
const contractAddress = await sdk.deployer.deployContractFromUri(
mockPublishUri,
["Direct Deploy ERC721Base", "DDE", admin, 5],
);
const contract = await sdk.getContract(contractAddress);
const name = await contract.call("name");
const owner = await contract.call("owner");
expect(name).to.equal("Direct Deploy ERC721Base");
expect(owner).to.equal(admin);
});
it("should deploy via old factory", async () => {
const mockPublisher = process.env.contractPublisherAddress;
process.env.contractPublisherAddress =
"0x664244560eBa21Bf82d7150C791bE1AbcD5B4cd7";
const adminAddress = await adminWallet.getAddress();
// mock publish as old-style factory deployable -- omit deployType, networksForDeployment etc
const mockPublishUri = await mockUploadMetadataWithBytecode(
"DropERC721",
DropERC721__factory.abi,
DropERC721__factory.bytecode,
"",
{
...extendedMetadataMock,
isDeployableViaFactory: true,
publisher: adminAddress,
},
);
// deploy implementation
const implementationAddress = await sdk.deployer.deployContractFromUri(
mockPublishUri,
[],
{
forceDirectDeploy: true,
},
);
await (
await thirdwebFactory.addImplementation(implementationAddress)
).wait();
// re-upload, with implementation address
const mockPublishUriUpdated = await mockUploadMetadataWithBytecode(
"DropERC721",
DropERC721__factory.abi,
DropERC721__factory.bytecode,
"",
{
...extendedMetadataMock,
isDeployableViaFactory: true,
factoryDeploymentData: {
...extendedMetadataMock?.factoryDeploymentData,
implementationAddresses: {
"31337": implementationAddress,
},
factoryAddresses: {
"31337": process.env.factoryAddress,
},
},
publisher: adminAddress,
},
);
// Deploy proxy DropERC721
const contractAddress = await sdk.deployer.deployContractFromUri(
mockPublishUriUpdated,
[
adminAddress,
"Drop Proxy Old",
"DPA",
"",
[],
adminAddress,
adminAddress,
5,
5,
adminAddress,
],
);
const drop = await sdk.getContract(contractAddress);
const bytecode = await sdk.getProvider().getCode(contractAddress);
const name = await drop.call("name");
expect(name).to.equal("Drop Proxy Old");
assert(bytecode.startsWith("0x363d3d373d3d3d363d73"));
});
});
// TODO: test any-evm vs standard chain deploys with same publish-uri
});
+65 -2
View File
@@ -1,4 +1,8 @@
import { resolveContractUriFromAddress } from "../../src/evm";
import {
FullPublishMetadata,
extractIPFSHashFromBytecode,
resolveContractUriFromAddress,
} from "../../src/evm";
import { defaultProvider } from "./before-setup";
import { mockUploadWithCID } from "./mock/MockStorage";
import { ContractInterface, ethers } from "ethers";
@@ -22,6 +26,8 @@ export const mockUploadContractMetadata = async (
contractName: string,
address: string,
abi: any,
extendedMetadata?: FullPublishMetadata | any,
publishUri?: string,
) => {
const ipfsHash = (await resolveContractUriFromAddress(
address,
@@ -57,11 +63,68 @@ export const mockUploadContractMetadata = async (
// mock raw-metadata
const rawMeta = {
...extendedMetadata,
name: contractName,
metadataUri: ipfsHash,
bytecodeUri: bytecodeUri,
};
const rawMetaUri = ipfsHash.concat("rawMeta");
const rawMetaUri = publishUri || ipfsHash.concat("rawMeta");
const rawMetaCID = rawMetaUri.replace("ipfs://", "");
await mockUploadWithCID(rawMetaCID, JSON.stringify(rawMeta));
return rawMetaUri;
};
export const mockUploadMetadataWithBytecode = async (
contractName: string,
abi: any,
bytecode: string,
deployedBytecode?: string,
extendedMetadata?: FullPublishMetadata | any,
publishUri?: string,
): Promise<string> => {
const ipfsHash =
deployedBytecode && deployedBytecode.length > 0
? (extractIPFSHashFromBytecode(deployedBytecode) as string)
: (extractIPFSHashFromBytecode(bytecode) as string);
const metadata = {
compiler: {},
output: {
abi,
devdoc: {},
userdoc: {},
},
settings: {
compilationTarget: {
contract: contractName,
},
evmVersion: {},
metadata: {},
optimizer: {},
remappings: [],
},
sources: {},
version: 1,
};
const metadataCID = ipfsHash.replace("ipfs://", "");
await mockUploadWithCID(metadataCID, JSON.stringify(metadata));
// mock bytecode
const bytecodeUri = ipfsHash.concat("bytecode");
const bytecodeCID = bytecodeUri.replace("ipfs://", "");
await mockUploadWithCID(bytecodeCID, bytecode);
// mock raw-metadata
const rawMeta = {
...extendedMetadata,
name: contractName,
metadataUri: ipfsHash,
bytecodeUri: bytecodeUri,
};
const rawMetaUri = publishUri || ipfsHash.concat("rawMeta");
const rawMetaCID = rawMetaUri.replace("ipfs://", "");
await mockUploadWithCID(rawMetaCID, JSON.stringify(rawMeta));
return rawMetaUri;
};
+26
View File
@@ -1,5 +1,31 @@
# @thirdweb-dev/unity-js-bridge
## 0.2.44
### Patch Changes
- Updated dependencies [[`2b113539`](https://github.com/thirdweb-dev/js/commit/2b113539098384f910b3c4d54e1fde9d35a6f053), [`c9535715`](https://github.com/thirdweb-dev/js/commit/c95357158819abd42d4b0900ecc2fa40fcb957f8), [`be606dd3`](https://github.com/thirdweb-dev/js/commit/be606dd3c93c1514834c1d970e864d7f949a07ab), [`5882091e`](https://github.com/thirdweb-dev/js/commit/5882091eab65978009a5a5305701f121851b10ad), [`998e5217`](https://github.com/thirdweb-dev/js/commit/998e521733efd6a67e42f9bf0beab5fbdccf08ae)]:
- @thirdweb-dev/wallets@1.0.3
- @thirdweb-dev/sdk@3.10.27
- @thirdweb-dev/chains@0.1.26
- @thirdweb-dev/auth@3.2.7
## 0.2.43
### Patch Changes
- Updated dependencies [[`ed711c8c`](https://github.com/thirdweb-dev/js/commit/ed711c8cb2ed6a0deb1b2a5eeec06df1d4edc5e8), [`05a7495a`](https://github.com/thirdweb-dev/js/commit/05a7495a132aabab5f48abbdb80a468ff6f65df8), [`e78da6b3`](https://github.com/thirdweb-dev/js/commit/e78da6b36cfa6d954234820563201bf760186ed1)]:
- @thirdweb-dev/wallets@1.0.2
- @thirdweb-dev/auth@3.2.6
## 0.2.42
### Patch Changes
- Updated dependencies [[`efb3546e`](https://github.com/thirdweb-dev/js/commit/efb3546ec8268156be00301af28b9d83ecd5ab08)]:
- @thirdweb-dev/wallets@1.0.1
- @thirdweb-dev/auth@3.2.5
## 0.2.41
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/unity-js-bridge",
"version": "0.2.41",
"version": "0.2.44",
"main": "dist/thirdweb-unity-bridge.js",
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/unity-js-bridge",
"license": "Apache-2.0",
+32
View File
@@ -1,5 +1,37 @@
# @thirdweb-dev/wallets
## 1.0.3
### Patch Changes
- [#1277](https://github.com/thirdweb-dev/js/pull/1277) [`2b113539`](https://github.com/thirdweb-dev/js/commit/2b113539098384f910b3c4d54e1fde9d35a6f053) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - remove ethers/lib/utils dependency
- [#1267](https://github.com/thirdweb-dev/js/pull/1267) [`be606dd3`](https://github.com/thirdweb-dev/js/commit/be606dd3c93c1514834c1d970e864d7f949a07ab) Thanks [@0xFirekeeper](https://github.com/0xFirekeeper)! - set signer null when disconnecting paper to avoid exceptions on event callbacks
- [#1269](https://github.com/thirdweb-dev/js/pull/1269) [`998e5217`](https://github.com/thirdweb-dev/js/commit/998e521733efd6a67e42f9bf0beab5fbdccf08ae) Thanks [@MananTank](https://github.com/MananTank)! - - Expose `advancedOptions` and `styles` for paper wallet
- Allow using "+" in paper email
- Allow using "+" in magic email
- Update Paper SDK version
- Updated dependencies [[`2b113539`](https://github.com/thirdweb-dev/js/commit/2b113539098384f910b3c4d54e1fde9d35a6f053), [`c9535715`](https://github.com/thirdweb-dev/js/commit/c95357158819abd42d4b0900ecc2fa40fcb957f8), [`5882091e`](https://github.com/thirdweb-dev/js/commit/5882091eab65978009a5a5305701f121851b10ad)]:
- @thirdweb-dev/sdk@3.10.27
- @thirdweb-dev/chains@0.1.26
## 1.0.2
### Patch Changes
- [#1263](https://github.com/thirdweb-dev/js/pull/1263) [`ed711c8c`](https://github.com/thirdweb-dev/js/commit/ed711c8cb2ed6a0deb1b2a5eeec06df1d4edc5e8) Thanks [@iketw](https://github.com/iketw)! - Pass a min set of chains if user passes more than 50 chains. We'll rely on "adding chains" for new chains
- [#1255](https://github.com/thirdweb-dev/js/pull/1255) [`05a7495a`](https://github.com/thirdweb-dev/js/commit/05a7495a132aabab5f48abbdb80a468ff6f65df8) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Handle passing RPC urls as chain in SmartWallet config
- [#1265](https://github.com/thirdweb-dev/js/pull/1265) [`e78da6b3`](https://github.com/thirdweb-dev/js/commit/e78da6b36cfa6d954234820563201bf760186ed1) Thanks [@MananTank](https://github.com/MananTank)! - Wallet Connect v2 Corrections
## 1.0.1
### Patch Changes
- [#1259](https://github.com/thirdweb-dev/js/pull/1259) [`efb3546e`](https://github.com/thirdweb-dev/js/commit/efb3546ec8268156be00301af28b9d83ecd5ab08) Thanks [@iketw](https://github.com/iketw)! - Fix. Respecting passed wallets if less then 50, otherwhise use defaults chains.
## 1.0.0
### Major Changes
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/wallets",
"version": "1.0.0",
"version": "1.0.3",
"main": "dist/thirdweb-dev-wallets.cjs.js",
"module": "dist/thirdweb-dev-wallets.esm.js",
"types": "dist/thirdweb-dev-wallets.cjs.d.ts",
@@ -325,7 +325,7 @@
"@magic-ext/connect": "^6.7.0",
"@magic-ext/oauth": "^7.6.2",
"@magic-sdk/provider": "^13.6.2",
"@paperxyz/embedded-wallet-service-sdk": "^1.0.0",
"@paperxyz/embedded-wallet-service-sdk": "^1.1.0",
"@safe-global/safe-core-sdk": "^3.3.2",
"@safe-global/safe-ethers-adapters": "^0.1.0-alpha.16",
"@safe-global/safe-ethers-lib": "^1.9.2",
@@ -13,8 +13,7 @@ import type {
} from "@coinbase/wallet-sdk";
import type { CoinbaseWalletSDKOptions } from "@coinbase/wallet-sdk/dist/CoinbaseWalletSDK";
import type { Chain } from "@thirdweb-dev/chains";
import { providers } from "ethers";
import { getAddress, hexValue } from "ethers/lib/utils.js";
import { providers, utils } from "ethers";
import { walletIds } from "../../constants/walletIds";
type Options = CoinbaseWalletSDKOptions & {
@@ -61,7 +60,7 @@ export class CoinbaseWalletConnector extends WagmiConnector<
this.emit("message", { type: "connecting" });
const accounts = await provider.enable();
const account = getAddress(accounts[0] as string);
const account = utils.getAddress(accounts[0] as string);
// Switch to chain if provided
let id = await this.getChainId();
let unsupported = this.isChainUnsupported(id);
@@ -120,7 +119,7 @@ export class CoinbaseWalletConnector extends WagmiConnector<
throw new Error("No accounts found");
}
// return checksum address
return getAddress(accounts[0] as string);
return utils.getAddress(accounts[0] as string);
}
async getChainId() {
@@ -197,7 +196,7 @@ export class CoinbaseWalletConnector extends WagmiConnector<
async switchChain(chainId: number): Promise<Chain> {
const provider = await this.getProvider();
const id = hexValue(chainId);
const id = utils.hexValue(chainId);
try {
await provider.request({
@@ -257,7 +256,7 @@ export class CoinbaseWalletConnector extends WagmiConnector<
if (accounts.length === 0) {
this.emit("disconnect");
} else {
this.emit("change", { account: getAddress(accounts[0] as string) });
this.emit("change", { account: utils.getAddress(accounts[0] as string) });
}
};
@@ -1,5 +1,4 @@
import { providers } from "ethers";
import { getAddress, hexValue } from "ethers/lib/utils.js";
import { providers, utils } from "ethers";
import type Provider from "ethereum-provider";
import type { Address } from "abitype";
import type { Chain } from "@thirdweb-dev/chains";
@@ -76,7 +75,7 @@ export class FrameConnector extends WagmiConnector<
const accounts: string[] = await provider.request({
method: "eth_requestAccounts",
});
const account = getAddress(accounts[0] as string);
const account = utils.getAddress(accounts[0] as string);
// Switch to chain if provided
let id = await this.getChainId();
let unsupported = this.isChainUnsupported(id);
@@ -132,7 +131,7 @@ export class FrameConnector extends WagmiConnector<
method: "eth_accounts",
})) as string[];
// return checksum address
return getAddress(accounts[0] as string);
return utils.getAddress(accounts[0] as string);
}
async getChainId() {
@@ -195,7 +194,7 @@ export class FrameConnector extends WagmiConnector<
if (!provider) {
throw new ConnectorNotFoundError();
}
const chainIdHex = hexValue(chainId);
const chainIdHex = utils.hexValue(chainId);
try {
await Promise.all([
@@ -320,7 +319,7 @@ export class FrameConnector extends WagmiConnector<
this.emit("disconnect");
} else {
this.emit("change", {
account: getAddress(accounts[0] as string),
account: utils.getAddress(accounts[0] as string),
});
}
};
@@ -5,7 +5,7 @@ import {
MagicOptions,
} from "./types";
import type { Chain } from "@thirdweb-dev/chains";
import { ethers, Signer } from "ethers";
import { ethers, Signer, utils } from "ethers";
import {
// OAuthProvider,
OAuthExtension,
@@ -17,7 +17,6 @@ import {
SDKBase,
} from "@magic-sdk/provider";
import { Address } from "@thirdweb-dev/sdk";
import { getAddress } from "ethers/lib/utils";
import { Magic } from "magic-sdk";
import type { AbstractProvider } from "web3-core";
import { RPCProviderModule } from "@magic-sdk/provider/dist/types/modules/rpc-provider";
@@ -102,7 +101,7 @@ export abstract class MagicBaseConnector extends WagmiConnector<
if (accounts.length === 0) {
this.emit("disconnect");
} else {
this.emit("change", { account: getAddress(accounts[0]) });
this.emit("change", { account: utils.getAddress(accounts[0]) });
}
}
@@ -8,6 +8,7 @@ import type {
AuthLoginReturnType,
InitializedUser,
PaperEmbeddedWalletSdk,
RecoveryShareManagement,
} from "@paperxyz/embedded-wallet-service-sdk";
import { UserStatus } from "@paperxyz/embedded-wallet-service-sdk";
import type { Chain } from "@thirdweb-dev/chains";
@@ -39,9 +40,15 @@ export class PaperWalletConnector extends Connector<PaperWalletConnectionArgs> {
"@paperxyz/embedded-wallet-service-sdk"
);
resolve(
new PaperEmbeddedWalletSdk({
new PaperEmbeddedWalletSdk<RecoveryShareManagement.USER_MANAGED>({
advancedOptions: {
// @ts-expect-error - Allow passing string instead of forcing enum
recoveryShareManagement:
this.options.advancedOptions?.recoveryShareManagement,
},
clientId: this.options.clientId,
chain: "Ethereum", // just pass Ethereum no matter what chain we are going to connect
chain: "Ethereum",
styles: this.options.styles,
}),
);
} catch (err) {
@@ -92,6 +99,7 @@ export class PaperWalletConnector extends Connector<PaperWalletConnectionArgs> {
async disconnect(): Promise<void> {
const paper = await this.#paper;
await paper?.auth.logout();
this.#signer = undefined;
this.user = null;
}
@@ -1,14 +1,26 @@
import type { Chain } from "@thirdweb-dev/chains";
import type {
RecoveryShareManagement,
PaperConstructorType,
} from "@paperxyz/embedded-wallet-service-sdk";
type PaperAdvanceOptions = {
recoveryShareManagement?: "USER_MANAGED" | "AWS_MANAGED";
};
export type PaperWalletAdditionalOptions = {
clientId: string;
chain: Pick<Chain, "chainId" | "rpc">;
advancedOptions?: PaperAdvanceOptions;
styles?: PaperConstructorType<RecoveryShareManagement>["styles"];
};
export interface PaperWalletConnectorOptions {
clientId: string;
chain: Pick<Chain, "chainId" | "rpc">;
chains: Chain[];
advancedOptions?: PaperAdvanceOptions;
styles?: PaperConstructorType<RecoveryShareManagement>["styles"];
}
export interface PaperWalletConnectionArgs {
@@ -1,4 +1,4 @@
import { Chain } from "@thirdweb-dev/chains";
import { Chain, getChainByChainId } from "@thirdweb-dev/chains";
import { ConnectParams, Connector } from "../../interfaces/connector";
import { ERC4337EthersProvider } from "./lib/erc4337-provider";
import { getVerifyingPaymaster } from "./lib/paymaster";
@@ -15,6 +15,7 @@ import { EVMWallet } from "../../interfaces";
import { ERC4337EthersSigner } from "./lib/erc4337-signer";
import { BigNumber, ethers, providers } from "ethers";
import {
ChainOrRpcUrl,
getChainProvider,
SmartContract,
Transaction,
@@ -36,14 +37,14 @@ export class SmartWalletConnector extends Connector<SmartWalletConnectionArgs> {
async initialize(personalWallet: EVMWallet) {
const config = this.config;
const chain =
typeof config.chain === "string"
? config.chain
: (config.chain as Chain).slug;
const originalProvider = getChainProvider(config.chain, {
thirdwebApiKey: config.thirdwebApiKey || DEFAULT_WALLET_API_KEY,
}) as providers.BaseProvider;
const chainSlug = await this.getChainSlug(config.chain, originalProvider);
const bundlerUrl =
this.config.bundlerUrl || `https://${chain}.bundler.thirdweb.com`;
this.config.bundlerUrl || `https://${chainSlug}.bundler.thirdweb.com`;
const paymasterUrl =
this.config.paymasterUrl || `https://${chain}.bundler.thirdweb.com`;
this.config.paymasterUrl || `https://${chainSlug}.bundler.thirdweb.com`;
const entryPointAddress = config.entryPointAddress || ENTRYPOINT_ADDRESS;
const localSigner = await personalWallet.getSigner();
const providerConfig: ProviderConfig = {
@@ -65,9 +66,6 @@ export class SmartWalletConnector extends Connector<SmartWalletConnectionArgs> {
accountInfo: config.accountInfo || this.defaultAccountInfo(),
thirdwebApiKey: config.thirdwebApiKey,
};
const originalProvider = getChainProvider(config.chain, {
thirdwebApiKey: config.thirdwebApiKey || DEFAULT_WALLET_API_KEY,
}) as providers.BaseProvider;
this.personalWallet = personalWallet;
const accountApi = new AccountAPI(providerConfig, originalProvider);
this.aaProvider = await create4337Provider(
@@ -251,4 +249,29 @@ export class SmartWalletConnector extends Connector<SmartWalletConnectionArgs> {
},
};
}
private async getChainSlug(
chainOrRpc: ChainOrRpcUrl,
provider: ethers.providers.Provider,
): Promise<string> {
if (typeof chainOrRpc === "object") {
return chainOrRpc.slug;
}
if (typeof chainOrRpc === "number") {
const chain = getChainByChainId(chainOrRpc);
return chain.slug;
}
if (typeof chainOrRpc === "string") {
if (chainOrRpc.startsWith("http") || chainOrRpc.startsWith("ws")) {
// if it's a url, try to get the chain id from the provider
const chainId = (await provider.getNetwork()).chainId;
const chain = getChainByChainId(chainId);
return chain.slug;
}
// otherwise its the network name
return chainOrRpc;
} else {
throw new Error(`Invalid network: ${chainOrRpc}`);
}
}
}
@@ -1,6 +1,5 @@
import { LOCAL_NODE_PKEY, SmartContract, ThirdwebSDK } from "@thirdweb-dev/sdk";
import { BigNumberish, BigNumber, ethers } from "ethers";
import { arrayify, hexConcat } from "ethers/lib/utils";
import { BigNumberish, BigNumber, ethers, utils } from "ethers";
import { AccountApiParams } from "../types";
import { BaseAccountAPI } from "./base-api";
import { MINIMAL_ACCOUNT_ABI } from "./constants";
@@ -64,7 +63,7 @@ export class AccountAPI extends BaseAccountAPI {
} catch (e) {
console.error("Cost to deploy smart wallet: unknown", e);
}
return hexConcat([factory.getAddress(), tx.encode()]);
return utils.hexConcat([factory.getAddress(), tx.encode()]);
}
async getFactoryContract() {
@@ -131,7 +130,9 @@ export class AccountAPI extends BaseAccountAPI {
}
async signUserOpHash(userOpHash: string): Promise<string> {
return await this.params.localSigner.signMessage(arrayify(userOpHash));
return await this.params.localSigner.signMessage(
utils.arrayify(userOpHash),
);
}
async isAcountDeployed() {
@@ -1,11 +1,10 @@
import { ethers, BigNumber, BigNumberish, providers } from "ethers";
import { ethers, BigNumber, BigNumberish, providers, utils } from "ethers";
import {
EntryPoint,
EntryPoint__factory,
UserOperationStruct,
} from "@account-abstraction/contracts";
import { resolveProperties } from "ethers/lib/utils";
import { NotPromise, packUserOp } from "@account-abstraction/utils";
import {
GasOverheads,
@@ -15,7 +14,11 @@ import {
import { TransactionDetailsForUserOp } from "./transaction-details";
import { getUserOpHashV06 } from "./utils";
import { DUMMY_PAYMASTER_AND_DATA, SIG_SIZE } from "./paymaster";
import { CeloAlfajoresTestnet, CeloBaklavaTestnet, Celo } from "@thirdweb-dev/chains"
import {
CeloAlfajoresTestnet,
CeloBaklavaTestnet,
Celo,
} from "@thirdweb-dev/chains";
export interface BaseApiParams {
provider: providers.Provider;
@@ -169,7 +172,7 @@ export abstract class BaseAccountAPI {
async getPreVerificationGas(
userOp: Partial<UserOperationStruct>,
): Promise<number> {
const p = await resolveProperties(userOp);
const p = await utils.resolveProperties(userOp);
return calcPreVerificationGas(p, this.overheads);
}
@@ -286,7 +289,11 @@ export abstract class BaseAccountAPI {
const network = await this.provider.getNetwork();
const chainId = network.chainId;
if (chainId === Celo.chainId || chainId === CeloAlfajoresTestnet.chainId || chainId === CeloBaklavaTestnet.chainId) {
if (
chainId === Celo.chainId ||
chainId === CeloAlfajoresTestnet.chainId ||
chainId === CeloBaklavaTestnet.chainId
) {
maxPriorityFeePerGas = maxFeePerGas;
}
}
@@ -1,6 +1,5 @@
/* eslint-disable @typescript-eslint/no-parameter-properties */
import { BigNumber, providers, Signer } from "ethers";
import { hexValue, resolveProperties } from "ethers/lib/utils";
import { BigNumber, providers, Signer, utils } from "ethers";
import {
EntryPoint,
@@ -134,7 +133,7 @@ export class ERC4337EthersProvider extends providers.BaseProvider {
async constructUserOpTransactionResponse(
userOp1: UserOperationStruct,
): Promise<providers.TransactionResponse> {
const userOp = await resolveProperties(userOp1);
const userOp = await utils.resolveProperties(userOp1);
const userOpHash = await this.smartAccountAPI.getUserOpHash(userOp);
const waitForUserOp = async (): Promise<providers.TransactionReceipt> =>
await new Promise((resolve, reject) => {
@@ -154,7 +153,7 @@ export class ERC4337EthersProvider extends providers.BaseProvider {
nonce: BigNumber.from(userOp.nonce).toNumber(),
gasLimit: BigNumber.from(userOp.callGasLimit), // ??
value: BigNumber.from(0),
data: hexValue(userOp.callData), // should extract the actual called method from this "execFromEntryPoint()" call
data: utils.hexValue(userOp.callData), // should extract the actual called method from this "execFromEntryPoint()" call
chainId: this.chainId,
wait: async (
// eslint-disable-next-line @typescript-eslint/no-unused-vars
@@ -1,10 +1,9 @@
import { ethers, providers } from "ethers";
import { ethers, providers, utils } from "ethers";
import { Bytes, Signer } from "ethers";
import { ClientConfig } from "@account-abstraction/sdk";
import { BaseAccountAPI } from "./base-api";
import type { ERC4337EthersProvider } from "./erc4337-provider";
import { defineReadOnly, Deferrable } from "ethers/lib/utils";
import { HttpRpcClient } from "./http-rpc-client";
export class ERC4337EthersSigner extends Signer {
@@ -23,7 +22,7 @@ export class ERC4337EthersSigner extends Signer {
smartAccountAPI: BaseAccountAPI,
) {
super();
defineReadOnly(this, "provider", erc4337provider);
utils.defineReadOnly(this, "provider", erc4337provider);
this.config = config;
this.originalSigner = originalSigner;
this.erc4337provider = erc4337provider;
@@ -35,7 +34,7 @@ export class ERC4337EthersSigner extends Signer {
// This one is called by Contract. It signs the request and passes in to Provider to be sent.
async sendTransaction(
transaction: Deferrable<providers.TransactionRequest>,
transaction: utils.Deferrable<providers.TransactionRequest>,
batched: boolean = false,
): Promise<providers.TransactionResponse> {
const tx = await ethers.utils.resolveProperties(transaction);
@@ -138,7 +137,7 @@ export class ERC4337EthersSigner extends Signer {
async signTransaction(
// eslint-disable-next-line @typescript-eslint/no-unused-vars
transaction: Deferrable<providers.TransactionRequest>,
transaction: utils.Deferrable<providers.TransactionRequest>,
): Promise<string> {
throw new Error("not implemented");
}
@@ -1,5 +1,4 @@
import { providers } from "ethers";
import { resolveProperties } from "ethers/lib/utils";
import { providers, utils } from "ethers";
import { UserOperationStruct } from "@account-abstraction/contracts";
import { deepHexlify } from "@account-abstraction/utils";
@@ -55,7 +54,7 @@ export class HttpRpcClient {
*/
async sendUserOpToBundler(userOp1: UserOperationStruct): Promise<string> {
await this.initializing;
const hexifiedUserOp = deepHexlify(await resolveProperties(userOp1));
const hexifiedUserOp = deepHexlify(await utils.resolveProperties(userOp1));
const jsonRequestData: [UserOperationStruct, string] = [
hexifiedUserOp,
this.entryPointAddress,
@@ -71,7 +70,7 @@ export class HttpRpcClient {
userOp1: Partial<UserOperationStruct>,
): Promise<string> {
await this.initializing;
const hexifiedUserOp = deepHexlify(await resolveProperties(userOp1));
const hexifiedUserOp = deepHexlify(await utils.resolveProperties(userOp1));
const jsonRequestData: [UserOperationStruct, string] = [
hexifiedUserOp,
this.entryPointAddress,
@@ -93,7 +92,7 @@ export class HttpRpcClient {
if (!DEBUG) {
return;
}
const userOp = await resolveProperties(userOp1);
const userOp = await utils.resolveProperties(userOp1);
console.debug(
"sending",
method,
@@ -1,7 +1,6 @@
/* eslint-disable @typescript-eslint/no-parameter-properties */
import { BigNumberish, Event, providers } from "ethers";
import { BigNumberish, Event, providers, utils } from "ethers";
import { EntryPoint } from "@account-abstraction/contracts";
import { defaultAbiCoder } from "ethers/lib/utils";
/**
* This class encapsulates Ethers.js listener function and necessary UserOperation details to
@@ -92,7 +91,7 @@ export class UserOperationEventListener {
let message = revertReasonEvents[0].args.revertReason;
if (message.startsWith("0x08c379a0")) {
// Error(string)
message = defaultAbiCoder
message = utils.defaultAbiCoder
.decode(["string"], "0x" + message.substring(10))
.toString();
}
@@ -1,10 +1,5 @@
import { ethers } from "ethers";
import { ethers, utils } from "ethers";
import { UserOperationStruct } from "@account-abstraction/contracts";
import {
resolveProperties,
keccak256,
defaultAbiCoder,
} from "ethers/lib/utils";
export function toJSON(op: Partial<UserOperationStruct>): Promise<any> {
return ethers.utils.resolveProperties(op).then((userOp) =>
@@ -38,18 +33,18 @@ export async function getUserOpHashV06(
entryPoint: string,
chainId: number,
): Promise<Promise<string>> {
const op = await resolveProperties(userOp);
const op = await utils.resolveProperties(userOp);
const hashedUserOp = {
sender: op.sender,
nonce: op.nonce,
initCodeHash: keccak256(op.initCode),
callDataHash: keccak256(op.callData),
initCodeHash: utils.keccak256(op.initCode),
callDataHash: utils.keccak256(op.callData),
callGasLimit: op.callGasLimit,
verificationGasLimit: op.verificationGasLimit,
preVerificationGas: op.preVerificationGas,
maxFeePerGas: op.maxFeePerGas,
maxPriorityFeePerGas: op.maxPriorityFeePerGas,
paymasterAndDataHash: keccak256(op.paymasterAndData),
paymasterAndDataHash: utils.keccak256(op.paymasterAndData),
};
const userOpType = {
@@ -68,16 +63,16 @@ export async function getUserOpHashV06(
name: "hashedUserOp",
type: "tuple",
};
let encoded = defaultAbiCoder.encode(
let encoded = utils.defaultAbiCoder.encode(
[userOpType as any],
[{ ...hashedUserOp }],
);
// remove leading word (total length) and trailing word (zero-length signature)
const userOpHash = keccak256(encoded);
const enc = defaultAbiCoder.encode(
const userOpHash = utils.keccak256(encoded);
const enc = utils.defaultAbiCoder.encode(
["bytes32", "address", "uint256"],
[userOpHash, entryPoint, chainId],
);
return keccak256(enc);
return utils.keccak256(enc);
}
@@ -12,6 +12,8 @@ import { providers, utils } from "ethers";
import { walletIds } from "../../constants/walletIds";
import { QRModalOptions } from "./qrModalOptions";
const chainsToRequest = new Set([1, 137, 10, 42161, 56]);
type WalletConnectOptions = {
qrModalOptions?: QRModalOptions;
projectId: string;
@@ -78,6 +80,7 @@ export class WalletConnectConnector extends WagmiConnector<
#provider?: WalletConnectProvider;
#initProviderPromise?: Promise<void>;
#storage: AsyncStorage;
filteredChains: Chain[];
constructor(config: { chains?: Chain[]; options: WalletConnectOptions }) {
super({
@@ -86,6 +89,13 @@ export class WalletConnectConnector extends WagmiConnector<
});
this.#storage = config.options.storage;
this.#createProvider();
this.filteredChains =
this.chains.length > 50
? this.chains.filter((c) => {
return chainsToRequest.has(c.chainId);
})
: this.chains;
}
async connect({ chainId: chainIdP, pairingTopic }: ConnectConfig = {}) {
@@ -101,7 +111,7 @@ export class WalletConnectConnector extends WagmiConnector<
if (lastUsedChainId && !this.isChainUnsupported(lastUsedChainId)) {
targetChainId = lastUsedChainId;
} else {
targetChainId = this.chains[0]?.chainId;
targetChainId = this.filteredChains[0]?.chainId;
}
}
if (!targetChainId) {
@@ -120,11 +130,9 @@ export class WalletConnectConnector extends WagmiConnector<
// If there no active session, or the chains are stale, connect.
if (!provider.session || isChainsStale) {
// defaultChains, passing ALL CHAINS hangs the connector
const optionalChains = [
1, 5, 84531, 137, 80001, 42161, 421613, 10, 420, 56, 97, 250, 4002,
43114, 43113, 1337,
].filter((chainId) => chainId !== targetChainId);
const optionalChains = this.filteredChains
.filter((chain) => chain.chainId !== targetChainId)
.map((optionalChain) => optionalChain.chainId);
this.emit("message", { type: "connecting" });
@@ -136,7 +144,7 @@ export class WalletConnectConnector extends WagmiConnector<
});
await this.#setRequestedChainsIds(
this.chains.map(({ chainId }) => chainId),
this.filteredChains.map(({ chainId }) => chainId),
);
}
@@ -305,7 +313,7 @@ export class WalletConnectConnector extends WagmiConnector<
OPTIONAL_EVENTS,
OPTIONAL_METHODS,
} = await import("@walletconnect/ethereum-provider");
const [defaultChain, ...optionalChains] = this.chains.map(
const [defaultChain, ...optionalChains] = this.filteredChains.map(
({ chainId }) => chainId,
);
@@ -326,7 +334,7 @@ export class WalletConnectConnector extends WagmiConnector<
icons: [this.options.dappMetadata.logoUrl || ""],
},
rpcMap: Object.fromEntries(
this.chains.map((chain) => [chain.chainId, chain.rpc[0]]),
this.filteredChains.map((chain) => [chain.chainId, chain.rpc[0]]),
),
qrModalOptions: this.options.qrModalOptions,
@@ -366,7 +374,7 @@ export class WalletConnectConnector extends WagmiConnector<
}
const requestedChains = await this.#getRequestedChainsIds();
const connectorChains = this.chains.map(({ chainId }) => chainId);
const connectorChains = this.filteredChains.map(({ chainId }) => chainId);
const namespaceChains = this.#getNamespaceChainsIds();
if (
@@ -1,6 +1,6 @@
import {
PaperWalletConnectionArgs,
PaperWalletAdditionalOptions,
PaperWalletAdditionalOptions as PaperWalletAdditionalOptions_,
} from "../connectors/paper/types";
import { Connector } from "../interfaces/connector";
import { AbstractClientWallet, WalletOptions } from "./base";
@@ -8,10 +8,12 @@ import type { Chain } from "@thirdweb-dev/chains";
import type { PaperWalletConnector } from "../connectors/paper";
import { walletIds } from "../constants/walletIds";
export type PaperWalletOptions = WalletOptions<PaperWalletAdditionalOptions>;
export type { PaperWalletAdditionalOptions } from "../connectors/paper/types";
export type PaperWalletOptions = WalletOptions<PaperWalletAdditionalOptions_>;
export class PaperWallet extends AbstractClientWallet<
PaperWalletAdditionalOptions,
PaperWalletAdditionalOptions_,
PaperWalletConnectionArgs
> {
connector?: Connector;
@@ -28,8 +30,8 @@ export class PaperWallet extends AbstractClientWallet<
return "Paper Wallet" as const;
}
clientId: PaperWalletAdditionalOptions["clientId"];
chain: PaperWalletAdditionalOptions["chain"];
clientId: PaperWalletAdditionalOptions_["clientId"];
chain: PaperWalletAdditionalOptions_["chain"];
constructor(options: PaperWalletOptions) {
super(PaperWallet.id, {
@@ -47,6 +49,11 @@ export class PaperWallet extends AbstractClientWallet<
clientId: this.clientId,
chain: this.chain,
chains: this.chains,
advancedOptions: {
recoveryShareManagement:
this.options?.advancedOptions?.recoveryShareManagement,
},
styles: this.options?.styles,
});
}
return this.connector;
+236 -2352
View File
File diff suppressed because it is too large Load Diff