Compare commits

...
Author SHA1 Message Date
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
1119f63a47 Version Packages (#991)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-05-09 23:23:42 -07:00
Joaquim VergesandGitHub 32908b7683 [SDK] rename extension detection functions (#992) 2023-05-10 06:03:26 +00:00
Joaquim VergesandGitHub 6a4aab0b8a [Wallets] LocalWalletNode fix + cleanup SmartWallet logs (#990) 2023-05-10 05:15:22 +00:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
e87d166d2b Version Packages (#965)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-05-09 20:46:05 -07:00
Adam MajmudarandGitHub af4b535637 [SDK] Add chunked snapshot input parsing (#986) 2023-05-09 04:29:35 +00:00
Joaquim VergesandGitHub a2df187bc1 [Sol] Fix token deployement (#985) 2023-05-09 03:30:41 +00:00
iketwandGitHub ea0f9479a3 [ReactNative] setup support for custom wallets UI with LocalWallet as example (#981) 2023-05-08 19:49:19 +00:00
Manan TankandGitHub bbe329a656 [react, react-core] Wallet UI API refactor (#979) 2023-05-08 15:09:46 +00:00
Joaquim VergesandGitHub 05ebbc15a0 [Wallets] export ethers and pkey wallets (#978) 2023-05-05 23:50:07 +00:00
Manan TankandGitHub 93bd57337b Connect wallet Modal UI fixes (#977) 2023-05-05 22:29:36 +00:00
Adam MajmudarandGitHub eb521d240a [SDK] Expose raw error on transaction error (#976) 2023-05-05 19:46:57 +00:00
Adam MajmudarandGitHub aa9b6acc3f [SDK] Add ability to resolveAddress (#975) 2023-05-05 19:16:33 +00:00
iketwandGitHub 4ca557ae4a [Wallet] Rename Connector to WagmiConnector (#974) 2023-05-05 16:41:52 +00:00
Manan TankandGitHub 93bdec061d [wallets] various fixes (#973) 2023-05-04 21:41:28 +00:00
Joaquim VergesandGitHub c6e9406808 Update wallets README (#971) 2023-05-04 19:00:34 +00:00
iketwandGitHub 12ad7fead2 [ReactNative] Remove gap for pre-71 RN apps (#969) 2023-05-04 17:23:21 +00:00
Manan TankandGitHub 7d7685e3fa [wallets] Magic and Local wallet improvements (#967) 2023-05-04 17:12:09 +00:00
iketwandGitHub 9640e0731f [ReactNative] Add guest mode warnings (#968) 2023-05-04 16:59:45 +00:00
Manan TankandGitHub 87021cee45 [react] Connect wallet UI improvements (#966) 2023-05-04 16:45:24 +00:00
Jarrod WattsandGitHub 5305b42db5 [REACT, SDK] - Use generated contract's types for useContract Read & Write (#954) 2023-05-04 14:42:15 +00:00
Manan TankandGitHub c8ba96fa00 [react] Wallet UI API refactor, and other fixes (#962) 2023-05-04 11:56:12 +00:00
nkrishangandGitHub 89c821d49e Build deployArgs.json only on deploy --dynamic command (#964) 2023-05-04 06:25:12 +00:00
213 changed files with 4988 additions and 4632 deletions
+14
View File
@@ -1,5 +1,19 @@
# @thirdweb-dev/auth
## 3.0.18
### Patch Changes
- Updated dependencies [[`6a4aab0b`](https://github.com/thirdweb-dev/js/commit/6a4aab0b8a2e0f6ff1b47992a3c1e5426a74f7ff)]:
- @thirdweb-dev/wallets@0.2.19
## 3.0.17
### Patch Changes
- Updated dependencies [[`93bdec06`](https://github.com/thirdweb-dev/js/commit/93bdec061dc05ab133e79f5f739dcae9b5393f53), [`ea0f9479`](https://github.com/thirdweb-dev/js/commit/ea0f9479a38d442201e367fce1234c130228fde6), [`05ebbc15`](https://github.com/thirdweb-dev/js/commit/05ebbc15a012855735fba2aa93887b88e14295d1), [`4ca557ae`](https://github.com/thirdweb-dev/js/commit/4ca557ae4ab225e39decc3b7a01a04c0d8e464c7), [`7d7685e3`](https://github.com/thirdweb-dev/js/commit/7d7685e3fab5780b3c1d26b8ef431b96f8486972), [`93bd5733`](https://github.com/thirdweb-dev/js/commit/93bd57337b7d2c2fcd252987d10df3206c839daf)]:
- @thirdweb-dev/wallets@0.2.18
## 3.0.16
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/auth",
"version": "3.0.16",
"version": "3.0.18",
"main": "dist/thirdweb-dev-auth.cjs.js",
"module": "dist/thirdweb-dev-auth.esm.js",
"browser": {
+12
View File
@@ -1,5 +1,17 @@
# @thirdweb-dev/chains
## 0.1.15
### Patch Changes
- [#990](https://github.com/thirdweb-dev/js/pull/990) [`6a4aab0b`](https://github.com/thirdweb-dev/js/commit/6a4aab0b8a2e0f6ff1b47992a3c1e5426a74f7ff) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - updated chains
## 0.1.14
### Patch Changes
- [#954](https://github.com/thirdweb-dev/js/pull/954) [`5305b42d`](https://github.com/thirdweb-dev/js/commit/5305b42db554b69f903b3d95f3ba0eeddabd6114) Thanks [@jarrodwatts](https://github.com/jarrodwatts)! - useContract now types correctly for thirdweb generate
## 0.1.13
### Patch Changes
+40
View File
@@ -0,0 +1,40 @@
import type { Chain } from "../src/types";
export default {
"name": "Coinbit Mainnet",
"chain": "Coinbit",
"rpc": [
"https://coinbit.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://coinbit-rpc-mainnet.chain.sbcrypto.app"
],
"faucets": [],
"nativeCurrency": {
"name": "Gas IDR",
"symbol": "GIDR",
"decimals": 18
},
"infoURL": "https://crypto.stockbit.com/",
"shortName": "coinbit",
"chainId": 112,
"networkId": 112,
"icon": {
"url": "ipfs://QmdaQRUbAXJGfHeJ8jaB8WVh8CCmHExq8VjvAfXpLWWQEo",
"width": 760,
"height": 760,
"format": "png"
},
"explorers": [
{
"name": "blockscout",
"url": "https://coinbit-explorer.chain.sbcrypto.app",
"icon": {
"url": "ipfs://bafybeifu5tpui7dk5cjoo54kde7pmuthvnl7sdykobuarsxgu7t2izurnq",
"width": 512,
"height": 512,
"format": "png"
},
"standard": "EIP3091"
}
],
"testnet": false,
"slug": "coinbit"
} as const satisfies Chain;
+34
View File
@@ -0,0 +1,34 @@
import type { Chain } from "../src/types";
export default {
"name": "ARC Mainnet",
"chain": "ARC",
"icon": {
"url": "ipfs://bafybeiady63oqduls2pm4aaykzjhahblagokhnpsc5qeq5dmkxqelh7i2i",
"width": 512,
"height": 512,
"format": "png"
},
"rpc": [
"https://arc.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://rpc-main-1.archiechain.io"
],
"faucets": [],
"nativeCurrency": {
"name": "ARC",
"symbol": "ARC",
"decimals": 18
},
"infoURL": "https://archiechain.io/",
"shortName": "ARC",
"chainId": 1243,
"networkId": 1243,
"explorers": [
{
"name": "archiescan",
"url": "https://app.archiescan.io",
"standard": "none"
}
],
"testnet": false,
"slug": "arc"
} as const satisfies Chain;
+36
View File
@@ -0,0 +1,36 @@
import type { Chain } from "../src/types";
export default {
"name": "ARC Testnet",
"chain": "ARC",
"icon": {
"url": "ipfs://bafybeiady63oqduls2pm4aaykzjhahblagokhnpsc5qeq5dmkxqelh7i2i",
"width": 512,
"height": 512,
"format": "png"
},
"rpc": [
"https://arc-testnet.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://rpc-test-1.archiechain.io"
],
"faucets": [
"https://faucet.archiechain.io"
],
"nativeCurrency": {
"name": "ARC",
"symbol": "ARC",
"decimals": 18
},
"infoURL": "https://archiechain.io/",
"shortName": "TARC",
"chainId": 1244,
"networkId": 1244,
"explorers": [
{
"name": "archiescan",
"url": "https://testnet.archiescan.io",
"standard": "none"
}
],
"testnet": true,
"slug": "arc-testnet"
} as const satisfies Chain;
+34
View File
@@ -0,0 +1,34 @@
import type { Chain } from "../src/types";
export default {
"name": "GIL Testnet",
"chain": "GIL",
"icon": {
"url": "ipfs://QmeDXUAYgQxwaSJLsqWgTqnrJVwicgEyNf9199xAMyRkqA",
"width": 243,
"height": 243,
"format": "svg"
},
"rpc": [
"https://gil-testnet.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://rpc.giltestnet.com"
],
"faucets": [],
"nativeCurrency": {
"name": "GANG",
"symbol": "GANG",
"decimals": 18
},
"infoURL": "https://gaussgang.com/",
"shortName": "gil",
"chainId": 1452,
"networkId": 1452,
"explorers": [
{
"name": "GIL Explorer",
"url": "https://explorer.giltestnet.com",
"standard": "EIP3091"
}
],
"testnet": true,
"slug": "gil-testnet"
} as const satisfies Chain;
+43
View File
@@ -0,0 +1,43 @@
import type { Chain } from "../src/types";
export default {
"name": "Tenet Testnet",
"title": "Tenet Testnet",
"chain": "TENET",
"icon": {
"url": "ipfs://Qmc1gqjWTzNo4pyFSGtQuCu7kRSZZBUVybtTjHn2nNEEPA",
"width": 640,
"height": 640,
"format": "svg"
},
"rpc": [
"https://tenet-testnet.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://rpc.testnet.tenet.org"
],
"faucets": [
"https://faucet.testnet.tenet.org"
],
"nativeCurrency": {
"name": "TENET",
"symbol": "TENET",
"decimals": 18
},
"infoURL": "https://tenet.org/",
"shortName": "tenet-testnet",
"chainId": 155,
"networkId": 155,
"explorers": [
{
"name": "TenetScan Testnet",
"url": "https://testnet.tenetscan.io",
"icon": {
"url": "ipfs://Qmc1gqjWTzNo4pyFSGtQuCu7kRSZZBUVybtTjHn2nNEEPA",
"width": 640,
"height": 640,
"format": "svg"
},
"standard": "EIP3091"
}
],
"testnet": true,
"slug": "tenet-testnet"
} as const satisfies Chain;
+41
View File
@@ -0,0 +1,41 @@
import type { Chain } from "../src/types";
export default {
"name": "Tenet",
"title": "Tenet Mainnet",
"chain": "TENET",
"icon": {
"url": "ipfs://Qmc1gqjWTzNo4pyFSGtQuCu7kRSZZBUVybtTjHn2nNEEPA",
"width": 640,
"height": 640,
"format": "svg"
},
"rpc": [
"https://tenet.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://rpc.tenet.org"
],
"faucets": [],
"nativeCurrency": {
"name": "TENET",
"symbol": "TENET",
"decimals": 18
},
"infoURL": "https://tenet.org/",
"shortName": "tenet",
"chainId": 1559,
"networkId": 1559,
"explorers": [
{
"name": "TenetScan Mainnet",
"url": "https://tenetscan.io",
"icon": {
"url": "ipfs://Qmc1gqjWTzNo4pyFSGtQuCu7kRSZZBUVybtTjHn2nNEEPA",
"width": 640,
"height": 640,
"format": "svg"
},
"standard": "EIP3091"
}
],
"testnet": false,
"slug": "tenet"
} as const satisfies Chain;
+1 -1
View File
@@ -4,7 +4,7 @@ export default {
"chain": "IRIShub",
"rpc": [
"https://irishub-testnet.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"http://34.80.202.172:8545"
"https://evmrpc.nyancat.irisnet.org"
],
"features": [
{
+34
View File
@@ -0,0 +1,34 @@
import type { Chain } from "../src/types";
export default {
"name": "Gauss Mainnet",
"chain": "Gauss",
"icon": {
"url": "ipfs://QmeDXUAYgQxwaSJLsqWgTqnrJVwicgEyNf9199xAMyRkqA",
"width": 243,
"height": 243,
"format": "svg"
},
"rpc": [
"https://gauss.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://rpc.gaussgang.com"
],
"faucets": [],
"nativeCurrency": {
"name": "GANG",
"symbol": "GANG",
"decimals": 18
},
"infoURL": "https://gaussgang.com/",
"shortName": "gauss",
"chainId": 1777,
"networkId": 1777,
"explorers": [
{
"name": "Gauss Explorer",
"url": "https://explorer.gaussgang.com",
"standard": "EIP3091"
}
],
"testnet": false,
"slug": "gauss"
} as const satisfies Chain;
+14 -2
View File
@@ -1,7 +1,7 @@
import type { Chain } from "../src/types";
export default {
"name": "Kerleano",
"title": "Proof of Carbon Reduction testnet",
"title": "Proof of Climate awaReness testnet",
"chain": "CRC",
"status": "active",
"rpc": [
@@ -13,7 +13,7 @@ export default {
"https://github.com/ethereum-pocr/kerleano/blob/main/docs/faucet.md"
],
"nativeCurrency": {
"name": "Carbon Reduction Coin",
"name": "Climate awaReness Coin",
"symbol": "CRC",
"decimals": 18
},
@@ -21,10 +21,22 @@ export default {
"shortName": "kerleano",
"chainId": 1804,
"networkId": 1804,
"icon": {
"url": "ipfs://QmRLwpq47tyEd3rfK4tKRhbTvyb3fc7PCutExnL1XAb37A",
"width": 334,
"height": 360,
"format": "png"
},
"explorers": [
{
"name": "Lite Explorer",
"url": "https://ethereum-pocr.github.io/explorer/kerleano",
"icon": {
"url": "ipfs://QmRLwpq47tyEd3rfK4tKRhbTvyb3fc7PCutExnL1XAb37A",
"width": 334,
"height": 360,
"format": "png"
},
"standard": "EIP3091"
}
],
+14 -2
View File
@@ -1,7 +1,7 @@
import type { Chain } from "../src/types";
export default {
"name": "PoCRNet",
"title": "Proof of Carbon Reduction mainnet",
"title": "Proof of Climate awaReness mainnet",
"chain": "CRC",
"status": "active",
"rpc": [
@@ -11,7 +11,7 @@ export default {
],
"faucets": [],
"nativeCurrency": {
"name": "Carbon Reduction Coin",
"name": "Climate awaReness Coin",
"symbol": "CRC",
"decimals": 18
},
@@ -19,10 +19,22 @@ export default {
"shortName": "pocrnet",
"chainId": 2606,
"networkId": 2606,
"icon": {
"url": "ipfs://QmRLwpq47tyEd3rfK4tKRhbTvyb3fc7PCutExnL1XAb37A",
"width": 334,
"height": 360,
"format": "png"
},
"explorers": [
{
"name": "Lite Explorer",
"url": "https://ethereum-pocr.github.io/explorer/pocrnet",
"icon": {
"url": "ipfs://QmRLwpq47tyEd3rfK4tKRhbTvyb3fc7PCutExnL1XAb37A",
"width": 334,
"height": 360,
"format": "png"
},
"standard": "EIP3091"
}
],
+1 -1
View File
@@ -21,7 +21,7 @@ export default {
"explorers": [
{
"name": "Cronos Testnet Explorer",
"url": "https://testnet.cronoscan.com",
"url": "https://explorer.cronos.org/testnet",
"standard": "none"
}
],
+10 -2
View File
@@ -6,7 +6,9 @@ export default {
"https://la-testnet.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://rpc.testnet.lachain.network"
],
"faucets": [],
"faucets": [
"https://faucet.lachain.network"
],
"nativeCurrency": {
"name": "Test La Coin",
"symbol": "TLA",
@@ -21,7 +23,13 @@ export default {
"shortName": "latestnet",
"chainId": 418,
"networkId": 418,
"explorers": [],
"explorers": [
{
"name": "LA Testnet Explorer",
"url": "https://testexplorer.lachain.network",
"standard": "EIP3091"
}
],
"testnet": true,
"slug": "la-testnet"
} as const satisfies Chain;
+1 -1
View File
@@ -25,7 +25,7 @@ export default {
"explorers": [
{
"name": "blockexplorer",
"url": "https://explorer.camino.foundation",
"url": "https://suite.camino.network/explorer",
"standard": "none"
}
],
-5
View File
@@ -20,11 +20,6 @@ export default {
"chainId": 71401,
"networkId": 71401,
"explorers": [
{
"name": "GWScout Explorer",
"url": "https://gw-testnet-explorer.nervosdao.community",
"standard": "none"
},
{
"name": "GWScan Block Explorer",
"url": "https://v1.testnet.gwscan.com",
-5
View File
@@ -17,11 +17,6 @@ export default {
"chainId": 71402,
"networkId": 71402,
"explorers": [
{
"name": "GWScout Explorer",
"url": "https://gw-mainnet-explorer.nervosdao.community",
"standard": "none"
},
{
"name": "GWScan Block Explorer",
"url": "https://v1.gwscan.com",
+1 -1
View File
@@ -3,7 +3,7 @@ export default {
"name": "TMY Chain",
"chain": "TMY",
"icon": {
"url": "ipfs://QmXQu3ib9gTo23mdVgMqmrExga6SmAzDQTTctpVBNtfDu9",
"url": "ipfs://Qmcd19ksUvNMD1XQFSC55jJhDPoF2zUzzV7woteFiugwBH",
"width": 1024,
"height": 1023,
"format": "svg"
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/chains",
"version": "0.1.13",
"version": "0.1.15",
"main": "dist/thirdweb-dev-chains.cjs.js",
"module": "dist/thirdweb-dev-chains.esm.js",
"browser": {
+1770 -2594
View File
File diff suppressed because one or more lines are too long
+18
View File
@@ -1,5 +1,23 @@
# thirdweb
## 0.10.15
### Patch Changes
- Updated dependencies [[`32908b76`](https://github.com/thirdweb-dev/js/commit/32908b76832c60e91a0a6e40dbdb1c8f56e9e5be), [`6a4aab0b`](https://github.com/thirdweb-dev/js/commit/6a4aab0b8a2e0f6ff1b47992a3c1e5426a74f7ff)]:
- @thirdweb-dev/sdk@3.10.14
- @thirdweb-dev/chains@0.1.15
## 0.10.14
### Patch Changes
- [#964](https://github.com/thirdweb-dev/js/pull/964) [`89c821d4`](https://github.com/thirdweb-dev/js/commit/89c821d49e95e9319443acf25748ef614c1af396) Thanks [@nkrishang](https://github.com/nkrishang)! - Build deployArgs only on deploy --dynamic cmd
- Updated dependencies [[`5305b42d`](https://github.com/thirdweb-dev/js/commit/5305b42db554b69f903b3d95f3ba0eeddabd6114), [`eb521d24`](https://github.com/thirdweb-dev/js/commit/eb521d240ae7102d44fe2c5223b0a18d867e09ad), [`af4b5356`](https://github.com/thirdweb-dev/js/commit/af4b5356372ffa084c8d0e747d8def46c2ff892c), [`93bd5733`](https://github.com/thirdweb-dev/js/commit/93bd57337b7d2c2fcd252987d10df3206c839daf), [`a2df187b`](https://github.com/thirdweb-dev/js/commit/a2df187bc1867beb2e90853da70dac271f604f12), [`aa9b6acc`](https://github.com/thirdweb-dev/js/commit/aa9b6acc3f5a118c2b5fe9e46732e72c0fc69376)]:
- @thirdweb-dev/chains@0.1.14
- @thirdweb-dev/sdk@3.10.13
## 0.10.13
### Patch Changes
+1
View File
@@ -2,6 +2,7 @@ export const CREATE_MESSAGES = {
typeOfProject: "What type of project do you want to create?",
projectName: "What is your project named?",
contractName: "What will be the name of your new smart contract?",
extensionName: "What will be the name of your new extension?",
chain: "Which blockchain do you want to use?",
framework: "What framework do you want to use?",
language: "What language do you want to use?",
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "thirdweb",
"main": "dist/cli/index.js",
"version": "0.10.13",
"version": "0.10.15",
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/cli",
"author": "thirdweb eng <[email protected]>",
"license": "Apache-2.0",
+23 -21
View File
@@ -220,27 +220,29 @@ export async function processProject(
const soliditySDKPackage = "@thirdweb-dev/contracts";
let usesSoliditySDK = false;
if (selectedRouter.name === "thirdweb-router") {
const deployArgs: TWRouterParams = {
extensionRegistry: EXTENSION_REGISTRY_ADDRESS,
extensionNames: selectedContracts.map((c) => c.name),
};
const outputDeployArgs = JSON.stringify(deployArgs, undefined, 2);
writeFileSync("./deployArgs.json", outputDeployArgs, "utf-8");
info(
"Deployment parameters written to deployArgs.json in your project directory",
);
} else {
const deployArgs: RouterParams = await formatToExtensions(
selectedContracts,
);
const outputDeployArgs = JSON.stringify(deployArgs, undefined, 2);
writeFileSync("./deployArgs.json", outputDeployArgs, "utf-8");
info(
"Deployment parameters written to deployArgs.json in your project directory",
);
if(options.dynamic) {
if (selectedRouter.name === "thirdweb-router") {
const deployArgs: TWRouterParams = {
extensionRegistry: EXTENSION_REGISTRY_ADDRESS,
extensionNames: selectedContracts.map((c) => c.name),
};
const outputDeployArgs = JSON.stringify(deployArgs, undefined, 2);
writeFileSync("./deployArgs.json", outputDeployArgs, "utf-8");
info(
"Deployment parameters written to deployArgs.json in your project directory",
);
} else {
const deployArgs: RouterParams = await formatToExtensions(
selectedContracts,
);
const outputDeployArgs = JSON.stringify(deployArgs, undefined, 2);
writeFileSync("./deployArgs.json", outputDeployArgs, "utf-8");
info(
"Deployment parameters written to deployArgs.json in your project directory",
);
}
}
const loader = spinner("Uploading contract data...");
+1 -1
View File
@@ -321,7 +321,7 @@ export async function twCreate(
const res = await prompts({
type: "text",
name: "path",
message: CREATE_MESSAGES.contractName,
message: projectType === "extension" ? CREATE_MESSAGES.extensionName : CREATE_MESSAGES.contractName,
initial: defaultName,
validate: (name: string) => {
const isValid = /(^[a-z0-9A-Z]+$)|(^[a-z0-9A-Z]+\.sol$)/.test(name);
+28
View File
@@ -1,5 +1,33 @@
# @thirdweb-dev/react-core
## 3.11.11
### Patch Changes
- Updated dependencies [[`32908b76`](https://github.com/thirdweb-dev/js/commit/32908b76832c60e91a0a6e40dbdb1c8f56e9e5be), [`6a4aab0b`](https://github.com/thirdweb-dev/js/commit/6a4aab0b8a2e0f6ff1b47992a3c1e5426a74f7ff), [`6a4aab0b`](https://github.com/thirdweb-dev/js/commit/6a4aab0b8a2e0f6ff1b47992a3c1e5426a74f7ff)]:
- @thirdweb-dev/sdk@3.10.14
- @thirdweb-dev/wallets@0.2.19
- @thirdweb-dev/chains@0.1.15
## 3.11.10
### Patch Changes
- [#954](https://github.com/thirdweb-dev/js/pull/954) [`5305b42d`](https://github.com/thirdweb-dev/js/commit/5305b42db554b69f903b3d95f3ba0eeddabd6114) Thanks [@jarrodwatts](https://github.com/jarrodwatts)! - useContract now types correctly for thirdweb generate
- [#966](https://github.com/thirdweb-dev/js/pull/966) [`87021cee`](https://github.com/thirdweb-dev/js/commit/87021cee45e81a6504e4e2279e6d2abb10cab8ec) Thanks [@MananTank](https://github.com/MananTank)! - Connect Wallet UI improvements
- Allow from EOA => smart wallet / safe
- Add warning to backup wallet for guest wallet
- Show "Guest" instead of address for guest wallet
- [#977](https://github.com/thirdweb-dev/js/pull/977) [`93bd5733`](https://github.com/thirdweb-dev/js/commit/93bd57337b7d2c2fcd252987d10df3206c839daf) Thanks [@MananTank](https://github.com/MananTank)! - Fix Connect Wallet Open/Close issues
- Updated dependencies [[`93bdec06`](https://github.com/thirdweb-dev/js/commit/93bdec061dc05ab133e79f5f739dcae9b5393f53), [`ea0f9479`](https://github.com/thirdweb-dev/js/commit/ea0f9479a38d442201e367fce1234c130228fde6), [`05ebbc15`](https://github.com/thirdweb-dev/js/commit/05ebbc15a012855735fba2aa93887b88e14295d1), [`5305b42d`](https://github.com/thirdweb-dev/js/commit/5305b42db554b69f903b3d95f3ba0eeddabd6114), [`4ca557ae`](https://github.com/thirdweb-dev/js/commit/4ca557ae4ab225e39decc3b7a01a04c0d8e464c7), [`7d7685e3`](https://github.com/thirdweb-dev/js/commit/7d7685e3fab5780b3c1d26b8ef431b96f8486972), [`eb521d24`](https://github.com/thirdweb-dev/js/commit/eb521d240ae7102d44fe2c5223b0a18d867e09ad), [`af4b5356`](https://github.com/thirdweb-dev/js/commit/af4b5356372ffa084c8d0e747d8def46c2ff892c), [`93bd5733`](https://github.com/thirdweb-dev/js/commit/93bd57337b7d2c2fcd252987d10df3206c839daf), [`a2df187b`](https://github.com/thirdweb-dev/js/commit/a2df187bc1867beb2e90853da70dac271f604f12), [`aa9b6acc`](https://github.com/thirdweb-dev/js/commit/aa9b6acc3f5a118c2b5fe9e46732e72c0fc69376)]:
- @thirdweb-dev/wallets@0.2.18
- @thirdweb-dev/chains@0.1.14
- @thirdweb-dev/sdk@3.10.13
## 3.11.9
### Patch Changes
+8 -7
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/react-core",
"version": "3.11.9",
"version": "3.11.11",
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/react-core",
"author": "thirdweb eng <[email protected]>",
"license": "Apache-2.0",
@@ -77,12 +77,15 @@
"@preconstruct/cli": "^2.2.1",
"@solana/wallet-adapter-react": "^0.15.19",
"@solana/web3.js": "^1.62.0",
"@thirdweb-dev/auth": "workspace:*",
"@thirdweb-dev/tsconfig": "workspace:*",
"@types/mime": "3.0.0",
"@types/react": "^18.0.5",
"@typescript-eslint/eslint-plugin": "^5.33.0",
"@typescript-eslint/parser": "^5.33.0",
"eslint": "^8.21.0",
"eslint-config-prettier": "^8.3.0",
"eslint-config-thirdweb": "workspace:*",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-inclusive-language": "^2.2.0",
"eslint-plugin-prettier": "^4.2.1",
@@ -90,17 +93,15 @@
"ethers": "^5.7.2",
"prettier": "^2.7.1",
"react": "^18.2.0",
"typescript": "^4.7.4",
"eslint-config-thirdweb": "workspace:*",
"@thirdweb-dev/tsconfig": "workspace:*",
"@thirdweb-dev/auth": "workspace:*"
"typescript": "^4.7.4"
},
"dependencies": {
"@tanstack/react-query": "^4.0.10",
"@thirdweb-dev/chains": "workspace:*",
"@thirdweb-dev/wallets": "workspace:*",
"@thirdweb-dev/storage": "workspace:*",
"@thirdweb-dev/generated-abis": "workspace:^",
"@thirdweb-dev/sdk": "workspace:*",
"@thirdweb-dev/storage": "workspace:*",
"@thirdweb-dev/wallets": "workspace:*",
"mime": "3.0.0",
"tiny-invariant": "^1.2.0"
},
@@ -2,7 +2,7 @@ import { ThirdwebAuthProvider } from "../../evm/contexts/thirdweb-auth";
import { useUpdateChainsWithApiKeys } from "../../evm/hooks/chain-hooks";
import { ThirdwebSDKProvider } from "../../evm/providers/thirdweb-sdk-provider";
import { ThirdwebSDKProviderProps } from "../../evm/providers/types";
import { Wallet } from "../types/wallet";
import { ConfiguredWallet } from "../types/wallet";
import { ThirdwebThemeContext } from "./theme-context";
import {
ThirdwebWalletProvider,
@@ -40,7 +40,7 @@ export interface ThirdwebProviderCoreProps<TChains extends Chain[]>
* };
* ```
*/
supportedWallets: Wallet[];
supportedWallets: ConfiguredWallet[];
/**
* Metadata to pass to wallet connect and walletlink wallet connect. (Used to show *which* dApp is being connected to in mobile wallets that support it)
@@ -1,5 +1,5 @@
import { DAppMetaData } from "../types/dAppMeta";
import type { Wallet, WalletInstance } from "../types/wallet";
import type { ConfiguredWallet, WalletInstance } from "../types/wallet";
import { ThirdwebThemeContext } from "./theme-context";
import { Chain } from "@thirdweb-dev/chains";
import {
@@ -44,21 +44,30 @@ type ConnectionStatus = "unknown" | "connected" | "disconnected" | "connecting";
type ConnectFnArgs<I extends WalletInstance> =
// if second argument is optional
undefined extends WalletConnectParams<I>
? [wallet: Wallet<I>, connectParams?: NonNullable<WalletConnectParams<I>>]
? [
wallet: ConfiguredWallet<I>,
connectParams?: NonNullable<WalletConnectParams<I>>,
]
: // if second argument is required
[wallet: Wallet<I>, connectParams: NonNullable<WalletConnectParams<I>>];
[
wallet: ConfiguredWallet<I>,
connectParams: NonNullable<WalletConnectParams<I>>,
];
type ThirdwebWalletContextData = {
wallets: Wallet[];
wallets: ConfiguredWallet[];
signer?: Signer;
activeWallet?: WalletInstance;
activeWalletConfig?: ConfiguredWallet;
connect: <I extends WalletInstance>(
...args: ConnectFnArgs<I>
) => Promise<void>;
disconnect: () => Promise<void>;
connectionStatus: ConnectionStatus;
setConnectionStatus: (status: ConnectionStatus) => void;
createWalletInstance: <I extends WalletInstance>(Wallet: Wallet<I>) => I;
createWalletInstance: <I extends WalletInstance>(
Wallet: ConfiguredWallet<I>,
) => I;
createWalletStorage: CreateAsyncStorage;
switchChain: (chain: number) => Promise<void>;
chainToConnect?: Chain;
@@ -76,7 +85,7 @@ const ThirdwebWalletContext = createContext<
export function ThirdwebWalletProvider(
props: PropsWithChildren<{
activeChain: Chain;
supportedWallets: Wallet[];
supportedWallets: ConfiguredWallet[];
shouldAutoConnect?: boolean;
createWalletStorage: CreateAsyncStorage;
dAppMeta?: DAppMetaData;
@@ -92,6 +101,10 @@ export function ThirdwebWalletProvider(
WalletInstance | undefined
>();
const [activeWalletConfig, setActiveConfig] = useState<
ConfiguredWallet | undefined
>();
if (!lastConnectedWalletStorage) {
lastConnectedWalletStorage =
props.createWalletStorage("coordinatorStorage");
@@ -117,7 +130,7 @@ export function ThirdwebWalletProvider(
}, [props.chains, props.dAppMeta, props.activeChain, theme]);
const createWalletInstance = useCallback(
<I extends WalletInstance>(wallet: Wallet<I>): I => {
<I extends WalletInstance>(wallet: ConfiguredWallet<I>): I => {
return wallet.create(walletParams);
},
[walletParams],
@@ -130,16 +143,38 @@ export function ThirdwebWalletProvider(
}
}, [activeWallet, props.chains]);
const getWalletConfig = useCallback(
(wallet: WalletInstance) => {
const id = wallet.walletId;
// find in personal wallets if wrapper wallet
if (
activeWalletConfig &&
"config" in activeWalletConfig &&
(activeWalletConfig.config as any).personalWallets
) {
// find in personal wallets
return (activeWalletConfig.config as any).personalWallets.find(
(w: ConfiguredWallet) => w.id === id,
);
}
return props.supportedWallets.find((w) => w.id === id);
},
[activeWalletConfig, props.supportedWallets],
);
const handleWalletConnect = useCallback(
async (
wallet: WalletInstance,
connectParams?: ConnectParams<Record<string, any>>,
isAutoConnect = false,
) => {
setActiveWallet(wallet);
setActiveConfig(getWalletConfig(wallet));
setConnectionStatus("connected");
const _signer = await wallet.getSigner();
setSigner(_signer);
setActiveWallet(wallet);
setConnectionStatus("connected");
// it autoconnected, then the details is already saved in storage, no need to store again
if (isAutoConnect) {
@@ -169,7 +204,7 @@ export function ThirdwebWalletProvider(
saveLastConnectedWalletInfo(walletInfo);
}
},
[],
[getWalletConfig],
);
const storeLastActiveChainId = useCallback(async (chainId: number) => {
@@ -256,7 +291,7 @@ export function ThirdwebWalletProvider(
if (personalWalletInfo) {
type Config = {
personalWallets: Wallet[];
personalWallets: ConfiguredWallet[];
};
const personalWallets =
@@ -349,6 +384,7 @@ export function ThirdwebWalletProvider(
setConnectionStatus("disconnected");
setSigner(undefined);
setActiveWallet(undefined);
setActiveConfig(undefined);
}, []);
const disconnectWallet = useCallback(async () => {
@@ -358,11 +394,11 @@ export function ThirdwebWalletProvider(
return;
}
const personalWallet = activeWallet.getPersonalWallet();
await activeWallet.disconnect();
if (activeWallet.getPersonalWallet()) {
await (
activeWallet.getPersonalWallet() as AbstractClientWallet
)?.disconnect();
if (personalWallet) {
await (personalWallet as AbstractClientWallet)?.disconnect();
}
onWalletDisconnect();
@@ -401,6 +437,7 @@ export function ThirdwebWalletProvider(
connect: connectWallet,
signer,
activeWallet,
activeWalletConfig,
connectionStatus,
setConnectionStatus,
createWalletInstance: createWalletInstance,
+28 -1
View File
@@ -23,15 +23,42 @@ export type WalletClass<I extends WalletInstance = WalletInstance> = {
meta: (typeof AbstractClientWallet)["meta"];
};
export type Wallet<
export type ConfiguredWallet<
I extends WalletInstance = WalletInstance,
Config extends Record<string, any> | undefined = undefined,
> = {
id: string;
meta: (typeof AbstractClientWallet)["meta"];
create: (options: WalletOptions) => I;
connectUI?: React.FC<ConnectUIProps>;
isInstalled?: () => boolean;
} & (Config extends undefined
? {}
: {
config: Config;
});
export type ConnectUIProps = {
/**
* close the connect wallet modal
* @param reset reset Connect Wallet Modal to initial state, so that if it's opened again, it will start from the wallet-selection screen
*
* default: `true`
*/
close: (reset?: boolean) => void;
/**
* indicates whether the connect wallet modal is open or not
*/
isOpen: boolean;
/**
* open the connect wallet modal
*/
open: () => void;
/**
* go back to the wallet selector screen in connect wallet modal
*/
goBack: () => void;
};
@@ -19,7 +19,7 @@ import {
useQueryClient,
UseQueryResult,
} from "@tanstack/react-query";
import { getCachedAbiForContract } from "@thirdweb-dev/sdk";
import { getCachedAbiForContract, TransactionResult } from "@thirdweb-dev/sdk";
import type {
Abi,
CommonContractSchemaInput,
@@ -32,10 +32,12 @@ import type {
SUPPORTED_CHAIN_ID,
ThirdwebSDK,
ValidContractInstance,
BaseContractForAddress,
} from "@thirdweb-dev/sdk";
import type { CallOverrides, ContractInterface, providers } from "ethers";
import { useEffect, useMemo } from "react";
import invariant from "tiny-invariant";
import { ContractAddress as GeneratedContractAddress } from "@thirdweb-dev/generated-abis";
// contract type
async function fetchContractType(
@@ -150,6 +152,16 @@ export type UseContractResult<
contract: TContract | undefined;
};
export function useContract<
TContractAddress extends ContractAddress | GeneratedContractAddress,
>(
contractAddress: RequiredParam<TContractAddress>,
): UseContractResult<
TContractAddress extends GeneratedContractAddress
? SmartContract<BaseContractForAddress<TContractAddress>>
: SmartContract
>;
/**
* Use this resolve a contract address to a smart contract instance.
*
@@ -514,12 +526,42 @@ export function useContractEvents(
* @beta
*/
export function useContractRead<
TContractInstance extends ValidContractInstance,
TFunctionName extends Parameters<TContractInstance["call"]>[0],
TContractAddress extends GeneratedContractAddress | ContractAddress,
TContract extends TContractAddress extends GeneratedContractAddress
? BaseContractForAddress<TContractAddress>
: ValidContractInstance,
TContractInstance extends TContractAddress extends GeneratedContractAddress
? SmartContract<BaseContractForAddress<TContractAddress>>
: ValidContractInstance,
TFunctionName extends TContractAddress extends GeneratedContractAddress
? TContract extends BaseContractForAddress<TContractAddress>
? keyof TContract["functions"]
: never
: Parameters<TContractInstance["call"]>[0],
TArgs extends TContractAddress extends GeneratedContractAddress
? TContract extends BaseContractForAddress<TContractAddress>
? TFunctionName extends keyof TContract["functions"]
? Parameters<TContract["functions"][TFunctionName]>
: unknown[]
: unknown[]
: unknown[],
TReturnType extends TContractAddress extends GeneratedContractAddress
? TContract extends BaseContractForAddress<TContractAddress>
? TFunctionName extends keyof TContract["functions"]
? ReturnType<TContract["functions"][TFunctionName]>
: any
: any
: any,
>(
contract: RequiredParam<TContractInstance>,
functionName: RequiredParam<TFunctionName | (string & {})>,
args?: unknown[],
contract: TContractInstance extends ValidContractInstance
? RequiredParam<TContractInstance> | undefined
: TContractAddress extends GeneratedContractAddress
?
| RequiredParam<SmartContract<BaseContractForAddress<TContractAddress>>>
| undefined
: RequiredParam<SmartContract> | undefined,
functionName: RequiredParam<TFunctionName & string>,
args?: TArgs,
overrides?: CallOverrides,
) {
const contractAddress = contract?.getAddress();
@@ -530,10 +572,10 @@ export function useContractRead<
requiredParamInvariant(functionName, "function name must be provided");
return (
contract.call as (
functionName: string,
args?: unknown[],
functionName: TFunctionName,
args?: TArgs,
overrides?: CallOverrides,
) => Promise<any>
) => Promise<TReturnType>
)(functionName, args, overrides);
},
{
@@ -561,11 +603,34 @@ export function useContractRead<
* @beta
*/
export function useContractWrite<
TContractInstance extends ValidContractInstance,
TFunctionName extends Parameters<TContractInstance["call"]>[0],
TContractAddress extends GeneratedContractAddress | ContractAddress,
TContract extends TContractAddress extends GeneratedContractAddress
? BaseContractForAddress<TContractAddress>
: ValidContractInstance,
TContractInstance extends TContractAddress extends GeneratedContractAddress
? SmartContract<BaseContractForAddress<TContractAddress>>
: ValidContractInstance,
TFunctionName extends TContractAddress extends GeneratedContractAddress
? TContract extends BaseContractForAddress<TContractAddress>
? keyof TContract["functions"]
: never
: Parameters<TContractInstance["call"]>[0],
TArgs extends TContractAddress extends GeneratedContractAddress
? TContract extends BaseContractForAddress<TContractAddress>
? TFunctionName extends keyof TContract["functions"]
? Parameters<TContract["functions"][TFunctionName]>
: unknown[]
: unknown[]
: any[],
>(
contract: RequiredParam<TContractInstance>,
functionName: RequiredParam<TFunctionName | (string & {})>,
contract: TContractInstance extends ValidContractInstance
? RequiredParam<TContractInstance> | undefined
: TContractAddress extends GeneratedContractAddress
?
| RequiredParam<SmartContract<BaseContractForAddress<TContractAddress>>>
| undefined
: RequiredParam<SmartContract> | undefined,
functionName: RequiredParam<TFunctionName & string>,
) {
const activeChainId = useSDKChainId();
const contractAddress = contract?.getAddress();
@@ -576,7 +641,7 @@ export function useContractWrite<
args,
overrides,
}: {
args?: unknown[];
args?: TArgs;
overrides?: CallOverrides;
}) => {
requiredParamInvariant(contract, "contract must be defined");
@@ -584,10 +649,10 @@ export function useContractWrite<
return (
contract.call as (
functionName: string,
args?: unknown[],
functionName: TFunctionName,
args?: TArgs,
overrides?: CallOverrides,
) => Promise<any>
) => Promise<TransactionResult>
)(functionName, args, overrides);
},
{
+2 -1
View File
@@ -34,7 +34,8 @@ export {
export { shouldNeverPersistQuery } from "../core/query-utils/query-key";
export type { RequiredParam } from "../core/query-utils/required-param";
export type {
Wallet,
ConfiguredWallet,
ConnectUIProps,
WalletClass,
WalletInstance,
ExtraCoreWalletOptions,
@@ -1,5 +1,9 @@
# @thirdweb-dev/react-native-compat
## 0.2.11
## 0.2.10
## 0.2.9
## 0.2.8
+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.9",
"version": "0.2.11",
"author": "thirdweb eng <[email protected]>",
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/react-native-compat",
"license": "Apache-2.0",
+109
View File
@@ -1,5 +1,114 @@
# @thirdweb-dev/react-native
## 0.2.11
### Patch Changes
- Updated dependencies [[`32908b76`](https://github.com/thirdweb-dev/js/commit/32908b76832c60e91a0a6e40dbdb1c8f56e9e5be), [`6a4aab0b`](https://github.com/thirdweb-dev/js/commit/6a4aab0b8a2e0f6ff1b47992a3c1e5426a74f7ff), [`6a4aab0b`](https://github.com/thirdweb-dev/js/commit/6a4aab0b8a2e0f6ff1b47992a3c1e5426a74f7ff)]:
- @thirdweb-dev/sdk@3.10.14
- @thirdweb-dev/wallets@0.2.19
- @thirdweb-dev/chains@0.1.15
- @thirdweb-dev/react-core@3.11.11
## 0.2.10
### Patch Changes
- [#981](https://github.com/thirdweb-dev/js/pull/981) [`ea0f9479`](https://github.com/thirdweb-dev/js/commit/ea0f9479a38d442201e367fce1234c130228fde6) Thanks [@iketw](https://github.com/iketw)! - ## [ReactNative] Allow custom wallets be added to the ConnectWallet button modal
With this new API you can build your own wallet and wallet UI and integrate it into our ConnectWallet modal:
```
import type { ConfiguredWallet, ConnectUIProps } from '@thirdweb-dev/react-native';
import { createAsyncLocalStorage, ConnectUIProps } from '@thirdweb-dev/react-native';
import { WalletOptions } from '@thirdweb-dev/wallets';
const WALLET_ID = "my-wallet-id";
export const myWallet = (): ConfiguredWallet => {
const asyncStorage = createAsyncLocalStorage(WALLET_ID);
const configuredWallet = {
id: WALLET_ID,
meta: {
id: WALLET_ID,
name: "My Custom Wallet", // This will be displayed in the connect wallet modal
iconURL: "ipfs or https url to an image or svg" // This will be used to fetch the icon to show in the connect wallet modal
},
create: (options: WalletOptions) =>
new MyWallet({
...options,
walletStorage: asyncStorage,
}),
connectUI(props: ConnectUIProps) {
return <MyWalletUI {...props} />; // This will show up in the modal after the user selects your wallet
},
};
return configuredWallet;
};
```
You can then add the wallet to your `supportedWallets` in the `ThirdwebProvider`:
```
import { Goerli } from '@thirdweb-dev/chains';
import {
ThirdwebProvider,
metamaskWallet,
} from '@thirdweb-dev/react-native';
const activeChain = Goerli;
const App = () => {
return (
<ThirdwebProvider
activeChain={activeChain}
supportedChains={[activeChain]}
supportedWallets={[
myWallet(),
metamaskWallet(),
]}>
<AppInner />
</ThirdwebProvider>
);
};
```
This new version exposes some utility functions:
1. `formatWalletConnectDisplayUri`
Formats the wallet connect `wc://` url for usage with custom wallet links
2. `shortenWalletAddress`
Shortens a wallet address for display purposes
3. `createAsyncLocalStorage` and `createSecureStorage`
You can use these methods to get wallet compatible storage types.
- `createAsyncLocalStorage` uses MMKV in the background
- `createSecureStorage` uses Expo SecureStore in the background
- [#969](https://github.com/thirdweb-dev/js/pull/969) [`12ad7fea`](https://github.com/thirdweb-dev/js/commit/12ad7fead2059005684c0762cbe951d23b509151) Thanks [@iketw](https://github.com/iketw)! - [ReactNative] Remove gap prop for backward compat
- [#966](https://github.com/thirdweb-dev/js/pull/966) [`87021cee`](https://github.com/thirdweb-dev/js/commit/87021cee45e81a6504e4e2279e6d2abb10cab8ec) Thanks [@MananTank](https://github.com/MananTank)! - Connect Wallet UI improvements
- Allow from EOA => smart wallet / safe
- Add warning to backup wallet for guest wallet
- Show "Guest" instead of address for guest wallet
- [#977](https://github.com/thirdweb-dev/js/pull/977) [`93bd5733`](https://github.com/thirdweb-dev/js/commit/93bd57337b7d2c2fcd252987d10df3206c839daf) Thanks [@MananTank](https://github.com/MananTank)! - Fix Connect Wallet Open/Close issues
- [#968](https://github.com/thirdweb-dev/js/pull/968) [`9640e073`](https://github.com/thirdweb-dev/js/commit/9640e0731f812ab9beaaa0cc67b1c5b61725c460) Thanks [@iketw](https://github.com/iketw)! - [ReactNative] Add guest mode warnings
- Updated dependencies [[`93bdec06`](https://github.com/thirdweb-dev/js/commit/93bdec061dc05ab133e79f5f739dcae9b5393f53), [`ea0f9479`](https://github.com/thirdweb-dev/js/commit/ea0f9479a38d442201e367fce1234c130228fde6), [`05ebbc15`](https://github.com/thirdweb-dev/js/commit/05ebbc15a012855735fba2aa93887b88e14295d1), [`5305b42d`](https://github.com/thirdweb-dev/js/commit/5305b42db554b69f903b3d95f3ba0eeddabd6114), [`4ca557ae`](https://github.com/thirdweb-dev/js/commit/4ca557ae4ab225e39decc3b7a01a04c0d8e464c7), [`7d7685e3`](https://github.com/thirdweb-dev/js/commit/7d7685e3fab5780b3c1d26b8ef431b96f8486972), [`87021cee`](https://github.com/thirdweb-dev/js/commit/87021cee45e81a6504e4e2279e6d2abb10cab8ec), [`eb521d24`](https://github.com/thirdweb-dev/js/commit/eb521d240ae7102d44fe2c5223b0a18d867e09ad), [`af4b5356`](https://github.com/thirdweb-dev/js/commit/af4b5356372ffa084c8d0e747d8def46c2ff892c), [`93bd5733`](https://github.com/thirdweb-dev/js/commit/93bd57337b7d2c2fcd252987d10df3206c839daf), [`a2df187b`](https://github.com/thirdweb-dev/js/commit/a2df187bc1867beb2e90853da70dac271f604f12), [`aa9b6acc`](https://github.com/thirdweb-dev/js/commit/aa9b6acc3f5a118c2b5fe9e46732e72c0fc69376)]:
- @thirdweb-dev/[email protected]
- @thirdweb-dev/[email protected]
- @thirdweb-dev/[email protected]
- @thirdweb-dev/[email protected]
## 0.2.9
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/react-native",
"version": "0.2.9",
"version": "0.2.11",
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/react-native",
"author": "thirdweb eng <[email protected]>",
"license": "Apache-2.0",
@@ -45,6 +45,12 @@ export class noopStorage implements IAsyncStorage {
}
}
/**
* Returns a new instance of AsyncLocalStorage
*
* @param name Name to namespace the storage with
* @returns A new instance of AsyncLocalStorage
*/
export function createAsyncLocalStorage(name: string) {
return new AsyncLocalStorage(name);
}
@@ -1,5 +1,5 @@
import { ISecureStorage } from '@thirdweb-dev/react-core';
import * as SecureStore from 'expo-secure-store';
import { ISecureStorage } from "@thirdweb-dev/react-core";
import * as SecureStore from "expo-secure-store";
const PREFIX = "__tw__";
@@ -17,7 +17,10 @@ export class SecureStorage implements ISecureStorage {
}
setItem(key: string, value: string) {
return this.asyncStorage.setItemAsync(`${PREFIX}_${this.name}_${key}`, value);
return this.asyncStorage.setItemAsync(
`${PREFIX}_${this.name}_${key}`,
value,
);
}
removeItem(key: string) {
@@ -25,6 +28,12 @@ export class SecureStorage implements ISecureStorage {
}
}
/**
* Returns a new instance of SecureStorage implemented by Expo SecureStore
*
* @param name Name to namespace the storage with
* @returns A new instance of SecureStorage
*/
export function createSecureStorage(name: string) {
return new SecureStorage(name);
}
@@ -11,14 +11,12 @@ import { WalletDetailsModalHeader } from "./WalletDetailsModalHeader";
import { useWallet, useBalance, useDisconnect } from "@thirdweb-dev/react-core";
import { useActiveChain } from "@thirdweb-dev/react-core/evm";
import { useCallback, useEffect, useState } from "react";
import { Linking, StyleSheet, View } from "react-native";
import { StyleSheet, View } from "react-native";
import { ExportLocalWalletModal } from "./ExportLocalWalletModal";
import { Toast } from "../base/Toast";
import { SmartWallet, walletIds } from "@thirdweb-dev/wallets";
import { LocalWallet, SmartWallet, walletIds } from "@thirdweb-dev/wallets";
import { SmartWalletAdditionalActions } from "./SmartWalletAdditionalActions";
import { useSmartWallet } from "../../providers/context-provider";
import { Link } from "../base/Link";
import Box from "../base/Box";
export type ConnectWalletDetailsProps = {
address: string;
@@ -112,20 +110,17 @@ export const ConnectWalletDetails = ({
>
<PocketWalletIcon size={16} />
<View style={styles.exportWalletInfo}>
<Text variant="bodySmall">Export wallet</Text>
<Text variant="bodySmall">Backup wallet</Text>
</View>
</BaseButton>
<Box flexDirection="row" alignItems="center" justifyContent="center">
<Link
text="Learn more"
onPress={() =>
Linking.openURL(
"https://portal.thirdweb.com/wallet/local-wallet",
)
{activeWallet?.walletId === LocalWallet.id ? (
<Text variant="error">
{
"This is a temporary guest wallet. Download a backup if you don't want to lose access to it."
}
/>
</Box>
</Text>
) : null}
</>
);
}
@@ -179,7 +174,11 @@ export const ConnectWalletDetails = ({
{balanceQuery.data?.displayValue.slice(0, 5)}{" "}
{balanceQuery.data?.symbol}
</Text>
<Address variant="bodySmallSecondary" address={address} />
{activeWallet?.walletId === LocalWallet.id ? (
<Text variant="error">Guest</Text>
) : (
<Address variant="bodySmallSecondary" address={address} />
)}
</View>
<WalletIcon size={32} iconUri={activeWallet?.getMeta().iconURL || ""} />
</BaseButton>
@@ -90,6 +90,7 @@ export const ExportLocalWalletModal = ({
encoding: FileSystem.EncodingType.UTF8,
},
);
setIsExporting(false);
} catch (e) {
console.error("Error writing the file", e);
setError("Error writing the file. Please try again.");
@@ -41,12 +41,12 @@ export const SmartWalletAdditionalActions = ({
useEffect(() => {
(async () => {
if (smartWallet) {
if (smartWallet && !smartWalletAddress) {
const addr = await smartWallet.getAddress();
setSmartWalletAddress(addr);
}
})();
}, [smartWallet]);
}, [smartWallet, smartWalletAddress]);
const onWalletPress = () => {
if (!wallet) {
@@ -76,14 +76,12 @@ export const SmartWalletAdditionalActions = ({
<WalletIcon size={32} iconUri={wallet?.getMeta().iconURL || ""} />
) : null}
<View style={styles.walletInfo}>
{personalWalletAddress ? (
<Address
variant="bodyLarge"
address={
showSmartWallet ? smartWalletAddress : personalWalletAddress
}
/>
) : null}
<Address
variant="bodyLarge"
address={
showSmartWallet ? smartWalletAddress : personalWalletAddress
}
/>
</View>
</>
<RightArrowIcon
@@ -94,22 +92,29 @@ export const SmartWalletAdditionalActions = ({
</BaseButton>
{wallet?.walletId === LocalWallet.id ||
activeWallet?.walletId === LocalWallet.id ? (
<BaseButton
backgroundColor="background"
borderColor="border"
mb="sm"
style={styles.exportWallet}
onPress={onExportPress}
>
<PocketWalletIcon size={16} />
<View style={styles.exportWalletInfo}>
<Text variant="bodySmall">
{wallet?.walletId === LocalWallet.id
? "Export personal wallet"
: "Export wallet"}
</Text>
</View>
</BaseButton>
<>
<BaseButton
backgroundColor="background"
borderColor="border"
mb="sm"
style={styles.exportWallet}
onPress={onExportPress}
>
<PocketWalletIcon size={16} />
<View style={styles.exportWalletInfo}>
<Text variant="bodySmall">
{wallet?.walletId === LocalWallet.id
? "Backup personal wallet"
: "Backup wallet"}
</Text>
</View>
</BaseButton>
<Text variant="error">
{
"This is a temporary guest wallet. Download a backup if you don't want to lose access to it."
}
</Text>
</>
) : null}
</>
);
@@ -46,11 +46,10 @@ export const WalletDetailsModalHeader = ({
<Box
flexDirection="row"
mr="sm"
gap="xs"
justifyContent="center"
alignItems="center"
>
<Address address={address} />
<Address mr="xs" address={address} />
<CopyIcon
width={14}
height={14}
@@ -5,16 +5,16 @@ import {
import { ModalFooter } from "../../base/modal/ModalFooter";
import { ModalHeaderTextClose } from "../../base/modal/ModalHeaderTextClose";
import { ChooseWalletContent } from "./ChooseWalletContent";
import { Wallet } from "@thirdweb-dev/react-core";
import { ConfiguredWallet } from "@thirdweb-dev/react-core";
import { ReactNode, useState } from "react";
import { View } from "react-native";
export type ChooseWalletProps = {
headerText?: ReactNode | string;
subHeaderText?: ReactNode | string;
onChooseWallet: (wallet: Wallet) => void;
onChooseWallet: (wallet: ConfiguredWallet) => void;
onClose: () => void;
wallets: Wallet[];
wallets: ConfiguredWallet[];
excludeWalletIds?: string[];
showGuestWalletAsButton?: boolean;
};
@@ -1,12 +1,12 @@
import { useMemo } from "react";
import type { Wallet } from "@thirdweb-dev/react-core";
import type { ConfiguredWallet } from "@thirdweb-dev/react-core";
import { StyleSheet, View, FlatList } from "react-native";
import { WalletButton } from "../../base/WalletButton";
interface InitialExplorerContentProps {
wallets: Wallet[];
wallets: ConfiguredWallet[];
excludeWalletIds?: string[];
onChooseWallet: (wallet: Wallet) => void;
onChooseWallet: (wallet: ConfiguredWallet) => void;
}
export const ChooseWalletContent = ({
@@ -4,27 +4,26 @@ import { TWModal } from "../base/modal/TWModal";
import { ChooseWallet } from "./ChooseWallet/ChooseWallet";
import { ConnectingWallet } from "./ConnectingWallet/ConnectingWallet";
import {
Wallet,
ConfiguredWallet,
useConnect,
useIsConnecting,
useThirdwebWallet,
useWallets,
} from "@thirdweb-dev/react-core";
import { useEffect, useState } from "react";
import { ActivityIndicator, StyleSheet } from "react-native";
import { SmartWallet } from "@thirdweb-dev/wallets";
import { SmartWalletFlow } from "./SmartWallet/SmartWalletFlow";
import { LocalWalletFlow } from "./LocalWalletFlow";
import { LocalWallet } from "../../wallets/wallets/local-wallet";
export const ConnectWalletFlow = () => {
const [modalVisible, setModalVisible] = useState(false);
const [activeWallet, setActiveWallet] = useState<Wallet | undefined>();
const [activeWallet, setActiveWallet] = useState<
ConfiguredWallet | undefined
>();
const [isConnecting, setIsConnecting] = useState(false);
const supportedWallets = useWallets();
const isWalletConnecting = useIsConnecting();
const [showButtonSpinner, setShowButtonSpinner] = useState(false);
const twWalletContext = useThirdwebWallet();
useEffect(() => {
setShowButtonSpinner(isWalletConnecting);
@@ -56,17 +55,19 @@ export const ConnectWalletFlow = () => {
setModalVisible(true);
};
const onClose = () => {
const onClose = (reset?: boolean) => {
setModalVisible(false);
reset();
if (reset) {
resetModal();
}
};
const onLocalWalletImported = async (localWallet: LocalWallet) => {
await localWallet.connect();
twWalletContext?.handleWalletConnect(localWallet);
const onOpenModal = () => {
setModalVisible(true);
};
const connectActiveWallet = async (wallet: Wallet) => {
const connectActiveWallet = async (wallet: ConfiguredWallet) => {
setIsConnecting(true);
connect(wallet, {}).catch((error) => {
console.error("Error connecting to the wallet", error);
@@ -74,7 +75,7 @@ export const ConnectWalletFlow = () => {
});
};
const onChooseWallet = (wallet: Wallet) => {
const onChooseWallet = (wallet: ConfiguredWallet) => {
setActiveWallet(() => wallet);
if (wallet.id !== SmartWallet.id) {
@@ -83,34 +84,29 @@ export const ConnectWalletFlow = () => {
};
const onBackPress = () => {
reset();
resetModal();
};
const reset = () => {
const resetModal = () => {
setActiveWallet(undefined);
setIsConnecting(false);
};
function getComponentForWallet(activeWalletP: Wallet) {
function getComponentForWallet(activeWalletP: ConfiguredWallet) {
switch (activeWalletP.id) {
case LocalWallet.id:
return (
<LocalWalletFlow
onClose={onClose}
onBackPress={supportedWallets.length > 1 ? onBackPress : undefined}
onWalletImported={onLocalWalletImported}
onConnectPress={() => connectActiveWallet(activeWalletP)}
/>
);
case SmartWallet.id:
return (
<SmartWalletFlow
onClose={() => {
onClose();
}}
onConnect={onBackPress}
/>
);
return <SmartWalletFlow onClose={onClose} onConnect={onBackPress} />;
}
if (activeWalletP.connectUI) {
return (
<activeWalletP.connectUI
goBack={onBackPress}
close={onClose}
isOpen={modalVisible}
open={onOpenModal}
/>
);
}
}
@@ -148,9 +144,6 @@ export const ConnectWalletFlow = () => {
onPress={onConnectPress}
style={styles.connectWalletButton}
>
{/* {showButtonSpinner ? (
<ActivityIndicator size="small" color="buttonTextColor" />
) : ( */}
<Text variant="bodyLarge" color="buttonTextColor">
{showButtonSpinner ? (
<ActivityIndicator size="small" color="buttonTextColor" />
@@ -158,7 +151,6 @@ export const ConnectWalletFlow = () => {
"Connect Wallet"
)}
</Text>
{/* )} */}
</BaseButton>
</>
);
@@ -2,7 +2,7 @@ import { useAppTheme } from "../../../styles/hooks";
import Text from "../../base/Text";
import { ModalFooter } from "../../base/modal/ModalFooter";
import { ConnectWalletHeader } from "./ConnectingWalletHeader";
import type { Wallet } from "@thirdweb-dev/react-core";
import type { ConfiguredWallet } from "@thirdweb-dev/react-core";
import { ReactNode } from "react";
import { ActivityIndicator, Linking, StyleSheet, View } from "react-native";
@@ -12,7 +12,7 @@ export type ConnectingWalletProps = {
content?: ReactNode;
onClose: () => void;
onBackPress: () => void;
wallet: Wallet;
wallet: ConfiguredWallet;
};
export function ConnectingWallet({
@@ -3,25 +3,31 @@ import { ActivityIndicator, StyleSheet, View } from "react-native";
import { ConnectWalletHeader } from "./ConnectingWallet/ConnectingWalletHeader";
import Text from "../base/Text";
import { ModalFooter } from "../base/modal/ModalFooter";
import { LocalWallet } from "../../wallets/wallets/local-wallet";
import { LocalWalletImportModal } from "./LocalWalletImportModal";
import { useState } from "react";
import {
ConfiguredWallet,
ConnectUIProps,
WalletInstance,
useCreateWalletInstance,
useThirdwebWallet,
} from "@thirdweb-dev/react-core";
export type LocalWalletFlowProps = {
onClose: () => void;
onBackPress?: () => void;
onConnectPress: () => void;
onWalletImported: (localWallet: LocalWallet) => void;
type LocalWalletFlowUIProps = ConnectUIProps & {
localWallet: ConfiguredWallet;
onConnected?: (wallet: WalletInstance) => void;
};
export function LocalWalletFlow({
onClose,
onBackPress,
onConnectPress,
onWalletImported,
}: LocalWalletFlowProps) {
goBack,
close,
localWallet,
onConnected,
}: LocalWalletFlowUIProps) {
const [isImportModalVisible, setIsImportModalVisible] = useState(false);
const [isCreatingWallet, setIsCreatingWallet] = useState(false);
const handleWalletConnect = useThirdwebWallet().handleWalletConnect;
const createInstance = useCreateWalletInstance();
const onImportPress = async () => {
setIsImportModalVisible(true);
@@ -31,19 +37,31 @@ export function LocalWalletFlow({
setIsImportModalVisible(false);
};
const onConnectPressInternal = () => {
const onConnectPressInternal = async () => {
setIsCreatingWallet(true);
onConnectPress();
const localWalletInstance = await createInstance(localWallet);
connect(localWalletInstance);
};
const connect = async (wallet: WalletInstance) => {
await wallet.connect();
if (onConnected) {
onConnected(wallet);
} else {
handleWalletConnect(wallet);
}
};
return (
<>
<ConnectWalletHeader
onBackPress={onBackPress}
onBackPress={goBack}
headerText="Guest Wallet"
alignHeader="flex-start"
subHeaderText={""}
onClose={onClose}
onClose={close}
/>
<View style={styles.connectingContainer}>
<BaseButton
@@ -68,7 +86,7 @@ export function LocalWalletFlow({
<LocalWalletImportModal
isVisible={isImportModalVisible}
onWalletImported={onWalletImported}
onWalletImported={connect}
onClose={onImportModalClose}
/>
</>
@@ -1,6 +1,6 @@
import { useCallback, useEffect, useState } from "react";
import {
Wallet,
ConfiguredWallet,
WalletInstance,
useConnect,
useCreateWalletInstance,
@@ -8,10 +8,7 @@ import {
useThirdwebWallet,
} from "@thirdweb-dev/react-core";
import { SmartWalletObj } from "../../../wallets/wallets/smart-wallet";
import {
LocalWallet,
localWallet,
} from "../../../wallets/wallets/local-wallet";
import { localWallet } from "../../../wallets/wallets/local-wallet";
import { ChooseWallet } from "../ChooseWallet/ChooseWallet";
import { LocalWalletFlow } from "../LocalWalletFlow";
import { ModalHeaderTextClose } from "../../base/modal/ModalHeaderTextClose";
@@ -75,7 +72,7 @@ export const SmartWalletFlow = ({
}, [connectSmartWallet, connectedPersonalWallet, mismatch]);
const connectPersonalWallet = useCallback(
async (wallet: Wallet) => {
async (wallet: ConfiguredWallet) => {
setIsConnecting(true);
const walletInstance = createWalletInstance(wallet);
await walletInstance.connect();
@@ -85,15 +82,14 @@ export const SmartWalletFlow = ({
[createWalletInstance],
);
const onLocalWalletImported = async (localWalletImported: LocalWallet) => {
const onConnectedLocalWallet = async (wallet: WalletInstance) => {
setIsConnecting(true);
await localWalletImported.connect();
connectSmartWallet(localWalletImported);
connectSmartWallet(wallet);
};
const onChoosePersonalWallet = useCallback(
async (wallet: Wallet) => {
async (wallet: ConfiguredWallet) => {
// if (wallet.id === LocalWallet.id) {
// setShowLocalWalletFlow(true);
// } else {
@@ -105,7 +101,6 @@ export const SmartWalletFlow = ({
const onLocalWalletBackPress = () => {
setShowLocalWalletFlow(false);
reset();
};
const onConnectingClosePress = () => {
@@ -153,7 +148,6 @@ export const SmartWalletFlow = ({
alignItems="center"
paddingHorizontal="md"
paddingVertical="sm"
gap="md"
borderRadius="sm"
backgroundColor="backgroundHighlight"
onPress={onSwitchNetworkPress}
@@ -168,10 +162,12 @@ export const SmartWalletFlow = ({
if (showLocalWalletFlow) {
return (
<LocalWalletFlow
onClose={onClose}
onBackPress={onLocalWalletBackPress}
onWalletImported={onLocalWalletImported}
onConnectPress={() => connectPersonalWallet(localWallet())}
close={onClose}
goBack={onLocalWalletBackPress}
onConnected={onConnectedLocalWallet}
isOpen={false}
open={() => {}}
localWallet={localWallet()}
/>
);
}
@@ -1,13 +1,20 @@
import { Theme } from "../../styles/theme";
import { shortenString } from "../../utils/addresses";
import { shortenWalletAddress } from "../../utils/addresses";
import Text from "./Text";
type AddressProps = {
address: string;
variant?: keyof Theme["textVariants"];
} & (typeof Text)["arguments"];
export const Address = ({
address,
variant = "bodyLarge",
}: {
address: string;
variant?: keyof Theme["textVariants"];
}) => {
return <Text variant={variant}>{shortenString(address)}</Text>;
...props
}: AddressProps) => {
return (
<Text variant={variant} {...props}>
{shortenWalletAddress(address)}
</Text>
);
};
@@ -31,14 +31,11 @@ export const WalletButton = ({
backgroundColor="backgroundHighlight"
onPress={onPress}
>
<Box
flexDirection="row"
justifyContent="flex-start"
alignItems="center"
gap="md"
>
<Box flexDirection="row" justifyContent="flex-start" alignItems="center">
<ImageSvgUri imageUrl={walletIconUrl} width={32} height={32} />
<Text variant="bodyLarge">{name}</Text>
<Text ml="md" variant="bodyLarge">
{name}
</Text>
</Box>
{labelText ? <Label text={labelText} /> : <View />}
</BaseButton>
@@ -1,8 +1,9 @@
import { ConfiguredWallet } from "@thirdweb-dev/react-core";
import { metamaskWallet } from "../wallets/wallets/metamask-wallet";
import { rainbowWallet } from "../wallets/wallets/rainbow-wallet";
import { trustWallet } from "../wallets/wallets/trust-wallet";
export const DEFAULT_WALLETS = [
export const DEFAULT_WALLETS: ConfiguredWallet[] = [
metamaskWallet(),
rainbowWallet(),
trustWallet(),
+6
View File
@@ -16,6 +16,12 @@ export { useTrustWallet } from "./wallets/hooks/useTrustWallet";
export { ConnectWallet } from "./components/ConnectWallet";
export { Web3Button } from "./components/Web3Button";
// utilities
export * from "./utils/uri";
export * from "./utils/addresses";
export { createAsyncLocalStorage } from "../core/AsyncStorage";
export { createSecureStorage } from "../core/SecureStorage";
// providers
// export * from "./providers/full";
export * from "./providers/thirdweb-provider";
@@ -3,7 +3,7 @@ import { DEFAULT_API_KEY } from "../constants/rpc";
import {
ThirdwebProviderCore,
ThirdwebProviderCoreProps,
Wallet,
ConfiguredWallet,
} from "@thirdweb-dev/react-core";
import { PropsWithChildren } from "react";
import type { Chain, defaultChains } from "@thirdweb-dev/chains";
@@ -27,7 +27,7 @@ interface ThirdwebProviderProps<TChains extends Chain[]>
* />
* ```
*/
supportedWallets?: Wallet[];
supportedWallets?: ConfiguredWallet[];
}
/**
@@ -12,8 +12,6 @@ export type Palette = {
iconSecondary: string;
iconHighlight: string;
warning: string;
linkPrimary: string;
labelBackground: string;
@@ -23,6 +21,10 @@ export type Palette = {
black: string;
white: string;
red: string;
warning: string;
error: string;
};
export const paletteBase = {
@@ -30,6 +32,7 @@ export const paletteBase = {
white: "white",
red: "red",
warning: "#F46565",
error: "#D85252",
};
export const paletteLight: Palette = {
@@ -39,4 +39,11 @@ export const textVariants = {
fontSize: 13,
lineHeight: 16,
},
error: {
color: "error",
fontWeight: "500",
fontSize: 12,
lineHeight: 16,
textAlign: "center",
},
};
@@ -1,4 +1,11 @@
export function shortenString(str: string, extraShort?: true) {
/**
* Shortens an EVM compatible address to show the first 6 and last 4 characters
*
* @param str The address to shorten
* @param extraShort If true, show the first 4 and last 3 characters
* @returns The shortened address
*/
export function shortenWalletAddress(str: string, extraShort?: true) {
return `${str.substring(0, extraShort ? 4 : 6)}...${str.substring(
str.length - (extraShort ? 3 : 4),
)}`;
+8 -1
View File
@@ -1,4 +1,11 @@
export function formatDisplayUri(
/**
* Build a WalletConnect display URI from a wc:// uri + a wallet specific link
*
* @param uri The wc:// uri
* @param links The wallet specific links
* @returns The WalletConnect display URI
*/
export function formatWalletConnectDisplayUri(
uri: string,
links: { universal: string; native: string },
) {
@@ -14,8 +14,8 @@ import {
Chain,
ProviderRpcError,
walletIds,
WagmiConnector,
} from "@thirdweb-dev/wallets";
import { Connector } from "@thirdweb-dev/wallets";
import type { Address } from "abitype";
import { providers } from "ethers";
import { getAddress, hexValue } from "ethers/lib/utils.js";
@@ -34,7 +34,7 @@ export type CoinbaseWalletConnectorOptions = WalletMobileSDKProviderOptions &
chainId?: number;
};
export class CoinbaseWalletConnector extends Connector<
export class CoinbaseWalletConnector extends WagmiConnector<
WalletMobileSDKEVMProvider,
CoinbaseWalletConnectorOptions,
providers.JsonRpcSigner
@@ -5,13 +5,13 @@ import type {
} from "../connectors/coinbase-wallet";
import {
AbstractClientWallet,
TWConnector,
Connector,
WagmiAdapter,
WalletOptions,
walletIds,
} from "@thirdweb-dev/wallets";
import {
Wallet,
ConfiguredWallet,
WalletOptions as WalletOptionsRC,
} from "@thirdweb-dev/react-core";
@@ -28,7 +28,7 @@ export class CoinbaseWallet extends AbstractClientWallet<CoinbaseWalletConnector
"ipfs://QmcJBHopbwfJcLqJpX2xEufSS84aLbF7bHavYhaXUcrLaH/coinbase.svg",
};
connector?: TWConnector;
connector?: Connector;
coinbaseConnector?: CoinbaseWalletConnector;
provider?: CoinbaseWalletConnector["provider"];
@@ -49,7 +49,7 @@ export class CoinbaseWallet extends AbstractClientWallet<CoinbaseWalletConnector
this.callbackURL = options.callbackURL;
}
protected async getConnector(): Promise<TWConnector> {
protected async getConnector(): Promise<Connector> {
if (!this.connector) {
// import the connector dynamically
const { CoinbaseWalletConnector: CoinbaseWalletConnector } = await import(
@@ -83,5 +83,5 @@ export const coinbaseWallet = (config?: { callbackURL?: URL }) => {
create: (options: WalletOptionsRC) =>
new CoinbaseWallet({ ...options, callbackURL: callbackURLNonNull }),
config: config || {},
} satisfies Wallet<CoinbaseWallet, { callbackURL?: URL }>;
} satisfies ConfiguredWallet<CoinbaseWallet, { callbackURL?: URL }>;
};
@@ -1,13 +1,14 @@
import {
LocalWallet as LocalWalletCore,
TWConnector,
Connector,
walletIds,
} from "@thirdweb-dev/wallets";
import { Wallet } from "@thirdweb-dev/react-core";
import { ConfiguredWallet, ConnectUIProps } from "@thirdweb-dev/react-core";
import type { WalletOptions } from "@thirdweb-dev/wallets";
import { ethers, utils } from "ethers";
import { createSecureStorage } from "../../../core/SecureStorage";
import { createAsyncLocalStorage } from "../../../core/AsyncStorage";
import { LocalWalletFlow } from "../../components/ConnectWalletFlow/LocalWalletFlow";
export class LocalWallet extends LocalWalletCore {
static meta = {
@@ -30,7 +31,7 @@ export class LocalWallet extends LocalWalletCore {
return this.ethersWallet.address;
}
protected async getConnector(): Promise<TWConnector> {
protected async getConnector(): Promise<Connector> {
if (!this.ethersWallet) {
const data = await this.getSavedData();
@@ -49,10 +50,10 @@ export class LocalWallet extends LocalWalletCore {
}
}
export const localWallet = () => {
export const localWallet = (): ConfiguredWallet => {
const secureStorage = createSecureStorage(walletIds.localWallet);
const asyncStorage = createAsyncLocalStorage(walletIds.localWallet);
return {
const configuredWallet = {
id: LocalWallet.id,
meta: LocalWallet.meta,
create: (options: WalletOptions) =>
@@ -61,5 +62,14 @@ export const localWallet = () => {
walletStorage: asyncStorage,
storage: secureStorage,
}) as LocalWalletCore,
} satisfies Wallet;
connectUI(props: ConnectUIProps) {
return <LocalWalletFlow {...props} localWallet={configuredWallet} />;
},
isInstalled() {
// TODO
return false;
},
};
return configuredWallet;
};
@@ -4,11 +4,11 @@ import {
WalletConnectV1,
walletIds,
} from "@thirdweb-dev/wallets";
import { formatDisplayUri } from "../../utils/uri";
import { formatWalletConnectDisplayUri } from "../../utils/uri";
import { Linking } from "react-native";
import {
WalletOptions as WalletOptionsRC,
Wallet,
ConfiguredWallet,
ExtraCoreWalletOptions,
} from "@thirdweb-dev/react-core";
import { createAsyncLocalStorage } from "../../../core/AsyncStorage";
@@ -52,11 +52,11 @@ export class MetaMaskWallet extends WalletConnectV1 {
const links = MetaMaskWallet.meta.links;
if (uri) {
const fullUrl = formatDisplayUri(uri, links);
const fullUrl = formatWalletConnectDisplayUri(uri, links);
Linking.openURL(fullUrl);
} else {
const fullUrl = formatDisplayUri("", links);
const fullUrl = formatWalletConnectDisplayUri("", links);
Linking.openURL(fullUrl);
}
@@ -68,5 +68,5 @@ export const metamaskWallet = () => {
id: MetaMaskWallet.id,
meta: MetaMaskWallet.meta,
create: (options: WalletOptionsRC) => new MetaMaskWallet(options),
} satisfies Wallet;
} satisfies ConfiguredWallet;
};
@@ -3,11 +3,11 @@ import {
WalletConnectV1Options,
WalletConnectV1,
} from "@thirdweb-dev/wallets";
import { formatDisplayUri } from "../../utils/uri";
import { formatWalletConnectDisplayUri } from "../../utils/uri";
import { Linking } from "react-native";
import {
WalletOptions as WalletOptionsRC,
Wallet,
ConfiguredWallet,
ExtraCoreWalletOptions,
} from "@thirdweb-dev/react-core";
import { createAsyncLocalStorage } from "../../../core/AsyncStorage";
@@ -50,11 +50,11 @@ export class RainbowWallet extends WalletConnectV1 {
const links = RainbowWallet.meta.links;
if (uri) {
const fullUrl = formatDisplayUri(uri, links);
const fullUrl = formatWalletConnectDisplayUri(uri, links);
Linking.openURL(fullUrl);
} else {
const fullUrl = formatDisplayUri("", links);
const fullUrl = formatWalletConnectDisplayUri("", links);
Linking.openURL(fullUrl);
}
@@ -66,5 +66,5 @@ export const rainbowWallet = () => {
id: RainbowWallet.id,
meta: RainbowWallet.meta,
create: (options: WalletOptionsRC) => new RainbowWallet(options),
} satisfies Wallet;
} satisfies ConfiguredWallet;
};
@@ -1,4 +1,4 @@
import type { Wallet, WalletOptions } from "@thirdweb-dev/react-core";
import type { ConfiguredWallet, WalletOptions } from "@thirdweb-dev/react-core";
import { SmartWallet } from "@thirdweb-dev/wallets";
import { DEFAULT_WALLETS } from "../../constants/wallets";
@@ -6,16 +6,16 @@ type SafeWalletConfig = {
factoryAddress: string;
thirdwebApiKey: string;
gasless: boolean;
personalWallets: Wallet[];
personalWallets: ConfiguredWallet[];
};
export type SmartWalletObj = Wallet & {
export type SmartWalletObj = ConfiguredWallet & {
config: SafeWalletConfig;
};
export const smartWallet = (
config: Omit<SafeWalletConfig, "personalWallets"> & {
personalWallets?: Wallet[];
personalWallets?: ConfiguredWallet[];
},
) => {
return {
@@ -27,5 +27,5 @@ export const smartWallet = (
...config,
personalWallets: config.personalWallets || DEFAULT_WALLETS,
},
} satisfies SmartWalletObj;
} satisfies ConfiguredWallet<SmartWallet, SafeWalletConfig>;
};
@@ -1,13 +1,13 @@
import { createAsyncLocalStorage } from "../../../core/AsyncStorage";
import { TW_WC_PROJECT_ID } from "../../constants/walletConnect";
import { formatDisplayUri } from "../../utils/uri";
import { formatWalletConnectDisplayUri } from "../../utils/uri";
import type {
WalletConnectOptions,
WalletOptions,
} from "@thirdweb-dev/wallets";
import {
WalletOptions as WalletOptionsRC,
Wallet,
ConfiguredWallet,
} from "@thirdweb-dev/react-core";
import { WalletConnect } from "@thirdweb-dev/wallets";
import { Linking } from "react-native";
@@ -51,11 +51,11 @@ export class TrustWallet extends WalletConnect {
const links = TrustWallet.meta.links;
if (uri) {
const fullUrl = formatDisplayUri(uri, links);
const fullUrl = formatWalletConnectDisplayUri(uri, links);
Linking.openURL(fullUrl);
} else {
const fullUrl = formatDisplayUri("", links);
const fullUrl = formatWalletConnectDisplayUri("", links);
Linking.openURL(fullUrl);
}
@@ -67,5 +67,5 @@ export const trustWallet = () => {
id: TrustWallet.id,
meta: TrustWallet.meta,
create: (options: WalletOptionsRC) => new TrustWallet(options),
} satisfies Wallet;
} satisfies ConfiguredWallet;
};
+33
View File
@@ -1,5 +1,38 @@
# @thirdweb-dev/react
## 3.11.11
### Patch Changes
- Updated dependencies [[`32908b76`](https://github.com/thirdweb-dev/js/commit/32908b76832c60e91a0a6e40dbdb1c8f56e9e5be), [`6a4aab0b`](https://github.com/thirdweb-dev/js/commit/6a4aab0b8a2e0f6ff1b47992a3c1e5426a74f7ff), [`6a4aab0b`](https://github.com/thirdweb-dev/js/commit/6a4aab0b8a2e0f6ff1b47992a3c1e5426a74f7ff)]:
- @thirdweb-dev/sdk@3.10.14
- @thirdweb-dev/wallets@0.2.19
- @thirdweb-dev/chains@0.1.15
- @thirdweb-dev/react-core@3.11.11
## 3.11.10
### Patch Changes
- [#967](https://github.com/thirdweb-dev/js/pull/967) [`7d7685e3`](https://github.com/thirdweb-dev/js/commit/7d7685e3fab5780b3c1d26b8ef431b96f8486972) Thanks [@MananTank](https://github.com/MananTank)! - - Fix type of connect() for magic wallet
- enforce smsLogin, emailLogin restriction for magic wallet
- update the icon for local wallet
- [#966](https://github.com/thirdweb-dev/js/pull/966) [`87021cee`](https://github.com/thirdweb-dev/js/commit/87021cee45e81a6504e4e2279e6d2abb10cab8ec) Thanks [@MananTank](https://github.com/MananTank)! - Connect Wallet UI improvements
- Allow from EOA => smart wallet / safe
- Add warning to backup wallet for guest wallet
- Show "Guest" instead of address for guest wallet
- [#977](https://github.com/thirdweb-dev/js/pull/977) [`93bd5733`](https://github.com/thirdweb-dev/js/commit/93bd57337b7d2c2fcd252987d10df3206c839daf) Thanks [@MananTank](https://github.com/MananTank)! - Fix Connect Wallet Open/Close issues
- Updated dependencies [[`93bdec06`](https://github.com/thirdweb-dev/js/commit/93bdec061dc05ab133e79f5f739dcae9b5393f53), [`ea0f9479`](https://github.com/thirdweb-dev/js/commit/ea0f9479a38d442201e367fce1234c130228fde6), [`05ebbc15`](https://github.com/thirdweb-dev/js/commit/05ebbc15a012855735fba2aa93887b88e14295d1), [`5305b42d`](https://github.com/thirdweb-dev/js/commit/5305b42db554b69f903b3d95f3ba0eeddabd6114), [`4ca557ae`](https://github.com/thirdweb-dev/js/commit/4ca557ae4ab225e39decc3b7a01a04c0d8e464c7), [`7d7685e3`](https://github.com/thirdweb-dev/js/commit/7d7685e3fab5780b3c1d26b8ef431b96f8486972), [`87021cee`](https://github.com/thirdweb-dev/js/commit/87021cee45e81a6504e4e2279e6d2abb10cab8ec), [`eb521d24`](https://github.com/thirdweb-dev/js/commit/eb521d240ae7102d44fe2c5223b0a18d867e09ad), [`af4b5356`](https://github.com/thirdweb-dev/js/commit/af4b5356372ffa084c8d0e747d8def46c2ff892c), [`93bd5733`](https://github.com/thirdweb-dev/js/commit/93bd57337b7d2c2fcd252987d10df3206c839daf), [`a2df187b`](https://github.com/thirdweb-dev/js/commit/a2df187bc1867beb2e90853da70dac271f604f12), [`aa9b6acc`](https://github.com/thirdweb-dev/js/commit/aa9b6acc3f5a118c2b5fe9e46732e72c0fc69376)]:
- @thirdweb-dev/wallets@0.2.18
- @thirdweb-dev/react-core@3.11.10
- @thirdweb-dev/chains@0.1.14
- @thirdweb-dev/sdk@3.10.13
## 3.11.9
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/react",
"version": "3.11.9",
"version": "3.11.11",
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/react",
"author": "thirdweb eng <[email protected]>",
"license": "Apache-2.0",
@@ -1,5 +1,5 @@
import { spacing, Theme } from "../../../../design-system";
import styled from "@emotion/styled";
import { spacing, Theme } from "../design-system";
export function Steps({ step }: { step: 1 | 2 }) {
return (
@@ -55,11 +55,11 @@ export const BackButton: React.FC<{
);
};
export const HelperLink = styled.a<{ theme?: Theme }>`
export const HelperLink = styled.a<{ theme?: Theme; md?: boolean }>`
all: unset;
cursor: pointer;
color: ${(p) => p.theme.link.primary};
font-size: ${fontSize.sm};
font-size: ${(p) => (p.md ? fontSize.md : fontSize.sm)};
text-decoration: none;
display: block;
${media.mobile} {
@@ -6,7 +6,9 @@ export function useSafe() {
const connect = useConnect();
return useCallback(
async (connectProps: SafeConnectionArgs) => {
const { safeWallet } = await import("../../../wallet/wallets/safeWallet");
const { safeWallet } = await import(
"../../../wallet/wallets/safe/safeWallet"
);
connect(safeWallet(), connectProps);
},
[connect],
@@ -1,10 +1,17 @@
import { useCallback } from "react";
// import { useConnect } from "../../../core/hooks/wallet-hooks";
import { useConnect } from "@thirdweb-dev/react-core";
import { MagicLinkAdditionalOptions } from "@thirdweb-dev/wallets/evm/wallets/magic";
import { MagicLinkConnectOptions } from "@thirdweb-dev/wallets";
export function useMagic() {
// const connect = useConnect();
return useCallback(() => {
throw new Error("not implemented yet");
}, []);
const connect = useConnect();
return useCallback(
async (options: MagicLinkConnectOptions & MagicLinkAdditionalOptions) => {
const { magicLink } = await import(
"../../../wallet/wallets/magic/magicLink"
);
return connect(magicLink(options), options);
},
[connect],
);
}
@@ -6,9 +6,9 @@ export function useCoinbaseWallet() {
return useCallback(
async (connectOptions?: { chainId?: number }) => {
const { coinbaseWallet } = await import(
"../../../wallet/wallets/coinbaseWallet"
"../../../wallet/wallets/coinbase/coinbaseWallet"
);
connect(coinbaseWallet(), connectOptions);
return connect(coinbaseWallet(), connectOptions);
},
[connect],
);
@@ -6,9 +6,9 @@ export function useMetamask() {
return useCallback(
async (connectOptions?: { chainId?: number }) => {
const { metamaskWallet } = await import(
"../../../wallet/wallets/metamaskWallet"
"../../../wallet/wallets/metamask/metamaskWallet"
);
connect(metamaskWallet(), connectOptions);
return connect(metamaskWallet(), connectOptions);
},
[connect],
);
@@ -15,7 +15,7 @@ export function usePaperWallet() {
const { paperWallet } = await import(
"../../../wallet/wallets/paperWallet"
);
connect(paperWallet({ clientId: options.clientId }), options);
return connect(paperWallet({ clientId: options.clientId }), options);
},
[connect],
);
@@ -8,7 +8,7 @@ export function useWalletConnectV1() {
const { walletConnectV1 } = await import(
"../../../wallet/wallets/walletConnectV1"
);
connect(walletConnectV1(), options);
return connect(walletConnectV1(), options);
},
[connect],
);
@@ -21,7 +21,7 @@ export function useWalletConnect() {
const { walletConnect } = await import(
"../../../wallet/wallets/walletConnect"
);
connect(walletConnect(), options || {});
return connect(walletConnect(), options || {});
},
[connect],
);
+1
View File
@@ -1,4 +1,5 @@
export { useSafe } from "./connectors/gnosis";
export { useMagic } from "./connectors/magic";
export { ConnectWallet } from "../wallet/ConnectWallet/ConnectWallet";
export { NetworkSelector } from "../wallet/ConnectWallet/NetworkSelector";
@@ -1,11 +1,11 @@
import { DEFAULT_API_KEY } from "../constants/rpc";
import {
Wallet,
ThirdwebProviderCore,
ThirdwebProviderCoreProps,
ConfiguredWallet,
} from "@thirdweb-dev/react-core";
import { WalletUIStatesProvider } from "./wallet-ui-states-provider";
import { ConnectModal } from "../../wallet/ConnectWallet/Connect";
import { ConnectModal } from "../../wallet/ConnectWallet/ConnectModal";
import { ThemeProvider } from "@emotion/react";
import { darkTheme, lightTheme } from "../../design-system";
import { PropsWithChildren } from "react";
@@ -30,7 +30,7 @@ interface ThirdwebProviderProps<TChains extends Chain[]>
* />
* ```
*/
supportedWallets?: Wallet[];
supportedWallets?: ConfiguredWallet[];
}
/**
@@ -63,7 +63,7 @@ export const ThirdwebProvider = <
children,
...restProps
}: PropsWithChildren<ThirdwebProviderProps<TChains>>) => {
const wallets: Wallet[] = supportedWallets || defaultWallets;
const wallets: ConfiguredWallet[] = supportedWallets || defaultWallets;
return (
<WalletUIStatesProvider theme={theme}>
@@ -2,89 +2,37 @@ import { useState, createContext, useContext } from "react";
type BoolSetter = (value: boolean) => void;
type ThemeSetter = (value: "light" | "dark") => void;
type Screen =
| "metamask/connecting"
| "walletList"
| "coinbase/connecting"
| "coinbase/scan"
| "metamask/scan"
| "metamask/get-started"
| "coinbase/get-started"
| "safe/select-wallet"
| "safe/form"
| "wallets/get-started"
| "localWallet/connect"
| "localWallet/export"
| "smartWallet/select-wallet"
| "smartWallet/form"
| "magic/connect";
type WalletWrapper = false | "safe" | "smartWallet";
const WalletWrapperConnection = createContext<WalletWrapper>(false);
const SetWalletWrapperConnection = createContext<
((value: WalletWrapper) => void) | undefined
>(undefined);
const WalletModalOpen = createContext(false);
const SetWalletModalOpen = createContext<BoolSetter | undefined>(undefined);
const ScreenContext = createContext<Screen>("walletList");
const SetScreenContext = createContext<
React.Dispatch<React.SetStateAction<Screen>> | undefined
>(undefined);
const ModalThemeContext = createContext<"light" | "dark">("dark");
const SetModalThemeContext = createContext<ThemeSetter | undefined>(undefined);
export const WalletUIStatesProvider = (
props: React.PropsWithChildren<{ theme?: "light" | "dark" }>,
) => {
const [isConnectingToWalletWrapper, setIsConnectingToWalletWrapper] =
useState<WalletWrapper>(false);
const [isWalletModalOpen, setIsWalletModalOpen] = useState(false);
const [screen, setScreen] = useState<Screen>("walletList");
const [modalTheme, setModalTheme] = useState(props.theme || "dark");
return (
<WalletWrapperConnection.Provider value={isConnectingToWalletWrapper}>
<SetWalletWrapperConnection.Provider
value={setIsConnectingToWalletWrapper}
>
<WalletModalOpen.Provider value={isWalletModalOpen}>
<SetWalletModalOpen.Provider value={setIsWalletModalOpen}>
<ScreenContext.Provider value={screen}>
<SetScreenContext.Provider value={setScreen}>
<ModalThemeContext.Provider value={modalTheme}>
<SetModalThemeContext.Provider value={setModalTheme}>
{props.children}
</SetModalThemeContext.Provider>
</ModalThemeContext.Provider>
</SetScreenContext.Provider>
</ScreenContext.Provider>
</SetWalletModalOpen.Provider>
</WalletModalOpen.Provider>
</SetWalletWrapperConnection.Provider>
</WalletWrapperConnection.Provider>
<WalletModalOpen.Provider value={isWalletModalOpen}>
<SetWalletModalOpen.Provider value={setIsWalletModalOpen}>
<ModalThemeContext.Provider value={modalTheme}>
<SetModalThemeContext.Provider value={setModalTheme}>
{props.children}
</SetModalThemeContext.Provider>
</ModalThemeContext.Provider>
</SetWalletModalOpen.Provider>
</WalletModalOpen.Provider>
);
};
export const useIsConnectingToWalletWrapper = () => {
return useContext(WalletWrapperConnection);
};
export const useIsWalletModalOpen = () => {
return useContext(WalletModalOpen);
};
export const useSetIsConnectingToWalletWrapper = () => {
const context = useContext(SetWalletWrapperConnection);
if (context === undefined) {
throw new Error(
"useSetIsConnectingToWalletWrapper must be used within a UIProvider",
);
}
return context;
};
export const useSetIsWalletModalOpen = () => {
const context = useContext(SetWalletModalOpen);
if (context === undefined) {
@@ -93,18 +41,6 @@ export const useSetIsWalletModalOpen = () => {
return context;
};
export const useScreen = () => {
return useContext(ScreenContext);
};
export const useSetScreen = () => {
const context = useContext(SetScreenContext);
if (context === undefined) {
throw new Error("useSetScreen must be used within a UIProvider");
}
return context;
};
export const useModalTheme = () => {
return useContext(ModalThemeContext);
};
+6 -6
View File
@@ -1,12 +1,12 @@
export { localWallet } from "./wallet/wallets/localWallet";
export { coinbaseWallet } from "./wallet/wallets/coinbaseWallet";
export { metamaskWallet } from "./wallet/wallets/metamaskWallet";
export { localWallet } from "./wallet/wallets/localWallet/localWallet";
export { coinbaseWallet } from "./wallet/wallets/coinbase/coinbaseWallet";
export { metamaskWallet } from "./wallet/wallets/metamask/metamaskWallet";
export { paperWallet } from "./wallet/wallets/paperWallet";
export { safeWallet } from "./wallet/wallets/safeWallet";
export { safeWallet } from "./wallet/wallets/safe/safeWallet";
export { walletConnect } from "./wallet/wallets/walletConnect";
export { walletConnectV1 } from "./wallet/wallets/walletConnectV1";
export { smartWallet } from "./wallet/wallets/smartWallet";
export { magicLink } from "./wallet/wallets/magicLink";
export { smartWallet } from "./wallet/wallets/smartWallet/smartWallet";
export { magicLink } from "./wallet/wallets/magic/magicLink";
// at the moment we'll re-export everything from the evm package
export * from "./evm";
@@ -1,418 +0,0 @@
import { Modal } from "../../components/Modal";
import { isMobile } from "../../evm/utils/isMobile";
import { useInstalledWallets } from "../hooks/useInstalledWallets";
import { WalletInfo } from "../types";
import { WalletSelector } from "./WalletSelector";
import { CoinbaseWalletSetup } from "./screens/Coinbase/CoinbaseConnecting";
import { CoinbaseGetStarted } from "./screens/Coinbase/CoinbaseGetStarted";
import { ScanCoinbase } from "./screens/Coinbase/CoinbaseScan";
import { MetamaskConnecting } from "./screens/Metamask/MetamaskConnecting";
import { MetamaskGetStarted } from "./screens/Metamask/MetamaskGetStarted";
import { ScanMetamask } from "./screens/Metamask/MetamaskScan";
import {
Wallet,
useConnect,
useConnectionStatus,
useDisconnect,
useWallet,
useWallets,
} from "@thirdweb-dev/react-core";
import { useCallback, useEffect, useRef, useState } from "react";
import { SelectpersonalWallet } from "./screens/Safe/SelectPersonalWallet";
import { SafeForm } from "./screens/Safe/SafeForm";
import { GetStartedWithWallets } from "./screens/GetStartedWithWallets";
import {
useIsConnectingToWalletWrapper,
useIsWalletModalOpen,
useModalTheme,
useScreen,
useSetIsConnectingToWalletWrapper,
useSetIsWalletModalOpen,
useSetScreen,
} from "../../evm/providers/wallet-ui-states-provider";
import { ifWaiting } from "../../evm/utils/ifWaiting";
import { ThemeProvider } from "@emotion/react";
import { darkTheme, lightTheme } from "../../design-system";
import { LocalWalletSetup } from "./screens/LocalWallet/LocalWalletSetup";
import { SmartWalletForm } from "./screens/SmartWallet/SmartWalletForm";
import { MagicConnect } from "./screens/Magic/MagicConnect";
import { SmartWalletSelect } from "./screens/SmartWallet/SmartWalletSelect";
import { SmartWalletObj } from "../wallets/smartWallet";
import { SafeWalletObj } from "../wallets/safeWallet";
import { walletIds } from "@thirdweb-dev/wallets";
import { WalletsInfoCtx } from "./walletInfo";
export const ConnectModal = () => {
const modalTheme = useModalTheme();
const isConnectingToWalletWrapper = useIsConnectingToWalletWrapper();
const showScreen = useScreen();
const setShowScreen = useSetScreen();
const setIsConnectingToWalletWrapper = useSetIsConnectingToWalletWrapper();
const isWalletModalOpen = useIsWalletModalOpen();
const setIsWalletModalOpen = useSetIsWalletModalOpen();
const connectionStatus = useConnectionStatus();
const disconnect = useDisconnect();
const connect = useConnect();
const wallets = useWallets();
const activeWallet = useWallet();
const installedWallets = useInstalledWallets();
// to hide the modal temporarily when showing the third party Modal to avoid z-index issues
const [hideModal, setHideModal] = useState(false);
const closeModalAndReset = useCallback(() => {
setShowScreen("walletList");
setIsConnectingToWalletWrapper(false);
setIsWalletModalOpen(false);
}, [setIsConnectingToWalletWrapper, setIsWalletModalOpen, setShowScreen]);
const onConnect = useCallback(() => {
if (isConnectingToWalletWrapper === "safe") {
setShowScreen("safe/form");
} else if (isConnectingToWalletWrapper === "smartWallet") {
setShowScreen("smartWallet/form");
} else {
closeModalAndReset();
}
}, [closeModalAndReset, isConnectingToWalletWrapper, setShowScreen]);
const onConnectError = useCallback(() => {
if (isConnectingToWalletWrapper === "safe") {
setShowScreen("safe/select-wallet");
} else if (isConnectingToWalletWrapper === "smartWallet") {
setShowScreen("smartWallet/select-wallet");
} else {
setShowScreen("walletList");
}
}, [isConnectingToWalletWrapper, setShowScreen]);
const onlyOneWallet = wallets.length === 1;
const handleBack = useCallback(() => {
if (isConnectingToWalletWrapper === "safe") {
setShowScreen("safe/select-wallet");
} else if (isConnectingToWalletWrapper === "smartWallet") {
setShowScreen("smartWallet/select-wallet");
} else {
setShowScreen("walletList");
}
}, [isConnectingToWalletWrapper, setShowScreen]);
type GetWalletInfo = (wallet: Wallet) => WalletInfo;
const getWalletInfo: GetWalletInfo = useCallback(
(wallet: Wallet) => ({
wallet,
installed:
wallet.id in installedWallets &&
installedWallets[wallet.id as keyof typeof installedWallets],
connect: async () => {
// Metamask
if (wallet.id === walletIds.metamask) {
if (installedWallets.metamask) {
try {
await ifWaiting({
for: connect(wallet, {}),
moreThan: 100,
do: () => {
setShowScreen("metamask/connecting");
},
});
onConnect();
} catch (e) {
onConnectError();
}
}
// if metamask is not injected
else {
// on mobile, open metamask app link
if (isMobile()) {
window.open(
`https://metamask.app.link/dapp/${window.location.toString()}`,
);
} else {
// on desktop, show the metamask scan qr code
setShowScreen("metamask/scan");
}
}
}
// Coinbase Wallet
else if (wallet.id === walletIds.coinbase) {
if (installedWallets.coinbaseWallet) {
try {
await ifWaiting({
for: connect(wallet, {}),
moreThan: 100,
do: () => {
setShowScreen("coinbase/connecting");
},
});
onConnect();
} catch (e) {
onConnectError();
}
} else {
if (isMobile()) {
// coinbase will redirect to download page for coinbase wallet apps
connect(wallet, {});
} else {
setShowScreen("coinbase/scan");
}
}
}
// Safe
else if (wallet.id === walletIds.safe) {
setIsConnectingToWalletWrapper("safe");
setShowScreen("safe/select-wallet");
}
// Smart Wallet
else if (wallet.id === walletIds.smartWallet) {
setIsConnectingToWalletWrapper("smartWallet");
setShowScreen("smartWallet/select-wallet");
}
// Local Wallet
else if (wallet.id === walletIds.localWallet) {
setShowScreen("localWallet/connect");
}
// Magic link
else if (wallet.id === walletIds.magicLink) {
setShowScreen("magic/connect");
}
// others ( they handle their own connection flow)
else {
try {
setHideModal(true);
await connect(wallet, {});
onConnect();
setHideModal(false);
} catch (e) {
onConnectError();
setHideModal(false);
console.error(e);
}
}
},
}),
[
connect,
installedWallets,
onConnect,
onConnectError,
setIsConnectingToWalletWrapper,
setShowScreen,
],
);
const smartWalletObj = wallets.find((w) => w.id === walletIds.smartWallet) as
| SmartWalletObj
| undefined;
const safeWalletObj = wallets.find((w) => w.id === walletIds.safe) as
| SafeWalletObj
| undefined;
const walletsInfo: WalletInfo[] = (() => {
if (isConnectingToWalletWrapper === "safe") {
return safeWalletObj?.config.personalWallets.map(getWalletInfo) || [];
}
if (isConnectingToWalletWrapper === "smartWallet") {
return smartWalletObj?.config.personalWallets.map(getWalletInfo) || [];
}
return wallets.map(getWalletInfo);
})();
// if only one wallet, auto select it
const connected = useRef(false);
useEffect(() => {
if (
showScreen === "walletList" &&
wallets.length === 1 &&
!activeWallet &&
isWalletModalOpen &&
!connected.current
) {
connected.current = true;
walletsInfo[0].connect();
}
}, [
walletsInfo,
showScreen,
wallets.length,
activeWallet,
isWalletModalOpen,
]);
useEffect(() => {
// if the modal is closed, and isConnectingToWalletWrapper is true, open again and connected
if (
isConnectingToWalletWrapper &&
!isWalletModalOpen &&
connectionStatus === "connected"
) {
setIsWalletModalOpen(true);
}
}, [
isConnectingToWalletWrapper,
isWalletModalOpen,
connectionStatus,
setIsWalletModalOpen,
]);
return (
<ThemeProvider
theme={
typeof modalTheme === "object"
? modalTheme
: modalTheme === "light"
? lightTheme
: darkTheme
}
>
<WalletsInfoCtx.Provider value={walletsInfo}>
<Modal
style={{
maxWidth: "480px",
}}
open={hideModal ? false : isWalletModalOpen}
setOpen={(value) => {
setIsWalletModalOpen(value);
if (!value) {
if (!isConnectingToWalletWrapper) {
closeModalAndReset();
if (connectionStatus === "connecting") {
disconnect();
}
}
}
}}
>
{showScreen === "walletList" && (
<WalletSelector
onGetStarted={() => {
setShowScreen("wallets/get-started");
}}
onGuestConnect={() => {
setShowScreen("localWallet/connect");
}}
/>
)}
{showScreen === "metamask/get-started" && (
<MetamaskGetStarted
onBack={() => {
setShowScreen("metamask/scan");
}}
/>
)}
{showScreen === "coinbase/get-started" && (
<CoinbaseGetStarted
onBack={() => {
setShowScreen("coinbase/scan");
}}
/>
)}
{showScreen === "metamask/connecting" && (
<MetamaskConnecting onBack={handleBack} />
)}
{showScreen === "metamask/scan" && (
<ScanMetamask
onBack={handleBack}
onConnected={onConnect}
onGetStarted={() => {
setShowScreen("metamask/get-started");
}}
/>
)}
{showScreen === "coinbase/scan" && (
<ScanCoinbase
onBack={handleBack}
onConnected={onConnect}
onGetStarted={() => {
setShowScreen("coinbase/get-started");
}}
/>
)}
{showScreen === "coinbase/connecting" && (
<CoinbaseWalletSetup onBack={handleBack} />
)}
{showScreen === "safe/select-wallet" && (
<SelectpersonalWallet
safeWallet={safeWalletObj as SafeWalletObj}
onBack={() => {
if (onlyOneWallet) {
return;
}
setIsConnectingToWalletWrapper(false);
setShowScreen("walletList");
}}
/>
)}
{showScreen === "safe/form" && (
<SafeForm
safeWallet={safeWalletObj as SafeWalletObj}
onBack={handleBack}
onConnect={() => {
closeModalAndReset();
}}
/>
)}
{showScreen === "wallets/get-started" && (
<GetStartedWithWallets
onBack={handleBack}
walletInfo={walletsInfo[0]}
/>
)}
{showScreen === "localWallet/connect" && (
<LocalWalletSetup onBack={handleBack} onConnected={onConnect} />
)}
{showScreen === "smartWallet/form" && (
<SmartWalletForm
smartWallet={smartWalletObj as SmartWalletObj}
onBack={handleBack}
onConnect={() => {
closeModalAndReset();
}}
/>
)}
{showScreen === "smartWallet/select-wallet" && (
<SmartWalletSelect
smartWallet={smartWalletObj as SmartWalletObj}
onBack={() => {
if (onlyOneWallet) {
return;
}
setShowScreen("walletList");
setIsConnectingToWalletWrapper(false);
}}
/>
)}
{showScreen === "magic/connect" && (
<MagicConnect onBack={handleBack} onConnect={onConnect} />
)}
</Modal>
</WalletsInfoCtx.Provider>
</ThemeProvider>
);
};
@@ -0,0 +1,119 @@
import { Modal } from "../../components/Modal";
import { WalletSelector } from "./WalletSelector";
import {
ConfiguredWallet,
useConnectionStatus,
useWallets,
} from "@thirdweb-dev/react-core";
import {
useIsWalletModalOpen,
useModalTheme,
useSetIsWalletModalOpen,
} from "../../evm/providers/wallet-ui-states-provider";
import { ThemeProvider } from "@emotion/react";
import { darkTheme, lightTheme } from "../../design-system";
import { useState, useCallback, useEffect } from "react";
import { GetStartedWithWallets } from "./screens/GetStartedWithWallets";
import { reservedScreens } from "./constants";
import { HeadlessConnectUI } from "../wallets/headlessConnectUI";
export const ConnectModal = () => {
const modalTheme = useModalTheme();
const [screen, setScreen] = useState<string | ConfiguredWallet>(
reservedScreens.main,
);
const isWalletModalOpen = useIsWalletModalOpen();
const setIsWalletModalOpen = useSetIsWalletModalOpen();
const connectionStatus = useConnectionStatus();
const configuredWallets = useWallets();
const handleClose = useCallback(
(reset = true) => {
if (reset) {
setScreen(reservedScreens.main);
}
setIsWalletModalOpen(false);
},
[setIsWalletModalOpen],
);
const handleBack = useCallback(() => {
setScreen(reservedScreens.main);
}, [setScreen]);
const isWrapper =
typeof screen !== "string" &&
"config" in screen &&
!!(screen.config as any).personalWallets;
// if the modal is closed, rendering a wrapper, personal wallet is connected - open modal back
useEffect(() => {
if (isWrapper && !isWalletModalOpen && connectionStatus === "connected") {
setIsWalletModalOpen(true);
}
}, [isWalletModalOpen, connectionStatus, setIsWalletModalOpen, isWrapper]);
return (
<ThemeProvider
theme={
typeof modalTheme === "object"
? modalTheme
: modalTheme === "light"
? lightTheme
: darkTheme
}
>
<Modal
style={{
maxWidth: "480px",
}}
open={isWalletModalOpen}
setOpen={(value) => {
setIsWalletModalOpen(value);
if (!value) {
setScreen(reservedScreens.main); // reset screen
}
}}
>
{screen === reservedScreens.main && (
<WalletSelector
configuredWallets={configuredWallets}
onGetStarted={() => {
setScreen(reservedScreens.getStarted);
}}
selectWallet={(wallet) => {
setScreen(wallet);
}}
/>
)}
{screen === reservedScreens.getStarted && (
<GetStartedWithWallets onBack={handleBack} />
)}
{typeof screen !== "string" && screen.connectUI && (
<screen.connectUI
goBack={handleBack}
close={handleClose}
isOpen={isWalletModalOpen}
open={() => {
setIsWalletModalOpen(true);
}}
/>
)}
{typeof screen !== "string" && !screen.connectUI && (
<HeadlessConnectUI
goBack={handleBack}
close={handleClose}
isOpen={isWalletModalOpen}
open={() => {
setIsWalletModalOpen(true);
}}
configuredWallet={screen}
/>
)}
</Modal>
</ThemeProvider>
);
};
@@ -13,7 +13,6 @@ import {
} from "@thirdweb-dev/react-core";
import { useContext, useState } from "react";
import {
useIsConnectingToWalletWrapper,
useSetIsWalletModalOpen,
useSetModalTheme,
} from "../../evm/providers/wallet-ui-states-provider";
@@ -54,7 +53,6 @@ export const ConnectWallet: React.FC<ConnectWalletProps> = (props) => {
const activeWallet = useWallet();
const themeFromProvider = useContext(ThirdwebThemeContext);
const theme = props.theme || themeFromProvider || "dark";
const isConnectingToWalletWrapper = useIsConnectingToWalletWrapper();
const connectionStatus = useConnectionStatus();
const isLoading =
@@ -122,7 +120,7 @@ export const ConnectWallet: React.FC<ConnectWalletProps> = (props) => {
)}
{!requiresSignIn &&
(!activeWallet || isConnectingToWalletWrapper ? (
(!activeWallet ? (
// connect wallet button
<AnimatedButton
disabled={isLoading}
@@ -21,9 +21,14 @@ import { ExitIcon } from "./icons/ExitIcon";
import { keyframes } from "@emotion/react";
import styled from "@emotion/styled";
import * as DropdownMenu from "@radix-ui/react-dropdown-menu";
import { ChevronRightIcon, ShuffleIcon } from "@radix-ui/react-icons";
import { defaultChains, Localhost } from "@thirdweb-dev/chains";
import {
ChevronRightIcon,
PinBottomIcon,
ShuffleIcon,
} from "@radix-ui/react-icons";
import { Localhost } from "@thirdweb-dev/chains";
import {
useActiveChain,
useAddress,
useBalance,
useChainId,
@@ -32,8 +37,9 @@ import {
useSupportedChains,
useThirdwebWallet,
useWallet,
WalletInstance,
} from "@thirdweb-dev/react-core";
import { useEffect, useMemo, useState } from "react";
import { useEffect, useState } from "react";
import { fadeInAnimation } from "../../components/FadeIn";
import {
AbstractClientWallet,
@@ -44,8 +50,8 @@ import {
import { Flex } from "../../components/basic";
import { FundsIcon } from "./icons/FundsIcon";
import { utils } from "ethers";
import { GenericWalletIcon } from "./icons/GenericWalletIcon";
import { ExportLocalWallet } from "./screens/LocalWallet/ExportLocalWallet";
import { ExportLocalWallet } from "../wallets/localWallet/ExportLocalWallet";
import { ErrorMessage } from "../../components/formElements";
export type DropDownPosition = {
side: "top" | "bottom" | "left" | "right";
@@ -68,25 +74,12 @@ export const ConnectedWalletDetails: React.FC<{
const address = useAddress();
const balanceQuery = useBalance();
const activeWallet = useWallet();
const walletContext = useThirdwebWallet();
const [personalWalletBalance, setPersonalWalletBalance] = useState<
string | undefined
>(undefined);
const [personalWalletAddress, setPersonalWalletAddress] = useState<
string | undefined
>(undefined);
const [showExportModal, setShowExportModal] = useState(false);
const [wrapperWallet, setWrapperWallet] = useState<
WalletInstance | undefined
>();
const chain = useMemo(() => {
return chains.find((_chain) => _chain.chainId === walletChainId);
}, [walletChainId, chains]);
const unknownChain = useMemo(() => {
if (!chain) {
return defaultChains.find((c) => c.chainId === walletChainId);
}
}, [walletChainId, chain]);
const chain = useActiveChain();
const activeWalletIconURL = activeWallet?.getMeta().iconURL || "";
const [showNetworkSelector, setShowNetworkSelector] = useState(false);
@@ -100,24 +93,6 @@ export const ConnectedWalletDetails: React.FC<{
const disableSwitchChain = !!personalWallet;
// get personal wallet address and balance
useEffect(() => {
if (!personalWallet) {
setPersonalWalletAddress(undefined);
setPersonalWalletBalance(undefined);
return;
}
personalWallet.getAddress().then((_address) => {
setPersonalWalletAddress(_address);
});
personalWallet.getSigner().then((signer) => {
signer.getBalance().then((balance) => {
setPersonalWalletBalance(utils.formatEther(balance));
});
});
}, [personalWallet]);
const trigger = (
<WalletInfoButton
type="button"
@@ -141,9 +116,24 @@ export const ConnectedWalletDetails: React.FC<{
<Skeleton height={fontSize.sm} width="82px" />
)}
<Spacer y="xs" />
<WalletAddress className={`${TW_CONNECTED_WALLET}__address`}>
{shortenString(address || "")}
</WalletAddress>
{activeWallet?.walletId === walletIds.localWallet ? (
<ErrorMessage
style={{
lineHeight: 1,
minWidth: "70px",
fontSize: fontSize.xs,
}}
>
Guest
</ErrorMessage>
) : address ? (
<WalletAddress className={`${TW_CONNECTED_WALLET}__address`}>
{shortenString(address || "")}
</WalletAddress>
) : (
<Skeleton height={fontSize.xs} width="88px" />
)}
</ColFlex>
<Img
@@ -156,81 +146,39 @@ export const ConnectedWalletDetails: React.FC<{
);
const networkSwitcherButton = (
<MenuButton
id="current-network"
type="button"
disabled={disableSwitchChain}
onClick={() => {
setOpen(false);
setShowNetworkSelector(true);
}}
<ToolTip
tip={
disableSwitchChain ? "Network Switching is disabled" : "Switch Network"
}
>
<div
style={{
display: "flex",
alignItems: "center",
position: "relative",
<MenuButton
type="button"
disabled={disableSwitchChain}
onClick={() => {
setOpen(false);
setShowNetworkSelector(true);
}}
>
<ChainIcon chain={chain || unknownChain} size={iconSize.lg} active />
</div>
{chain?.name || unknownChain?.name || `Unknown chain #${walletChainId}`}
<StyledChevronRightIcon
width={iconSize.sm}
height={iconSize.sm}
style={{
flexShrink: 0,
marginLeft: "auto",
}}
/>
</MenuButton>
);
const switchToPersonalWallet = personalWallet && (
<MenuButton
type="button"
onClick={() => {
walletContext.handleWalletConnect(personalWallet);
}}
>
<div
style={{
display: "flex",
alignItems: "center",
position: "relative",
}}
>
<Img
src={personalWallet.getMeta().iconURL}
width={iconSize.lg}
height={iconSize.lg}
<div
style={{
display: "flex",
alignItems: "center",
position: "relative",
}}
>
<ChainIcon chain={chain} size={iconSize.lg} active />
</div>
{chain?.name || `Unknown chain #${walletChainId}`}
<StyledChevronRightIcon
width={iconSize.sm}
height={iconSize.sm}
style={{
flexShrink: 0,
marginLeft: "auto",
}}
/>
</div>
<ColFlex>
{personalWalletBalance ? (
<WalletBalance>
{String(personalWalletBalance).slice(0, 5)}{" "}
{balanceQuery.data?.symbol}
</WalletBalance>
) : (
<Skeleton height={fontSize.sm} width="82px" />
)}
<Spacer y="xxs" />
<WalletAddress>
{shortenString(personalWalletAddress || "")}
</WalletAddress>
</ColFlex>
<StyledChevronRightIcon
width={iconSize.sm}
height={iconSize.sm}
style={{
flexShrink: 0,
marginLeft: "auto",
}}
/>
</MenuButton>
</MenuButton>
</ToolTip>
);
const content = (
@@ -305,7 +253,7 @@ export const ConnectedWalletDetails: React.FC<{
{/* Network Switcher */}
<div>
<DropdownLabel htmlFor="current-network">Current Network</DropdownLabel>
<DropdownLabel>Current Network</DropdownLabel>
<Spacer y="sm" />
{networkSwitcherButton}
</div>
@@ -314,13 +262,31 @@ export const ConnectedWalletDetails: React.FC<{
{personalWallet && (
<div>
<Spacer y="lg" />
<DropdownLabel htmlFor="current-network">
Personal Wallet
<DropdownLabel>Switch to Personal Wallet</DropdownLabel>
<Spacer y="xs" />
<WalletSwitcher
wallet={personalWallet}
onSwitch={() => {
setWrapperWallet(activeWallet);
}}
/>
</div>
)}
{/* Switch to Wrapper Wallet */}
{wrapperWallet && (
<div>
<Spacer y="lg" />
<DropdownLabel>
Switch to {wrapperWallet.getMeta().name}
</DropdownLabel>
<Spacer y="sm" />
<ToolTip tip="Switch To Personal Wallet">
{switchToPersonalWallet}
</ToolTip>
<Spacer y="xs" />
<WalletSwitcher
wallet={wrapperWallet}
onSwitch={() => {
setWrapperWallet(undefined);
}}
/>
</div>
)}
@@ -334,6 +300,9 @@ export const ConnectedWalletDetails: React.FC<{
(activeWallet as MetaMaskWallet).switchAccount();
setOpen(false);
}}
style={{
fontSize: fontSize.sm,
}}
>
<ShuffleIcon width={iconSize.sm} height={iconSize.sm} />
Switch Account
@@ -386,10 +355,21 @@ export const ConnectedWalletDetails: React.FC<{
}}
>
<SecondaryIconContainer>
<GenericWalletIcon size={iconSize.sm} />
<PinBottomIcon width={iconSize.sm} height={iconSize.sm} />
</SecondaryIconContainer>
Export Wallet{" "}
Backup wallet{" "}
</MenuButton>
<Spacer y="sm" />
<ErrorMessage
style={{
fontSize: fontSize.xs,
textAlign: "center",
}}
>
This is a temporary guest wallet <br />
Backup if you {`don't `}
want to lose access to it
</ErrorMessage>
</>
)}
</div>
@@ -581,6 +561,12 @@ const MenuButton = styled.button<{ theme?: Theme }>`
display: none;
}
}
&[disabled]:hover {
transition: box-shadow 250ms ease, border-color 250ms ease;
border: 1px solid ${(props) => props.theme.text.danger};
box-shadow: 0 0 0 1px ${(props) => props.theme.text.danger};
}
`;
const MenuLink = MenuButton.withComponent("a");
@@ -611,3 +597,81 @@ const SecondaryIconContainer = styled.div<{ theme?: Theme }>`
justify-content: center;
color: ${(props) => props.theme.icon.secondary};
`;
function WalletSwitcher({
wallet,
onSwitch,
}: {
wallet: WalletInstance;
onSwitch: () => void;
}) {
const walletContext = useThirdwebWallet();
const balanceQuery = useBalance();
const [walletBalance, setWalletBalance] = useState<string | undefined>(
undefined,
);
const [address, setAddress] = useState<string | undefined>(undefined);
// get personal wallet address and balance
useEffect(() => {
if (!wallet) {
setAddress(undefined);
setWalletBalance(undefined);
return;
}
wallet.getAddress().then((_address) => {
setAddress(_address);
});
wallet.getSigner().then((signer) => {
signer.getBalance().then((balance) => {
setWalletBalance(utils.formatEther(balance));
});
});
}, [wallet]);
return (
<MenuButton
type="button"
onClick={() => {
walletContext.handleWalletConnect(wallet);
onSwitch();
}}
>
<div
style={{
display: "flex",
alignItems: "center",
position: "relative",
}}
>
<Img
src={wallet.getMeta().iconURL}
width={iconSize.lg}
height={iconSize.lg}
/>
</div>
<ColFlex>
{walletBalance ? (
<WalletBalance>
{String(walletBalance).slice(0, 5)} {balanceQuery.data?.symbol}
</WalletBalance>
) : (
<Skeleton height={fontSize.sm} width="82px" />
)}
<Spacer y="xxs" />
<WalletAddress>{shortenString(address || "")}</WalletAddress>
</ColFlex>
<StyledChevronRightIcon
width={iconSize.sm}
height={iconSize.sm}
style={{
flexShrink: 0,
marginLeft: "auto",
}}
/>
</MenuButton>
);
}
@@ -11,35 +11,43 @@ import {
Theme,
} from "../../design-system";
import styled from "@emotion/styled";
import { useWalletInfo, useWalletsInfo } from "./walletInfo";
import { WalletInfo } from "../types";
import { ConfiguredWallet } from "@thirdweb-dev/react-core";
import { useConfiguredWallet } from "../hooks/useConfiguredWallet";
import { walletIds } from "@thirdweb-dev/wallets";
export const WalletSelector: React.FC<{
configuredWallets: ConfiguredWallet[];
selectWallet: (wallet: ConfiguredWallet) => void;
onGetStarted: () => void;
onGuestConnect: () => void;
}> = (props) => {
const _allWalletsInfo = useWalletsInfo();
const allWalletsInfo = _allWalletsInfo.filter(
(w) => w.wallet.id !== walletIds.localWallet,
const localWalletInfo = useConfiguredWallet("localWallet", false);
const configuredWallets = props.configuredWallets.filter(
(w) => w.id !== walletIds.localWallet,
);
const localWalletInfo = useWalletInfo("localWallet", false);
const showGetStarted =
!localWalletInfo && !!allWalletsInfo[0].wallet.meta.urls;
!localWalletInfo && !!props.configuredWallets[0].meta.urls;
return (
<>
<ModalTitle>Choose your wallet</ModalTitle>
<Spacer y="xl" />
<WalletSelection walletsInfo={allWalletsInfo} />
<WalletSelection
configuredWallets={configuredWallets}
selectWallet={props.selectWallet}
/>
{localWalletInfo && (
<>
<Spacer y="xl" />
<Flex justifyContent="center">
<Button variant="link" onClick={props.onGuestConnect}>
<Button
variant="link"
onClick={() => {
props.selectWallet(localWalletInfo);
}}
>
Continue as guest
</Button>
</Flex>
@@ -66,15 +74,19 @@ export const WalletSelector: React.FC<{
);
};
export const WalletSelection: React.FC<{ walletsInfo: WalletInfo[] }> = ({
walletsInfo,
}) => {
export const WalletSelection: React.FC<{
configuredWallets: ConfiguredWallet[];
selectWallet: (wallet: ConfiguredWallet) => void;
}> = (props) => {
// show the installed wallets first
const sortedWalletsInfo = walletsInfo.sort((a, b) => {
if (a.installed && !b.installed) {
const configuredWallets = props.configuredWallets.sort((a, b) => {
const aInstalled = a.isInstalled ? a.isInstalled() : false;
const bInstalled = b.isInstalled ? b.isInstalled() : false;
if (aInstalled && !bInstalled) {
return -1;
}
if (!a.installed && b.installed) {
if (!aInstalled && bInstalled) {
return 1;
}
return 0;
@@ -82,23 +94,26 @@ export const WalletSelection: React.FC<{ walletsInfo: WalletInfo[] }> = ({
return (
<WalletList>
{sortedWalletsInfo.map((walletInfo) => {
{configuredWallets.map((configuredWallet) => {
const isInstalled = configuredWallet.isInstalled
? configuredWallet.isInstalled()
: false;
return (
<li key={walletInfo.wallet.id}>
<li key={configuredWallet.id}>
<WalletButton
type="button"
onClick={() => {
walletInfo.connect();
props.selectWallet(configuredWallet);
}}
>
<Img
src={walletInfo.wallet.meta.iconURL}
src={configuredWallet.meta.iconURL}
width={iconSize.lg}
height={iconSize.lg}
loading="eager"
/>
<WalletName>{walletInfo.wallet.meta.name}</WalletName>
{walletInfo.installed && <InstallBadge> Installed </InstallBadge>}
<WalletName>{configuredWallet.meta.name}</WalletName>
{isInstalled && <InstallBadge> Installed </InstallBadge>}
</WalletButton>
</li>
);
@@ -0,0 +1,4 @@
export const reservedScreens = {
main: "main",
getStarted: "getStarted",
};
@@ -1,17 +0,0 @@
import { IconFC } from "./types";
export const GenericWalletIcon: IconFC = ({ size }) => {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
height={size}
viewBox="0 96 960 960"
width={size}
>
<path
fill="currentColor"
d="M652 640q25 0 44.5-19.5t19.5-45q0-25.5-19.5-44.5T652 512q-25 0-44.5 19T588 575.5q0 25.5 19.5 45T652 640ZM180 823v53-600 547Zm0 113q-23 0-41.5-18T120 876V276q0-23 18.5-41.5T180 216h600q24 0 42 18.5t18 41.5v134h-60V276H180v600h600V743h60v133q0 24-18 42t-42 18H180Zm358-173q-34 0-54-20t-20-53V463q0-34 20-53.5t54-19.5h270q34 0 54 19.5t20 53.5v227q0 33-20 53t-54 20H538Zm284-60V450H524v253h298Z"
/>
</svg>
);
};
@@ -1,17 +0,0 @@
import { ConnectingScreen } from "../ConnectingScreen";
import { useWalletInfo } from "../../walletInfo";
export const CoinbaseWalletSetup: React.FC<{
onBack: () => void;
}> = ({ onBack }) => {
const coinbase = useWalletInfo("coinbase", true);
return (
<ConnectingScreen
onBack={onBack}
walletName={coinbase.wallet.meta.name}
walletIconURL={coinbase.wallet.meta.iconURL}
supportLink="https://help.coinbase.com/en/wallet/other-topics/troubleshooting-and-tips"
/>
);
};
@@ -1,20 +0,0 @@
import { GetStartedScreen } from "../GetStartedScreen";
import { useWalletInfo } from "../../walletInfo";
export const CoinbaseGetStarted: React.FC<{
onBack: () => void;
}> = ({ onBack }) => {
const coinbaseWalletObj = useWalletInfo("coinbase", true);
const { name, iconURL } = coinbaseWalletObj.wallet.meta;
return (
<GetStartedScreen
walletIconURL={iconURL}
walletName={name}
chromeExtensionLink="https://chrome.google.com/webstore/detail/coinbase-wallet-extension/hnfanknocfeofbddgcijnmhnfnkdnaad"
googlePlayStoreLink="https://play.google.com/store/apps/details?id=org.toshi"
appleStoreLink="https://apps.apple.com/us/app/coinbase-wallet-nfts-crypto/id1278383455"
onBack={onBack}
/>
);
};
@@ -8,14 +8,17 @@ import {
import { fontSize, iconSize, spacing, Theme } from "../../../design-system";
import { GetStartedScreen } from "./GetStartedScreen";
import styled from "@emotion/styled";
import { WalletInfo } from "../../types";
import { SecondaryText } from "../../../components/text";
import { useWallets } from "@thirdweb-dev/react-core";
export const GetStartedWithWallets: React.FC<{
onBack: () => void;
walletInfo: WalletInfo;
}> = ({ walletInfo, onBack }) => {
const { meta } = walletInfo.wallet;
}> = ({ onBack }) => {
const configuredWallets = useWallets();
console.log({
configuredWallets,
});
const { meta } = configuredWallets[0];
return (
<GetStartedScreen
@@ -1,156 +0,0 @@
import { Spacer } from "../../../../components/Spacer";
import { Button } from "../../../../components/buttons";
import { FormFooter, Label } from "../../../../components/formElements";
import {
ModalTitle,
ModalDescription,
} from "../../../../components/modalElements";
import { Theme, iconSize } from "../../../../design-system";
import styled from "@emotion/styled";
import { fontSize } from "../../../../design-system";
import { EyeClosedIcon, EyeOpenIcon } from "@radix-ui/react-icons";
import { FormFieldWithIconButton } from "../../../../components/formFields";
import { useEffect, useState } from "react";
import { shortenAddress } from "../../../../evm/utils/addresses";
import { LocalWallet } from "@thirdweb-dev/wallets";
import { GenericWalletIcon } from "../../icons/GenericWalletIcon";
export const ExportLocalWallet: React.FC<{
onBack: () => void;
onExport: () => void;
localWallet: LocalWallet;
}> = (props) => {
const [password, setPassword] = useState("");
const [showPassword, setShowPassword] = useState(false);
const [isWrongPassword, setIsWrongPassword] = useState(false);
const [address, setAddress] = useState("");
useEffect(() => {
props.localWallet.getAddress().then((add) => {
setAddress(add);
});
}, [props.localWallet]);
const exportFromLocalStorage = async () => {
if (!props.localWallet) {
throw new Error("invalid state");
}
try {
// await props.localWallet.import({
// encryptedJson: walletData.data,
// password,
// });
const json = await props.localWallet.export({
strategy: "encryptedJson",
password,
});
downloadAsFile(JSON.parse(json), "wallet.json", "application/json");
props.onExport();
} catch (e) {
setIsWrongPassword(true);
return;
}
};
return (
<>
<GenericWalletIcon size={iconSize.xl} />
<Spacer y="lg" />
<ModalTitle
style={{
textAlign: "left",
}}
>
Export Wallet
</ModalTitle>
<Spacer y="md" />
<ModalDescription>
This will download a JSON file containing your wallet information onto
your device encrypted with the password.
</ModalDescription>
<Spacer y="xl" />
<form
onSubmit={(e) => {
e.preventDefault();
exportFromLocalStorage();
}}
>
<Label>Wallet Address</Label>
<Spacer y="sm" />
<SavedWalletAddress>
{address ? shortenAddress(address) : "Loading"}
</SavedWalletAddress>
<Spacer y="lg" />
{/* Hidden Account Address as Username */}
<input
type="text"
name="username"
autoComplete="off"
value={address}
disabled
style={{ display: "none" }}
/>
{/* password */}
<FormFieldWithIconButton
noSave
required
name="current-password"
autocomplete="current-password"
id="current-password"
onChange={(value) => {
setPassword(value);
setIsWrongPassword(false);
}}
right={{
onClick: () => setShowPassword(!showPassword),
icon: showPassword ? <EyeClosedIcon /> : <EyeOpenIcon />,
}}
label="Password"
type={showPassword ? "text" : "password"}
value={password}
error={isWrongPassword ? "Wrong Password" : ""}
/>
<Spacer y="xl" />
<FormFooter>
<Button disabled={isWrongPassword} variant="inverted" type="submit">
Export
</Button>
</FormFooter>
</form>
</>
);
};
function downloadAsFile(data: any, fileName: string, fileType: string) {
const blob = new Blob([JSON.stringify(data, null, 2)], {
type: fileType,
});
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = fileName;
document.body.appendChild(a);
a.style.display = "none";
a.click();
URL.revokeObjectURL(a.href);
}
const SavedWalletAddress = styled.p<{ theme?: Theme }>`
font-size: ${fontSize.md};
color: ${(props) => props.theme.text.secondary};
margin: 0;
`;
@@ -1,51 +0,0 @@
import { Spinner } from "../../../../components/Spinner";
import styled from "@emotion/styled";
import { useLocalWalletInfo } from "./useLocalWalletInfo";
import {
CreateLocalWallet_Guest,
CreateLocalWallet_Password,
} from "./CreateLocalWallet";
import { ReconnectLocalWallet } from "./ReconnectLocalWallet";
import { LocalWalletObj } from "../../../wallets/localWallet";
import { useWalletInfo } from "../../walletInfo";
export const LocalWalletSetup: React.FC<{
onBack: () => void;
onConnected: () => void;
}> = (props) => {
const { walletData } = useLocalWalletInfo();
const localWalletInfo = useWalletInfo("localWallet", true);
const wallet = localWalletInfo.wallet as LocalWalletObj;
if (!wallet.config.persist) {
return <CreateLocalWallet_Guest {...props} />;
}
if (walletData === "loading") {
return (
<LoadingSpinnerContainer>
<Spinner size="lg" color="primary" />
</LoadingSpinnerContainer>
);
}
if (walletData) {
return (
<ReconnectLocalWallet
onConnected={props.onConnected}
onBack={props.onBack}
/>
);
}
return <CreateLocalWallet_Password {...props} />;
};
const LoadingSpinnerContainer = styled.div`
height: 300px;
display: flex;
align-items: center;
justify-content: center;
`;
@@ -1,19 +0,0 @@
import { iconSize } from "../../../../design-system";
import { Img } from "../../../../components/Img";
import { Spacer } from "../../../../components/Spacer";
import { BackButton } from "../../../../components/modalElements";
import { Wallet } from "@thirdweb-dev/react-core";
export const LocalWalletModalHeader: React.FC<{
onBack: () => void;
meta: Wallet["meta"];
}> = (props) => {
return (
<>
<BackButton onClick={props.onBack} />
<Spacer y="md" />
<Img src={props.meta.iconURL} width={iconSize.xl} height={iconSize.xl} />
<Spacer y="sm" />
</>
);
};
@@ -1,17 +0,0 @@
import { ConnectingScreen } from "../ConnectingScreen";
import { useWalletInfo } from "../../walletInfo";
export const MetamaskConnecting: React.FC<{
onBack: () => void;
}> = (props) => {
const metamaskInfo = useWalletInfo("metamask", true);
const { name, iconURL } = metamaskInfo.wallet.meta;
return (
<ConnectingScreen
onBack={props.onBack}
walletName={name}
walletIconURL={iconURL}
supportLink="https://support.metamask.io/hc/en-us/articles/4406430256539-User-Guide-Troubleshooting"
/>
);
};
@@ -1,20 +0,0 @@
import { GetStartedScreen } from "../GetStartedScreen";
import { useWalletInfo } from "../../walletInfo";
export const MetamaskGetStarted: React.FC<{
onBack: () => void;
}> = (props) => {
const walletInfo = useWalletInfo("metamask", true);
const { name, iconURL } = walletInfo?.wallet.meta;
return (
<GetStartedScreen
walletIconURL={iconURL}
walletName={name}
chromeExtensionLink="https://chrome.google.com/webstore/detail/metamask/nkbihfbeogaeaoehlefnkodbefgpgknn"
googlePlayStoreLink="https://play.google.com/store/apps/details?id=io.metamask"
appleStoreLink="https://apps.apple.com/us/app/metamask-blockchain-wallet/id1438144202"
onBack={props.onBack}
/>
);
};
@@ -1,69 +0,0 @@
import { walletIds } from "@thirdweb-dev/wallets";
import { Img } from "../../../../components/Img";
import { Spacer } from "../../../../components/Spacer";
import { Flex } from "../../../../components/basic";
import { Button } from "../../../../components/buttons";
import {
BackButton,
ModalTitle,
ModalDescription,
} from "../../../../components/modalElements";
import { iconSize, spacing } from "../../../../design-system";
import { WalletSelection } from "../../WalletSelector";
import styled from "@emotion/styled";
import { SmartWalletObj } from "../../../wallets/smartWallet";
import { useWalletInfo, useWalletsInfo } from "../../walletInfo";
export const SmartWalletSelect: React.FC<{
onBack: () => void;
smartWallet: SmartWalletObj;
}> = (props) => {
const guestWallet = useWalletInfo("localWallet", false);
const allWalletsInfo = useWalletsInfo();
const walletsInfo = allWalletsInfo.filter(
(w) =>
w.wallet.id !== walletIds.smartWallet &&
w.wallet.id !== walletIds.safe &&
w.wallet.id !== walletIds.localWallet,
);
return (
<>
<BackButton onClick={props.onBack} />
<IconContainer>
<Img
src={props.smartWallet.meta.iconURL}
width={iconSize.xl}
height={iconSize.xl}
/>
</IconContainer>
<Spacer y="lg" />
<ModalTitle>Choose your wallet</ModalTitle>
<Spacer y="sm" />
<ModalDescription>
Select a personal wallet to connect to smart wallet
</ModalDescription>
<Spacer y="lg" />
<WalletSelection walletsInfo={walletsInfo} />
{guestWallet && (
<>
<Spacer y="xl" />
<Flex justifyContent="center">
<Button variant="link" onClick={guestWallet.connect}>
Continue as guest
</Button>
</Flex>
</>
)}
</>
);
};
const IconContainer = styled.div`
margin-top: ${spacing.lg};
`;
@@ -1,29 +0,0 @@
import { walletIds } from "@thirdweb-dev/wallets";
import { createContext, useContext } from "react";
import { WalletInfo } from "../types";
export const WalletsInfoCtx = createContext<WalletInfo[]>([]);
export function useWalletInfo(
id: keyof typeof walletIds,
required: true,
): WalletInfo;
export function useWalletInfo(
id: keyof typeof walletIds,
required: false,
): WalletInfo | undefined;
export function useWalletInfo(
id: keyof typeof walletIds,
required: true | false,
): WalletInfo | undefined {
const walletsInfo = useContext(WalletsInfoCtx);
const found = walletsInfo.find((w) => w.wallet.id === id);
if (required && !found) {
throw new Error(`Wallet ${id} not found`);
}
return found;
}
export function useWalletsInfo() {
return useContext(WalletsInfoCtx);
}
@@ -0,0 +1,24 @@
import { walletIds } from "@thirdweb-dev/wallets";
import { ConfiguredWallet, useWallets } from "@thirdweb-dev/react-core";
// TODO :may be this is not needed anymore - only used in a few places
export function useConfiguredWallet(
id: keyof typeof walletIds,
required: true,
): ConfiguredWallet;
export function useConfiguredWallet(
id: keyof typeof walletIds,
required: false,
): ConfiguredWallet | undefined;
export function useConfiguredWallet(
id: keyof typeof walletIds,
required: true | false,
): ConfiguredWallet | undefined {
const wallets = useWallets();
const found = wallets.find((w) => w.id === id);
if (required && !found) {
throw new Error(`Wallet ${id} not found`);
}
return found;
}
-7
View File
@@ -1,7 +0,0 @@
import { Wallet } from "@thirdweb-dev/react-core";
export type WalletInfo = {
wallet: Wallet;
installed: boolean;
connect: () => Promise<void>;
};
@@ -0,0 +1,103 @@
import {
ConfiguredWallet,
ConnectUIProps,
useConnect,
} from "@thirdweb-dev/react-core";
import { ConnectingScreen } from "../../ConnectWallet/screens/ConnectingScreen";
import { isMobile } from "../../../evm/utils/isMobile";
import { useEffect, useRef, useState } from "react";
import { GetStartedScreen } from "../../ConnectWallet/screens/GetStartedScreen";
import { CoinbaseScan } from "./CoinbaseScan";
type CoinbaseConnectUIProps = ConnectUIProps & {
configuredWallet: ConfiguredWallet;
};
export const CoinbaseConnectUI = ({
configuredWallet,
close,
goBack,
}: CoinbaseConnectUIProps) => {
const connect = useConnect();
const { meta } = configuredWallet;
const [screen, setScreen] = useState<
"connecting" | "loading" | "scanning" | "get-started"
>("loading");
const connectPrompted = useRef(false);
useEffect(() => {
if (screen !== "loading") {
return;
}
if (connectPrompted.current) {
return;
}
const isInstalled = configuredWallet.isInstalled
? configuredWallet.isInstalled()
: false;
// if loading
(async () => {
if (isInstalled) {
try {
connectPrompted.current = true;
setScreen("connecting");
await connect(configuredWallet);
close();
} catch (e) {
goBack();
console.error(e);
}
}
// if metamask is not injected
else {
if (isMobile()) {
// coinbase will redirect to download page for coinbase wallet apps
connect(configuredWallet);
} else {
setScreen("scanning");
}
}
})();
}, [screen, configuredWallet, close, connect, goBack]);
if (screen === "connecting" || screen === "loading") {
return (
<ConnectingScreen
onBack={goBack}
walletName={meta.name}
walletIconURL={meta.iconURL}
supportLink="https://help.coinbase.com/en/wallet/other-topics/troubleshooting-and-tips"
/>
);
}
if (screen === "get-started") {
return (
<GetStartedScreen
walletIconURL={meta.iconURL}
walletName={meta.name}
chromeExtensionLink={meta.urls?.chrome}
googlePlayStoreLink={meta.urls?.android}
appleStoreLink={meta.urls?.ios}
onBack={goBack}
/>
);
}
if (screen === "scanning") {
return (
<CoinbaseScan
onBack={goBack}
onConnected={close}
onGetStarted={() => setScreen("get-started")}
configuredWallet={configuredWallet}
/>
);
}
return null;
};
@@ -1,28 +1,25 @@
import { ScanScreen } from "../ScanScreen";
import { CoinbaseWallet } from "@thirdweb-dev/wallets";
import {
ConfiguredWallet,
useCreateWalletInstance,
useThirdwebWallet,
} from "@thirdweb-dev/react-core";
import { useEffect, useState } from "react";
import { useWalletInfo } from "../../walletInfo";
import { ScanScreen } from "../../ConnectWallet/screens/ScanScreen";
export const ScanCoinbase: React.FC<{
export const CoinbaseScan: React.FC<{
onBack: () => void;
onGetStarted: () => void;
onConnected: () => void;
}> = (props) => {
configuredWallet: ConfiguredWallet;
}> = ({ configuredWallet, onConnected, onGetStarted, onBack }) => {
const createInstance = useCreateWalletInstance();
const [qrCodeUri, setQrCodeUri] = useState<string | undefined>(undefined);
const twWalletContext = useThirdwebWallet();
const { onConnected } = props;
const coinbaseWalletInfo = useWalletInfo("coinbase", true);
const cbWalletObj = coinbaseWalletInfo?.wallet;
const { name, iconURL } = cbWalletObj.meta;
useEffect(() => {
(async () => {
const wallet = createInstance(cbWalletObj) as InstanceType<
const wallet = createInstance(configuredWallet) as InstanceType<
typeof CoinbaseWallet
>;
@@ -39,15 +36,15 @@ export const ScanCoinbase: React.FC<{
onConnected();
});
})();
}, [createInstance, twWalletContext, onConnected, cbWalletObj]);
}, [createInstance, twWalletContext, onConnected, configuredWallet]);
return (
<ScanScreen
onBack={props.onBack}
onGetStarted={props.onGetStarted}
onBack={onBack}
onGetStarted={onGetStarted}
qrCodeUri={qrCodeUri}
walletName={name}
walletIconURL={iconURL}
walletName={configuredWallet.meta.name}
walletIconURL={configuredWallet.meta.iconURL}
/>
);
};

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