Compare commits

...
Author SHA1 Message Date
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
0a5d2b5bdd Version Packages (#1247)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-06-27 17:36:21 -07:00
FirekeeperandGitHub ac3e019cd1 [unity-js-bridge] wcv2 and paper support (#1256) 2023-06-28 00:28:16 +00:00
3c5dc4804a Migration to WalletConnectV2 (#1215)
Co-authored-by: Manan Tank <[email protected]>
2023-06-28 00:04:07 +00:00
Joaquim VergesandGitHub 30ac3aef84 [SDK] Handle base64 encoded NFTs (#1254) 2023-06-27 23:31:52 +00:00
YashandGitHub ef4cb09219 [SDK] Any EVM deployments: update EIP155 error substrings (#1249) 2023-06-27 22:55:16 +00:00
ad7dae3b16 [React] Stop using useActiveChain deprecated hook (#1253)
Co-authored-by: Nacho Iacovino <[email protected]>
2023-06-27 21:50:28 +00:00
0fd8aa04a9 fix(chains): Celo Alfajores Explorer URL (#1243)
Co-authored-by: Joaquim Verges <[email protected]>
Co-authored-by: Jonas Daniels <[email protected]>
2023-06-27 19:37:23 +00:00
620e89dc25 [React] Export smart wallet hooks (#1248)
Co-authored-by: Jonas Daniels <[email protected]>
2023-06-27 19:01:45 +00:00
Nacho IacovinoandGitHub 86d33c4b3e [React] Fix marketplace v3 cache keys (#1251) 2023-06-27 18:06:30 +00:00
Adam MajmudarandGitHub d43f6171d9 Allow setting login payload duration (#1250) 2023-06-27 18:04:33 +00:00
06ca1c407d [SDK] Add tests for SmartWallet and SmartWalletFactory; add smartWallet.setAccess and smartWallet.isAdmin (#1227)
Co-authored-by: Nacho Iacovino <[email protected]>
Co-authored-by: Nacho Iacovino <[email protected]>
2023-06-27 01:57:33 +00:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
c578a437cc Version Packages (#1234)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-06-26 18:42:04 -07:00
Danny FridayandGitHub 0c5b03b8d6 Remove ipfs-2 gateway (#1246) 2023-06-27 00:31:06 +00:00
nkrishangandGitHub 9af346eead [SDK][React] SmartWalletFactory: use getAllAccounts instead of event logs of AccountCreated to get accounts of a factory (#1241) 2023-06-26 17:51:22 +00:00
iketwandGitHub d9fb134ad4 [RN] better design for custom details button (#1245) 2023-06-26 15:25:19 +00:00
Mariano FuentesandGitHub 1c7264b403 Fix install bug (#1240) 2023-06-24 00:59:52 +00:00
Joaquim VergesandGitHub a140ee99b2 add zora extension in auto docs (#1239) 2023-06-24 00:23:07 +00:00
iketwandGitHub 6e1fe6e582 [React] Add Rainbow and Trust wallets connection hooks (#1238) 2023-06-23 23:06:59 +00:00
iketwandGitHub 536b0f1240 [React] Add Rainbow Wallet (#1237) 2023-06-23 22:11:40 +00:00
b626782b0e [React/Wallets] Add Trust Wallet (#1236)
Co-authored-by: Manan Tank <[email protected]>
2023-06-23 21:23:13 +00:00
Nacho IacovinoandGitHub 1d76334dd3 [React] Fix required type for address on useSmartWalletsForAddress (#1235) 2023-06-23 18:44:00 +00:00
nkrishangandGitHub d498c79a91 [SDK] Fix smartWalletFactory.getAllWallets (#1233) 2023-06-23 18:20:03 +00:00
106 changed files with 37237 additions and 2363 deletions
+16
View File
@@ -1,5 +1,21 @@
# @thirdweb-dev/auth
## 3.2.4
### Patch Changes
- [#1250](https://github.com/thirdweb-dev/js/pull/1250) [`d43f6171`](https://github.com/thirdweb-dev/js/commit/d43f6171d910e31b748b171b012174390aac3fff) Thanks [@adam-maj](https://github.com/adam-maj)! - Allow setting login payload validity duration
- Updated dependencies [[`ac3e019c`](https://github.com/thirdweb-dev/js/commit/ac3e019cd1776dbdb0d06b213420ad17586f678e), [`3c5dc480`](https://github.com/thirdweb-dev/js/commit/3c5dc4804abc56b933ec45e9e1da11eb182296cc)]:
- @thirdweb-dev/wallets@1.0.0
## 3.2.3
### Patch Changes
- Updated dependencies [[`536b0f12`](https://github.com/thirdweb-dev/js/commit/536b0f1240ab446aac22cf547a4e09e73ee6bf7b), [`b626782b`](https://github.com/thirdweb-dev/js/commit/b626782b0e8c6b76673472d3aee1c802dfb11b5f)]:
- @thirdweb-dev/wallets@0.3.3
## 3.2.2
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/auth",
"version": "3.2.2",
"version": "3.2.4",
"main": "dist/thirdweb-dev-auth.cjs.js",
"module": "dist/thirdweb-dev-auth.esm.js",
"browser": {
@@ -25,6 +25,11 @@ export default async function handler(
version: ctx.authOptions?.version,
chainId: parsedPayload.data.chainId || ctx.authOptions?.chainId,
resources: ctx.authOptions?.resources,
expirationTime: ctx.authOptions?.loginPayloadDurationInSeconds
? new Date(
Date.now() + 1000 * ctx.authOptions.loginPayloadDurationInSeconds,
)
: undefined,
});
return res.status(200).json({ payload });
+1
View File
@@ -44,6 +44,7 @@ export type ThirdwebAuthConfig<
validateTokenId?:
| ((tokenId: string) => void)
| ((tokenId: string) => Promise<void>);
loginPayloadDurationInSeconds?: number;
tokenDurationInSeconds?: number;
refreshIntervalInSeconds?: number;
};
+5
View File
@@ -25,6 +25,11 @@ export default async function handler(
version: ctx.authOptions?.version,
chainId: parsedPayload.data.chainId || ctx.authOptions?.chainId,
resources: ctx.authOptions?.resources,
expirationTime: ctx.authOptions?.loginPayloadDurationInSeconds
? new Date(
Date.now() + 1000 * ctx.authOptions.loginPayloadDurationInSeconds,
)
: undefined,
});
return res.status(200).json({ payload });
+1
View File
@@ -55,6 +55,7 @@ export type ThirdwebAuthConfig<
validateTokenId?:
| ((tokenId: string) => void)
| ((tokenId: string) => Promise<void>);
loginPayloadDurationInSeconds?: number;
tokenDurationInSeconds?: number;
refreshIntervalInSeconds?: number;
};
+14
View File
@@ -1,5 +1,19 @@
# @thirdweb-dev/chains
## 0.1.25
### Patch Changes
- [#1248](https://github.com/thirdweb-dev/js/pull/1248) [`620e89dc`](https://github.com/thirdweb-dev/js/commit/620e89dc25c91557e2164a602c7aedd733525087) Thanks [@nachoiacovino](https://github.com/nachoiacovino)! - update chains
- [#1243](https://github.com/thirdweb-dev/js/pull/1243) [`0fd8aa04`](https://github.com/thirdweb-dev/js/commit/0fd8aa04a9424497758d13a51a72363edcc30e19) Thanks [@therealharpaljadeja](https://github.com/therealharpaljadeja)! - update chains
## 0.1.24
### Patch Changes
- [#1241](https://github.com/thirdweb-dev/js/pull/1241) [`9af346ee`](https://github.com/thirdweb-dev/js/commit/9af346eeada1037be27c8d3c9e1777f7be11a8ea) Thanks [@nkrishang](https://github.com/nkrishang)! - Use fn getAllAccounts instead of event logs AccountCreated
## 0.1.23
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
import type { Chain } from "../src/types";
export default {
"name": "Optimism",
"name": "OP Mainnet",
"chain": "ETH",
"rpc": [
"https://optimism.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
-51
View File
@@ -1,51 +0,0 @@
import type { Chain } from "../src/types";
export default {
"name": "Horizen Yuma Testnet",
"shortName": "Yuma",
"chain": "Yuma",
"icon": {
"url": "ipfs://QmSFMBk3rMyu45Sy9KQHjgArFj4HdywANNYrSosLMUdcti",
"width": 1213,
"height": 1213,
"format": "png"
},
"rpc": [
"https://horizen-yuma-testnet.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://yuma-testnet.horizenlabs.io/ethv1"
],
"features": [
{
"name": "EIP155"
},
{
"name": "EIP1559"
}
],
"faucets": [
"https://yuma-testnet-faucet.horizen.io"
],
"nativeCurrency": {
"name": "Testnet Zen",
"symbol": "tZEN",
"decimals": 18
},
"infoURL": "https://horizen.io/",
"chainId": 1662,
"networkId": 1662,
"slip44": 121,
"explorers": [
{
"name": "Yuma Testnet Block Explorer",
"url": "https://yuma-explorer.horizen.io",
"icon": {
"url": "ipfs://QmSFMBk3rMyu45Sy9KQHjgArFj4HdywANNYrSosLMUdcti",
"width": 1213,
"height": 1213,
"format": "png"
},
"standard": "EIP3091"
}
],
"testnet": true,
"slug": "horizen-yuma-testnet"
} as const satisfies Chain;
+37
View File
@@ -0,0 +1,37 @@
import type { Chain } from "../src/types";
export default {
"name": "Pego Network",
"chain": "PEGO",
"rpc": [
"https://pego-network.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://pegorpc.com",
"https://node1.pegorpc.com",
"https://node2.pegorpc.com",
"https://node3.pegorpc.com"
],
"faucets": [],
"nativeCurrency": {
"name": "Pego Native Token",
"symbol": "PG",
"decimals": 18
},
"infoURL": "https://pego.network",
"shortName": "pg",
"chainId": 20201022,
"networkId": 20201022,
"icon": {
"url": "ipfs://QmVf1afskRHuZjFSLCZH8397KrVNAoYgyAePX9VMBrPVtx",
"width": 246,
"height": 247,
"format": "png"
},
"explorers": [
{
"name": "Pego Network Explorer",
"url": "https://scan.pego.network",
"standard": "EIP3091"
}
],
"testnet": false,
"slug": "pego-network"
} as const satisfies Chain;
+40
View File
@@ -0,0 +1,40 @@
import type { Chain } from "../src/types";
export default {
"name": "Peperium Chain Testnet",
"chain": "PERIUM",
"rpc": [
"https://peperium-chain-testnet.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://rpc-testnet.peperium.io"
],
"faucets": [],
"nativeCurrency": {
"name": "Peperium Chain Testnet",
"symbol": "PERIUM",
"decimals": 18
},
"infoURL": "https://peperium.io",
"shortName": "PERIUM",
"chainId": 4001,
"networkId": 4001,
"icon": {
"url": "ipfs://Qmag2hr5DQghRzKPN3oUFBkjWzqd5CndQzZeb5LfoiMCXf",
"width": 160,
"height": 160,
"format": "png"
},
"explorers": [
{
"name": "Peperium Chain Explorer",
"url": "https://scan-testnet.peperium.io",
"icon": {
"url": "ipfs://Qmag2hr5DQghRzKPN3oUFBkjWzqd5CndQzZeb5LfoiMCXf",
"width": 160,
"height": 160,
"format": "png"
},
"standard": "EIP3091"
}
],
"testnet": true,
"slug": "peperium-chain-testnet"
} as const satisfies Chain;
+9 -4
View File
@@ -12,15 +12,20 @@ export default {
},
"rpc": [
"https://celo-alfajores-testnet.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://alfajores-forno.celo-testnet.org",
"wss://alfajores-forno.celo-testnet.org/ws"
"wss://alfajores-forno.celo-testnet.org/ws",
"https://alfajores-forno.celo-testnet.org"
],
"faucets": [
"https://celo.org/developers/faucet",
"https://cauldron.pretoriaresearchlab.io/alfajores-faucet"
"https://cauldron.pretoriaresearchlab.io/alfajores-faucet",
"https://celo.org/developers/faucet"
],
"infoURL": "https://docs.celo.org/",
"explorers": [
{
"name": "Celoscan",
"url": "https://alfajores.celoscan.io/",
"standard": "EIP3091"
},
{
"name": "Celoscan",
"url": "https://celoscan.io",
+2 -1
View File
@@ -5,7 +5,8 @@ export default {
"rpc": [
"https://rollux.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://rpc.rollux.com",
"wss://rpc.rollux.com/wss"
"wss://rpc.rollux.com/wss",
"https://rpc.ankr.com/rollux/${ANKR_API_KEY}"
],
"faucets": [
"https://rollux.id/faucetapp"
+1 -1
View File
@@ -4,7 +4,7 @@ export default {
"chain": "ETC",
"rpc": [
"https://ethereum-classic.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://www.ethercluster.com/etc"
"https://etc.rivet.link"
],
"faucets": [
"https://free-online-app.com/faucet-for-eth-evm-chains/?"
+1 -1
View File
@@ -4,7 +4,7 @@ export default {
"chain": "ETC",
"rpc": [
"https://ethereum-classic-testnet-mordor.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://www.ethercluster.com/mordor"
"https://rpc.mordor.etccooperative.org"
],
"faucets": [],
"nativeCurrency": {
+13
View File
@@ -0,0 +1,13 @@
// @ts-check
/** @typedef { import("../../src/types").Chain } Chain */
/** @type {Partial<Chain>} */
export default {
explorers: [
{
name: "Celoscan",
url: "https://alfajores.celoscan.io/",
standard: "EIP3091",
},
],
};
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/chains",
"version": "0.1.23",
"version": "0.1.25",
"main": "dist/thirdweb-dev-chains.cjs.js",
"module": "dist/thirdweb-dev-chains.esm.js",
"browser": {
@@ -146,6 +146,10 @@ function findSlug(chain) {
if (slug === "base-goerli-testnet") {
slug = "base-goerli";
}
// optimisim rename handling
if (slug === "op") {
slug = "optimism";
}
// end special cases
takenSlugs[slug] = true;
File diff suppressed because one or more lines are too long
+19
View File
@@ -1,5 +1,24 @@
# thirdweb
## 0.10.27
### Patch Changes
- Updated dependencies [[`ef4cb092`](https://github.com/thirdweb-dev/js/commit/ef4cb092192c58c9b292b29590b888f45f9fd23d), [`30ac3aef`](https://github.com/thirdweb-dev/js/commit/30ac3aef840bde51a61acf786c709cffd3c47354), [`620e89dc`](https://github.com/thirdweb-dev/js/commit/620e89dc25c91557e2164a602c7aedd733525087), [`0fd8aa04`](https://github.com/thirdweb-dev/js/commit/0fd8aa04a9424497758d13a51a72363edcc30e19)]:
- @thirdweb-dev/sdk@3.10.26
- @thirdweb-dev/chains@0.1.25
## 0.10.26
### Patch Changes
- [#1240](https://github.com/thirdweb-dev/js/pull/1240) [`1c7264b4`](https://github.com/thirdweb-dev/js/commit/1c7264b40330d068ece67df537a0f5db0f6aedca) Thanks [@Marfuen](https://github.com/Marfuen)! - Small bug fix for updating packages using --nightly
- Updated dependencies [[`d498c79a`](https://github.com/thirdweb-dev/js/commit/d498c79a911d478077dfb8a2490eb1bf91523186), [`0c5b03b8`](https://github.com/thirdweb-dev/js/commit/0c5b03b8d6cc6a4e69bb2a4647d3626e69f1283c), [`9af346ee`](https://github.com/thirdweb-dev/js/commit/9af346eeada1037be27c8d3c9e1777f7be11a8ea)]:
- @thirdweb-dev/sdk@3.10.25
- @thirdweb-dev/storage@1.1.5
- @thirdweb-dev/chains@0.1.24
## 0.10.25
### Patch Changes
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "thirdweb",
"main": "dist/cli/index.js",
"version": "0.10.25",
"version": "0.10.27",
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/cli",
"author": "thirdweb eng <[email protected]>",
"license": "Apache-2.0",
+5 -5
View File
@@ -1,8 +1,8 @@
import * as toml from "@iarna/toml";
import { PackageManagerType } from "../core/types/ProjectType";
import { existsSync, readFileSync } from "fs";
import { runCommand } from "../create/helpers/run-command";
import path from "path";
import { PackageManagerType } from "../core/types/ProjectType";
import { runCommand } from "../create/helpers/run-command";
import { IProcessAppTypeArgs, IProcessContractAppTypeArgs } from "./types";
export function convertDependenciesToStringFormat(dependenciesObject: object) {
@@ -174,7 +174,7 @@ export const installOrUpdate = async (packageManager: PackageManagerType, depend
case "npm":
runner = "npm";
installCommand = ["install"];
updateCommand = ["update"]
updateCommand = ["install", "--legacy-peer-deps"]
break;
case "yarn":
runner = "yarn";
@@ -223,7 +223,7 @@ export const installOrUpdate = async (packageManager: PackageManagerType, depend
}
if (typeOfAction === "install") {
if (!dependenciesToAdd.length) {return;}
if (!dependenciesToAdd.length) { return; }
const commands = [...installCommand, ...dependenciesToAdd];
await runCommand(runner, commands, printLogs);
@@ -233,7 +233,7 @@ export const installOrUpdate = async (packageManager: PackageManagerType, depend
}
}
if (typeOfAction === "update") {
if (!dependenciesToUpdate.length) {return;}
if (!dependenciesToUpdate.length) { return; }
const commands = [...updateCommand, ...dependenciesToUpdate];
await runCommand(runner, commands, printLogs);
}
+6
View File
@@ -1,5 +1,11 @@
# @thirdweb-dev/contracts-js
## 1.3.7
### Patch Changes
- [#1241](https://github.com/thirdweb-dev/js/pull/1241) [`9af346ee`](https://github.com/thirdweb-dev/js/commit/9af346eeada1037be27c8d3c9e1777f7be11a8ea) Thanks [@nkrishang](https://github.com/nkrishang)! - Use fn getAllAccounts instead of event logs AccountCreated
## 1.3.6
### Patch Changes
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/contracts-js",
"version": "1.3.6",
"version": "1.3.7",
"main": "dist/thirdweb-dev-contracts-js.cjs.js",
"module": "dist/thirdweb-dev-contracts-js.esm.js",
"browser": {
@@ -10,7 +10,7 @@
"license": "Apache-2.0",
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/contracts-js",
"dependencies": {
"@thirdweb-dev/contracts": "3.5.8"
"@thirdweb-dev/contracts": "3.5.9"
},
"devDependencies": {
"@babel/preset-env": "^7.18.10",
+29
View File
@@ -1,5 +1,34 @@
# @thirdweb-dev/react-core
## 3.14.4
### Patch Changes
- [#1227](https://github.com/thirdweb-dev/js/pull/1227) [`06ca1c40`](https://github.com/thirdweb-dev/js/commit/06ca1c407dfee9d3d1fdaed1b6223ad0f9b8857b) Thanks [@nkrishang](https://github.com/nkrishang)! - Add react hooks for smart wallet
- [#1248](https://github.com/thirdweb-dev/js/pull/1248) [`620e89dc`](https://github.com/thirdweb-dev/js/commit/620e89dc25c91557e2164a602c7aedd733525087) Thanks [@nachoiacovino](https://github.com/nachoiacovino)! - Export account hooks
- [#1253](https://github.com/thirdweb-dev/js/pull/1253) [`ad7dae3b`](https://github.com/thirdweb-dev/js/commit/ad7dae3b163e61c7a6eb57f654885a7fdaa4cbb6) Thanks [@rattrayd11](https://github.com/rattrayd11)! - Stop using useActiveChain deprecated hook
- Updated dependencies [[`ef4cb092`](https://github.com/thirdweb-dev/js/commit/ef4cb092192c58c9b292b29590b888f45f9fd23d), [`ac3e019c`](https://github.com/thirdweb-dev/js/commit/ac3e019cd1776dbdb0d06b213420ad17586f678e), [`30ac3aef`](https://github.com/thirdweb-dev/js/commit/30ac3aef840bde51a61acf786c709cffd3c47354), [`3c5dc480`](https://github.com/thirdweb-dev/js/commit/3c5dc4804abc56b933ec45e9e1da11eb182296cc), [`620e89dc`](https://github.com/thirdweb-dev/js/commit/620e89dc25c91557e2164a602c7aedd733525087), [`0fd8aa04`](https://github.com/thirdweb-dev/js/commit/0fd8aa04a9424497758d13a51a72363edcc30e19)]:
- @thirdweb-dev/sdk@3.10.26
- @thirdweb-dev/wallets@1.0.0
- @thirdweb-dev/chains@0.1.25
## 3.14.3
### Patch Changes
- [#1235](https://github.com/thirdweb-dev/js/pull/1235) [`1d76334d`](https://github.com/thirdweb-dev/js/commit/1d76334dd3884703629835422f241d2825128f6f) Thanks [@nachoiacovino](https://github.com/nachoiacovino)! - Fix required type for address on useSmartWalletsForAddress
- [#1241](https://github.com/thirdweb-dev/js/pull/1241) [`9af346ee`](https://github.com/thirdweb-dev/js/commit/9af346eeada1037be27c8d3c9e1777f7be11a8ea) Thanks [@nkrishang](https://github.com/nkrishang)! - Use fn getAllAccounts instead of event logs AccountCreated
- Updated dependencies [[`536b0f12`](https://github.com/thirdweb-dev/js/commit/536b0f1240ab446aac22cf547a4e09e73ee6bf7b), [`b626782b`](https://github.com/thirdweb-dev/js/commit/b626782b0e8c6b76673472d3aee1c802dfb11b5f), [`d498c79a`](https://github.com/thirdweb-dev/js/commit/d498c79a911d478077dfb8a2490eb1bf91523186), [`0c5b03b8`](https://github.com/thirdweb-dev/js/commit/0c5b03b8d6cc6a4e69bb2a4647d3626e69f1283c), [`9af346ee`](https://github.com/thirdweb-dev/js/commit/9af346eeada1037be27c8d3c9e1777f7be11a8ea)]:
- @thirdweb-dev/wallets@0.3.3
- @thirdweb-dev/sdk@3.10.25
- @thirdweb-dev/storage@1.1.5
- @thirdweb-dev/chains@0.1.24
## 3.14.2
### Patch Changes
+1 -5
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/react-core",
"version": "3.14.2",
"version": "3.14.4",
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/react-core",
"author": "thirdweb eng <[email protected]>",
"license": "Apache-2.0",
@@ -113,10 +113,6 @@
"ethers": ">=5.5.1",
"react": ">=18.0.0"
},
"resolutions": {
"ansi-regex": "^6.0.1",
"typescript": "^4.6.2"
},
"peerDependenciesMeta": {
"@solana/web3.js": {
"optional": true
@@ -1,6 +1,6 @@
import { Chain, defaultChains } from "@thirdweb-dev/chains";
import { useCallback, useState } from "react";
import { useActiveChain, useChainId } from "../../evm/hooks/wallet";
import { useChain, useChainId } from "../../evm/hooks/wallet";
import { useSupportedChains } from "../../evm/hooks/useSupportedChains";
import { useSwitchChain, useWallet } from "./wallet-hooks";
import { assertWindowEthereum, walletIds } from "@thirdweb-dev/wallets";
@@ -29,7 +29,7 @@ type NetworkMetadata = {
/**
*
* @deprecated - use `useActiveChain`, `useSwitchChain`, `useChainId` instead
* @deprecated - use `useChain`, `useSwitchChain`, `useChainId` instead
*
* Hook for getting metadata about the network the current wallet is connected to and switching networks
*
@@ -55,7 +55,7 @@ type NetworkMetadata = {
* @public
*/
export function useNetwork(): [NetworkMetadata, SwitchNetwork | undefined] {
const chain = useActiveChain();
const chain = useChain();
const chainId = useChainId();
const switchChain = useSwitchChain();
const supportedChains = useSupportedChains();
@@ -504,7 +504,7 @@ export function useEnglishAuctionWinningBid(
) {
const contractAddress = contract?.getAddress();
return useQueryWithNetwork(
cacheKeys.contract.marketplace.auction.getWinningBid(
cacheKeys.contract.marketplace.englishAuctions.getWinningBid(
contractAddress,
auctionId,
),
@@ -596,7 +596,7 @@ export function useBidBuffer(
) {
const contractAddress = contract?.getAddress();
return useQueryWithNetwork(
cacheKeys.contract.marketplace.getBidBufferBps(contractAddress),
cacheKeys.contract.marketplace.auction.getBidBufferBps(contractAddress, listingId),
() => {
requiredParamInvariant(contract, "No Contract instance provided");
@@ -647,7 +647,7 @@ export function useMinimumNextBid(
) {
const contractAddress = contract?.getAddress();
return useQueryWithNetwork(
cacheKeys.contract.marketplace.auction.getWinner(
cacheKeys.contract.marketplace.auction.getMinimumNextBid(
contractAddress,
listingId,
),
@@ -14,14 +14,10 @@ import {
useQueryClient,
UseQueryResult,
} from "@tanstack/react-query";
import type {
AccountEvent, AddressOrEns, SmartContract,
} from "@thirdweb-dev/sdk";
import type { AccountEvent, SmartContract } from "@thirdweb-dev/sdk";
import type { BytesLike } from "ethers";
import invariant from "tiny-invariant";
/** **********************/
/** READ HOOKS **/
/** **********************/
@@ -41,7 +37,7 @@ import invariant from "tiny-invariant";
* @beta
*/
export function useSmartWallets(
contract: RequiredParam<SmartContract>
contract: RequiredParam<SmartContract>,
): UseQueryResult<AccountEvent[]> {
const contractAddress = contract?.getAddress();
return useQueryWithNetwork(
@@ -49,10 +45,10 @@ export function useSmartWallets(
() => {
requiredParamInvariant(contract, "No Contract instance provided");
invariant(
contract.smartWalletFactory.getAllWallets,
"Contract instance does not support contract.smartWalletFactory.getAllWallets",
contract.smartWalletFactory.getAllAccounts,
"Contract instance does not support contract.smartWalletFactory.getAllAccounts",
);
return contract.smartWalletFactory.getAllWallets();
return contract.smartWalletFactory.getAllAccounts();
},
{ enabled: !!contract },
);
@@ -74,18 +70,21 @@ export function useSmartWallets(
*/
export function useSmartWalletsForAddress(
contract: RequiredParam<SmartContract>,
address: AddressOrEns,
address: RequiredParam<WalletAddress>,
): UseQueryResult<string[]> {
const contractAddress = contract?.getAddress();
return useQueryWithNetwork(
cacheKeys.contract.smartWalletFactory.getAllForAddress(contractAddress, address),
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);
return contract.smartWalletFactory.getAssociatedWallets(address || "");
},
{ enabled: !!contract },
);
@@ -112,7 +111,10 @@ export function useIsSmartWalletDeployed(
): UseQueryResult<boolean> {
const contractAddress = contract?.getAddress();
return useQueryWithNetwork(
cacheKeys.contract.smartWalletFactory.isSmartWalletDeployed(contractAddress, admin),
cacheKeys.contract.smartWalletFactory.isSmartWalletDeployed(
contractAddress,
admin,
),
() => {
requiredParamInvariant(contract, "No Contract instance provided");
invariant(
@@ -163,9 +165,7 @@ export function useIsSmartWalletDeployed(
* @see {@link https://portal.thirdweb.com/react/react.usecreatesmartwallet?utm_source=sdk | Documentation}
* @beta
*/
export function useCreateSmartWallet(
contract: RequiredParam<SmartContract>,
) {
export function useCreateSmartWallet(contract: RequiredParam<SmartContract>) {
const activeChainId = useSDKChainId();
const contractAddress = contract?.getAddress();
const queryClient = useQueryClient();
@@ -174,10 +174,7 @@ export function useCreateSmartWallet(
async (admin: string, extraData?: BytesLike) => {
requiredParamInvariant(contract, "contract is undefined");
return contract.smartWalletFactory.createWallet(
admin,
extraData,
);
return contract.smartWalletFactory.createWallet(admin, extraData);
},
{
onSettled: () =>
@@ -0,0 +1,125 @@
import type { providers } from "ethers";
import {
RequiredParam,
requiredParamInvariant,
} from "../../../core/query-utils/required-param";
import { useSDKChainId } from "../../providers/thirdweb-sdk-provider";
import {
cacheKeys,
invalidateContractAndBalances,
} from "../../utils/cache-keys";
import { useQueryWithNetwork } from "../query-utils/useQueryWithNetwork";
import {
useMutation,
UseMutationResult,
useQueryClient,
UseQueryResult,
} from "@tanstack/react-query";
import type {
SignerWithRestrictions,
SignerWithRestrictionsBatchInput,
SmartContract,
} from "@thirdweb-dev/sdk";
import invariant from "tiny-invariant";
/** **********************/
/** READ HOOKS **/
/** **********************/
/**
* Get all wallets
*
* @example
* ```javascript
* const { data: smartWallets, isLoading, error } = useSmartWalletSigners(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}
* @beta
*/
export function useSmartWalletSigners(
contract: RequiredParam<SmartContract>,
): UseQueryResult<SignerWithRestrictions[]> {
const contractAddress = contract?.getAddress();
return useQueryWithNetwork(
cacheKeys.contract.smartWallet.signers(contractAddress),
() => {
requiredParamInvariant(contract, "No Contract instance provided");
invariant(
contract.smartWallet.getSignersWithRestrictions,
"Contract instance does not support contract.smartWallet.getSignersWithRestrictions",
);
return contract.smartWallet.getSignersWithRestrictions();
},
{ enabled: !!contract },
);
}
/** **********************/
/** WRITE HOOKS **/
/** **********************/
/**
* Set the wallet's entire snapshot of permissions
*
* @example
* ```jsx
* const Component = () => {
* const { contract } = useContract("{{contract_address}}");
* const {
* mutate: setSmartWalletSigners,
* isLoading,
* error,
* } = useSetSmartWalletSigners(contract);
*
* if (error) {
* console.error("failed to set smart wallet signers", error);
* }
*
* return (
* <button
* disabled={isLoading}
* onClick={() => setSmartWalletSigners("0x...")}
* >
* Set Smart Wallet 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}
* @beta
*/
export function useSetSmartWalletSigners(
contract: RequiredParam<SmartContract>,
): UseMutationResult<
{ receipt: providers.TransactionReceipt },
unknown,
SignerWithRestrictionsBatchInput,
unknown
> {
const activeChainId = useSDKChainId();
const contractAddress = contract?.getAddress();
const queryClient = useQueryClient();
return useMutation(
async (permissionsSnapshot: SignerWithRestrictionsBatchInput) => {
requiredParamInvariant(contract, "contract is undefined");
return contract.smartWallet.setAccess(permissionsSnapshot);
},
{
onSettled: () =>
invalidateContractAndBalances(
queryClient,
contractAddress,
activeChainId,
),
},
);
}
+6
View File
@@ -202,6 +202,12 @@ export {
useSmartWalletsForAddress,
} from "./hooks/async/smart-wallet-factory";
// smart wallet
export {
useSmartWalletSigners,
useSetSmartWalletSigners,
} from "./hooks/async/smart-wallet";
// thirdweb hooks (work as long as at least `<ThirdwebSdkProvider>` is used)
// auth hooks
+38 -13
View File
@@ -127,6 +127,11 @@ export const cacheKeys = {
createContractCacheKey(contractAdress, ["getAllForAddress", address]),
},
smartWallet: {
signers: (contractAddress: RequiredParam<ContractAddress>) =>
createContractCacheKey(contractAddress, ["smartWallet", "signers"]),
},
app: {
get: (contractAddress: RequiredParam<ContractAddress>) =>
createContractCacheKey(contractAddress, ["appURI"]),
@@ -273,10 +278,16 @@ export const cacheKeys = {
contractAddress,
params ? ["getActiveListings", params] : ["getActiveListings"],
),
getBidBufferBps: (contractAddress: RequiredParam<ContractAddress>) =>
createContractCacheKey(contractAddress, ["getBidBufferBps"]),
auction: {
getBidBufferBps: (
contractAddress: RequiredParam<ContractAddress>,
listingId: RequiredParam<BigNumberish>,
) =>
createContractCacheKey(contractAddress, [
"auction",
"getBidBufferBps",
{ listingId },
]),
getWinningBid: (
contractAddress: RequiredParam<ContractAddress>,
listingId: RequiredParam<BigNumberish>,
@@ -286,6 +297,15 @@ export const cacheKeys = {
"getWinningBid",
{ listingId },
]),
getMinimumNextBid: (
contractAddress: RequiredParam<ContractAddress>,
listingId: RequiredParam<BigNumberish>,
) =>
createContractCacheKey(contractAddress, [
"auction",
"getMinimumNextBid",
{ listingId },
]),
getWinner: (
contractAddress: RequiredParam<ContractAddress>,
listingId: RequiredParam<BigNumberish>,
@@ -305,8 +325,8 @@ export const cacheKeys = {
createContractCacheKey(
contractAddress,
params
? ["getAllDirectListings", params]
: ["getAllDirectListings"],
? ["directListings", "getAll", params]
: ["directListings", "getAll"],
),
getAllValid: (
contractAddress: RequiredParam<ContractAddress>,
@@ -315,20 +335,22 @@ export const cacheKeys = {
createContractCacheKey(
contractAddress,
params
? ["getAllValidDirectListings", params]
: ["getAllValidDirectListings"],
? ["directListings", "getAllValid", params]
: ["directListings", "getAllValid"],
),
getListing: (
contractAddress: RequiredParam<ContractAddress>,
listingId: RequiredParam<BigNumberish>,
) =>
createContractCacheKey(contractAddress, [
"directListings",
"getListing",
{ listingId },
]),
getTotalCount: (contractAddress: RequiredParam<ContractAddress>) =>
createContractCacheKey(contractAddress, [
"getTotalDirectListingsCount",
"directListings",
"getTotalCount",
]),
},
@@ -340,8 +362,8 @@ export const cacheKeys = {
createContractCacheKey(
contractAddress,
params
? ["getAllEnglishAuctions", params]
: ["getAllEnglishAuctions"],
? ["englishAuctions", "getAll", params]
: ["englishAuctions", "getAll"],
),
getAllValid: (
contractAddress: RequiredParam<ContractAddress>,
@@ -350,14 +372,15 @@ export const cacheKeys = {
createContractCacheKey(
contractAddress,
params
? ["getAllEValidEnglishAuctions", params]
: ["getAllEValidEnglishAuctions"],
? ["englishAuctions", "getAllValid", params]
: ["englishAuctions", "getAllValid"],
),
getAuction: (
contractAddress: RequiredParam<ContractAddress>,
auctionId: RequiredParam<BigNumberish>,
) =>
createContractCacheKey(contractAddress, [
"englishAuctions",
"getAuction",
{ auctionId },
]),
@@ -366,12 +389,14 @@ export const cacheKeys = {
auctionId: RequiredParam<BigNumberish>,
) =>
createContractCacheKey(contractAddress, [
"englishAuctions",
"getWinningBid",
{ auctionId },
]),
getTotalCount: (contractAddress: RequiredParam<ContractAddress>) =>
createContractCacheKey(contractAddress, [
"getTotalEnglishAuctionsCount",
"englishAuctions",
"getTotalCount",
]),
},
},
@@ -1,5 +1,9 @@
# @thirdweb-dev/react-native-compat
## 0.2.23
## 0.2.22
## 0.2.21
## 0.2.20
+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.21",
"version": "0.2.23",
"author": "thirdweb eng <[email protected]>",
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/react-native-compat",
"license": "Apache-2.0",
+57
View File
@@ -1,5 +1,62 @@
# @thirdweb-dev/react-native
## 0.2.23
### Patch Changes
- [#1215](https://github.com/thirdweb-dev/js/pull/1215) [`3c5dc480`](https://github.com/thirdweb-dev/js/commit/3c5dc4804abc56b933ec45e9e1da11eb182296cc) Thanks [@iketw](https://github.com/iketw)! - Migrate wallets to WCV2 and remove WCV1 support.
Please, check our changelogs for more info: https://blog.thirdweb.com/changelog/
- Updated dependencies [[`ef4cb092`](https://github.com/thirdweb-dev/js/commit/ef4cb092192c58c9b292b29590b888f45f9fd23d), [`06ca1c40`](https://github.com/thirdweb-dev/js/commit/06ca1c407dfee9d3d1fdaed1b6223ad0f9b8857b), [`620e89dc`](https://github.com/thirdweb-dev/js/commit/620e89dc25c91557e2164a602c7aedd733525087), [`ac3e019c`](https://github.com/thirdweb-dev/js/commit/ac3e019cd1776dbdb0d06b213420ad17586f678e), [`30ac3aef`](https://github.com/thirdweb-dev/js/commit/30ac3aef840bde51a61acf786c709cffd3c47354), [`3c5dc480`](https://github.com/thirdweb-dev/js/commit/3c5dc4804abc56b933ec45e9e1da11eb182296cc), [`ad7dae3b`](https://github.com/thirdweb-dev/js/commit/ad7dae3b163e61c7a6eb57f654885a7fdaa4cbb6), [`620e89dc`](https://github.com/thirdweb-dev/js/commit/620e89dc25c91557e2164a602c7aedd733525087), [`0fd8aa04`](https://github.com/thirdweb-dev/js/commit/0fd8aa04a9424497758d13a51a72363edcc30e19)]:
- @thirdweb-dev/sdk@3.10.26
- @thirdweb-dev/react-core@3.14.4
- @thirdweb-dev/wallets@1.0.0
- @thirdweb-dev/chains@0.1.25
## 0.2.22
### Patch Changes
- [#1245](https://github.com/thirdweb-dev/js/pull/1245) [`d9fb134a`](https://github.com/thirdweb-dev/js/commit/d9fb134ad40ae067416625eb43cf3708f87db7b5) Thanks [@iketw](https://github.com/iketw)! - [RN]
- Added the option to import a new wallet in guest mode
- Added a "Request Testnet Funds" option when on testnets
- [#1245](https://github.com/thirdweb-dev/js/pull/1245) [`d9fb134a`](https://github.com/thirdweb-dev/js/commit/d9fb134ad40ae067416625eb43cf3708f87db7b5) Thanks [@iketw](https://github.com/iketw)! - [ReactNative] Export more components for easy development
- IconTextButton
Component that renders an icon and a text inside a button with border:
```javascript
<IconTextButton
mt="xs"
text="Switch Accounts"
icon={<SwitchIcon height={10} width={10} />}
onPress={() => {}}
/>
```
- NetworkButton: Used to render a Network
```javascript
<NetworkButton
chainIconUrl={chain?.icon?.url || ""}
chainName={chain?.name || "Unknown Network"}
onPress={onChangeNetworkPress}
/>
```
- Exported all icons from the assets folder for external use
- Updated dependencies [[`536b0f12`](https://github.com/thirdweb-dev/js/commit/536b0f1240ab446aac22cf547a4e09e73ee6bf7b), [`1d76334d`](https://github.com/thirdweb-dev/js/commit/1d76334dd3884703629835422f241d2825128f6f), [`b626782b`](https://github.com/thirdweb-dev/js/commit/b626782b0e8c6b76673472d3aee1c802dfb11b5f), [`d498c79a`](https://github.com/thirdweb-dev/js/commit/d498c79a911d478077dfb8a2490eb1bf91523186), [`0c5b03b8`](https://github.com/thirdweb-dev/js/commit/0c5b03b8d6cc6a4e69bb2a4647d3626e69f1283c), [`9af346ee`](https://github.com/thirdweb-dev/js/commit/9af346eeada1037be27c8d3c9e1777f7be11a8ea)]:
- @thirdweb-dev/[email protected]
- @thirdweb-dev/[email protected]
- @thirdweb-dev/[email protected]
- @thirdweb-dev/[email protected]
- @thirdweb-dev/[email protected]
## 0.2.21
### Patch Changes
+1 -5
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/react-native",
"version": "0.2.21",
"version": "0.2.23",
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/react-native",
"author": "thirdweb eng <[email protected]>",
"license": "Apache-2.0",
@@ -44,10 +44,6 @@
"react-native-svg": "^13.8.0",
"tiny-invariant": "^1.2.0"
},
"resolutions": {
"react-native-mmkv": "2.5.1",
"@magic-sdk/provider": "17.2.0"
},
"devDependencies": {
"@babel/core": "^7.19.3",
"@babel/preset-env": "^7.1.6",
@@ -1,7 +1,10 @@
import { ThemeProvider } from "../styles/ThemeProvider";
import { WalletDetailsButton } from "./ConnectWalletDetails/WalletDetailsButton";
import {
ConnectWalletDetailsProps,
WalletDetailsButton,
} from "./ConnectWalletDetails/WalletDetailsButton";
import { useAddress } from "@thirdweb-dev/react-core";
import React, { useEffect, useRef } from "react";
import { useEffect, useRef } from "react";
import { Animated } from "react-native";
import { ConnectWalletButton } from "./ConnectWalletFlow/ConnectWalletButton";
import { ConnectWalletButtonProps } from "./ConnectWalletFlow/ConnectWalletButton";
@@ -10,7 +13,7 @@ export type ConnectWalletProps = {
/**
* render a custom button to display the connected wallet details instead of the default button
*/
detailsButton?: React.ReactElement;
detailsButton?: ConnectWalletDetailsProps["detailsButton"];
} & ConnectWalletButtonProps;
export const ConnectWallet = ({
@@ -1,4 +1,4 @@
import { StyleSheet, View } from "react-native";
import { Linking, StyleSheet, View } from "react-native";
import Box from "../base/Box";
import { Toast } from "../base/Toast";
import { ExportLocalWalletModal } from "./ExportLocalWalletModal";
@@ -22,6 +22,8 @@ import { useModalState } from "../../providers/ui-context-provider";
import { CLOSE_MODAL_STATE, WalletDetailsModal } from "../../utils/modalTypes";
import { useAppTheme } from "../../styles/hooks";
import { LocalWalletImportModal } from "../ConnectWalletFlow/LocalWalletImportModal";
import { IconTextButton } from "../base/IconTextButton";
import MoneyIcon from "../../assets/money";
export const ConnectWalletDetailsModal = () => {
const theme = useAppTheme();
@@ -192,6 +194,18 @@ export const ConnectWalletDetailsModal = () => {
chainName={chain?.name || "Unknown Network"}
onPress={onChangeNetworkPress}
/>
{chain?.testnet && chain?.faucets?.length ? (
<IconTextButton
mt="xs"
text="Request Testnet Funds"
icon={<MoneyIcon height={10} width={10} />}
onPress={() => {
if (chain?.faucets?.[0]) {
Linking.openURL(chain.faucets[0]);
}
}}
/>
) : null}
{getAdditionalActions()}
{addressCopied === true ? (
<Toast text={"Address copied to clipboard"} />
@@ -3,8 +3,7 @@ import BaseButton from "../base/BaseButton";
import { ChainIcon } from "../base/ChainIcon";
import Text from "../base/Text";
import { WalletIcon } from "../base/WalletIcon";
import { useWallet, useBalance } from "@thirdweb-dev/react-core";
import { useActiveChain } from "@thirdweb-dev/react-core/evm";
import { useWallet, useBalance, useChain } from "@thirdweb-dev/react-core";
import { StyleSheet } from "react-native";
import { LocalWallet } from "@thirdweb-dev/wallets";
import { useModalState } from "../../providers/ui-context-provider";
@@ -12,7 +11,7 @@ import Box from "../base/Box";
export type ConnectWalletDetailsProps = {
address: string;
detailsButton?: React.ReactElement;
detailsButton?: React.FC<{ onPress: () => void }>;
};
export const WalletDetailsButton = ({
@@ -20,7 +19,7 @@ export const WalletDetailsButton = ({
detailsButton,
}: ConnectWalletDetailsProps) => {
const activeWallet = useWallet();
const chain = useActiveChain();
const chain = useChain();
const balanceQuery = useBalance();
const { setModalState } = useModalState();
@@ -38,15 +37,15 @@ export const WalletDetailsButton = ({
return (
<>
<BaseButton
backgroundColor="background"
borderColor="border"
style={styles.walletDetails}
onPress={onPress}
>
{detailsButton ? (
detailsButton
) : (
{detailsButton ? (
detailsButton({ onPress })
) : (
<BaseButton
backgroundColor="background"
borderColor="border"
style={styles.walletDetails}
onPress={onPress}
>
<Box flex={1} flexDirection="row" justifyContent="space-between">
<ChainIcon size={32} chainIconUrl={chain?.icon?.url} />
<Box justifyContent="center" alignItems="flex-start">
@@ -70,8 +69,8 @@ export const WalletDetailsButton = ({
iconUri={activeWallet?.getMeta().iconURL || ""}
/>
</Box>
)}
</BaseButton>
</BaseButton>
)}
</>
);
};
-1
View File
@@ -17,7 +17,6 @@ export { localWallet } from "./wallets/wallets/local-wallet";
export { LocalWallet } from "./wallets/wallets/LocalWallet";
export { magicWallet } from "./wallets/wallets/magic-wallet";
export { MagicWallet } from "./wallets/wallets/MagicWallet";
export * from "./wallets/wallets/WalletConnectV1";
export * from "./wallets/wallets/WalletConnectV2";
export type { WCMeta } from "./wallets/types/wc";
@@ -1,52 +0,0 @@
import {
WalletOptions,
WalletConnectV1Options,
WalletConnectV1 as WalletConnectV1Wallets,
} from "@thirdweb-dev/wallets";
import { formatWalletConnectDisplayUri } from "../../utils/uri";
import { Linking } from "react-native";
import { ExtraCoreWalletOptions } from "@thirdweb-dev/react-core";
import { WCMeta } from "../types/wc";
import { createAsyncLocalStorage } from "../../../core/AsyncStorage";
export type WC1Options = Omit<
WalletOptions<WalletConnectV1Options>,
"qrcode" | "walletId"
> &
ExtraCoreWalletOptions & {
walletId: NonNullable<WalletOptions["walletId"]>;
};
export abstract class WalletConnectV1 extends WalletConnectV1Wallets {
constructor(options: WC1Options) {
super({
...options,
walletId: options.walletId,
walletStorage:
options.walletStorage || createAsyncLocalStorage(options.walletId),
qrcode: false,
});
this.on("open_wallet", this._onWCOpenWallet);
this.on("disconnect", () => {
this.removeListener("open_wallet", this._onWCOpenWallet);
});
}
abstract getMeta(): WCMeta;
_onWCOpenWallet(uri?: string) {
const links = this.getMeta().links;
if (uri) {
const fullUrl = formatWalletConnectDisplayUri(uri, links);
Linking.openURL(fullUrl);
} else {
const fullUrl = formatWalletConnectDisplayUri("", links);
Linking.openURL(fullUrl);
}
}
}
@@ -1,9 +1,9 @@
import { walletIds } from "@thirdweb-dev/wallets";
import { WalletOptions, WalletConfig } from "@thirdweb-dev/react-core";
import { WalletConnectV1 } from "./WalletConnectV1";
import { WalletConnectV2 } from "./WalletConnectV2";
import { WCMeta } from "../types/wc";
export class MetaMaskWallet extends WalletConnectV1 {
export class MetaMaskWallet extends WalletConnectV2 {
static id = walletIds.metamask;
static meta = {
name: "MetaMask",
@@ -20,11 +20,17 @@ export class MetaMaskWallet extends WalletConnectV1 {
}
}
export const metamaskWallet = () => {
type MetaMaskWalletConfig = { projectId?: string };
export const metamaskWallet = (config?: MetaMaskWalletConfig) => {
return {
id: MetaMaskWallet.id,
meta: MetaMaskWallet.meta,
create: (options: WalletOptions) =>
new MetaMaskWallet({ ...options, walletId: walletIds.metamask }),
new MetaMaskWallet({
...options,
walletId: walletIds.metamask,
projectId: config?.projectId,
}),
} satisfies WalletConfig;
};
@@ -2,9 +2,9 @@ import {
WalletOptions as WalletOptionsRC,
WalletConfig,
} from "@thirdweb-dev/react-core";
import { WalletConnectV1 } from "./WalletConnectV1";
import { WalletConnectV2 } from "./WalletConnectV2";
export class RainbowWallet extends WalletConnectV1 {
export class RainbowWallet extends WalletConnectV2 {
static id = "rainbow" as const;
static meta = {
name: "Rainbow",
@@ -21,11 +21,17 @@ export class RainbowWallet extends WalletConnectV1 {
}
}
export const rainbowWallet = () => {
type RainbowWalletConfig = { projectId?: string };
export const rainbowWallet = (config?: RainbowWalletConfig) => {
return {
id: RainbowWallet.id,
meta: RainbowWallet.meta,
create: (options: WalletOptionsRC) =>
new RainbowWallet({ ...options, walletId: RainbowWallet.id }),
new RainbowWallet({
...options,
walletId: RainbowWallet.id,
projectId: config?.projectId,
}),
} satisfies WalletConfig;
};
@@ -1,5 +1,5 @@
import { WalletOptions, WalletConfig } from "@thirdweb-dev/react-core";
import { WC2Options, WalletConnectV2 } from "./WalletConnectV2";
import { WalletConnectV2 } from "./WalletConnectV2";
import { WCMeta } from "../types/wc";
export class TrustWallet extends WalletConnectV2 {
@@ -14,10 +14,6 @@ export class TrustWallet extends WalletConnectV2 {
},
};
constructor(options: WC2Options) {
super(options);
}
getMeta(): WCMeta {
return TrustWallet.meta;
}
+65
View File
@@ -1,5 +1,70 @@
# @thirdweb-dev/react
## 3.14.4
### Patch Changes
- [#1215](https://github.com/thirdweb-dev/js/pull/1215) [`3c5dc480`](https://github.com/thirdweb-dev/js/commit/3c5dc4804abc56b933ec45e9e1da11eb182296cc) Thanks [@iketw](https://github.com/iketw)! - Migrate wallets to WCV2 and remove WCV1 support.
Please, check our changelogs for more info: https://blog.thirdweb.com/changelog/
- Updated dependencies [[`ef4cb092`](https://github.com/thirdweb-dev/js/commit/ef4cb092192c58c9b292b29590b888f45f9fd23d), [`06ca1c40`](https://github.com/thirdweb-dev/js/commit/06ca1c407dfee9d3d1fdaed1b6223ad0f9b8857b), [`620e89dc`](https://github.com/thirdweb-dev/js/commit/620e89dc25c91557e2164a602c7aedd733525087), [`ac3e019c`](https://github.com/thirdweb-dev/js/commit/ac3e019cd1776dbdb0d06b213420ad17586f678e), [`30ac3aef`](https://github.com/thirdweb-dev/js/commit/30ac3aef840bde51a61acf786c709cffd3c47354), [`3c5dc480`](https://github.com/thirdweb-dev/js/commit/3c5dc4804abc56b933ec45e9e1da11eb182296cc), [`ad7dae3b`](https://github.com/thirdweb-dev/js/commit/ad7dae3b163e61c7a6eb57f654885a7fdaa4cbb6), [`620e89dc`](https://github.com/thirdweb-dev/js/commit/620e89dc25c91557e2164a602c7aedd733525087), [`0fd8aa04`](https://github.com/thirdweb-dev/js/commit/0fd8aa04a9424497758d13a51a72363edcc30e19)]:
- @thirdweb-dev/sdk@3.10.26
- @thirdweb-dev/react-core@3.14.4
- @thirdweb-dev/wallets@1.0.0
- @thirdweb-dev/chains@0.1.25
## 3.14.3
### Patch Changes
- [#1238](https://github.com/thirdweb-dev/js/pull/1238) [`6e1fe6e5`](https://github.com/thirdweb-dev/js/commit/6e1fe6e582d4178884fc1793b330a9e477df5b4d) Thanks [@iketw](https://github.com/iketw)! - '[React] Adds Rainbow and Trust wallets hooks'
```javascript
import { useRainbowWallet, useTrustWallet } from "@thirdweb-dev/react";
const connectWithRainbow = useRainbowWallet();
const connectWithTrust = useTrustWallet();
```
- [#1237](https://github.com/thirdweb-dev/js/pull/1237) [`536b0f12`](https://github.com/thirdweb-dev/js/commit/536b0f1240ab446aac22cf547a4e09e73ee6bf7b) Thanks [@iketw](https://github.com/iketw)! - [React] Add Rainbow Wallet (implementing the WalletConnect wallet)
```javascript
import { ThirdwebProvider, rainbowWallet } from "@thirdweb-dev/react";
const activeChain = "ethereum";
<ThirdwebProvider
activeChain={activeChain}
supportedWallets={[rainbowWallet()]}
>
<App />
</ThirdwebProvider>;
```
- [#1236](https://github.com/thirdweb-dev/js/pull/1236) [`b626782b`](https://github.com/thirdweb-dev/js/commit/b626782b0e8c6b76673472d3aee1c802dfb11b5f) Thanks [@iketw](https://github.com/iketw)! - [Wallets/React] Add Trust Wallet
```javascript
import { ThirdwebProvider, trustWallet } from "@thirdweb-dev/react";
const activeChain = "mumbai";
<ThirdwebProvider
activeChain={activeChain}
autoSwitch={true}
supportedWallets={[trustWallet()]}
>
<App />
</ThirdwebProvider>;
```
- Updated dependencies [[`536b0f12`](https://github.com/thirdweb-dev/js/commit/536b0f1240ab446aac22cf547a4e09e73ee6bf7b), [`1d76334d`](https://github.com/thirdweb-dev/js/commit/1d76334dd3884703629835422f241d2825128f6f), [`b626782b`](https://github.com/thirdweb-dev/js/commit/b626782b0e8c6b76673472d3aee1c802dfb11b5f), [`d498c79a`](https://github.com/thirdweb-dev/js/commit/d498c79a911d478077dfb8a2490eb1bf91523186), [`9af346ee`](https://github.com/thirdweb-dev/js/commit/9af346eeada1037be27c8d3c9e1777f7be11a8ea)]:
- @thirdweb-dev/[email protected]
- @thirdweb-dev/[email protected]
- @thirdweb-dev/[email protected]
- @thirdweb-dev/[email protected]
## 3.14.2
### Patch Changes
+1 -5
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/react",
"version": "3.14.2",
"version": "3.14.4",
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/react",
"author": "thirdweb eng <[email protected]>",
"license": "Apache-2.0",
@@ -153,10 +153,6 @@
"react": ">=18.0.0",
"react-dom": ">=18.0.0"
},
"resolutions": {
"ansi-regex": "^6.0.1",
"typescript": "^4.6.2"
},
"peerDependenciesMeta": {
"@solana/wallet-adapter-base": {
"optional": true
@@ -0,0 +1,15 @@
import { useConnect } from "@thirdweb-dev/react-core";
import { useCallback } from "react";
export function useRainbowWallet() {
const connect = useConnect();
return useCallback(
async (connectOptions?: { chainId?: number }) => {
const { rainbowWallet } = await import(
"../../../wallet/wallets/rainbow/RainbowWallet"
);
return connect(rainbowWallet(), connectOptions);
},
[connect],
);
}
@@ -0,0 +1,15 @@
import { useConnect } from "@thirdweb-dev/react-core";
import { useCallback } from "react";
export function useTrustWallet() {
const connect = useConnect();
return useCallback(
async (connectOptions?: { chainId?: number }) => {
const { trustWallet } = await import(
"../../../wallet/wallets/trustWallet/TrustWallet"
);
return connect(trustWallet(), connectOptions);
},
[connect],
);
}
+2
View File
@@ -20,6 +20,8 @@ export { ThirdwebProvider } from "./providers/thirdweb-provider";
export { useInstalledWallets } from "../wallet/hooks/useInstalledWallets";
// wallet connection hooks
export { useRainbowWallet } from "./hooks/wallets/useRainbowWallet";
export { useTrustWallet } from "./hooks/wallets/useTrustWallet";
export { useMetamask } from "./hooks/wallets/useMetamask";
export { useCoinbaseWallet } from "./hooks/wallets/useCoinbaseWallet";
export { useFrameWallet } from "./hooks/wallets/useFrame";
@@ -18,15 +18,15 @@ interface ThirdwebProviderProps<TChains extends Chain[]>
"createWalletStorage" | "supportedWallets"
> {
/**
* Wallets that will be supported by the dApp
* @defaultValue [metamaskWallet(), coinbaseWallet(), walletConnectV1()]
* Wallets supported by the dApp
* @defaultValue `[ metamaskWallet(), coinbaseWallet(), walletConnect() ]`
*
* @example
* ```jsx
* import { metamaskWallet, coinbaseWallet, walletConnectV1 } from "@thirdweb-dev/react";
* import { metamaskWallet, coinbaseWallet, walletConnect } from "@thirdweb-dev/react";
*
* <ThirdwebProvider
* supportedWallets={[metamaskWallet(), coinbaseWallet(), walletConnectV1()]}
* supportedWallets={[metamaskWallet(), coinbaseWallet(), walletConnect()]}
* />
* ```
*/
+2
View File
@@ -4,7 +4,9 @@ export { coinbaseWallet } from "./wallet/wallets/coinbase/coinbaseWallet";
export { frameWallet } from "./wallet/wallets/frame/frameWallet";
export { metamaskWallet } from "./wallet/wallets/metamask/metamaskWallet";
export { paperWallet } from "./wallet/wallets/paperWallet";
export { rainbowWallet } from "./wallet/wallets/rainbow/RainbowWallet";
export { safeWallet } from "./wallet/wallets/safe/safeWallet";
export { trustWallet } from "./wallet/wallets/trustWallet/TrustWallet";
export { walletConnect } from "./wallet/wallets/walletConnect";
export { walletConnectV1 } from "./wallet/wallets/walletConnectV1";
export { smartWallet } from "./wallet/wallets/smartWallet/smartWallet";
@@ -385,6 +385,17 @@ const NetworkList = /* @__PURE__ */ memo(function NetworkList(props: {
const [switchingChainId, setSwitchingChainId] = useState(-1);
const [errorSwitchingChainId, setErrorSwitchingChainId] = useState(-1);
const close = props.close;
useEffect(() => {
// if switching and switched successfully - close modal
if (switchingChainId !== -1 && activeChainId === switchingChainId) {
if (close) {
close();
}
}
}, [switchingChainId, close, activeChainId]);
const handleSwitch = async (chain: Chain) => {
setErrorSwitchingChainId(-1);
setSwitchingChainId(chain.chainId);
@@ -3,6 +3,7 @@ import { assertWindowEthereum } from "@thirdweb-dev/wallets";
export function useInstalledWallets() {
let isMetamaskInstalled = false;
let isCoinbaseWalletInstalled = false;
let isTrustWalletInstalled: boolean | undefined = false;
if (assertWindowEthereum(globalThis.window)) {
isMetamaskInstalled = globalThis.window.ethereum?.isMetaMask;
@@ -10,11 +11,13 @@ export function useInstalledWallets() {
globalThis.window.ethereum?.isCoinbaseWallet ||
globalThis.window.ethereum?.providers?.some((p) => p.isCoinbaseWallet) ||
false;
isTrustWalletInstalled = globalThis.window.ethereum?.isTrust;
}
const installedWallets = {
metamask: !!isMetamaskInstalled,
coinbaseWallet: !!isCoinbaseWalletInstalled,
trustWallet: !!isTrustWalletInstalled,
};
return installedWallets;
@@ -1,10 +1,10 @@
import { WalletConfig } from "@thirdweb-dev/react-core";
import { coinbaseWallet } from "./coinbase/coinbaseWallet";
import { metamaskWallet } from "./metamask/metamaskWallet";
import { walletConnectV1 } from "./walletConnectV1";
import { walletConnect } from "./walletConnect";
export const defaultWallets: WalletConfig<any>[] = /* @__PURE__ */ (() => [
metamaskWallet(),
coinbaseWallet(),
walletConnectV1(),
walletConnect(),
])();
@@ -2,12 +2,28 @@ import type { WalletOptions, WalletConfig } from "@thirdweb-dev/react-core";
import { MetaMaskWallet, assertWindowEthereum } from "@thirdweb-dev/wallets";
import { MetamaskConnectUI } from "./MetamaskConnectUI";
export const metamaskWallet = (): WalletConfig<MetaMaskWallet> => {
type MetamaskWalletOptions = {
/**
* When connecting MetaMask using the QR Code - Wallet Connect connector is used which requires a project id.
* This project id is Your project’s unique identifier for wallet connect that can be obtained at cloud.walletconnect.com.
*
* https://docs.walletconnect.com/2.0/web3modal/options#projectid-required
*/
projectId?: string;
};
export const metamaskWallet = (
options?: MetamaskWalletOptions,
): WalletConfig<MetaMaskWallet> => {
return {
id: MetaMaskWallet.id,
meta: MetaMaskWallet.meta,
create: (options: WalletOptions) => {
return new MetaMaskWallet({ ...options, qrcode: false });
create: (walletOptions: WalletOptions) => {
return new MetaMaskWallet({
...walletOptions,
projectId: options?.projectId,
qrcode: false,
});
},
connectUI: MetamaskConnectUI,
isInstalled() {
@@ -0,0 +1,74 @@
import { ConnectUIProps, useConnect } from "@thirdweb-dev/react-core";
import { WalletConnect } from "@thirdweb-dev/wallets";
import { useEffect, useRef, useState } from "react";
import { isMobile } from "../../../evm/utils/isMobile";
import { ConnectingScreen } from "../../ConnectWallet/screens/ConnectingScreen";
import { GetStartedScreen } from "../../ConnectWallet/screens/GetStartedScreen";
import { RainbowScan } from "./RainbowScan";
export const RainbowConnectUI = (props: ConnectUIProps<WalletConnect>) => {
const [screen, setScreen] = useState<
"connecting" | "scanning" | "get-started"
>("connecting");
const { walletConfig, close } = props;
const connect = useConnect();
const { goBack } = props;
const connectPrompted = useRef(false);
useEffect(() => {
if (connectPrompted.current) {
return;
}
// if loading
(async () => {
// on mobile, open rainbow app link
if (isMobile()) {
window.open("https://rnbwapp.com");
} else {
// on desktop, show the rainbow scan qr code
setScreen("scanning");
}
})();
}, [walletConfig, close, connect, goBack]);
if (screen === "connecting") {
return (
<ConnectingScreen
onBack={props.goBack}
walletName={walletConfig.meta.name}
walletIconURL={walletConfig.meta.iconURL}
supportLink="https://rainbow.me/"
/>
);
}
if (screen === "get-started") {
return (
<GetStartedScreen
walletIconURL={walletConfig.meta.iconURL}
walletName={walletConfig.meta.name}
chromeExtensionLink={walletConfig.meta.urls?.chrome}
googlePlayStoreLink={walletConfig.meta.urls?.android}
appleStoreLink={walletConfig.meta.urls?.ios}
onBack={props.goBack}
/>
);
}
if (screen === "scanning") {
return (
<RainbowScan
onBack={props.goBack}
onConnected={close}
onGetStarted={() => {
setScreen("get-started");
}}
walletConfig={walletConfig}
/>
);
}
return null;
};
@@ -0,0 +1,59 @@
import { ScanScreen } from "../../ConnectWallet/screens/ScanScreen";
import {
useCreateWalletInstance,
useWalletContext,
} from "@thirdweb-dev/react-core";
import { useEffect, useRef, useState } from "react";
import type { WalletConnect } from "@thirdweb-dev/wallets";
import type { WalletConfig } from "@thirdweb-dev/react-core";
export const RainbowScan: React.FC<{
onBack: () => void;
onGetStarted: () => void;
onConnected: () => void;
walletConfig: WalletConfig<WalletConnect>;
}> = ({ onBack, onConnected, onGetStarted, walletConfig }) => {
const createInstance = useCreateWalletInstance();
const [qrCodeUri, setQrCodeUri] = useState<string | undefined>();
const { setConnectedWallet, chainToConnect, setConnectionStatus } =
useWalletContext();
const scanStarted = useRef(false);
useEffect(() => {
if (scanStarted.current) {
return;
}
scanStarted.current = true;
const rainbow = createInstance(walletConfig);
setConnectionStatus("connecting");
rainbow.connectWithQrCode({
chainId: chainToConnect?.chainId,
onQrCodeUri(uri) {
setQrCodeUri(uri);
},
onConnected() {
setConnectedWallet(rainbow);
onConnected();
},
});
}, [
createInstance,
setConnectedWallet,
chainToConnect,
onConnected,
walletConfig,
setConnectionStatus,
]);
return (
<ScanScreen
onBack={onBack}
onGetStarted={onGetStarted}
qrCodeUri={qrCodeUri}
walletName={walletConfig.meta.name}
walletIconURL={walletConfig.meta.iconURL}
/>
);
};
@@ -0,0 +1,38 @@
import type { WalletOptions, WalletConfig } from "@thirdweb-dev/react-core";
import { WalletConnect } from "@thirdweb-dev/wallets";
import { RainbowConnectUI } from "./RainbowConnectUI";
type RainbowWalletOptions = {
/**
* When connecting Rainbow using the QR Code - Wallet Connect connector is used which requires a project id.
* This project id is Your project’s unique identifier for wallet connect that can be obtained at cloud.walletconnect.com.
*
* https://docs.walletconnect.com/2.0/web3modal/options#projectid-required
*/
projectId?: string;
};
export const rainbowWallet = (
options?: RainbowWalletOptions,
): WalletConfig<WalletConnect> => {
return {
id: "rainbow",
meta: {
name: "Rainbow Wallet",
iconURL:
"ipfs://QmSZn47p4DVVBfzvg9BAX2EqwnPxkT1YAE7rUnrtd9CybQ/rainbow-logo.png",
},
create: (walletOptions: WalletOptions) => {
return new WalletConnect({
...walletOptions,
walletId: "rainbow",
projectId: options?.projectId,
qrcode: false,
});
},
connectUI: RainbowConnectUI,
isInstalled() {
return false;
},
};
};
@@ -0,0 +1,92 @@
import { ConnectUIProps, useConnect } from "@thirdweb-dev/react-core";
import { TrustWallet } from "@thirdweb-dev/wallets";
import { useEffect, useRef, useState } from "react";
import { isMobile } from "../../../evm/utils/isMobile";
import { ConnectingScreen } from "../../ConnectWallet/screens/ConnectingScreen";
import { GetStartedScreen } from "../../ConnectWallet/screens/GetStartedScreen";
import { TrustScan } from "./TrustScan";
export const TrustConnectUI = (props: ConnectUIProps<TrustWallet>) => {
const [screen, setScreen] = useState<
"connecting" | "scanning" | "get-started"
>("connecting");
const { walletConfig, close } = props;
const connect = useConnect();
const { goBack } = props;
const connectPrompted = useRef(false);
useEffect(() => {
if (connectPrompted.current) {
return;
}
const isInstalled = walletConfig.isInstalled
? walletConfig.isInstalled()
: false;
// if loading
(async () => {
if (isInstalled) {
try {
connectPrompted.current = true;
setScreen("connecting");
await connect(walletConfig);
close();
} catch (e) {
goBack();
}
}
// if trust is not injected
else {
// on mobile, open trust app link
if (isMobile()) {
window.open("https://link.trustwallet.com");
} else {
// on desktop, show the trust scan qr code
setScreen("scanning");
}
}
})();
}, [walletConfig, close, connect, goBack]);
if (screen === "connecting") {
return (
<ConnectingScreen
onBack={props.goBack}
walletName={walletConfig.meta.name}
walletIconURL={walletConfig.meta.iconURL}
supportLink="https://community.trustwallet.com/c/helpcenter/8"
/>
);
}
if (screen === "get-started") {
return (
<GetStartedScreen
walletIconURL={walletConfig.meta.iconURL}
walletName={walletConfig.meta.name}
chromeExtensionLink={walletConfig.meta.urls?.chrome}
googlePlayStoreLink={walletConfig.meta.urls?.android}
appleStoreLink={walletConfig.meta.urls?.ios}
onBack={props.goBack}
/>
);
}
if (screen === "scanning") {
return (
<TrustScan
onBack={props.goBack}
onConnected={close}
onGetStarted={() => {
setScreen("get-started");
}}
walletConfig={walletConfig}
/>
);
}
return null;
};
@@ -0,0 +1,59 @@
import { ScanScreen } from "../../ConnectWallet/screens/ScanScreen";
import {
useCreateWalletInstance,
useWalletContext,
} from "@thirdweb-dev/react-core";
import { useEffect, useRef, useState } from "react";
import type { TrustWallet } from "@thirdweb-dev/wallets";
import type { WalletConfig } from "@thirdweb-dev/react-core";
export const TrustScan: React.FC<{
onBack: () => void;
onGetStarted: () => void;
onConnected: () => void;
walletConfig: WalletConfig<TrustWallet>;
}> = ({ onBack, onConnected, onGetStarted, walletConfig }) => {
const createInstance = useCreateWalletInstance();
const [qrCodeUri, setQrCodeUri] = useState<string | undefined>();
const { setConnectedWallet, chainToConnect, setConnectionStatus } =
useWalletContext();
const scanStarted = useRef(false);
useEffect(() => {
if (scanStarted.current) {
return;
}
scanStarted.current = true;
const trust = createInstance(walletConfig);
setConnectionStatus("connecting");
trust.connectWithQrCode({
chainId: chainToConnect?.chainId,
onQrCodeUri(uri) {
setQrCodeUri(uri);
},
onConnected() {
setConnectedWallet(trust);
onConnected();
},
});
}, [
createInstance,
setConnectedWallet,
chainToConnect,
onConnected,
walletConfig,
setConnectionStatus,
]);
return (
<ScanScreen
onBack={onBack}
onGetStarted={onGetStarted}
qrCodeUri={qrCodeUri}
walletName={walletConfig.meta.name}
walletIconURL={walletConfig.meta.iconURL}
/>
);
};
@@ -0,0 +1,36 @@
import type { WalletOptions, WalletConfig } from "@thirdweb-dev/react-core";
import { TrustWallet, assertWindowEthereum } from "@thirdweb-dev/wallets";
import { TrustConnectUI } from "./TrustConnectUI";
type TrustWalletOptions = {
/**
* When connecting Trust using the QR Code - Wallet Connect connector is used which requires a project id.
* This project id is Your project’s unique identifier for wallet connect that can be obtained at cloud.walletconnect.com.
*
* https://docs.walletconnect.com/2.0/web3modal/options#projectid-required
*/
projectId?: string;
};
export const trustWallet = (
options?: TrustWalletOptions,
): WalletConfig<TrustWallet> => {
return {
id: TrustWallet.id,
meta: TrustWallet.meta,
create: (walletOptions: WalletOptions) => {
return new TrustWallet({
...walletOptions,
projectId: options?.projectId,
qrcode: false,
});
},
connectUI: TrustConnectUI,
isInstalled() {
if (assertWindowEthereum(globalThis.window)) {
return !!globalThis.window.ethereum.isTrust;
}
return false;
},
};
};
@@ -1,12 +1,9 @@
import { WalletConnectV1 } from "@thirdweb-dev/wallets";
import { WalletConfig, WalletOptions } from "@thirdweb-dev/react-core";
import { walletConnect } from "./walletConnect";
export const walletConnectV1 = (): WalletConfig<WalletConnectV1> => {
return {
id: WalletConnectV1.id,
meta: WalletConnectV1.meta,
create(options: WalletOptions) {
return new WalletConnectV1({ ...options, qrcode: true });
},
};
};
/**
* @deprecated Use `walletConnect` instead
*
* The WalletConnect v1.0 protocol has been shut down and no longer works.
* To avoid breaking change, `walletConnectV1` is still available but is an alias of `walletConnect`.
*/
export const walletConnectV1 = walletConnect;
@@ -2,12 +2,28 @@ import type { WalletOptions, WalletConfig } from "@thirdweb-dev/react-core";
import { ZerionWallet, assertWindowEthereum } from "@thirdweb-dev/wallets";
import { ZerionConnectUI } from "./ZerionConnectUI";
export const zerionWallet = (): WalletConfig<ZerionWallet> => {
type ZerionkWalletOptions = {
/**
* When connecting MetaMask using the QR Code - Wallet Connect connector is used which requires a project id.
* This project id is Your project’s unique identifier for wallet connect that can be obtained at cloud.walletconnect.com.
*
* https://docs.walletconnect.com/2.0/web3modal/options#projectid-required
*/
projectId?: string;
};
export const zerionWallet = (
options?: ZerionkWalletOptions,
): WalletConfig<ZerionWallet> => {
return {
id: ZerionWallet.id,
meta: ZerionWallet.meta,
create: (options: WalletOptions) => {
return new ZerionWallet(options);
create: (walletOptions: WalletOptions) => {
return new ZerionWallet({
...walletOptions,
projectId: options?.projectId,
qrcode: false,
});
},
connectUI: ZerionConnectUI,
isInstalled() {
+1
View File
@@ -24,6 +24,7 @@ module.exports = {
files: "./test/**/*",
rules: {
"@typescript-eslint/no-restricted-imports": "off",
"better-tree-shaking/no-top-level-side-effects": "off",
},
},
],
+24
View File
@@ -1,5 +1,29 @@
# @thirdweb-dev/sdk
## 3.10.26
### Patch Changes
- [#1249](https://github.com/thirdweb-dev/js/pull/1249) [`ef4cb092`](https://github.com/thirdweb-dev/js/commit/ef4cb092192c58c9b292b29590b888f45f9fd23d) Thanks [@kumaryash90](https://github.com/kumaryash90)! - Update EIP155 error substrings for any evm deployments
- [#1254](https://github.com/thirdweb-dev/js/pull/1254) [`30ac3aef`](https://github.com/thirdweb-dev/js/commit/30ac3aef840bde51a61acf786c709cffd3c47354) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Fetch base64 NFTs
- Updated dependencies [[`620e89dc`](https://github.com/thirdweb-dev/js/commit/620e89dc25c91557e2164a602c7aedd733525087), [`0fd8aa04`](https://github.com/thirdweb-dev/js/commit/0fd8aa04a9424497758d13a51a72363edcc30e19)]:
- @thirdweb-dev/chains@0.1.25
## 3.10.25
### Patch Changes
- [#1233](https://github.com/thirdweb-dev/js/pull/1233) [`d498c79a`](https://github.com/thirdweb-dev/js/commit/d498c79a911d478077dfb8a2490eb1bf91523186) Thanks [@nkrishang](https://github.com/nkrishang)! - Fix smartWalletFactory.getAllEvents
- [#1241](https://github.com/thirdweb-dev/js/pull/1241) [`9af346ee`](https://github.com/thirdweb-dev/js/commit/9af346eeada1037be27c8d3c9e1777f7be11a8ea) Thanks [@nkrishang](https://github.com/nkrishang)! - Use fn getAllAccounts instead of event logs AccountCreated
- Updated dependencies [[`0c5b03b8`](https://github.com/thirdweb-dev/js/commit/0c5b03b8d6cc6a4e69bb2a4647d3626e69f1283c), [`9af346ee`](https://github.com/thirdweb-dev/js/commit/9af346eeada1037be27c8d3c9e1777f7be11a8ea)]:
- @thirdweb-dev/storage@1.1.5
- @thirdweb-dev/contracts-js@1.3.7
- @thirdweb-dev/chains@0.1.24
## 3.10.24
### Patch Changes
+1 -7
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/sdk",
"version": "3.10.24",
"version": "3.10.26",
"description": "The main thirdweb SDK.",
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/sdk",
"license": "Apache-2.0",
@@ -197,12 +197,6 @@
"yaml": "^2.1.1",
"zod": "^3.20.2"
},
"resolutions": {
"bn.js": "^5.2.1",
"nanoid": "^3.1.31",
"typescript": "^4.6.2",
"@microsoft/tsdoc": "^0.14.1"
},
"bugs": {
"url": "https://github.com/thirdweb-dev/js/issues"
},
@@ -10,6 +10,8 @@ export const ERROR_SUBSTRINGS = [
"recovered sender mismatch",
"transaction hash mismatch",
"chainid no support",
"chainid (0)",
"chainid(0)",
];
export const ERROR_SUBSTRINGS_COMPOSITE = [
+16
View File
@@ -46,6 +46,22 @@ export async function fetchTokenMetadata(
tokenUri: string,
storage: ThirdwebStorage,
): Promise<NFTMetadata> {
// check for base64 encoded JSON
if (
tokenUri.startsWith("data:application/json;base64") &&
typeof Buffer !== "undefined"
) {
const base64 = tokenUri.split(",")[1];
const jsonMetadata = JSON.parse(
Buffer.from(base64, "base64").toString("utf-8"),
);
return CommonNFTOutput.parse({
...jsonMetadata,
id: BigNumber.from(tokenId).toString(),
uri: tokenUri,
});
}
// handle dynamic id URIs (2 possible formats)
const parsedUri = tokenUri.replace(
"{id}",
utils.hexZeroPad(BigNumber.from(tokenId).toHexString(), 32).slice(2),
+2 -2
View File
@@ -669,7 +669,7 @@ export class Erc721<
* @param quantity - Quantity of the tokens you want to claim
*
* @returns - an array of results containing the id of the token claimed, the transaction receipt and a promise to optionally fetch the nft metadata
* @twfeature ERC721ClaimCustom | ERC721ClaimPhasesV2 | ERC721ClaimPhasesV1 | ERC721ClaimConditionsV2 | ERC721ClaimConditionsV1
* @twfeature ERC721ClaimCustom | ERC721ClaimPhasesV2 | ERC721ClaimPhasesV1 | ERC721ClaimConditionsV2 | ERC721ClaimConditionsV1 | ERC721ClaimZora
*/
claim = /* @__PURE__ */ buildTransactionFunction(
async (quantity: BigNumberish, options?: ClaimOptions) => {
@@ -701,7 +701,7 @@ export class Erc721<
* @param quantity - Quantity of the tokens you want to claim
* @param options
* @returns - an array of results containing the id of the token claimed, the transaction receipt and a promise to optionally fetch the nft metadata
* @twfeature ERC721ClaimCustom | ERC721ClaimPhasesV2 | ERC721ClaimPhasesV1 | ERC721ClaimConditionsV2 | ERC721ClaimConditionsV1
* @twfeature ERC721ClaimCustom | ERC721ClaimPhasesV2 | ERC721ClaimPhasesV1 | ERC721ClaimConditionsV2 | ERC721ClaimConditionsV1 | ERC721ClaimZora
*/
claimTo = /* @__PURE__ */ buildTransactionFunction(
async (
@@ -103,17 +103,22 @@ export class SmartWalletFactory<TContract extends IAccountFactory>
*
* @twfeature SmartWalletFactory
*/
public async getAllWallets(): Promise<AccountEvent[]> {
const filter = {
fromBlock: 0,
toBlock: "latest",
};
public async getAllAccounts(): Promise<AccountEvent[]> {
const allAccounts =
await this.contractWrapper.readContract.getAllAccounts();
const events = await this.events.getEvents("AccountCreated", filter);
/**
* Note: an account can have multiple admins. In this function, we only return the first signer associated with
* the account. This should be the admin that created the account, unless this admin has lost their admin status.
*/
return await Promise.all(
allAccounts.map(async (account) => {
const assosiatedSigners = await this.getAssociatedSigners(account);
const admin = assosiatedSigners[0];
return events.map((event) => {
return { account: event.data.account, admin: event.data.accountAdmin };
});
return { account, admin };
}),
);
}
/**
+254 -16
View File
@@ -18,13 +18,14 @@ import {
SignerWithRestrictions,
AccessRestrictionsInput,
AccessRestrictionsSchema,
SignerWithRestrictionsBatchInput,
} from "../../types";
import invariant from "tiny-invariant";
import { buildTransactionFunction } from "../../common/transactions";
import { SmartWalletFactory } from "./smart-wallet-factory";
import { resolveOrGenerateId } from "../../common/signature-minting";
import { AddressOrEns } from "../../schema";
import { resolveAddress } from "../../common";
import { Signer } from "ethers";
export class SmartWallet<TContract extends IAccountCore>
implements DetectableFeature
@@ -47,6 +48,20 @@ export class SmartWallet<TContract extends IAccountCore>
* HELPER FUNCTIONS
********************************/
private hasDuplicateSigners(
signers: SignerWithRestrictionsBatchInput,
): boolean {
const encounteredSigners = new Set();
return signers
.map((item) => item.signer)
.some((signer) => {
const isDuplicate = encounteredSigners.has(signer);
encounteredSigners.add(signer);
return isDuplicate;
});
}
/**
* Format the access restrictions for a given role
*
@@ -127,7 +142,7 @@ export class SmartWallet<TContract extends IAccountCore>
* @returns Returns the address of the factory
*
*/
private async getFactory(): Promise<SmartWalletFactory<IAccountFactory>> {
private async getFactory(): Promise<ContractWrapper<IAccountFactory>> {
// Get factory.
const chainId = await this.contractWrapper.getChainID();
const factoryAddress = await this.getFactoryAddress();
@@ -137,13 +152,33 @@ export class SmartWallet<TContract extends IAccountCore>
IAccountFactoryAbi,
this.contractWrapper.options,
);
return new SmartWalletFactory(wrapper);
wrapper.updateSignerOrProvider(this.contractWrapper.getSigner() as Signer);
return wrapper;
}
/*********************************
* READ FUNCTIONS
********************************/
/**
* Get whether a signer is an admin on the smart wallet.
*
* @example
* ```javascript
* const isAdmin = await contract.smartWallet.isAdmin(signer);
* ```
* @param signer - The address of a signer of the smart wallet.
* @returns whether a signer is an admin on the smart wallet.
*
* @twfeature SmartWallet
*/
public async isAdmin(signerAddress: AddressOrEns): Promise<boolean> {
const resolvedSignerAddress = await resolveAddress(signerAddress);
return await this.contractWrapper.readContract.isAdmin(
resolvedSignerAddress,
);
}
/**
* Get the restrictions under which a given signer can use the smart wallet.
*
@@ -168,7 +203,8 @@ export class SmartWallet<TContract extends IAccountCore>
}
/**
* Get the address of the EIP-4337 factory contract which deployed the smart wallet
* Get the deployer of the smart wallet
* @remarks Get the address of the EIP-4337 factory contract which deployed the smart wallet
*
* @example
* ```javascript
@@ -183,7 +219,7 @@ export class SmartWallet<TContract extends IAccountCore>
}
/**
* Get all signers (admin or non-admin) of the smart wallet, along with their access restrictions.
* Get all signers with their access restrictions
*
* @example
* ```javascript
@@ -195,8 +231,8 @@ export class SmartWallet<TContract extends IAccountCore>
*/
public async getSignersWithRestrictions(): Promise<SignerWithRestrictions[]> {
// Get all associated signers.
const factory = await this.getFactory();
const signers: string[] = await factory.getAssociatedSigners(
const contract = await this.getFactory();
const signers: string[] = await contract.readContract.getSignersOfAccount(
this.getAddress(),
);
@@ -214,7 +250,7 @@ export class SmartWallet<TContract extends IAccountCore>
********************************/
/**
* Grant an address admin access to the smart wallet.
* Grant an address admin access to the smart wallet
*
* @remarks Grants an address admin access to the smart wallet. The admin will have complete authority over the smart wallet.
*
@@ -240,7 +276,7 @@ export class SmartWallet<TContract extends IAccountCore>
);
/**
* Revoke an address' admin access to the smart wallet.
* Revoke an address' admin access to the smart wallet
*
* @remarks Revokes an address' admin access to the smart wallet.
*
@@ -266,7 +302,7 @@ export class SmartWallet<TContract extends IAccountCore>
);
/**
* Grant an address access to the smart wallet with certain restrictions.
* Grant an address access to the smart wallet with restrictions
*
* @remarks Grants an address access to the smart wallet with certain restrictions.
*
@@ -312,8 +348,9 @@ export class SmartWallet<TContract extends IAccountCore>
const roleRestrictions: IAccountPermissions.RoleRestrictionsStruct = {
role,
approvedTargets: parsedRestrictions.approvedCallTargets,
maxValuePerTransaction:
maxValuePerTransaction: utils.parseEther(
parsedRestrictions.nativeTokenLimitPerTransaction,
),
startTimestamp: parsedRestrictions.startDate,
endTimestamp: parsedRestrictions.expirationDate,
};
@@ -358,7 +395,7 @@ 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 smart wallet
*
* @remarks Approves an address as a call target for a given signer on the smart wallet.
*
@@ -407,7 +444,7 @@ 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 smart wallet
*
* @remarks Disapprove an address as a call target for a given signer on the smart wallet.
*
@@ -459,7 +496,7 @@ export class SmartWallet<TContract extends IAccountCore>
);
/**
* Update an address' access to the smart wallet.
* Update the restrictions of an address for using the smart wallet
*
* @remarks Updates an address' access to the smart wallet.
*
@@ -497,8 +534,9 @@ export class SmartWallet<TContract extends IAccountCore>
const roleRestrictions: IAccountPermissions.RoleRestrictionsStruct = {
role: currentRole,
approvedTargets: parsedRestrictions.approvedCallTargets,
maxValuePerTransaction:
maxValuePerTransaction: utils.parseEther(
parsedRestrictions.nativeTokenLimitPerTransaction,
),
startTimestamp: parsedRestrictions.startDate,
endTimestamp: parsedRestrictions.expirationDate,
};
@@ -512,7 +550,7 @@ export class SmartWallet<TContract extends IAccountCore>
);
/**
* Revoke an address' access to the smart wallet.
* Revoke a scoped access address to the smart wallet
*
* @remarks Revokes an address' access to the smart wallet.
*
@@ -560,4 +598,204 @@ export class SmartWallet<TContract extends IAccountCore>
});
},
);
/**
* Set the wallet's entire snapshot of permissions
*
* @remarks Sets the wallet's entire snapshot of permissions.
*
* @param permissionsSnapshot - the snapshot to set as the wallet's entire permission snapshot.
*
* @example
* ```javascript
* const tx = await contract.smartWallet.setAccess(permissionsSnapshot);
* const receipt = tx.receipt();
* ```
*
* @twfeature SmartWallet
*/
setAccess = /* @__PURE__ */ buildTransactionFunction(
async (
permissionsSnapshot: SignerWithRestrictionsBatchInput,
): Promise<Transaction> => {
/**
* All cases
*
* - Add new admin :check:
* - Remove current admin :check:
* - Add new scoped :check:
* - Remove current scoped :check:
* - Update current scoped :check:
* - Current admin -> new scoped :check:
* - Current scoped -> new admin :check:
**/
// No duplicate signers in input!
if (this.hasDuplicateSigners(permissionsSnapshot)) {
throw new Error("Duplicate signers found in input.");
}
const currentPermissionsSnapshot =
await this.getSignersWithRestrictions();
// Performing a multicall.
const encoded: string[] = [];
// First make all calls related to admin access.
const allCurrentAdmins = currentPermissionsSnapshot
.filter((result) => result.isAdmin)
.map((result) => result.signer);
const allNewAdmins = permissionsSnapshot
.filter((result) => result.isAdmin)
.map((result) => result.signer);
// All remove-admin actions.
for (const currentAdmin of allCurrentAdmins) {
if (!allNewAdmins.includes(currentAdmin)) {
encoded.push(
this.contractWrapper.readContract.interface.encodeFunctionData(
"setAdmin",
[currentAdmin, false],
),
);
}
}
// All add-admin actions.
const toRemoveAsScoped: Record<string, string> = {};
for (const newAdmin of allNewAdmins) {
if (!allCurrentAdmins.includes(newAdmin)) {
const data =
this.contractWrapper.readContract.interface.encodeFunctionData(
"setAdmin",
[newAdmin, true],
);
// If the new admin is already a scoped account, we need to remove them as a scoped account first.
const currentRole = (
await this.contractWrapper.readContract.getRoleRestrictionsForAccount(
newAdmin,
)
).role;
if (currentRole === this.emptyRole) {
encoded.push(data);
} else {
toRemoveAsScoped[newAdmin] = data;
}
}
}
// All scoped actions.
const allCurrentScoped = currentPermissionsSnapshot
.filter((result) => !result.isAdmin)
.map((result) => result.signer);
const allNewScoped = permissionsSnapshot
.filter((result) => !result.isAdmin)
.map((result) => result.signer);
// All remove-scoped actions.
const newAdminsToRemoveAsScoped = Object.keys(toRemoveAsScoped);
for (const currentScoped of allCurrentScoped) {
if (!allNewScoped.includes(currentScoped)) {
const { payload, signature } = await this.generatePayload(
currentScoped,
RoleAction.REVOKE,
);
encoded.push(
this.contractWrapper.readContract.interface.encodeFunctionData(
"changeRole",
[payload, signature],
),
);
if (newAdminsToRemoveAsScoped.includes(currentScoped)) {
encoded.push(toRemoveAsScoped[currentScoped]);
}
}
}
// All add-scoped actions.
for (const newScoped of allNewScoped) {
if (!allCurrentScoped.includes(newScoped)) {
// Derive role for target signer.
const role = utils.solidityKeccak256(["string"], [newScoped]);
const parsedRestrictions = await AccessRestrictionsSchema.parseAsync(
permissionsSnapshot.find((result) => result.signer === newScoped)
?.restrictions,
);
// Get role restrictions struct.
const roleRestrictions: IAccountPermissions.RoleRestrictionsStruct = {
role,
approvedTargets: parsedRestrictions.approvedCallTargets,
maxValuePerTransaction: utils.parseEther(
parsedRestrictions.nativeTokenLimitPerTransaction,
),
startTimestamp: parsedRestrictions.startDate,
endTimestamp: parsedRestrictions.expirationDate,
};
encoded.push(
this.contractWrapper.readContract.interface.encodeFunctionData(
"setRoleRestrictions",
[roleRestrictions],
),
);
const { payload, signature } = await this.generatePayload(
newScoped,
RoleAction.GRANT,
);
encoded.push(
this.contractWrapper.readContract.interface.encodeFunctionData(
"changeRole",
[payload, signature],
),
);
}
}
// All update-scoped actions.
for (const currentScoped of allCurrentScoped) {
if (allNewScoped.includes(currentScoped)) {
// Derive role for target signer.
const role = utils.solidityKeccak256(["string"], [currentScoped]);
const parsedRestrictions = await AccessRestrictionsSchema.parseAsync(
permissionsSnapshot.find(
(result) => result.signer === currentScoped,
)?.restrictions,
);
// Get role restrictions struct.
const roleRestrictions: IAccountPermissions.RoleRestrictionsStruct = {
role,
approvedTargets: parsedRestrictions.approvedCallTargets,
maxValuePerTransaction: utils.parseEther(
parsedRestrictions.nativeTokenLimitPerTransaction,
),
startTimestamp: parsedRestrictions.startDate,
endTimestamp: parsedRestrictions.expirationDate,
};
encoded.push(
this.contractWrapper.readContract.interface.encodeFunctionData(
"setRoleRestrictions",
[roleRestrictions],
),
);
}
}
// Perform multicall
return Transaction.fromContractWrapper({
contractWrapper: this.contractWrapper,
method: "multicall",
args: [encoded],
});
},
);
}
@@ -24,7 +24,7 @@ import {
} from "ethers";
import EventEmitter from "eventemitter3";
import invariant from "tiny-invariant";
import { BlockTag } from "@ethersproject/abstract-provider";
import type { BlockTag } from "@ethersproject/abstract-provider";
import { fetchCurrencyValue } from "../../common/currency/fetchCurrencyValue";
import { isNativeToken } from "../../common/currency/isNativeToken";
import { normalizePriceValue } from "../../common/currency/normalizePriceValue";
@@ -32,6 +32,17 @@ export type SignerWithRestrictions = {
restrictions: AccessRestrictions;
}
export const SignerWithRestrictionsSchema = /* @__PURE__ */ z.object({
signer: AddressOrEnsSchema,
isAdmin: /* @__PURE__ */ z.boolean(),
restrictions: AccessRestrictionsSchema,
});
export type SignerWithRestrictionsInput = z.input<typeof SignerWithRestrictionsSchema>;
export const SignerWithRestrictionsBatchSchema = /* @__PURE__ */ z.array(SignerWithRestrictionsSchema);
export type SignerWithRestrictionsBatchInput = z.input<typeof SignerWithRestrictionsBatchSchema>;
export type SignedAccountPermissionsPayload = {
payload: IAccountPermissions.RoleRequestStruct;
signature: BytesLike;
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+839
View File
@@ -0,0 +1,839 @@
import { sdk, signers } from "./before-setup";
import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers";
import { assert, expect } from "chai";
import {
SignerWithRestrictions,
AccessRestrictions,
SmartWallet,
SmartWalletFactory,
SignerWithRestrictionsInput,
SignerWithRestrictionsBatchInput,
} from "../../src/evm";
import { ContractFactory, utils } from "ethers";
import EntrypointArtifact from "./mock/EntryPoint.json";
import AccountFactoryArtifact from "./mock/AccountFactory.json";
// Target ABIs
import IAccountCoreAbi from "@thirdweb-dev/contracts-js/dist/abis/IAccountCore.json";
import {
deployContractAndUploadMetadata,
mockUploadContractMetadata,
} from "./utils";
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>;
let adminWallet: SignerWithAddress;
let signer1Wallet: SignerWithAddress;
let signer2Wallet: SignerWithAddress;
let signer3Wallet: SignerWithAddress;
let signer4Wallet: SignerWithAddress;
let signer5Wallet: SignerWithAddress;
let signer6Wallet: SignerWithAddress;
let signer7Wallet: SignerWithAddress;
let signer8Wallet: SignerWithAddress;
let signer9Wallet: SignerWithAddress;
before(async () => {
[
adminWallet,
signer1Wallet,
signer2Wallet,
signer3Wallet,
signer4Wallet,
signer5Wallet,
signer6Wallet,
signer7Wallet,
signer8Wallet,
signer9Wallet,
] = signers;
});
beforeEach(async () => {
const entrypoint = await new ContractFactory(
EntrypointArtifact.abi,
EntrypointArtifact.bytecode.object,
adminWallet,
).deploy();
const factoryAddress = await deployContractAndUploadMetadata(
AccountFactoryArtifact.abi,
AccountFactoryArtifact.bytecode.object,
adminWallet,
[entrypoint.address],
);
smartWalletFactory = (await sdk.getContract(factoryAddress))
.smartWalletFactory;
});
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(
adminWallet.address,
);
assert.isFalse(
isDeployed,
"Smart wallet is should not be deployed for admin.",
);
const predictedAccountAddress: string =
await smartWalletFactory.predictWalletAddress(adminWallet.address);
const allWalletsBefore = (await smartWalletFactory.getAllAccounts()).map(
(wallet) => wallet.account,
);
assert.isFalse(
allWalletsBefore.includes(predictedAccountAddress),
"Account should not be deployed for admin already.",
);
assert.isTrue(
(await smartWalletFactory.getAssociatedSigners(predictedAccountAddress))
.length === 0,
"Wallet should have no associated signers.",
);
assert.isTrue(
(await smartWalletFactory.getAssociatedWallets(adminWallet.address))
.length === 0,
"Signer should have no associated wallets.",
);
// Create smart wallet for `adminWallet`
const tx = await smartWalletFactory.createWallet(adminWallet.address);
// Checks after creating a smart wallet for `adminWallet`
assert.strictEqual(
utils.getAddress(tx.address),
utils.getAddress(predictedAccountAddress),
"Smart wallet address should match predicted address.",
);
assert.isTrue(
await smartWalletFactory.isWalletDeployed(adminWallet.address),
"Smart wallet is should be deployed for admin.",
);
const allWalletsAfter = await smartWalletFactory.getAllAccounts();
assert.isTrue(
allWalletsAfter.length === 1,
"Only one wallet should be created on the factory.",
);
const { account, admin } = allWalletsAfter.filter(
(wallet) =>
utils.getAddress(wallet.account) ===
utils.getAddress(predictedAccountAddress),
)[0];
assert.strictEqual(
utils.getAddress(account),
utils.getAddress(predictedAccountAddress),
"Stored account address should match predicted address.",
);
assert.strictEqual(
utils.getAddress(admin),
utils.getAddress(adminWallet.address),
"Correct admin for smart wallet.",
);
const associatedWallets = await smartWalletFactory.getAssociatedWallets(
admin,
);
const associatedSigner = await smartWalletFactory.getAssociatedSigners(
account,
);
assert.isTrue(
associatedWallets.length === 1,
"Wallet should have only the admin as an associated signer.",
);
assert.strictEqual(
utils.getAddress(account),
utils.getAddress(associatedWallets[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.",
);
assert.strictEqual(
utils.getAddress(admin),
utils.getAddress(associatedSigner[0]),
"Signer should have only the created wallet as an associated wallet.",
);
});
it("Should throw if creating wallet for an admin who already has one", async () => {
// Create a wallet for admin
await smartWalletFactory.createWallet(adminWallet.address);
// Try creating another wallet for the same admin
try {
await smartWalletFactory.createWallet(adminWallet.address);
expect.fail();
} catch (err: any) {
expect(err.message).to.equal(
`Wallet already deployed for admin: ${adminWallet.address}`,
);
}
});
});
describe("Test: SmartWallet actions", () => {
beforeEach(async () => {
// Create a wallet for admin
const tx = await smartWalletFactory.createWallet(adminWallet.address);
const smartWalletAddress = tx.address;
sdk.updateSignerOrProvider(adminWallet);
await mockUploadContractMetadata(
"smart-wallet",
smartWalletAddress,
IAccountCoreAbi,
);
smartWallet = (await sdk.getContract(smartWalletAddress)).smartWallet;
});
it("Should be able to add another admin to the wallet.", async () => {
assert.isFalse(
await smartWallet.isAdmin(signer1Wallet.address),
"New signer1 should not be an admin on the smart wallet.",
);
await smartWallet.grantAdminAccess(signer1Wallet.address);
assert.isTrue(
await smartWallet.isAdmin(signer1Wallet.address),
"New signer1 should be an admin on the smart wallet.",
);
const isAdmin = (
(await smartWallet.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(
(
await smartWalletFactory.getAssociatedSigners(
smartWallet.getAddress(),
)
).includes(signer1Wallet.address),
"New signer1 is an associated signer of the wallet.",
);
assert.isTrue(
(
await smartWalletFactory.getAssociatedWallets(signer1Wallet.address)
).includes(smartWallet.getAddress()),
"Wallet is an associated wallet of the signer.",
);
});
it("Should be able to remove an admin from the wallet.", async () => {
await smartWallet.grantAdminAccess(signer1Wallet.address);
assert.isTrue(
await smartWallet.isAdmin(signer1Wallet.address),
"New signer1 should be an admin on the smart wallet.",
);
await smartWallet.revokeAdminAccess(signer1Wallet.address);
assert.isFalse(
await smartWallet.isAdmin(signer1Wallet.address),
"New signer1 should not be an admin on the smart wallet.",
);
assert.isFalse(
(await smartWallet.getSignersWithRestrictions())
.map((result) => utils.getAddress(result.signer))
.includes(signer1Wallet.address),
"New signer1 should not be an admin on the wallet.",
);
assert.isFalse(
(
await smartWalletFactory.getAssociatedSigners(
smartWallet.getAddress(),
)
).includes(signer1Wallet.address),
"New signer1 is not an associated signer of the wallet.",
);
assert.isFalse(
(
await smartWalletFactory.getAssociatedWallets(signer1Wallet.address)
).includes(smartWallet.getAddress()),
"Wallet is not an associated wallet of the signer.",
);
});
it("Should be able to grant restricted access to a new signer.", async () => {
const signersWithRestrictions =
await smartWallet.getSignersWithRestrictions();
assert.isFalse(
signersWithRestrictions
.map((result) => utils.getAddress(result.signer))
.includes(signer1Wallet.address),
"New signer should not already have access to the wallet.",
);
// Grant access
await smartWallet.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.",
);
const newSignerWithRestrictions =
await smartWallet.getSignersWithRestrictions();
const restrictions = newSignerWithRestrictions.find(
(result) =>
utils.getAddress(result.signer) ===
utils.getAddress(signer1Wallet.address),
)?.restrictions as AccessRestrictions;
assert.isTrue(
newSignerWithRestrictions
.map((result) => utils.getAddress(result.signer))
.includes(signer1Wallet.address),
"New signer1 should be a signer on the wallet.",
);
assert.strictEqual(
restrictions.nativeTokenLimitPerTransaction.toString(),
utils.parseEther("1").toString(),
"New signer1 should have the expected native token limit.",
);
assert.strictEqual(
restrictions.approvedCallTargets.length,
1,
"New signer1 should have one approved call targets.",
);
assert.strictEqual(
restrictions.approvedCallTargets[0],
adminWallet.address,
"New signer1 should have the expected approved call targets.",
);
assert.isTrue(
(
await smartWalletFactory.getAssociatedSigners(
smartWallet.getAddress(),
)
).includes(signer1Wallet.address),
"New signer1 is an associated signer of the wallet.",
);
assert.isTrue(
(
await smartWalletFactory.getAssociatedWallets(signer1Wallet.address)
).includes(smartWallet.getAddress()),
"Wallet is an associated wallet 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, {
nativeTokenLimitPerTransaction: "1",
approvedCallTargets: [adminWallet.address],
});
// Try granting access to signer1 again
try {
await smartWallet.grantAccess(signer1Wallet.address, {
approvedCallTargets: [adminWallet.address],
});
expect.fail();
} catch (err: any) {
expect(err.message).to.equal(`Signer already has access`);
}
});
it("Should be able to revoke restricted access from an authorized signer.", async () => {
// Grant access to signer1
await smartWallet.grantAccess(signer1Wallet.address, {
nativeTokenLimitPerTransaction: "1",
approvedCallTargets: [adminWallet.address],
});
// Revoke access
await smartWallet.revokeAccess(signer1Wallet.address);
const signersWithRestrictions =
await smartWallet.getSignersWithRestrictions();
assert.isFalse(
signersWithRestrictions
.map((result) => utils.getAddress(result.signer))
.includes(signer1Wallet.address),
"New signer should not have access to the wallet.",
);
assert.isFalse(
await smartWallet.isAdmin(signer1Wallet.address),
"New signer1 should not be an admin on the smart wallet.",
);
assert.isFalse(
(
await smartWalletFactory.getAssociatedSigners(
smartWallet.getAddress(),
)
).includes(signer1Wallet.address),
"New signer1 is not an associated signer of the wallet.",
);
assert.isFalse(
(
await smartWalletFactory.getAssociatedWallets(signer1Wallet.address)
).includes(smartWallet.getAddress()),
"Wallet is not an associated wallet 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();
assert.isFalse(
signersWithRestrictions
.map((result) => utils.getAddress(result.signer))
.includes(signer1Wallet.address),
"New signer should not already have access to the wallet.",
);
// Try revoking access from signer1
try {
await smartWallet.revokeAccess(signer1Wallet.address);
expect.fail();
} catch (err: any) {
expect(err.message).to.equal(`Signer does not have any access`);
}
});
it("Should be able to update access of an authorized signer.", async () => {
// Grant access
await smartWallet.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.",
);
const signerWithRestrictions =
await smartWallet.getSignersWithRestrictions();
const restrictions = signerWithRestrictions.find(
(result) =>
utils.getAddress(result.signer) ===
utils.getAddress(signer1Wallet.address),
)?.restrictions as AccessRestrictions;
assert.isTrue(
signerWithRestrictions
.map((result) => utils.getAddress(result.signer))
.includes(signer1Wallet.address),
"New signer1 should be a signer on the wallet.",
);
assert.strictEqual(
restrictions.nativeTokenLimitPerTransaction.toString(),
utils.parseEther("1").toString(),
"New signer1 should have the expected native token limit.",
);
assert.strictEqual(
restrictions.approvedCallTargets.length,
1,
"New signer1 should have one approved call targets.",
);
assert.strictEqual(
restrictions.approvedCallTargets[0],
adminWallet.address,
"New signer1 should have the expected approved call targets.",
);
// Update access
await smartWallet.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.",
);
const newSignerWithRestrictions =
await smartWallet.getSignersWithRestrictions();
const newRestrictions = newSignerWithRestrictions.find(
(result) =>
utils.getAddress(result.signer) ===
utils.getAddress(signer1Wallet.address),
)?.restrictions as AccessRestrictions;
assert.isTrue(
newSignerWithRestrictions
.map((result) => utils.getAddress(result.signer))
.includes(signer1Wallet.address),
"New signer1 should be a signer on the wallet.",
);
assert.strictEqual(
newRestrictions.nativeTokenLimitPerTransaction.toString(),
utils.parseEther("3").toString(),
"New signer1 should have the expected native token limit.",
);
assert.strictEqual(
newRestrictions.approvedCallTargets.length,
1,
"New signer1 should have one approved call targets.",
);
assert.strictEqual(
newRestrictions.approvedCallTargets[0],
signer2Wallet.address,
"New signer1 should have the expected approved call targets.",
);
});
it("Should not be able to update access of a signer who doesn't have access.", async () => {
const signersWithRestrictions =
await smartWallet.getSignersWithRestrictions();
assert.isFalse(
signersWithRestrictions
.map((result) => utils.getAddress(result.signer))
.includes(signer1Wallet.address),
"New signer should not already have access to the wallet.",
);
// Try updating access of signer1
try {
await smartWallet.updateAccess(signer1Wallet.address, {
approvedCallTargets: [signer2Wallet.address],
});
expect.fail();
} catch (err: any) {
expect(err.message).to.equal(`Signer does not have any access`);
}
});
it("Should be able to append an approved target for an authorized signer.", async () => {
// Grant access
await smartWallet.grantAccess(signer1Wallet.address, {
nativeTokenLimitPerTransaction: "1",
approvedCallTargets: [adminWallet.address],
});
const restrictions = (
await smartWallet.getSignersWithRestrictions()
).find(
(result) =>
utils.getAddress(result.signer) ===
utils.getAddress(signer1Wallet.address),
)?.restrictions as AccessRestrictions;
assert.strictEqual(
restrictions.approvedCallTargets.length,
1,
"New signer1 should have one approved call targets.",
);
assert.strictEqual(
restrictions.approvedCallTargets[0],
adminWallet.address,
"New signer1 should have the expected approved call targets.",
);
// Append approved target
await smartWallet.approveTargetForSigner(
signer1Wallet.address,
signer2Wallet.address,
);
const newRestrictions = (
await smartWallet.getSignersWithRestrictions()
).find(
(result) =>
utils.getAddress(result.signer) ===
utils.getAddress(signer1Wallet.address),
)?.restrictions as AccessRestrictions;
assert.strictEqual(
newRestrictions.approvedCallTargets.length,
2,
"New signer1 should have two approved call targets.",
);
assert.strictEqual(
newRestrictions.approvedCallTargets[0],
adminWallet.address,
"New signer1 should have the expected approved call targets.",
);
assert.strictEqual(
newRestrictions.approvedCallTargets[1],
signer2Wallet.address,
"New signer1 should have the expected approved call targets.",
);
});
it("Should be able to remove an approved target for an authorized signer.", async () => {
// Grant access
await smartWallet.grantAccess(signer1Wallet.address, {
nativeTokenLimitPerTransaction: "1",
approvedCallTargets: [adminWallet.address, signer2Wallet.address],
});
const restrictions = (
await smartWallet.getSignersWithRestrictions()
).find(
(result) =>
utils.getAddress(result.signer) ===
utils.getAddress(signer1Wallet.address),
)?.restrictions as AccessRestrictions;
assert.strictEqual(
restrictions.approvedCallTargets.length,
2,
"New signer1 should have two approved call targets.",
);
assert.strictEqual(
restrictions.approvedCallTargets[0],
adminWallet.address,
"New signer1 should have the expected approved call targets.",
);
assert.strictEqual(
restrictions.approvedCallTargets[1],
signer2Wallet.address,
"New signer1 should have the expected approved call targets.",
);
// Remove approved target
await smartWallet.disapproveTargetForSigner(
signer1Wallet.address,
signer2Wallet.address,
);
const newRestrictions = (
await smartWallet.getSignersWithRestrictions()
).find(
(result) =>
utils.getAddress(result.signer) ===
utils.getAddress(signer1Wallet.address),
)?.restrictions as AccessRestrictions;
assert.strictEqual(
newRestrictions.approvedCallTargets.length,
1,
"New signer1 should have one approved call targets.",
);
assert.strictEqual(
newRestrictions.approvedCallTargets[0],
adminWallet.address,
"New signer1 should have the expected approved call targets.",
);
});
it("Should be able to set access for the smart wallet in a batch.", async () => {
/**
* All cases to test
*
* - 1. Adding a new admin
* - 2. Removing existing admin
* - 3. Adding a new scoped signer
* - 4. Removing an existing scoped signer
* - 5. Updating the restrictions of an existing scoped signer
* - 6. Existing admin -> new scoped signer (demote)
* - 7. Existing scoped signer -> new admin :check: (promote)
**/
const signersWithRestrictions: SignerWithRestrictionsBatchInput = [];
// Setup
const restrictionsForAdmin: SignerWithRestrictionsInput = {
signer: adminWallet.address,
isAdmin: true,
restrictions: { approvedCallTargets: [] },
};
signersWithRestrictions.push(restrictionsForAdmin);
// Adding a new admin: Signer3
const restrictionsSigner3: SignerWithRestrictionsInput = {
signer: signer3Wallet.address,
isAdmin: true,
restrictions: { approvedCallTargets: [] },
};
signersWithRestrictions.push(restrictionsSigner3);
// Removing existing admin
await smartWallet.grantAdminAccess(signer4Wallet.address);
const restrictionsSigner4: SignerWithRestrictionsInput = {
signer: signer4Wallet.address,
isAdmin: false,
restrictions: { approvedCallTargets: [] },
};
signersWithRestrictions.push(restrictionsSigner4);
// Adding a new scoped signer
const restrictionsSigner5: SignerWithRestrictionsInput = {
signer: signer5Wallet.address,
isAdmin: false,
restrictions: { approvedCallTargets: [adminWallet.address] },
};
signersWithRestrictions.push(restrictionsSigner5);
// Removing an existing scoped signer
await smartWallet.grantAccess(signer6Wallet.address, {
approvedCallTargets: [adminWallet.address],
});
const restrictionsSigner6: SignerWithRestrictionsInput = {
signer: signer6Wallet.address,
isAdmin: false,
restrictions: { approvedCallTargets: [] },
};
signersWithRestrictions.push(restrictionsSigner6);
// Updating the restrictions of an existing scoped signer
await smartWallet.grantAccess(signer7Wallet.address, {
approvedCallTargets: [adminWallet.address],
});
const restrictionsSigner7: SignerWithRestrictionsInput = {
signer: signer7Wallet.address,
isAdmin: false,
restrictions: { approvedCallTargets: [signer1Wallet.address] },
};
signersWithRestrictions.push(restrictionsSigner7);
// Existing admin -> new scoped signer (demote)
await smartWallet.grantAdminAccess(signer8Wallet.address);
const restrictionsSigner8: SignerWithRestrictionsInput = {
signer: signer8Wallet.address,
isAdmin: false,
restrictions: { approvedCallTargets: [adminWallet.address] },
};
signersWithRestrictions.push(restrictionsSigner8);
// Existing scoped signer -> new admin (promote)
await smartWallet.grantAccess(signer9Wallet.address, {
approvedCallTargets: [adminWallet.address],
});
const restrictionsSigner9: SignerWithRestrictionsInput = {
signer: signer9Wallet.address,
isAdmin: true,
restrictions: { approvedCallTargets: [] },
};
signersWithRestrictions.push(restrictionsSigner9);
// Set access in batch
await smartWallet.setAccess(signersWithRestrictions);
// Now checking if permissions are set correctly
const signersWithRestrictionsAfter =
await smartWallet.getSignersWithRestrictions();
for (const restrictions of signersWithRestrictionsAfter) {
switch (restrictions.signer) {
// `adminWallet` should be an admin
case adminWallet.address:
assert.isTrue(
restrictions.isAdmin,
"Admin wallet should be an admin.",
);
break;
// `signer3Wallet` should be an admin
case signer3Wallet.address:
assert.isTrue(
restrictions.isAdmin,
"Signer3 wallet 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.",
);
assert.strictEqual(
restrictions.restrictions.approvedCallTargets.length,
0,
"Signer4 wallet 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.",
);
assert.strictEqual(
restrictions.restrictions.approvedCallTargets.length,
1,
"Signer5 wallet should have one approved call target.",
);
assert.strictEqual(
restrictions.restrictions.approvedCallTargets[0],
adminWallet.address,
"Signer5 wallet 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.",
);
assert.strictEqual(
restrictions.restrictions.approvedCallTargets.length,
0,
"Signer6 wallet 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.",
);
assert.strictEqual(
restrictions.restrictions.approvedCallTargets.length,
1,
"Signer7 wallet should have one approved call target.",
);
assert.strictEqual(
restrictions.restrictions.approvedCallTargets[0],
signer1Wallet.address,
"Signer7 wallet 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.",
);
assert.strictEqual(
restrictions.restrictions.approvedCallTargets.length,
1,
"Signer8 wallet should have one approved call target.",
);
assert.strictEqual(
restrictions.restrictions.approvedCallTargets[0],
adminWallet.address,
"Signer8 wallet 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.",
);
break;
default:
assert.fail("Unexpected signer.");
}
}
});
});
});
+1 -1
View File
@@ -18,7 +18,7 @@ export const deployContractAndUploadMetadata = async (
return deployed.address;
};
const mockUploadContractMetadata = async (
export const mockUploadContractMetadata = async (
contractName: string,
address: string,
abi: any,
+6
View File
@@ -1,5 +1,11 @@
# @thirdweb-dev/storage
## 1.1.5
### Patch Changes
- [#1246](https://github.com/thirdweb-dev/js/pull/1246) [`0c5b03b8`](https://github.com/thirdweb-dev/js/commit/0c5b03b8d6cc6a4e69bb2a4647d3626e69f1283c) Thanks [@nessup](https://github.com/nessup)! - Remove the ipfs-2 gateway due to connectivity errors that seem to happen with certain ISPs
## 1.1.4
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/storage",
"version": "1.1.4",
"version": "1.1.5",
"main": "dist/thirdweb-dev-storage.cjs.js",
"module": "dist/thirdweb-dev-storage.esm.js",
"browser": {
-1
View File
@@ -6,7 +6,6 @@ import { GatewayUrls } from "../types";
export const DEFAULT_GATEWAY_URLS: GatewayUrls = {
// Note: Gateway URLs should have trailing slashes (we clean this on user input)
"ipfs://": [
"https://ipfs-2.thirdwebcdn.com/ipfs/",
"https://ipfs-3.thirdwebcdn.com/ipfs/",
"https://ipfs-4.thirdwebcdn.com/ipfs/",
"https://ipfs-5.thirdwebcdn.com/ipfs/",
+23
View File
@@ -1,5 +1,28 @@
# @thirdweb-dev/unity-js-bridge
## 0.2.41
### Patch Changes
- [#1256](https://github.com/thirdweb-dev/js/pull/1256) [`ac3e019c`](https://github.com/thirdweb-dev/js/commit/ac3e019cd1776dbdb0d06b213420ad17586f678e) Thanks [@0xFirekeeper](https://github.com/0xFirekeeper)! - WCv2 and Paper support for WebGL Unity
- Updated dependencies [[`ef4cb092`](https://github.com/thirdweb-dev/js/commit/ef4cb092192c58c9b292b29590b888f45f9fd23d), [`d43f6171`](https://github.com/thirdweb-dev/js/commit/d43f6171d910e31b748b171b012174390aac3fff), [`ac3e019c`](https://github.com/thirdweb-dev/js/commit/ac3e019cd1776dbdb0d06b213420ad17586f678e), [`30ac3aef`](https://github.com/thirdweb-dev/js/commit/30ac3aef840bde51a61acf786c709cffd3c47354), [`3c5dc480`](https://github.com/thirdweb-dev/js/commit/3c5dc4804abc56b933ec45e9e1da11eb182296cc), [`620e89dc`](https://github.com/thirdweb-dev/js/commit/620e89dc25c91557e2164a602c7aedd733525087), [`0fd8aa04`](https://github.com/thirdweb-dev/js/commit/0fd8aa04a9424497758d13a51a72363edcc30e19)]:
- @thirdweb-dev/sdk@3.10.26
- @thirdweb-dev/auth@3.2.4
- @thirdweb-dev/wallets@1.0.0
- @thirdweb-dev/chains@0.1.25
## 0.2.40
### Patch Changes
- Updated dependencies [[`536b0f12`](https://github.com/thirdweb-dev/js/commit/536b0f1240ab446aac22cf547a4e09e73ee6bf7b), [`b626782b`](https://github.com/thirdweb-dev/js/commit/b626782b0e8c6b76673472d3aee1c802dfb11b5f), [`d498c79a`](https://github.com/thirdweb-dev/js/commit/d498c79a911d478077dfb8a2490eb1bf91523186), [`0c5b03b8`](https://github.com/thirdweb-dev/js/commit/0c5b03b8d6cc6a4e69bb2a4647d3626e69f1283c), [`9af346ee`](https://github.com/thirdweb-dev/js/commit/9af346eeada1037be27c8d3c9e1777f7be11a8ea)]:
- @thirdweb-dev/wallets@0.3.3
- @thirdweb-dev/sdk@3.10.25
- @thirdweb-dev/storage@1.1.5
- @thirdweb-dev/chains@0.1.24
- @thirdweb-dev/auth@3.2.3
## 0.2.39
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/unity-js-bridge",
"version": "0.2.39",
"version": "0.2.41",
"main": "dist/thirdweb-unity-bridge.js",
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/unity-js-bridge",
"license": "Apache-2.0",
@@ -6,7 +6,6 @@ import { ThirdwebStorage } from "@thirdweb-dev/storage";
import {
DAppMetaData,
SmartWalletConfig,
WalletConnectV1,
walletIds,
} from "@thirdweb-dev/wallets";
import type { AbstractClientWallet } from "@thirdweb-dev/wallets/evm/wallets/base";
@@ -17,8 +16,10 @@ import { InjectedWallet } from "@thirdweb-dev/wallets/evm/wallets/injected";
import { MetaMaskWallet } from "@thirdweb-dev/wallets/evm/wallets/metamask";
import { MagicLink } from "@thirdweb-dev/wallets/evm/wallets/magic";
import { SmartWallet } from "@thirdweb-dev/wallets/evm/wallets/smart-wallet";
import { WalletConnect } from "@thirdweb-dev/wallets/evm/wallets/wallet-connect";
import { PaperWallet } from "@thirdweb-dev/wallets/evm/wallets/paper-wallet";
import { BigNumber } from "ethers";
import { allChains } from "@thirdweb-dev/chains";
import { Ethereum, allChains, defaultChains } from "@thirdweb-dev/chains";
import type { ContractInterface, Signer } from "ethers";
declare global {
@@ -50,7 +51,8 @@ const bigNumberReplacer = (_key: string, value: any) => {
const WALLETS = [
MetaMaskWallet,
InjectedWallet,
WalletConnectV1,
WalletConnect,
PaperWallet,
CoinbaseWallet,
LocalWallet,
MagicLink,
@@ -152,10 +154,11 @@ class ThirdwebBridge implements TWBridge {
chains: allChains,
});
break;
case walletIds.walletConnectV1:
walletInstance = new WalletConnectV1({
case walletIds.walletConnect:
walletInstance = new WalletConnect({
projectId: sdkOptions.wallet?.walletConnectProjectId,
dappMetadata,
chains: allChains,
chains: defaultChains,
});
break;
case walletIds.coinbase:
@@ -172,11 +175,20 @@ class ThirdwebBridge implements TWBridge {
break;
case walletIds.magicLink:
walletInstance = new MagicLink({
dappMetadata,
apiKey: sdkOptions.wallet?.magicLinkApiKey,
emailLogin: true,
chains: allChains,
});
break;
case walletIds.paper:
walletInstance = new PaperWallet({
clientId: sdkOptions.wallet?.paperClientId,
chain: Ethereum,
dappMetadata,
chains: allChains,
});
break;
case walletIds.smartWallet:
const config: SmartWalletConfig = {
chain: chain,
@@ -235,6 +247,12 @@ class ThirdwebBridge implements TWBridge {
throw new Error("Email is required for Magic Link Wallet");
}
await magicLinkWallet.connect({ chainId, email });
} else if (walletInstance.walletId === walletIds.paper) {
const paperWallet = walletInstance as PaperWallet;
if (!email) {
throw new Error("Email is required for Paper Wallet");
}
await paperWallet.connect({ chainId, email });
} else if (walletInstance.walletId === walletIds.smartWallet) {
const smartWallet = walletInstance as SmartWallet;
const eoaWallet = this.walletMap.get(personalWallet);
+55
View File
@@ -1,5 +1,60 @@
# @thirdweb-dev/wallets
## 1.0.0
### Major Changes
- [#1215](https://github.com/thirdweb-dev/js/pull/1215) [`3c5dc480`](https://github.com/thirdweb-dev/js/commit/3c5dc4804abc56b933ec45e9e1da11eb182296cc) Thanks [@iketw](https://github.com/iketw)! - Migrate wallets to WCV2 and remove WCV1 support.
Please, check our changelogs for more info: https://blog.thirdweb.com/changelog/
### Patch Changes
- [#1256](https://github.com/thirdweb-dev/js/pull/1256) [`ac3e019c`](https://github.com/thirdweb-dev/js/commit/ac3e019cd1776dbdb0d06b213420ad17586f678e) Thanks [@0xFirekeeper](https://github.com/0xFirekeeper)! - WCv2 and Paper support for WebGL Unity
- Updated dependencies [[`ef4cb092`](https://github.com/thirdweb-dev/js/commit/ef4cb092192c58c9b292b29590b888f45f9fd23d), [`30ac3aef`](https://github.com/thirdweb-dev/js/commit/30ac3aef840bde51a61acf786c709cffd3c47354), [`620e89dc`](https://github.com/thirdweb-dev/js/commit/620e89dc25c91557e2164a602c7aedd733525087), [`0fd8aa04`](https://github.com/thirdweb-dev/js/commit/0fd8aa04a9424497758d13a51a72363edcc30e19)]:
- @thirdweb-dev/sdk@3.10.26
- @thirdweb-dev/chains@0.1.25
## 0.3.3
### Patch Changes
- [#1237](https://github.com/thirdweb-dev/js/pull/1237) [`536b0f12`](https://github.com/thirdweb-dev/js/commit/536b0f1240ab446aac22cf547a4e09e73ee6bf7b) Thanks [@iketw](https://github.com/iketw)! - [React] Add Rainbow Wallet (implementing the WalletConnect wallet)
```javascript
import { ThirdwebProvider, rainbowWallet } from "@thirdweb-dev/react";
const activeChain = "ethereum";
<ThirdwebProvider
activeChain={activeChain}
supportedWallets={[rainbowWallet()]}
>
<App />
</ThirdwebProvider>;
```
- [#1236](https://github.com/thirdweb-dev/js/pull/1236) [`b626782b`](https://github.com/thirdweb-dev/js/commit/b626782b0e8c6b76673472d3aee1c802dfb11b5f) Thanks [@iketw](https://github.com/iketw)! - [Wallets/React] Add Trust Wallet
```javascript
import { ThirdwebProvider, trustWallet } from "@thirdweb-dev/react";
const activeChain = "mumbai";
<ThirdwebProvider
activeChain={activeChain}
autoSwitch={true}
supportedWallets={[trustWallet()]}
>
<App />
</ThirdwebProvider>;
```
- Updated dependencies [[`d498c79a`](https://github.com/thirdweb-dev/js/commit/d498c79a911d478077dfb8a2490eb1bf91523186), [`9af346ee`](https://github.com/thirdweb-dev/js/commit/9af346eeada1037be27c8d3c9e1777f7be11a8ea)]:
- @thirdweb-dev/[email protected]
- @thirdweb-dev/[email protected]
## 0.3.2
### Patch Changes
@@ -0,0 +1,7 @@
{
"main": "dist/thirdweb-dev-wallets-evm-connectors-trust.cjs.js",
"module": "dist/thirdweb-dev-wallets-evm-connectors-trust.esm.js",
"browser": {
"./dist/thirdweb-dev-wallets-evm-connectors-trust.esm.js": "./dist/thirdweb-dev-wallets-evm-connectors-trust.browser.esm.js"
}
}
@@ -0,0 +1,7 @@
{
"main": "dist/thirdweb-dev-wallets-evm-wallets-trust.cjs.js",
"module": "dist/thirdweb-dev-wallets-evm-wallets-trust.esm.js",
"browser": {
"./dist/thirdweb-dev-wallets-evm-wallets-trust.esm.js": "./dist/thirdweb-dev-wallets-evm-wallets-trust.browser.esm.js"
}
}
+21 -22
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/wallets",
"version": "0.3.2",
"version": "1.0.0",
"main": "dist/thirdweb-dev-wallets.cjs.js",
"module": "dist/thirdweb-dev-wallets.esm.js",
"types": "dist/thirdweb-dev-wallets.cjs.d.ts",
@@ -57,6 +57,13 @@
},
"default": "./evm/wallets/magic/dist/thirdweb-dev-wallets-evm-wallets-magic.cjs.js"
},
"./evm/wallets/trust": {
"module": {
"browser": "./evm/wallets/trust/dist/thirdweb-dev-wallets-evm-wallets-trust.browser.esm.js",
"default": "./evm/wallets/trust/dist/thirdweb-dev-wallets-evm-wallets-trust.esm.js"
},
"default": "./evm/wallets/trust/dist/thirdweb-dev-wallets-evm-wallets-trust.cjs.js"
},
"./evm/wallets/ethers": {
"module": {
"browser": "./evm/wallets/ethers/dist/thirdweb-dev-wallets-evm-wallets-ethers.browser.esm.js",
@@ -176,6 +183,13 @@
},
"default": "./evm/connectors/paper/dist/thirdweb-dev-wallets-evm-connectors-paper.cjs.js"
},
"./evm/connectors/trust": {
"module": {
"browser": "./evm/connectors/trust/dist/thirdweb-dev-wallets-evm-connectors-trust.browser.esm.js",
"default": "./evm/connectors/trust/dist/thirdweb-dev-wallets-evm-connectors-trust.esm.js"
},
"default": "./evm/connectors/trust/dist/thirdweb-dev-wallets-evm-connectors-trust.cjs.js"
},
"./evm/wallets/wallet-connect": {
"module": {
"browser": "./evm/wallets/wallet-connect/dist/thirdweb-dev-wallets-evm-wallets-wallet-connect.browser.esm.js",
@@ -317,36 +331,21 @@
"@safe-global/safe-ethers-lib": "^1.9.2",
"@thirdweb-dev/chains": "workspace:*",
"@thirdweb-dev/sdk": "workspace:*",
"@walletconnect/client": "^1.8.0",
"@walletconnect/core": "2.7.3",
"@walletconnect/ethereum-provider": "2.6.0",
"@walletconnect/jsonrpc-http-connection": "^1.0.0",
"@walletconnect/jsonrpc-provider": "^1.0.3",
"@walletconnect/jsonrpc-types": "^1.0.1",
"@walletconnect/core": "^2.8.4",
"@walletconnect/ethereum-provider": "^2.8.4",
"@walletconnect/jsonrpc-utils": "^1.0.3",
"@walletconnect/legacy-client": "^2.0.0-rc.0",
"@walletconnect/legacy-modal": "^2.0.0-rc.0",
"@walletconnect/legacy-types": "^2.0.0-rc.0",
"@walletconnect/legacy-utils": "^2.0.0-rc.0",
"@walletconnect/types": "2.7.3",
"@walletconnect/utils": "2.7.3",
"@walletconnect/web3wallet": "^1.7.1",
"@web3modal/standalone": "^2.4.1",
"@walletconnect/modal": "^2.5.4",
"@walletconnect/types": "^2.8.4",
"@walletconnect/utils": "^2.8.4",
"@walletconnect/web3wallet": "^1.8.1",
"buffer": "^6.0.3",
"cross-fetch": "^3.1.5",
"crypto-js": "^4.1.1",
"eip1193-provider": "^1.0.1",
"eth-provider": "^0.13.6",
"eventemitter3": "^5.0.0",
"magic-sdk": "^13.6.2",
"web3-core": "1.5.2"
},
"resolutions": {
"@walletconnect/ethereum-provider": "2.6.0",
"@walletconnect/core": "2.7.3",
"@walletconnect/types": "2.7.3",
"@walletconnect/utils": "2.7.3"
},
"peerDependencies": {
"@aws-sdk/client-secrets-manager": "^3.256.0",
"@noble/ed25519": "^1.7.1",
@@ -1,374 +0,0 @@
import {
WCSession,
WalletConnectHandler,
WalletConnectReceiverConfig,
WCMetadata,
} from "../types/walletConnect";
import WalletConnect from "@walletconnect/client";
import { EIP155_SIGNING_METHODS } from "../../evm/constants/wc";
import { AbstractClientWallet } from "../../evm/wallets/base";
import { SyncStorage } from "../SyncStorage";
import {
ISessionStorage,
IWalletConnectSession,
} from "@walletconnect/legacy-types";
import { ethers, utils } from "ethers";
type WalletConnectV1WalletConfig = Omit<
WalletConnectReceiverConfig,
"enableConnectApp"
> & {
storage: SyncStorage;
};
const STORAGE_URI_KEY = "storage_uri_key";
export class WalletConnectV1Handler extends WalletConnectHandler {
#wcMetadata: WCMetadata;
#wcWallet: WalletConnect | undefined;
#activeSessionPayload: any;
#activeCallRequest: any;
#storage: SyncStorage;
#sessionStorage: ISessionStorage;
constructor(options: WalletConnectV1WalletConfig) {
super();
this.#wcMetadata = options?.walletConnectWalletMetadata || {
name: "Thirdweb Smart Wallet",
description: "Thirdweb Smart Wallet",
url: "https://thirdweb.com",
icons: ["https://thirdweb.com/favicon.ico"],
};
this.#storage = options.storage;
this.#sessionStorage = new SessionStorage(options.storage);
}
async init(): Promise<void> {
const uri = this.#storage.getItem(STORAGE_URI_KEY);
if (uri) {
this.#init(uri);
}
return Promise.resolve();
}
async connectApp(uri: string): Promise<void> {
if (!this.#wcWallet) {
this.#storage.setItem(STORAGE_URI_KEY, uri);
this.#init(uri);
}
return Promise.resolve();
}
async approveSession(wallet: AbstractClientWallet): Promise<void> {
if (!this.#wcWallet) {
throw new Error(
"Please, init the wallet before making session requests.",
);
}
const address = await wallet.getAddress();
const chainId = await wallet.getChainId();
await this.#wcWallet.approveSession({
accounts: [address],
chainId: chainId,
});
this.emit("session_approved");
return Promise.resolve();
}
rejectSession(): Promise<void> {
if (!this.#wcWallet) {
throw new Error(
"Please, init the wallet before making session requests.",
);
}
this.#wcWallet.rejectSession();
return Promise.resolve();
}
async approveEIP155Request(wallet: AbstractClientWallet): Promise<void> {
const { params, method } = this.#activeCallRequest;
try {
let result;
switch (method) {
case EIP155_SIGNING_METHODS.ETH_SIGN:
const ethMsg = params[1];
result = await wallet.signMessage(utils.arrayify(ethMsg));
break;
case EIP155_SIGNING_METHODS.PERSONAL_SIGN:
const message = params[0];
result = await wallet.signMessage(utils.arrayify(message));
break;
case EIP155_SIGNING_METHODS.ETH_SEND_TRANSACTION:
const chainId = await wallet.getChainId();
const signer = await wallet.getSigner();
const tx = await signer.sendTransaction(
this.#parseTxParams(params, chainId),
);
const { transactionHash } = await tx.wait();
result = transactionHash;
break;
case EIP155_SIGNING_METHODS.ETH_SIGN_TRANSACTION:
const chainIdSign = await wallet.getChainId();
const signerSign = await wallet.getSigner();
const txParams = await signerSign.populateTransaction(
this.#parseTxParams(params, chainIdSign),
);
result = await signerSign.signTransaction(txParams);
break;
case EIP155_SIGNING_METHODS.SWITCH_CHAIN: {
await wallet.switchChain(params[0].chainId);
result = params[0].chainId;
break;
}
}
this.#wcWallet?.approveRequest({
id: this.#activeCallRequest?.id,
result,
});
} catch (error) {
let message;
if (error instanceof Error) {
message = error.message;
} else {
message = `Error executing the method: ${method}`;
}
this.#wcWallet?.rejectRequest({
id: this.#activeCallRequest?.id,
error: { message },
});
}
return Promise.resolve();
}
rejectEIP155Request(): Promise<void> {
this.#wcWallet?.rejectRequest({
id: 1,
error: {
message: "Rejected by user",
},
});
return Promise.resolve();
}
getActiveSessions(): WCSession[] {
const session = this.#wcWallet?.session;
if (!session) {
return [];
}
const result: WCSession[] = [
{
topic: session?.clientId,
peer: {
metadata: session.peerMeta
? session.peerMeta
: {
name: "Thirdweb Powered Wallet",
description: "Thirdweb Powered Wallet",
url: "https://thirdweb.com",
icons: ["https://thirdweb.com/favicon.ico"],
},
},
},
];
return result;
}
async disconnectSession(): Promise<void> {
this.#wcWallet?.killSession();
this.#wcWallet?.off("session_request");
this.#wcWallet?.off("call_request");
this.#wcWallet?.off("disconnect");
this.#storage.removeItem(STORAGE_URI_KEY);
this.#wcWallet = undefined;
this.emit("session_delete");
return Promise.resolve();
}
#init(uri: string) {
this.#wcWallet = new WalletConnect({
uri: uri,
clientMeta: this.#wcMetadata,
storage: this.#sessionStorage,
});
this.#setupWalletConnectEventsListeners();
}
#parseTxParams(params: any, chainId: number) {
let txParams: ethers.providers.TransactionRequest = {
from: params[0].from,
data: params[0].data,
chainId: chainId,
};
if (params[0].gas) {
txParams = {
...txParams,
gasLimit: params[0].gas,
};
}
if (params[0].to) {
txParams = {
...txParams,
to: params[0].to,
};
}
return txParams;
}
#setupWalletConnectEventsListeners() {
if (!this.#wcWallet) {
throw new Error(
"Please, init the wallet before making session requests.",
);
}
// Subscribe to session requests
this.#wcWallet.on("session_request", (error, payload) => {
if (error) {
throw new Error(`WCV1H.session_request error: ${error.message}`);
}
this.#activeSessionPayload = payload;
this.emit("session_proposal", {
proposer: {
metadata: payload.params[0].peerMeta,
},
});
});
// Subscribe to call requests
this.#wcWallet.on("call_request", (error, payload) => {
if (error) {
throw new Error(`WCV1H.call_request error: ${error.message}`);
}
const { params, method } = payload;
this.#activeCallRequest = payload;
switch (method) {
case EIP155_SIGNING_METHODS.ETH_SIGN:
case EIP155_SIGNING_METHODS.PERSONAL_SIGN:
const message = params[0];
const decodedMessage = new TextDecoder().decode(
utils.arrayify(message),
);
const paramsCopy = [...params];
paramsCopy[0] = decodedMessage;
this.emit("session_request", {
topic: params[1],
params: paramsCopy,
peer: {
metadata: this.#activeSessionPayload.params[0].peerMeta,
},
method: method,
});
return;
case EIP155_SIGNING_METHODS.SWITCH_CHAIN:
const chain = params[0]; // {"chainId":"0x13881"}
const chainId = parseInt(chain.chainId, 16);
chain.chainId = chainId;
const paramsCopyWithChain = [...params];
paramsCopyWithChain[0] = chain;
this.emit("session_request", {
topic: params[1],
params: paramsCopyWithChain,
peer: {
metadata: this.#activeSessionPayload.params[0].peerMeta,
},
method: method,
});
return;
this.emit("session_request", {
topic: params[1],
params: params,
peer: {
metadata: this.#activeSessionPayload.params[0].peerMeta,
},
method: method,
});
return;
case EIP155_SIGNING_METHODS.ETH_SEND_TRANSACTION:
case EIP155_SIGNING_METHODS.ETH_SIGN_TRANSACTION:
this.emit("session_request", {
topic: method,
params: params,
peer: {
metadata: this.#activeSessionPayload.params[0].peerMeta,
},
method: method,
});
return;
default:
throw new Error(
`WalletConnectV1Handler.call_request. Method not implemented: ${method}`,
);
}
});
this.#wcWallet.on("disconnect", (error) => {
if (error) {
throw new Error(`WCV1H.disconnect error: ${error.message}`);
}
this.disconnectSession();
});
}
}
class SessionStorage implements ISessionStorage {
#storage: SyncStorage;
constructor(storage: SyncStorage) {
this.#storage = storage;
}
getSession(): IWalletConnectSession | null {
const session = this.#storage.getItem("session");
if (!session) {
return null;
}
const sessionObj = JSON.parse(session);
return sessionObj as IWalletConnectSession;
}
setSession(session: IWalletConnectSession): IWalletConnectSession {
this.#storage.setItem("session", JSON.stringify(session));
return session;
}
removeSession() {
this.#storage.removeItem("session");
}
}
-1
View File
@@ -6,5 +6,4 @@ export type { DAppMetaData } from "./types/dAppMeta";
export { CredentialsStorage } from "./CredentialsStorage";
export * from "./types/walletConnect";
export { WalletConnectV1Handler } from "./WalletConnect/WalletConnectV1Handler";
export { WalletConnectV2Handler } from "./WalletConnect/WalletConnectV2Handler";
@@ -81,6 +81,10 @@ export class PaperWalletConnector extends Connector<PaperWalletConnectionArgs> {
throw new Error("Error connecting User");
}
if (options?.chainId) {
this.switchChain(options.chainId);
}
this.setupListeners();
return this.getAddress();
}
@@ -0,0 +1,48 @@
import { InjectedConnector, InjectedConnectorOptions } from "../injected";
import { assertWindowEthereum } from "../../utils/assertWindowEthereum";
import { Ethereum } from "../injected/types";
import type { Chain } from "@thirdweb-dev/chains";
import { AsyncStorage } from "../../../core/AsyncStorage";
type TrustConnectorConstructorArg = {
chains?: Chain[];
connectorStorage: AsyncStorage;
options?: InjectedConnectorOptions;
};
export class TrustConnector extends InjectedConnector {
constructor(arg: TrustConnectorConstructorArg) {
const defaultOptions = {
name: "Trust",
getProvider() {
function getReady(ethereum?: Ethereum) {
const isTrust = !!ethereum?.isTrust;
if (!isTrust) {
return;
}
return ethereum;
}
if (typeof window === "undefined") {
return;
}
if (assertWindowEthereum(globalThis.window)) {
if (globalThis.window.ethereum?.providers) {
return globalThis.window.ethereum.providers.find(getReady);
}
return getReady(globalThis.window.ethereum);
}
},
};
const options = {
...defaultOptions,
...arg.options,
};
super({
chains: arg.chains,
options,
connectorStorage: arg.connectorStorage,
});
}
}
@@ -1,361 +1,9 @@
import { AsyncStorage } from "../../../core/AsyncStorage";
import {
SwitchChainError,
UserRejectedRequestError,
normalizeChainId,
WagmiConnector,
ProviderRpcError,
} from "../../../lib/wagmi-core";
import { walletIds } from "../../constants/walletIds";
import type WalletConnectProvider from "./walletconnect-legacy-provider/index";
import type { Chain } from "@thirdweb-dev/chains";
import { IWalletConnectSession } from "@walletconnect/legacy-types";
import { utils, providers } from "ethers";
import { WalletConnectConnector } from "../wallet-connect";
/**
* Wallets that support chain switching through WalletConnect
* - imToken (token.im)
* - MetaMask (metamask.io)
* - Rainbow (rainbow.me)
* - Trust Wallet (trustwallet.com)
* @deprecated Use `WalletConnectConnector` instead
*
* The WalletConnect v1.0 protocol has been shut down and no longer works.
* To avoid breaking change, `WalletConnectV1Connector` is still available but is an alias of `WalletConnectConnector`.
*/
// const switchChainAllowedRegex = /(imtoken|metamask|rainbow|trust wallet)/i;
const LAST_USED_CHAIN_ID = "last-used-chain-id";
const LAST_SESSION = "last-session";
type WalletConnectOptions = ConstructorParameters<
typeof WalletConnectProvider
>[0];
type WalletConnectSigner = providers.JsonRpcSigner;
export class WalletConnectV1Connector extends WagmiConnector<
WalletConnectProvider,
WalletConnectOptions,
WalletConnectSigner
> {
readonly id = walletIds.walletConnectV1;
readonly name = "WalletConnectV1";
readonly ready = true;
#provider?: WalletConnectProvider;
#storage: AsyncStorage;
walletName?: string;
constructor(config: {
chains?: Chain[];
storage: AsyncStorage;
options: WalletConnectOptions;
}) {
super(config);
this.#storage = config.storage;
}
async connect({ chainId }: { chainId?: number } = {}) {
try {
let targetChainId = chainId;
if (!targetChainId) {
const lastUsedChainIdStr = await this.#storage.getItem(
LAST_USED_CHAIN_ID,
);
const lastUsedChainId = lastUsedChainIdStr
? parseInt(lastUsedChainIdStr)
: undefined;
if (lastUsedChainId && !this.isChainUnsupported(lastUsedChainId)) {
targetChainId = lastUsedChainId;
}
}
const provider = await this.getProvider({
chainId: targetChainId,
create: true,
});
this.setupListeners();
// Defer message to the next tick to ensure wallet connect data (provided by `.enable()`) is available
setTimeout(() => this.emit("message", { type: "connecting" }), 0);
const accounts = await provider.enable();
const account = utils.getAddress(accounts[0] as string);
let id = await this.getChainId();
let unsupported = this.isChainUnsupported(id);
// Not all WalletConnect options support programmatic chain switching
// Only enable for wallet options that do
this.walletName = provider.connector?.peerMeta?.name ?? "";
// switch to target chainId
if (chainId) {
try {
await this.switchChain(chainId);
id = chainId;
unsupported = this.isChainUnsupported(id);
} catch (e) {
console.error(`could not switch to desired chain id: ${chainId} `, e);
}
}
this.#handleConnected();
this.emit("connect", {
account,
chain: { id, unsupported },
provider,
});
return {
account,
chain: { id, unsupported },
provider: new providers.Web3Provider(
provider as providers.ExternalProvider,
),
};
} catch (error) {
if (/user closed modal/i.test((error as ProviderRpcError).message)) {
throw new UserRejectedRequestError(error);
}
throw error;
}
}
async disconnect() {
const provider = await this.getProvider();
await provider.disconnect();
}
async getAccount() {
const provider = await this.getProvider();
const accounts = provider.accounts;
// return checksum address
return utils.getAddress(accounts[0] as string);
}
async getChainId() {
const provider = await this.getProvider();
const chainId = normalizeChainId(provider.chainId);
return chainId;
}
async getProvider({
chainId,
create,
}: { chainId?: number; create?: boolean } = {}) {
// Force create new provider
if (!this.#provider || chainId || create) {
const rpc = !this.options?.infuraId
? this.chains.reduce(
(rpc_, chain) => ({
...rpc_,
[chain.chainId]: chain.rpc[0],
}),
{},
)
: {};
const WalletConnectProvider = (
await import("./walletconnect-legacy-provider/index")
).default;
const sessionStr = await this.#storage.getItem(LAST_SESSION);
const session = sessionStr ? JSON.parse(sessionStr) : undefined;
this.walletName = session?.peerMeta?.name || undefined;
this.#provider = new WalletConnectProvider({
...this.options,
chainId,
rpc: { ...rpc, ...this.options?.rpc },
session: session ? (session as IWalletConnectSession) : undefined,
});
}
return this.#provider;
}
async getSigner({ chainId }: { chainId?: number } = {}) {
const [provider, account] = await Promise.all([
this.getProvider({ chainId }),
this.getAccount(),
]);
return new providers.Web3Provider(
provider as providers.ExternalProvider,
chainId,
).getSigner(account);
}
async isAuthorized() {
try {
const account = await this.getAccount();
return !!account;
} catch {
return false;
}
}
async switchChain(chainId: number) {
const provider = await this.getProvider();
const chainIdHex = utils.hexValue(chainId);
try {
// Set up a race between `wallet_switchEthereumChain` & the `chainChanged` event
// to ensure the chain has been switched. This is because there could be a case
// where a wallet may not resolve the `wallet_switchEthereumChain` method, or
// resolves slower than `chainChanged`.
await Promise.race([
provider.request({
method: "wallet_switchEthereumChain",
params: [{ chainId: chainIdHex }],
}),
new Promise((res) =>
this.on("change", ({ chain }) => {
if (chain?.id === chainId) {
res(chainId);
}
}),
),
]);
return (
this.chains.find((x) => x.chainId === chainId) ??
({
chainId: chainId,
name: `Chain ${chainIdHex}`,
network: `${chainIdHex}`,
nativeCurrency: { name: "Ether", decimals: 18, symbol: "ETH" },
rpc: [""],
shortName: "eth",
chain: "ETH",
slug: "ethereum",
testnet: false,
} as Chain)
);
} catch (error) {
const message =
typeof error === "string"
? error
: (error as ProviderRpcError)?.message;
if (/user rejected request/i.test(message)) {
throw new UserRejectedRequestError(error);
}
const chain = this.chains.find((x) => x.chainId === chainId);
// if chain is not supported
if (!chain) {
throw new SwitchChainError(
`Chain ${chainId} is not added in the list of supported chains`,
);
}
// if chain is not configured in the wallet
if (/Unrecognized chain ID/i.test(message)) {
// configure it
this.emit("message", { type: "add_chain" });
const blockExplorerUrls = this.getBlockExplorerUrls(chain);
await provider.request({
method: "wallet_addEthereumChain",
params: [
{
chainId: chainIdHex,
chainName: chain.name,
nativeCurrency: chain.nativeCurrency,
rpcUrls: chain.rpc,
blockExplorerUrls,
},
],
});
return chain;
} else {
throw new SwitchChainError(error);
}
}
}
async #handleConnected() {
const session = this.#provider?.connector.session;
this.walletName = session?.peerMeta?.name || "";
const sessionStr = JSON.stringify(session);
this.#storage.setItem(LAST_SESSION, sessionStr);
}
async setupListeners() {
if (!this.#provider) {
return;
}
this.#provider.on("accountsChanged", this.onAccountsChanged);
this.#provider.on("chainChanged", this.onChainChanged);
this.#provider.on("disconnect", this.onDisconnect);
this.#provider.on("message", this.onMessage);
this.#provider.on("switchChain", this.onSwitchChain);
this.#provider.on("display_uri", this.onDisplayUri);
this.#provider.on("call_request_sent", this.onRequestSent);
}
#removeListeners() {
if (!this.#provider) {
return;
}
this.#provider.removeListener("accountsChanged", this.onAccountsChanged);
this.#provider.removeListener("chainChanged", this.onChainChanged);
this.#provider.removeListener("disconnect", this.onDisconnect);
this.#provider.removeListener("message", this.onMessage);
this.#provider.removeListener("switchChain", this.onSwitchChain);
this.#provider.removeListener("display_uri", this.onDisplayUri);
this.#provider.removeListener("call_request_sent", this.onRequestSent);
}
protected onSwitchChain = () => {
this.emit("message", { type: "switch_chain" });
};
protected onDisplayUri = async (
error: any,
payload: { params: string[] },
) => {
if (error) {
this.emit("message", { data: error, type: "display_uri_error" });
}
this.emit("message", { data: payload.params[0], type: "display_uri" });
};
protected onRequestSent = (error: any, payload: { params: string[] }) => {
if (error) {
this.emit("message", { data: error, type: "request" });
}
this.emit("message", { data: payload.params[0], type: "request" });
};
protected onMessage = (message: { type: string; data: unknown }) => {
this.emit("message", message);
};
protected onAccountsChanged = (accounts: string[]) => {
if (accounts.length === 0) {
this.emit("disconnect");
} else {
this.emit("change", { account: utils.getAddress(accounts[0] as string) });
}
};
protected onChainChanged = (chainId: number | string) => {
const id = normalizeChainId(chainId);
const unsupported = this.isChainUnsupported(id);
this.#storage.setItem(LAST_USED_CHAIN_ID, String(chainId));
this.emit("change", { chain: { id, unsupported } });
};
protected onDisconnect = async () => {
this.walletName = undefined;
this.#storage.removeItem(LAST_USED_CHAIN_ID);
this.#storage.removeItem(LAST_SESSION);
this.#removeListeners();
this.emit("disconnect");
};
}
export const WalletConnectV1Connector = WalletConnectConnector;
@@ -1,150 +0,0 @@
import SignerConnection from "./signer";
import { IWCEthRpcConnectionOptionsWithSession } from "./types";
import { HttpConnection } from "@walletconnect/jsonrpc-http-connection";
import { JsonRpcProvider } from "@walletconnect/jsonrpc-provider";
import {
IConnector,
IRpcConfig,
SIGNING_METHODS,
} from "@walletconnect/legacy-types";
import { getRpcUrl } from "@walletconnect/legacy-utils";
import {
IEthereumProvider,
ProviderAccounts,
RequestArguments,
} from "eip1193-provider";
import EventEmitter from "eventemitter3";
class WalletConnectProvider implements IEthereumProvider {
public events: any = new EventEmitter();
private rpc: IRpcConfig;
private signer: JsonRpcProvider;
private http: JsonRpcProvider | undefined;
constructor(opts?: IWCEthRpcConnectionOptionsWithSession) {
this.rpc = { infuraId: opts?.infuraId, custom: opts?.rpc };
this.signer = new JsonRpcProvider(new SignerConnection(opts));
const chainId =
(this.signer.connection as SignerConnection).chainId ||
opts?.chainId ||
1;
this.http = this.setHttpProvider(chainId);
this.registerEventListeners();
}
get connected(): boolean {
return (this.signer.connection as SignerConnection).connected;
}
get connector(): IConnector {
return (this.signer.connection as SignerConnection).connector;
}
get accounts(): string[] {
return (this.signer.connection as SignerConnection).accounts;
}
get chainId(): number {
return (this.signer.connection as SignerConnection).chainId;
}
get rpcUrl(): string {
return (this.http?.connection as HttpConnection).url || "";
}
public async request<T = unknown>(args: RequestArguments): Promise<T> {
switch (args.method) {
case "eth_requestAccounts":
await this.connect();
return (this.signer.connection as any).accounts;
case "eth_accounts":
return (this.signer.connection as any).accounts;
case "eth_chainId":
return (this.signer.connection as any).chainId;
default:
break;
}
if (SIGNING_METHODS.includes(args.method)) {
return this.signer.request(args);
}
if (typeof this.http === "undefined") {
throw new Error(
`Cannot request JSON-RPC method (${args.method}) without provided rpc url`,
);
}
return this.http.request(args);
}
public async enable(): Promise<ProviderAccounts> {
const accounts = await this.request({ method: "eth_requestAccounts" });
return accounts as ProviderAccounts;
}
public async connect(): Promise<void> {
if (!this.signer.connection.connected) {
await this.signer.connect();
}
}
public async disconnect(): Promise<void> {
if (this.signer.connection.connected) {
await this.signer.disconnect();
}
}
public on(event: any, listener: any): void {
this.events.on(event, listener);
}
public once(event: string, listener: any): void {
this.events.once(event, listener);
}
public removeListener(event: string, listener: any): void {
this.events.removeListener(event, listener);
}
public off(event: string, listener: any): void {
this.events.off(event, listener);
}
get isWalletConnect() {
return true;
}
// ---------- Private ----------------------------------------------- //
private registerEventListeners() {
this.signer.connection.on("accountsChanged", (accounts: string[]) => {
this.events.emit("accountsChanged", accounts);
});
this.signer.connection.on("chainChanged", (chainId: number) => {
this.http = this.setHttpProvider(chainId);
this.events.emit("chainChanged", chainId);
});
this.connector.on(
"display_uri",
(error: any, payload: { params: string[] }) => {
this.events.emit("display_uri", error, payload);
},
);
this.connector.on(
"call_request_sent",
(error: any, payload: { params: string[] }) => {
this.events.emit("call_request_sent", error, payload);
},
);
this.signer.on("disconnect", () => {
this.events.emit("disconnect");
});
}
private setHttpProvider(chainId: number): JsonRpcProvider | undefined {
const rpcUrl = getRpcUrl(chainId, this.rpc);
if (typeof rpcUrl === "undefined") {
return undefined;
}
const http = new JsonRpcProvider(new HttpConnection(rpcUrl));
return http;
}
}
export default WalletConnectProvider;
@@ -1,263 +0,0 @@
import type { IWCEthRpcConnectionOptionsWithSession } from "./types";
import {
IJsonRpcConnection,
JsonRpcError,
JsonRpcResponse,
} from "@walletconnect/jsonrpc-types";
import { formatJsonRpcError } from "@walletconnect/jsonrpc-utils";
import WalletConnect from "@walletconnect/legacy-client";
import QRCodeModal from "@walletconnect/legacy-modal";
import {
IConnector,
IJsonRpcResponseError,
IJsonRpcResponseSuccess,
IQRCodeModalOptions,
} from "@walletconnect/legacy-types";
import EventEmitter from "eventemitter3";
export class SignerConnection extends IJsonRpcConnection {
public events: any = new EventEmitter();
public accounts: string[] = [];
public chainId = 1;
private pending = false;
private wc: IConnector | undefined;
private bridge = "https://bridge.walletconnect.org";
private qrcode = true;
private qrcodeModalOptions: IQRCodeModalOptions | undefined = undefined;
private opts: IWCEthRpcConnectionOptionsWithSession | undefined;
constructor(opts?: IWCEthRpcConnectionOptionsWithSession) {
super();
this.opts = opts;
this.chainId = opts?.chainId || this.chainId;
this.wc = this.register(opts);
}
get connected(): boolean {
return typeof this.wc !== "undefined" && this.wc.connected;
}
get connecting(): boolean {
return this.pending;
}
get connector(): IConnector {
this.wc = this.register(this.opts);
return this.wc;
}
public on(event: string, listener: any) {
this.events.on(event, listener);
}
public once(event: string, listener: any) {
this.events.once(event, listener);
}
public off(event: string, listener: any) {
this.events.off(event, listener);
}
public removeListener(event: string, listener: any) {
this.events.removeListener(event, listener);
}
public async open(chainId?: number): Promise<void> {
if (this.connected) {
this.onOpen();
return;
}
return new Promise((resolve, reject): void => {
this.on("error", (err: any) => {
reject(err);
});
this.on("open", () => {
resolve();
});
this.create(chainId);
});
}
public async close(): Promise<void> {
if (typeof this.wc === "undefined") {
return;
}
if (this.wc.connected) {
this.wc.killSession();
}
this.onClose();
}
public async send(payload: any) {
this.wc = this.register(this.opts);
if (!this.connected) {
await this.open();
}
this.sendPayload(payload)
.then((res: any) => this.events.emit("payload", res))
.catch((e) =>
this.events.emit("payload", formatJsonRpcError(payload.id, e.message)),
);
}
public async sendAsync(payload: any) {
// noop
console.log("sendAsync", payload);
}
// ---------- Private ----------------------------------------------- //
private register(opts?: IWCEthRpcConnectionOptionsWithSession): IConnector {
if (this.wc) {
return this.wc;
}
this.opts = opts || this.opts;
this.bridge = opts?.connector
? opts.connector.bridge
: opts?.bridge || "https://bridge.walletconnect.org";
this.qrcode = typeof opts?.qrcode === "undefined" || opts.qrcode !== false;
this.chainId =
typeof opts?.chainId !== "undefined" ? opts.chainId : this.chainId;
this.qrcodeModalOptions = opts?.qrcodeModalOptions;
const connectorOpts = {
bridge: this.bridge,
qrcodeModal: this.qrcode ? QRCodeModal : undefined,
qrcodeModalOptions: this.qrcodeModalOptions,
storageId: opts?.storageId,
signingMethods: opts?.signingMethods,
clientMeta: opts?.clientMeta,
session: opts?.session,
};
this.wc =
typeof opts?.connector !== "undefined"
? opts.connector
: new WalletConnect(connectorOpts);
if (typeof this.wc === "undefined") {
throw new Error("Failed to register WalletConnect connector");
}
if (this.wc.accounts.length) {
this.accounts = this.wc.accounts;
}
if (this.wc.chainId) {
this.chainId = this.wc.chainId;
}
// this.accounts = this.wc.accounts;
// this.chainId = this.wc.chainId;
this.registerConnectorEvents();
return this.wc;
}
private onOpen(wc?: IConnector) {
this.pending = false;
if (wc) {
this.wc = wc;
}
this.events.emit("open");
}
private onClose() {
this.pending = false;
if (this.wc) {
this.wc = undefined;
}
this.events.emit("close");
}
public onError(
payload: any,
message = "Failed or Rejected Request",
code = -32000,
): JsonRpcError {
const errorPayload = {
id: payload.id,
jsonrpc: payload.jsonrpc,
error: { code, message },
};
this.events.emit("payload", errorPayload);
return errorPayload;
}
private create(chainId?: number): void {
this.wc = this.register(this.opts);
this.chainId = chainId || this.chainId;
if (this.connected || this.pending) {
return;
}
this.pending = true;
this.registerConnectorEvents();
this.wc
.createSession({ chainId: this.chainId })
.then(() => this.events.emit("created"))
.catch((e: Error) => this.events.emit("error", e));
}
private registerConnectorEvents() {
this.wc = this.register(this.opts);
this.wc.on("connect", (err: Error | null) => {
if (err) {
this.events.emit("error", err);
return;
}
this.accounts = this.wc?.accounts || [];
this.chainId = this.wc?.chainId || this.chainId;
this.onOpen();
});
this.wc.on("disconnect", (err: Error | null) => {
if (err) {
this.events.emit("error", err);
return;
}
this.onClose();
});
this.wc.on("modal_closed", () => {
this.events.emit("error", new Error("User closed modal"));
});
this.wc.on("session_update", (_error, payload) => {
const { accounts, chainId } = payload.params[0];
if (!this.accounts || (accounts && this.accounts !== accounts)) {
this.accounts = accounts;
this.events.emit("accountsChanged", accounts);
}
if (!this.chainId || (chainId && this.chainId !== chainId)) {
this.chainId = chainId;
this.events.emit("chainChanged", chainId);
}
});
}
private async sendPayload(payload: any): Promise<JsonRpcResponse> {
this.wc = this.register(this.opts);
try {
const response = await this.wc.unsafeSend(payload);
return this.sanitizeResponse(response);
} catch (error) {
return this.onError(payload, (error as any).message);
}
}
private sanitizeResponse(
response: IJsonRpcResponseSuccess | IJsonRpcResponseError,
): JsonRpcResponse {
return typeof (response as IJsonRpcResponseError).error !== "undefined" &&
typeof (response as IJsonRpcResponseError).error.code === "undefined"
? formatJsonRpcError(
response.id,
(response as IJsonRpcResponseError).error.message,
)
: (response as JsonRpcResponse);
}
}
export default SignerConnection;
@@ -1,7 +0,0 @@
import {
IWCEthRpcConnectionOptions,
IWalletConnectSession,
} from "@walletconnect/legacy-types";
export type IWCEthRpcConnectionOptionsWithSession =
IWCEthRpcConnectionOptions & { session?: IWalletConnectSession };
@@ -120,9 +120,11 @@ export class WalletConnectConnector extends WagmiConnector<
// If there no active session, or the chains are stale, connect.
if (!provider.session || isChainsStale) {
const optionalChains = this.chains
.filter((chain) => chain.chainId !== targetChainId)
.map((optionalChain) => optionalChain.chainId);
// 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);
this.emit("message", { type: "connecting" });
@@ -133,7 +135,9 @@ export class WalletConnectConnector extends WagmiConnector<
optionalChains.length > 0 ? optionalChains : [targetChainId],
});
this.#setRequestedChainsIds(this.chains.map(({ chainId }) => chainId));
await this.#setRequestedChainsIds(
this.chains.map(({ chainId }) => chainId),
);
}
// If session exists and chains are authorized, enable provider for required chain
@@ -168,7 +172,7 @@ export class WalletConnectConnector extends WagmiConnector<
}
} finally {
this.#removeListeners();
this.#setRequestedChainsIds([]);
await this.#setRequestedChainsIds([]);
}
}
@@ -267,7 +271,7 @@ export class WalletConnectConnector extends WagmiConnector<
});
const requestedChains = await this.#getRequestedChainsIds();
requestedChains.push(chainId);
this.#setRequestedChainsIds(requestedChains);
await this.#setRequestedChainsIds(requestedChains);
}
await provider.request({
method: "wallet_switchEthereumChain",
@@ -314,6 +318,7 @@ export class WalletConnectConnector extends WagmiConnector<
optionalEvents: OPTIONAL_EVENTS,
chains: [defaultChain],
optionalChains: optionalChains,
metadata: {
name: this.options.dappMetadata.name,
description: this.options.dappMetadata.description || "",
@@ -324,11 +329,7 @@ export class WalletConnectConnector extends WagmiConnector<
this.chains.map((chain) => [chain.chainId, chain.rpc[0]]),
),
qrModalOptions: {
...this.options.qrModalOptions,
explorerAllowList: [],
explorerDenyList: [],
},
qrModalOptions: this.options.qrModalOptions,
});
}
}
@@ -403,8 +404,8 @@ export class WalletConnectConnector extends WagmiConnector<
this.#provider.removeListener("connect", this.onConnect);
}
#setRequestedChainsIds(chains: number[]) {
this.#storage.setItem(REQUESTED_CHAINS_KEY, JSON.stringify(chains));
async #setRequestedChainsIds(chains: number[]) {
await this.#storage.setItem(REQUESTED_CHAINS_KEY, JSON.stringify(chains));
}
async #getRequestedChainsIds(): Promise<number[]> {
@@ -438,16 +439,16 @@ export class WalletConnectConnector extends WagmiConnector<
}
};
protected onChainChanged = (chainId: number | string) => {
protected onChainChanged = async (chainId: number | string) => {
const id = Number(chainId);
const unsupported = this.isChainUnsupported(id);
this.#storage.setItem(LAST_USED_CHAIN_ID, String(chainId));
await this.#storage.setItem(LAST_USED_CHAIN_ID, String(chainId));
this.emit("change", { chain: { id, unsupported } });
};
protected onDisconnect = () => {
this.#setRequestedChainsIds([]);
this.#storage.removeItem(LAST_USED_CHAIN_ID);
protected onDisconnect = async () => {
await this.#setRequestedChainsIds([]);
await this.#storage.removeItem(LAST_USED_CHAIN_ID);
this.emit("disconnect");
};
@@ -1,3 +1,5 @@
import type { WalletConnectModalConfig } from "@walletconnect/modal";
export type QRModalOptions = {
/**
* When using Web3Modal in standalone mode (without wagmi) you can define array of custom chains via this option.
@@ -6,7 +8,7 @@ export type QRModalOptions = {
*
* https://docs.walletconnect.com/2.0/web3modal/options#standalonechains-optional
*/
standaloneChains?: string[];
standaloneChains?: string[];
/**
* You can define an array of custom mobile wallets.
*
@@ -18,7 +20,7 @@ export type QRModalOptions = {
*
* https://docs.walletconnect.com/2.0/web3modal/options#mobilewallets-optional
*/
mobileWallets?: MobileWallet[];
mobileWallets?: WalletConnectModalConfig['mobileWallets']
/**
* You can define an array of custom desktop or web based wallets.
*
@@ -30,7 +32,7 @@ export type QRModalOptions = {
*
* https://docs.walletconnect.com/2.0/web3modal/options#desktopwallets-optional
*/
desktopWallets?: DesktopWallet[];
desktopWallets?: WalletConnectModalConfig['desktopWallets'];
/**
* Array of wallet id's and their logo mappings.
*
@@ -149,62 +151,7 @@ export type QRModalOptions = {
*
* https://docs.walletconnect.com/2.0/web3modal/options#themevariables-optional
*/
themeVariables?: {
"--w3m-z-index"?: string;
"--w3m-accent-color"?: string;
"--w3m-accent-fill-color"?: string;
"--w3m-background-color"?: string;
"--w3m-background-image-url"?: string;
"--w3m-logo-image-url"?: string;
"--w3m-background-border-radius"?: string;
"--w3m-container-border-radius"?: string;
"--w3m-wallet-icon-border-radius"?: string;
"--w3m-wallet-icon-large-border-radius"?: string;
"--w3m-wallet-icon-small-border-radius"?: string;
"--w3m-input-border-radius"?: string;
"--w3m-notification-border-radius"?: string;
"--w3m-button-border-radius"?: string;
"--w3m-secondary-button-border-radius"?: string;
"--w3m-icon-button-border-radius"?: string;
"--w3m-button-hover-highlight-border-radius"?: string;
"--w3m-font-family"?: string;
"--w3m-text-big-bold-size"?: string;
"--w3m-text-big-bold-weight"?: string;
"--w3m-text-big-bold-line-height"?: string;
"--w3m-text-big-bold-letter-spacing"?: string;
"--w3m-text-big-bold-text-transform"?: string;
"--w3m-text-big-bold-font-family"?: string;
"--w3m-text-medium-regular-size"?: string;
"--w3m-text-medium-regular-weight"?: string;
"--w3m-text-medium-regular-line-height"?: string;
"--w3m-text-medium-regular-letter-spacing"?: string;
"--w3m-text-medium-regular-text-transform"?: string;
"--w3m-text-medium-regular-font-family"?: string;
"--w3m-text-small-regular-size"?: string;
"--w3m-text-small-regular-weight"?: string;
"--w3m-text-small-regular-line-height"?: string;
"--w3m-text-small-regular-letter-spacing"?: string;
"--w3m-text-small-regular-text-transform"?: string;
"--w3m-text-small-regular-font-family"?: string;
"--w3m-text-small-thin-size"?: string;
"--w3m-text-small-thin-weight"?: string;
"--w3m-text-small-thin-line-height"?: string;
"--w3m-text-small-thin-letter-spacing"?: string;
"--w3m-text-small-thin-text-transform"?: string;
"--w3m-text-small-thin-font-family"?: string;
"--w3m-text-xsmall-bold-size"?: string;
"--w3m-text-xsmall-bold-weight"?: string;
"--w3m-text-xsmall-bold-line-height"?: string;
"--w3m-text-xsmall-bold-letter-spacing"?: string;
"--w3m-text-xsmall-bold-text-transform"?: string;
"--w3m-text-xsmall-bold-font-family"?: string;
"--w3m-text-xsmall-regular-size"?: string;
"--w3m-text-xsmall-regular-weight"?: string;
"--w3m-text-xsmall-regular-line-height"?: string;
"--w3m-text-xsmall-regular-letter-spacing"?: string;
"--w3m-text-xsmall-regular-text-transform"?: string;
"--w3m-text-xsmall-regular-font-family"?: string;
};
themeVariables?: WalletConnectModalConfig['themeVariables']
/**
* Puts Web3Modal into dark or light mode. Defaults to user's system preference.
*
@@ -213,19 +160,3 @@ export type QRModalOptions = {
themeMode?: "dark" | "light";
};
export interface MobileWallet {
id: string;
name: string;
links: {
universal: string;
native?: string;
};
}
export interface DesktopWallet {
id: string;
name: string;
links: {
native: string;
universal: string;
};
}
@@ -5,6 +5,7 @@ export const walletIds = {
localWallet: "localWallet",
smartWallet: "smartWallet",
safe: "safe",
trust: "trust",
walletConnectV1: "walletConnectV1",
walletConnect: "walletConnect",
magicLink: "magicLink",
+1
View File
@@ -38,6 +38,7 @@ export * from "./wallets/smart-wallet";
export * from "./wallets/ethers";
export * from "./wallets/private-key";
export * from "./wallets/zerion";
export * from "./wallets/trust";
export type { Chain } from "@thirdweb-dev/chains";
+30 -23
View File
@@ -1,15 +1,32 @@
import type { WalletConnectV1Connector as WalletConnectV1ConnectorType } from "../connectors/wallet-connect-v1";
import type { WalletConnectConnector as WalletConnectConnectorType } from "../connectors/wallet-connect";
import type { QRModalOptions } from "../connectors/wallet-connect/qrModalOptions";
import { Connector, WagmiAdapter } from "../interfaces/connector";
import { assertWindowEthereum } from "../utils/assertWindowEthereum";
import { AbstractClientWallet, WalletOptions } from "./base";
import type { MetaMaskConnector as MetamaskConnectorType } from "../connectors/metamask";
import { walletIds } from "../constants/walletIds";
import { TW_WC_PROJECT_ID } from "../constants/wc";
type MetamaskAdditionalOptions = {
/**
* Whether to display the Wallet Connect QR code Modal for connecting to MetaMask on mobile if MetaMask is not injected.
* Whether to open the default Wallet Connect QR code Modal for connecting to Zerion Wallet on mobile if Zerion is not injected when calling connect().
*/
qrcode?: boolean;
/**
* When connecting MetaMask using the QR Code - Wallet Connect connector is used which requires a project id.
* This project id is Your project’s unique identifier for wallet connect that can be obtained at cloud.walletconnect.com.
*
* https://docs.walletconnect.com/2.0/web3modal/options#projectid-required
*/
projectId?: string;
/**
* options to customize the Wallet Connect QR Code Modal ( only relevant when qrcode is true )
*
* https://docs.walletconnect.com/2.0/web3modal/options
*/
qrModalOptions?: QRModalOptions;
};
export type MetamaskWalletOptions = WalletOptions<MetamaskAdditionalOptions>;
@@ -22,7 +39,7 @@ type ConnectWithQrCodeArgs = {
export class MetaMaskWallet extends AbstractClientWallet<MetamaskAdditionalOptions> {
connector?: Connector;
walletConnectConnector?: WalletConnectV1ConnectorType;
walletConnectConnector?: WalletConnectConnectorType;
metamaskConnector?: MetamaskConnectorType;
isInjected: boolean;
@@ -74,21 +91,18 @@ export class MetaMaskWallet extends AbstractClientWallet<MetamaskAdditionalOptio
this.connector = new WagmiAdapter(metamaskConnector);
} else {
const { WalletConnectV1Connector } = await import(
"../connectors/wallet-connect-v1"
const { WalletConnectConnector } = await import(
"../connectors/wallet-connect"
);
const walletConnectConnector = new WalletConnectV1Connector({
const walletConnectConnector = new WalletConnectConnector({
chains: this.chains,
storage: this.walletStorage,
options: {
clientMeta: {
name: this.dappMetadata.name,
description: this.dappMetadata.description || "",
url: this.dappMetadata.url,
icons: [this.dappMetadata.logoUrl || ""],
},
projectId: this.options?.projectId || TW_WC_PROJECT_ID, // TODO,
storage: this.walletStorage,
qrcode: this.options?.qrcode,
dappMetadata: this.dappMetadata,
qrModalOptions: this.options?.qrModalOptions,
},
});
@@ -128,16 +142,9 @@ export class MetaMaskWallet extends AbstractClientWallet<MetamaskAdditionalOptio
const wcProvider = await wcConnector.getProvider();
// set a listener for display_uri event
wcProvider.connector.on(
"display_uri",
(error, payload: { params: string[] }) => {
options.onQrCodeUri(payload.params[0]);
},
);
// trigger the display_uri event to get the QR code
await wcProvider.enable();
// connected to app here
wcProvider.on("display_uri", (uri) => {
options.onQrCodeUri(uri);
});
// trigger connect flow
this.connect({ chainId: options.chainId }).then(options.onConnected);

Some files were not shown because too many files have changed in this diff Show More