Compare commits

...
Author SHA1 Message Date
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
e1b05fcea3 Version Packages (#1625)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-09-27 13:49:09 +02:00
Phillip HoandGitHub 426c75a330 refactor: Update cf-worker to use sample rate + Redis (#1657) 2023-09-27 02:49:40 +00:00
Manan TankandGitHub a9854e69ce [react] Fix Img element crashing app on invalid URL (#1664) 2023-09-26 23:23:12 +00:00
FirekeeperandGitHub 2097e233cb [Unity] Add EmbeddedWallet option WebGL (#1663) 2023-09-26 22:12:15 +00:00
c29042b71e [SDK] Update Promise.all (#1660)
Co-authored-by: Jonas Daniels <[email protected]>
2023-09-26 15:08:19 -07:00
Adam MajmudarandGitHub b5b7e5243d [SDK] Add support for prepare to account extension (#1643) 2023-09-26 21:18:51 +00:00
Jonas DanielsandGitHub d5fafddea5 chore(chains): update (#1662) 2023-09-26 20:45:01 +00:00
Joaquim VergesandGitHub b6df6b8957 Fix not resolving custom chain currencies (#1661) 2023-09-26 20:01:25 +00:00
iketwandGitHub 45112a613e [RN] ConnectWalletV3 Details Modal (#1642) 2023-09-26 18:21:50 +00:00
Winston YeoandGitHub ea5b9c3ecd fix(react): show recovery password modal for USER_MANAGED in paperWallet (#1659) 2023-09-26 15:45:08 +00:00
Manan TankandGitHub bd18e4f5ef [react] NetworkSelector component UI tweaks (#1653) 2023-09-25 20:19:56 +00:00
Manan TankandGitHub 1018eb56a6 [react] ConnectWallet V3 updates/fixes (#1650) 2023-09-25 18:27:05 +00:00
Winston YeoandGitHub def6d400ab Winston/fix embedded wallet login type and paperWallet init with USER_MANAGED flow (#1647) 2023-09-25 16:14:51 +00:00
iketwandGitHub 2ce89cfea9 [RN] Update json upload to use new endpoint (#1644) 2023-09-22 13:03:33 +00:00
Adam MajmudarandGitHub 48906a9d8e [SDK] [Bug] Always update network if configured in fromSigner (#1624) 2023-09-22 04:51:47 +00:00
64528263f4 [react] ConnectWallet v3 ( v2 update ) (#1618)
Co-authored-by: ElasticBottle <[email protected]>
2023-09-21 22:52:04 +00:00
iketwandGitHub cbbb8ccbe3 [RN] Coinbase listener only handles Coinbase links (#1632) 2023-09-21 17:26:39 +00:00
Tair AsimandGitHub e2e6bdd4cd service-utils: add embedded wallets to service definitions (#1640) 2023-09-21 10:18:01 +00:00
Phillip HoandGitHub d66ff413de chore: Add platform metrics fields (#1641) 2023-09-21 03:20:07 +00:00
FirekeeperandGitHub 9d553746b0 [Unity] Pass wallets to wc modal from client + avoid throwing on paper init from bridge (#1638) 2023-09-20 17:29:06 +00:00
iketwandGitHub 57af96c288 [RN] Fix json upload (#1635) 2023-09-20 14:52:15 +00:00
Winston YeoandGitHub 94bdcc142a Winston/embeddedWallet polish (#1634) 2023-09-19 22:21:03 +00:00
Jonas DanielsandGitHub dd3d1a87c2 [tooling] faster chains generation (#1631) 2023-09-19 16:14:15 +00:00
4268775f9c Winston/add embedded wallet export for react (#1622)
Co-authored-by: Manan Tank <[email protected]>
2023-09-19 14:58:07 +00:00
Jonas DanielsandGitHub 710739c7d2 [cli] - unlock node 20 (#1628) 2023-09-19 06:38:57 +00:00
Phillip HoandGitHub c54db48c0d fix: try/catch logHttpRequest (#1627) 2023-09-19 01:17:35 +00:00
Tair AsimandGitHub 903145a4ae feat: rate limit worker (#1584) 2023-09-18 22:24:12 +00:00
Winston YeoandGitHub 43f188c8a7 Winston/add embedded wallet connector (#1598) 2023-09-18 20:56:18 +00:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
8a3993d42b Version Packages (#1613)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-09-18 12:13:49 -07:00
Manan TankandGitHub bab39980e7 [react] ConnectWallet minor UI fixes (#1621) 2023-09-18 18:56:07 +00:00
48295c0604 feat(sdk): centralize all blockchain reads via a single entrypoint (#1534)
Co-authored-by: Joaquim Verges <[email protected]>
2023-09-18 17:21:37 +00:00
YashandGitHub 3fd39cea0d Update addresses (#1617) 2023-09-15 20:26:16 +00:00
1fe867e205 [ReactNative] Connect Wallet V3 (#1592)
Co-authored-by: Manan Tank <[email protected]>
2023-09-15 17:47:40 +00:00
Manan TankandGitHub f5991387ef [react] ConnectWallet UI improvements + code cleanup (#1615) 2023-09-15 14:29:55 +00:00
3f3c63c01e [react] ConnectWallet v3 UI (#1550)
Co-authored-by: Joaquim Verges <[email protected]>
2023-09-15 11:21:03 +00:00
YashandGitHub f80cf0e17e [SDK] Remove ipfs dependency in tests (#1612) 2023-09-15 02:43:17 +00:00
Phillip HoandGitHub 10b7648857 chore: add evmMethod to usage traacker body (#1611) 2023-09-14 12:14:00 -07:00
374 changed files with 23038 additions and 5930 deletions
+14
View File
@@ -1,5 +1,19 @@
# @thirdweb-dev/auth
## 3.2.43
### Patch Changes
- Updated dependencies [[`9d553746`](https://github.com/thirdweb-dev/js/commit/9d553746b025ac489f9b8ee357372c9d01c835e1), [`64528263`](https://github.com/thirdweb-dev/js/commit/64528263f42bd2c564aad5e777f9f6dbba30af54), [`94bdcc14`](https://github.com/thirdweb-dev/js/commit/94bdcc142a7fe1e9f53273560404fa6b5ac3a7c4), [`43f188c8`](https://github.com/thirdweb-dev/js/commit/43f188c8a7ec02f394604120b414a039a2650525), [`ea5b9c3e`](https://github.com/thirdweb-dev/js/commit/ea5b9c3ecdd588461fb00f0e9da463de4a30ed1d), [`def6d400`](https://github.com/thirdweb-dev/js/commit/def6d400ab463bda3118d4c9cb00e5cc25a415c2)]:
- @thirdweb-dev/wallets@1.3.0
## 3.2.42
### Patch Changes
- Updated dependencies [[`3f3c63c0`](https://github.com/thirdweb-dev/js/commit/3f3c63c01e34242ae1f074e62b51787b305c059e)]:
- @thirdweb-dev/wallets@1.2.1
## 3.2.41
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/auth",
"version": "3.2.41",
"version": "3.2.43",
"main": "dist/thirdweb-dev-auth.cjs.js",
"module": "dist/thirdweb-dev-auth.esm.js",
"browser": {
+14
View File
@@ -1,5 +1,19 @@
# @thirdweb-dev/chains
## 0.1.53
### Patch Changes
- [#1631](https://github.com/thirdweb-dev/js/pull/1631) [`dd3d1a87`](https://github.com/thirdweb-dev/js/commit/dd3d1a87c2dadbadecc9ac3722941a8992bc8131) Thanks [@jnsdls](https://github.com/jnsdls)! - add & update chains
- [#1662](https://github.com/thirdweb-dev/js/pull/1662) [`d5fafdde`](https://github.com/thirdweb-dev/js/commit/d5fafddea58bc307c9b514a1c9578cafd18b5861) Thanks [@jnsdls](https://github.com/jnsdls)! - added & updated chains
## 0.1.52
### Patch Changes
- [#1534](https://github.com/thirdweb-dev/js/pull/1534) [`48295c06`](https://github.com/thirdweb-dev/js/commit/48295c060499371035980d08e362d9858d0fc18b) Thanks [@ElasticBottle](https://github.com/ElasticBottle)! - feat(chains): add new chain information
## 0.1.51
### Patch Changes
+1 -1
View File
@@ -19,7 +19,7 @@ export default {
"decimals": 18
},
"infoURL": "https://blx.org",
"shortName": "blxq",
"shortName": "tblxq",
"chainId": 1107,
"networkId": 1107,
"explorers": [
+40
View File
@@ -0,0 +1,40 @@
import type { Chain } from "../src/types";
export default {
"name": "BLXq Mainnet",
"chain": "BLXQ",
"icon": {
"url": "ipfs://QmS9kDKr1rgcz5W55yCQVfFs1vRTCneaLHt1t9cBizpqpH",
"width": 1000,
"height": 1000,
"format": "png"
},
"rpc": [
"https://blxq.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://mainnet.blxq.org"
],
"faucets": [],
"nativeCurrency": {
"name": "BLXQ",
"symbol": "BLXQ",
"decimals": 18
},
"infoURL": "https://blx.org",
"shortName": "blxq",
"chainId": 1108,
"networkId": 1108,
"explorers": [
{
"name": "BLXq Explorer",
"url": "https://explorer.blxq.org",
"icon": {
"url": "ipfs://QmS9kDKr1rgcz5W55yCQVfFs1vRTCneaLHt1t9cBizpqpH",
"width": 1000,
"height": 1000,
"format": "png"
},
"standard": "EIP3091"
}
],
"testnet": false,
"slug": "blxq"
} as const satisfies Chain;
+32
View File
@@ -0,0 +1,32 @@
import type { Chain } from "../src/types";
export default {
"name": "Astar zkEVM",
"shortName": "astrzk",
"title": "Astar zkEVM Mainnet",
"chain": "ETH",
"icon": {
"url": "ipfs://QmZm2RfbEfWjnqu7MLSG5ChhAsjnKqTvmHM5eJWvYGAwG8",
"width": 800,
"height": 264,
"format": "png"
},
"rpc": [],
"faucets": [],
"nativeCurrency": {
"name": "Ether",
"symbol": "ETH",
"decimals": 18
},
"infoURL": "https://astar.network",
"chainId": 12611,
"networkId": 12611,
"explorers": [],
"parent": {
"type": "L2",
"chain": "eip155-1",
"bridges": []
},
"status": "incubating",
"testnet": false,
"slug": "astar-zkevm"
} as const satisfies Chain;
+32
View File
@@ -0,0 +1,32 @@
import type { Chain } from "../src/types";
export default {
"name": "zKatana",
"shortName": "azktn",
"title": "Astar zkEVM Testnet zKatana",
"chain": "ETH",
"icon": {
"url": "ipfs://QmZm2RfbEfWjnqu7MLSG5ChhAsjnKqTvmHM5eJWvYGAwG8",
"width": 800,
"height": 264,
"format": "png"
},
"rpc": [],
"faucets": [],
"nativeCurrency": {
"name": "Sepolia Ether",
"symbol": "ETH",
"decimals": 18
},
"infoURL": "https://astar.network",
"chainId": 1261120,
"networkId": 1261120,
"explorers": [],
"parent": {
"type": "L2",
"chain": "eip155-11155111",
"bridges": []
},
"status": "incubating",
"testnet": true,
"slug": "zkatana"
} as const satisfies Chain;
+3 -3
View File
@@ -1,7 +1,7 @@
import type { Chain } from "../src/types";
export default {
"name": "Quantum Chain Testnet",
"chain": "QNET",
"chain": "tQNET",
"icon": {
"url": "ipfs://bafkreibwywok67uewqbtqdzgr7nlk3lvvg7hxmbgwtn7kdwxe34useucvm",
"width": 1024,
@@ -15,11 +15,11 @@ export default {
"faucets": [],
"nativeCurrency": {
"name": "Quantum Chain",
"symbol": "QNET",
"symbol": "tQNET",
"decimals": 18
},
"infoURL": "https://quantumnetwork.gg",
"shortName": "qnet",
"shortName": "tqnet",
"chainId": 12890,
"networkId": 12890,
"explorers": [
+2 -1
View File
@@ -10,7 +10,8 @@ export default {
},
"rpc": [
"https://flare.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://flare-api.flare.network/ext/C/rpc"
"https://flare-api.flare.network/ext/C/rpc",
"https://rpc.ftso.au/flare"
],
"faucets": [],
"nativeCurrency": {
+41
View File
@@ -0,0 +1,41 @@
import type { Chain } from "../src/types";
export default {
"name": "ShimmerEVM Mainnet",
"title": "ShimmerEVM Mainnet",
"chain": "ShimmerEVM",
"rpc": [
"https://shimmerevm.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://json-rpc.evm.shimmer.network"
],
"faucets": [],
"nativeCurrency": {
"name": "SMR",
"symbol": "SMR",
"decimals": 18
},
"infoURL": "https://shimmer.network",
"shortName": "shimmerevm-mainnet",
"chainId": 148,
"networkId": 148,
"icon": {
"url": "ipfs://bafkreibky2sy6qhi6arktayvologkrgu5kudpgdxfkx4uosbvmstz7v4di",
"width": 720,
"height": 720,
"format": "png"
},
"explorers": [
{
"name": "explorer",
"url": "https://explorer.evm.shimmer.network",
"icon": {
"url": "ipfs://bafkreibky2sy6qhi6arktayvologkrgu5kudpgdxfkx4uosbvmstz7v4di",
"width": 720,
"height": 720,
"format": "png"
},
"standard": "EIP3091"
}
],
"testnet": false,
"slug": "shimmerevm"
} as const satisfies Chain;
+35
View File
@@ -0,0 +1,35 @@
import type { Chain } from "../src/types";
export default {
"name": "Taiko Jolnir L2",
"chain": "ETH",
"status": "incubating",
"icon": {
"url": "ipfs://QmcHdmVr5VRUJq13jnM6tgah5Ge7hn3Dm14eY6vwivJ5ui",
"width": 288,
"height": 258,
"format": "png"
},
"rpc": [
"https://taiko-jolnir-l2.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://rpc.jolnir.taiko.xyz"
],
"faucets": [],
"nativeCurrency": {
"name": "Ether",
"symbol": "ETH",
"decimals": 18
},
"infoURL": "https://taiko.xyz",
"shortName": "tko-jolnir",
"chainId": 167007,
"networkId": 167007,
"explorers": [
{
"name": "blockscout",
"url": "https://explorer.jolnir.taiko.xyz",
"standard": "EIP3091"
}
],
"testnet": false,
"slug": "taiko-jolnir-l2"
} as const satisfies Chain;
+2 -1
View File
@@ -13,7 +13,8 @@ export default {
"https://songbird-api.flare.network/ext/C/rpc",
"https://sgb.ftso.com.au/ext/bc/C/rpc",
"https://sgb.lightft.so/rpc",
"https://sgb-rpc.ftso.eu"
"https://sgb-rpc.ftso.eu",
"https://rpc.ftso.au/songbird"
],
"faucets": [],
"nativeCurrency": {
+17 -3
View File
@@ -1,8 +1,11 @@
import type { Chain } from "../src/types";
export default {
"name": "Stratos Mainnet",
"name": "Stratos",
"chain": "STOS",
"rpc": [],
"rpc": [
"https://stratos.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://web3-rpc.thestratos.org"
],
"faucets": [],
"nativeCurrency": {
"name": "STOS",
@@ -13,7 +16,18 @@ export default {
"shortName": "stos-mainnet",
"chainId": 2048,
"networkId": 2048,
"status": "incubating",
"explorers": [
{
"name": "Stratos EVM Explorer (Blockscout)",
"url": "https://web3-explorer.thestratos.org",
"standard": "none"
},
{
"name": "Stratos Cosmos Explorer (BigDipper)",
"url": "https://explorer.thestratos.org",
"standard": "none"
}
],
"testnet": false,
"slug": "stratos"
} as const satisfies Chain;
+1 -1
View File
@@ -4,7 +4,7 @@ export default {
"chain": "REAP",
"rpc": [
"https://reapchain.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://rpc.reapchain.org"
"https://eth.reapchain.org"
],
"faucets": [],
"nativeCurrency": {
+39
View File
@@ -0,0 +1,39 @@
import type { Chain } from "../src/types";
export default {
"name": "The Krest Network",
"chain": "Krest",
"icon": {
"url": "ipfs://bafkreid3bhzhughhjwq3rpgiic5zesj5mekqktawt646itdyqtvjxoupca",
"width": 257,
"height": 257,
"format": "svg"
},
"rpc": [
"https://the-krest-network.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://erpc-krest.peaq.network"
],
"faucets": [],
"nativeCurrency": {
"name": "Krest",
"symbol": "KRST",
"decimals": 18
},
"infoURL": "https://www.peaq.network",
"shortName": "KRST",
"chainId": 2241,
"networkId": 2241,
"explorers": [
{
"name": "Polkadot.js",
"url": "https://polkadot.js.org/apps/?rpc=wss://wss-krest.peaq.network#/explorer",
"standard": "none"
},
{
"name": "Subscan",
"url": "https://krest.subscan.io",
"standard": "none"
}
],
"testnet": false,
"slug": "the-krest-network"
} as const satisfies Chain;
+2 -2
View File
@@ -14,8 +14,8 @@ export default {
],
"faucets": [],
"nativeCurrency": {
"name": "MAP",
"symbol": "MAP",
"name": "MAPO",
"symbol": "MAPO",
"decimals": 18
},
"infoURL": "https://maplabs.io",
+23 -6
View File
@@ -1,30 +1,47 @@
import type { Chain } from "../src/types";
export default {
"name": "RSK Mainnet",
"chain": "RSK",
"name": "Rootstock Mainnet",
"chain": "Rootstock",
"rpc": [
"https://rsk.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://rootstock.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://public-node.rsk.co",
"https://mycrypto.rsk.co"
],
"faucets": [],
"icon": {
"url": "ipfs://bafkreigidzbf22dnpmmlfxv6u7oifq6ln33j4n57ox4ipiproalufrheym",
"width": 3000,
"height": 3325,
"format": "png"
},
"nativeCurrency": {
"name": "Smart Bitcoin",
"symbol": "RBTC",
"decimals": 18
},
"infoURL": "https://rsk.co",
"infoURL": "https://rootstock.io",
"shortName": "rsk",
"chainId": 30,
"networkId": 30,
"slip44": 137,
"explorers": [
{
"name": "RSK Explorer",
"name": "blockscout",
"url": "https://rootstock.blockscout.com",
"icon": {
"url": "ipfs://QmYtUimyqHkkFxYdbXXRbUqNg2VLPUg6Uu2C2nmFWowiZM",
"width": 551,
"height": 540,
"format": "png"
},
"standard": "EIP3091"
},
{
"name": "Rootstock Explorer",
"url": "https://explorer.rsk.co",
"standard": "EIP3091"
}
],
"testnet": false,
"slug": "rsk"
"slug": "rootstock"
} as const satisfies Chain;
+11 -5
View File
@@ -1,21 +1,27 @@
import type { Chain } from "../src/types";
export default {
"name": "RSK Testnet",
"chain": "RSK",
"name": "Rootstock Testnet",
"chain": "Rootstock",
"rpc": [
"https://rsk-testnet.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://rootstock-testnet.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://public-node.testnet.rsk.co",
"https://mycrypto.testnet.rsk.co"
],
"faucets": [
"https://faucet.rsk.co/"
],
"icon": {
"url": "ipfs://bafkreigidzbf22dnpmmlfxv6u7oifq6ln33j4n57ox4ipiproalufrheym",
"width": 3000,
"height": 3325,
"format": "png"
},
"nativeCurrency": {
"name": "Testnet Smart Bitcoin",
"symbol": "tRBTC",
"decimals": 18
},
"infoURL": "https://rsk.co",
"infoURL": "https://rootstock.io",
"shortName": "trsk",
"chainId": 31,
"networkId": 31,
@@ -27,5 +33,5 @@ export default {
}
],
"testnet": true,
"slug": "rsk-testnet"
"slug": "rootstock-testnet"
} as const satisfies Chain;
+2 -1
View File
@@ -12,7 +12,8 @@ export default {
"https://filecoin.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://api.node.glif.io/",
"https://rpc.ankr.com/filecoin",
"https://filecoin-mainnet.chainstacklabs.com/rpc/v1"
"https://filecoin-mainnet.chainstacklabs.com/rpc/v1",
"https://filfox.info/rpc/v1"
],
"faucets": [],
"nativeCurrency": {
+2 -1
View File
@@ -13,7 +13,8 @@ export default {
"https://api.calibration.node.glif.io/rpc/v1",
"https://rpc.ankr.com/filecoin_testnet",
"https://filecoin-calibration.chainstacklabs.com/rpc/v1",
"https://filecoin-calibration.chainup.net/rpc/v1"
"https://filecoin-calibration.chainup.net/rpc/v1",
"https://calibration.filfox.info/rpc/v1"
],
"faucets": [
"https://faucet.calibration.fildev.network/"
+30
View File
@@ -0,0 +1,30 @@
import type { Chain } from "../src/types";
export default {
"name": "Oone Chain Testnet",
"chain": "OONE Testnet",
"rpc": [
"https://oone-chain-testnet.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://rpc.testnet.oonechain.com"
],
"faucets": [
"https://apps-test.adigium.com/faucet"
],
"nativeCurrency": {
"name": "tOONE",
"symbol": "tOONE",
"decimals": 18
},
"infoURL": "https://oonechain.com",
"shortName": "oonetest",
"chainId": 333666,
"networkId": 333666,
"explorers": [
{
"name": "blockscout",
"url": "https://testnet.oonescan.com",
"standard": "none"
}
],
"testnet": true,
"slug": "oone-chain-testnet"
} as const satisfies Chain;
+10 -10
View File
@@ -1,30 +1,30 @@
import type { Chain } from "../src/types";
export default {
"name": "Oone Chain Testnet",
"chain": "OONE",
"name": "Oone Chain Devnet",
"chain": "OONE Devnet",
"rpc": [
"https://oone-chain-testnet.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://blockchain-test.adigium.world"
"https://oone-chain-devnet.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://rpc.dev.oonechain.com"
],
"faucets": [
"https://apps-test.adigium.com/faucet"
],
"nativeCurrency": {
"name": "Oone",
"name": "tOONE",
"symbol": "tOONE",
"decimals": 18
},
"infoURL": "https://oone.world",
"shortName": "oonetest",
"infoURL": "https://oonechain.com",
"shortName": "oonedev",
"chainId": 333777,
"networkId": 333777,
"explorers": [
{
"name": "expedition",
"url": "https://explorer-test.adigium.world",
"name": "blockscout",
"url": "https://dev.oonescan.com",
"standard": "none"
}
],
"testnet": true,
"slug": "oone-chain-testnet"
"slug": "oone-chain-devnet"
} as const satisfies Chain;
+1 -1
View File
@@ -21,7 +21,7 @@ export default {
}
],
"infoURL": "https://manta-testnet.caldera.dev/",
"shortName": "manta",
"shortName": "mantaTestnet",
"chainId": 3441005,
"networkId": 3441005,
"icon": {
+3 -1
View File
@@ -11,7 +11,9 @@ export default {
"https://rpc.pulsechain.com",
"wss://rpc.pulsechain.com",
"https://pulsechain.publicnode.com",
"wss://pulsechain.publicnode.com"
"wss://pulsechain.publicnode.com",
"https://rpc-pulsechain.g4mm4.io",
"wss://rpc-pulsechain.g4mm4.io"
],
"slip44": 60,
"features": [
+34
View File
@@ -0,0 +1,34 @@
import type { Chain } from "../src/types";
export default {
"name": "ConnectorManager",
"chain": "Rangers",
"icon": {
"url": "ipfs://QmXR5e5SDABWfQn6XT9uMsVYAo5Bv7vUv4jVs8DFqatZWG",
"width": 2000,
"height": 2000,
"format": "png"
},
"rpc": [
"https://connectormanager.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://cm.rangersprotocol.com/api/jsonrpc"
],
"faucets": [],
"nativeCurrency": {
"name": "Rangers Protocol Gas",
"symbol": "cmRPG",
"decimals": 18
},
"infoURL": "https://rangersprotocol.com",
"shortName": "cmrpg",
"chainId": 38400,
"networkId": 38400,
"explorers": [
{
"name": "rangersscan",
"url": "https://scan.rangersprotocol.com",
"standard": "none"
}
],
"testnet": false,
"slug": "connectormanager"
} as const satisfies Chain;
+35
View File
@@ -0,0 +1,35 @@
import type { Chain } from "../src/types";
export default {
"name": "Humans.ai Testnet",
"chain": "Humans Testnet",
"rpc": [
"https://humans-ai-testnet.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://evm-rpc.testnet.humans.zone"
],
"faucets": [],
"nativeCurrency": {
"name": "HEART",
"symbol": "HEART",
"decimals": 18
},
"features": [
{
"name": "EIP155"
},
{
"name": "EIP1559"
}
],
"infoURL": "https://humans.ai",
"shortName": "humans_testnet",
"chainId": 4139,
"networkId": 4139,
"icon": {
"url": "ipfs://QmX6XuoQDTTjYqAmdNJiieLDZSwHHyUx44yQb4E3tmHmEA",
"width": 400,
"height": 400,
"format": "png"
},
"testnet": true,
"slug": "humans-ai-testnet"
} as const satisfies Chain;
+6
View File
@@ -36,6 +36,12 @@ export default {
}
]
},
"icon": {
"url": "ipfs://QmX2Z71QszaZvpMppdNWeCLqmeWk8NA6KMDLMqAt5VRyVi/synapse.webp",
"height": 720,
"width": 720,
"format": "webp"
},
"testnet": true,
"slug": "synapse-chain-testnet"
} as const satisfies Chain;
+2 -2
View File
@@ -27,8 +27,8 @@ export default {
"standard": "EIP3091"
},
{
"name": "Celoscan",
"url": "https://celoscan.io",
"name": "Alfajoresscan",
"url": "https://alfajores.celoscan.io",
"standard": "EIP3091"
}
],
+2 -2
View File
@@ -15,11 +15,11 @@ export default {
"faucets": [],
"nativeCurrency": {
"name": "Areon",
"symbol": "AREA",
"symbol": "TAREA",
"decimals": 18
},
"infoURL": "https://areon.network",
"shortName": "area",
"shortName": "tarea",
"chainId": 462,
"networkId": 462,
"explorers": [
+13 -1
View File
@@ -16,7 +16,19 @@ export default {
"shortName": "hmnd",
"chainId": 5234,
"networkId": 5234,
"explorers": [],
"explorers": [
{
"name": "Subscan",
"url": "https://humanode.subscan.io",
"standard": "EIP3091",
"icon": {
"url": "ipfs://Qma2GfW5nQHuA7nGqdEfwaXPL63G9oTwRTQKaGTfjNtM2W",
"width": 400,
"height": 400,
"format": "png"
}
}
],
"testnet": false,
"slug": "humanode"
} as const satisfies Chain;
+10 -2
View File
@@ -5,7 +5,10 @@ export default {
"status": "active",
"rpc": [
"https://scroll-sepolia-testnet.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://sepolia-rpc.scroll.io/"
"https://sepolia-rpc.scroll.io",
"https://rpc.ankr.com/scroll_sepolia_testnet",
"https://scroll-sepolia.chainstacklabs.com",
"https://scroll-testnet-public.unifra.io"
],
"faucets": [],
"nativeCurrency": {
@@ -19,7 +22,12 @@ export default {
"networkId": 534351,
"explorers": [
{
"name": "Scroll Sepolia Testnet Block Explorer",
"name": "Scroll Sepolia Etherscan",
"url": "https://sepolia.scrollscan.dev",
"standard": "EIP3091"
},
{
"name": "Scroll Sepolia Blockscout",
"url": "https://sepolia-blockscout.scroll.io",
"standard": "EIP3091"
}
+36
View File
@@ -0,0 +1,36 @@
import type { Chain } from "../src/types";
export default {
"name": "VEX EVM TESTNET",
"chain": "vex",
"icon": {
"url": "ipfs://QmcZiEAGFYEEax1uxYnDUh8X5YBvZqMi9K92yZoq4o2zeM",
"width": 451,
"height": 446,
"format": "png"
},
"rpc": [
"https://vex-evm-testnet.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://testnet.vexascan.com/evmapi"
],
"faucets": [
"https://t.me/vexfaucetbot"
],
"nativeCurrency": {
"name": "VEX EVM TESTNET",
"symbol": "VEX",
"decimals": 18
},
"infoURL": "https://vexanium.com",
"shortName": "VEX",
"chainId": 5522,
"networkId": 5522,
"explorers": [
{
"name": "Vexascan-EVM-TestNet",
"url": "https://testnet.vexascan.com/evmexplorer",
"standard": "EIP3091"
}
],
"testnet": true,
"slug": "vex-evm-testnet"
} as const satisfies Chain;
+36
View File
@@ -0,0 +1,36 @@
import type { Chain } from "../src/types";
export default {
"name": "Foundry Chain Testnet",
"chain": "tFNC",
"icon": {
"url": "ipfs://bafkreifx4vef7ubqz4iiaxckcvkdwjrp2hbliljwjlwu2vytlngg3r4pg4",
"width": 192,
"height": 192,
"format": "png"
},
"rpc": [
"https://foundry-chain-testnet.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://testnet-rpc.foundryscan.org/"
],
"faucets": [
"https://faucet.foundryscan.org"
],
"nativeCurrency": {
"name": "Foundry Chain Testnet",
"symbol": "tFNC",
"decimals": 18
},
"infoURL": "https://foundrychain.org",
"shortName": "fnc",
"chainId": 77238,
"networkId": 77238,
"explorers": [
{
"name": "Foundry Scan Testnet",
"url": "https://testnet-explorer.foundryscan.org",
"standard": "EIP3091"
}
],
"testnet": true,
"slug": "foundry-chain-testnet"
} as const satisfies Chain;
+34
View File
@@ -0,0 +1,34 @@
import type { Chain } from "../src/types";
export default {
"name": "Quantum Chain Mainnet",
"chain": "QNET",
"icon": {
"url": "ipfs://bafkreibwywok67uewqbtqdzgr7nlk3lvvg7hxmbgwtn7kdwxe34useucvm",
"width": 1024,
"height": 1024,
"format": "png"
},
"rpc": [
"https://quantum-chain.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://rpc.quantumscan.org"
],
"faucets": [],
"nativeCurrency": {
"name": "Quantum Chain",
"symbol": "QNET",
"decimals": 18
},
"infoURL": "https://quantumnetwork.gg",
"shortName": "qnet",
"chainId": 81720,
"networkId": 81720,
"explorers": [
{
"name": "Quantum Scan Mainnet",
"url": "https://quantumscan.org",
"standard": "EIP3091"
}
],
"testnet": false,
"slug": "quantum-chain"
} as const satisfies Chain;
+6 -10
View File
@@ -14,10 +14,12 @@ export default {
"infoURL": "https://pulsechain.com",
"rpc": [
"https://pulsechain-testnet-v4.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://rpc.v4.testnet.pulsechain.com/",
"wss://rpc.v4.testnet.pulsechain.com/",
"https://rpc.v4.testnet.pulsechain.com",
"wss://rpc.v4.testnet.pulsechain.com",
"https://pulsechain-testnet.publicnode.com",
"wss://pulsechain-testnet.publicnode.com"
"wss://pulsechain-testnet.publicnode.com",
"https://rpc-testnet-pulsechain.g4mm4.io",
"wss://rpc-testnet-pulsechain.g4mm4.io"
],
"features": [
{
@@ -43,13 +45,7 @@ export default {
"explorers": [
{
"name": "blockscout",
"url": "https://scan.v4.testnet.pulsechain.com",
"icon": {
"url": "ipfs://QmYtUimyqHkkFxYdbXXRbUqNg2VLPUg6Uu2C2nmFWowiZM",
"width": 551,
"height": 540,
"format": "png"
},
"url": "https://otter-testnet-pulsechain.g4mm4.io",
"standard": "EIP3091"
}
],
+12
View File
@@ -0,0 +1,12 @@
// @ts-check
/** @typedef { import("../../src/types").Chain } Chain */
/** @type {Partial<Chain>} */
export default {
icon: {
url: "ipfs://QmX2Z71QszaZvpMppdNWeCLqmeWk8NA6KMDLMqAt5VRyVi/synapse.webp",
height: 720,
width: 720,
format: "webp",
},
};
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/chains",
"version": "0.1.51",
"version": "0.1.53",
"main": "dist/thirdweb-dev-chains.cjs.js",
"module": "dist/thirdweb-dev-chains.esm.js",
"browser": {
+142 -118
View File
@@ -1,7 +1,7 @@
// @ts-check
import axios from "axios";
import merge from "deepmerge";
import fs from "fs";
import fs from "fs/promises";
import path from "path";
/** @typedef {import("../src/types").Chain} Chain */
@@ -73,7 +73,7 @@ let overrides = {};
// get all overides by reading the overrides directory and importing them
const overridesDir = path.join(process.cwd(), "./data/overrides");
const overridesFiles = fs.readdirSync(overridesDir);
const overridesFiles = await fs.readdir(overridesDir);
for (const file of overridesFiles) {
// file:// is required for windows builds
const override = await import(path.join("file://", overridesDir, file));
@@ -93,7 +93,7 @@ chains = chains.filter((c) => c.chainId !== 1337);
// get all additional chains by reading the additional chains directory and importing them
const additionalChainsDir = path.join(process.cwd(), "./data/additional");
const additionalChainsFiles = fs.readdirSync(additionalChainsDir);
const additionalChainsFiles = await fs.readdir(additionalChainsDir);
for (const file of additionalChainsFiles) {
// file:// is required for windows builds
const additionalChain = await import(
@@ -103,58 +103,10 @@ for (const file of additionalChainsFiles) {
chains = chains.filter((c) => c.chainId !== additionalChain.default.chainId);
chains.push(additionalChain.default);
}
chains = chains
// Keep scroll-alpha-testnet for now even though its deprecated
.filter((c) => c.status !== "deprecated" || c.chainId === 534353)
.map((chain) => {
if (overrides[chain.chainId]) {
chain = merge(chain, overrides[chain.chainId], {
arrayMerge: combineMerge,
});
}
// apparently this is the best way to do this off of raw data
const testnet =
chain.testnet === false
? false
: JSON.stringify(chain).toLowerCase().includes("test");
const resultChain = {
...chain,
testnet,
};
// only add the explroers if they were there in the first place
if (resultChain.explorers) {
resultChain.explorers = sortExplorers(resultChain.explorers);
// check every key that *could* contain a url and validate it
resultChain.explorers = filterOutErroringValues(
resultChain.explorers,
(explorer) => new URL(explorer.url),
);
}
if (resultChain.faucets) {
resultChain.faucets = filterOutErroringValues(
resultChain.faucets,
(faucet) => new URL(faucet),
);
}
if (resultChain.rpc) {
resultChain.rpc = filterOutErroringValues(
resultChain.rpc,
(rpc) => new URL(rpc),
);
}
if (resultChain.infoURL) {
try {
new URL(resultChain.infoURL);
} catch (e) {
delete resultChain.infoURL;
}
}
return resultChain;
});
// Keep scroll-alpha-testnet for now even though its deprecated
chains = chains.filter(
(c) => c.status !== "deprecated" || c.chainId === 534353,
);
/**
* Sort RPCs in chain
@@ -179,15 +131,9 @@ function sortRPCs(chain) {
});
chain.rpc = [...thirdwebRPCs, ...infuraRPCs, ...alchemyRPCs, ...otherRPCs];
return chain;
}
chains.forEach(sortRPCs);
const imports = [];
const exports = [];
const exportNames = [];
const exportNameToChain = {};
const takenSlugs = {};
const iconMetaMap = new Map();
@@ -261,81 +207,157 @@ function findSlug(chain) {
const chainDir = `${chainsDir}`;
// clean out the chains directory
fs.rmdirSync(chainDir, { recursive: true });
await fs.rmdir(chainDir, { recursive: true });
// make sure the chain directory exists
fs.mkdirSync(chainDir, { recursive: true });
await fs.mkdir(chainDir, { recursive: true });
for (const chain of chains) {
try {
if ("icon" in chain) {
if (typeof chain.icon === "string") {
const iconMeta = await downloadIcon(chain.icon);
if (iconMeta) {
chain.icon = iconMeta;
}
const results = await Promise.all(
chains.map(async (chain) => {
if (overrides[chain.chainId]) {
chain = merge(chain, overrides[chain.chainId], {
arrayMerge: combineMerge,
});
}
// apparently this is the best way to do this off of raw data
const testnet =
chain.testnet === false
? false
: JSON.stringify(chain).toLowerCase().includes("test");
chain = {
...chain,
testnet,
};
// only add the explroers if they were there in the first place
if (chain.explorers) {
chain.explorers = sortExplorers(chain.explorers);
// check every key that *could* contain a url and validate it
chain.explorers = filterOutErroringValues(
chain.explorers,
(explorer) => new URL(explorer.url),
);
}
if (chain.faucets) {
chain.faucets = filterOutErroringValues(
chain.faucets,
(faucet) => new URL(faucet),
);
}
if (chain.rpc) {
chain.rpc = filterOutErroringValues(chain.rpc, (rpc) => new URL(rpc));
}
if (chain.infoURL) {
try {
new URL(chain.infoURL);
} catch (e) {
delete chain.infoURL;
}
}
if ("explorers" in chain && Array.isArray(chain.explorers)) {
for (const explorer of chain.explorers) {
if ("icon" in explorer) {
if (typeof explorer.icon === "string") {
const iconMeta = await downloadIcon(explorer.icon);
if (iconMeta) {
explorer.icon = iconMeta;
chain = sortRPCs(chain);
try {
if ("icon" in chain) {
if (typeof chain.icon === "string") {
const iconMeta = await downloadIcon(chain.icon);
if (iconMeta) {
chain.icon = iconMeta;
}
}
}
if ("explorers" in chain && Array.isArray(chain.explorers)) {
for (const explorer of chain.explorers) {
if ("icon" in explorer) {
if (typeof explorer.icon === "string") {
const iconMeta = await downloadIcon(explorer.icon);
if (iconMeta) {
explorer.icon = iconMeta;
}
}
}
}
}
} catch (err) {
console.log(err.message);
}
} catch (err) {
console.log(err.message);
}
// figure out a slug for the chain
// figure out a slug for the chain
const slug = findSlug(chain);
chain.slug = slug;
// if the chain has RPCs that we can use then prepend our RPC to the list
const chainHasHttpRpc = chain.rpc.some((rpc) => rpc.startsWith("http"));
// if the chain has RPCs that we can use then prepend our RPC to the list
// we're exlcuding localhost because we don't want to use our RPC for localhost
if (chainHasHttpRpc && chain.chainId !== 1337) {
chain.rpc = [
`https://${slug}.rpc.thirdweb.com/${"${THIRDWEB_API_KEY}"}`,
...chain.rpc,
];
}
// unique rpcs
chain.rpc = [...new Set(chain.rpc)];
const slug = findSlug(chain);
chain.slug = slug;
// if the chain has RPCs that we can use then prepend our RPC to the list
const chainHasHttpRpc = chain.rpc.some((rpc) => rpc.startsWith("http"));
// if the chain has RPCs that we can use then prepend our RPC to the list
// we're exlcuding localhost because we don't want to use our RPC for localhost
if (chainHasHttpRpc && chain.chainId !== 1337) {
chain.rpc = [
`https://${slug}.rpc.thirdweb.com/${"${THIRDWEB_API_KEY}"}`,
...chain.rpc,
];
}
// unique rpcs
chain.rpc = [...new Set(chain.rpc)];
fs.writeFileSync(
`${chainDir}/${chain.chainId}.ts`,
`import type { Chain } from "../src/types";
// @ts-ignore
await fs.writeFile(
`${chainDir}/${chain.chainId}.ts`,
`import type { Chain } from "../src/types";
export default ${JSON.stringify(chain, null, 2)} as const satisfies Chain;`,
);
);
let exportName = slug
.split("-")
.map((s) => s[0].toUpperCase() + s.slice(1))
.join("");
let exportName = slug
.split("-")
.map((s) => s[0].toUpperCase() + s.slice(1))
.join("");
// if chainName starts with a number, prepend an underscore
if (exportName.match(/^[0-9]/)) {
exportName = `_${exportName}`;
}
// if chainName starts with a number, prepend an underscore
if (exportName.match(/^[0-9]/)) {
exportName = `_${exportName}`;
}
imports.push(`import c${chain.chainId} from "../chains/${chain.chainId}";`);
// imports.push(`import c${chain.chainId} from "../chains/${chain.chainId}";`);
exports.push(
`export { default as ${exportName} } from "../chains/${chain.chainId}"`,
);
// exports.push(
// `export { default as ${exportName} } from "../chains/${chain.chainId}"`,
// );
const key = `c${chain.chainId}`;
exportNames.push(key);
exportNameToChain[key] = chain;
}
const key = `c${chain.chainId}`;
fs.writeFileSync(
// exportNames.push(key);
// exportNameToChain[key] = chain;
return {
imp: `import c${chain.chainId} from "../chains/${chain.chainId}";`,
exp: `export { default as ${exportName} } from "../chains/${chain.chainId}"`,
chain,
key,
exportName,
};
}),
);
const { imports, exports, exportNames, exportNameToChain } = results.reduce(
(acc, result) => {
// @ts-ignore
acc.imports.push(result.imp);
// @ts-ignore
acc.exports.push(result.exp);
// @ts-ignore
acc.exportNames.push(result.key);
acc.exportNameToChain[result.key] = result.chain;
return acc;
},
{
imports: [],
exports: [],
exportNames: [],
exportNameToChain: {},
},
);
// @ts-ignore
await fs.writeFile(
`./src/index.ts`,
`${imports.join("\n")}
import type { Chain } from "./types";
@@ -349,6 +371,7 @@ export const allChains: Chain[] = [${exportNames.join(", ")}];
type ChainsById = {
${exportNames
// @ts-ignore
.map((n) => `${exportNameToChain[n].chainId}: typeof ${n}`)
.join(",\n")}
};
@@ -356,6 +379,7 @@ type ChainsById = {
type ChainIdsBySlug = {
${exportNames
.map(
// @ts-ignore
(n) => `"${exportNameToChain[n].slug}": ${exportNameToChain[n].chainId}`,
)
.join(",\n")}
File diff suppressed because one or more lines are too long
+22
View File
@@ -1,5 +1,27 @@
# thirdweb
## 0.12.5
### Patch Changes
- [#1628](https://github.com/thirdweb-dev/js/pull/1628) [`710739c7`](https://github.com/thirdweb-dev/js/commit/710739c7d20760711efbf291e287860f90630765) Thanks [@jnsdls](https://github.com/jnsdls)! - allow node20 usage
- Updated dependencies [[`dd3d1a87`](https://github.com/thirdweb-dev/js/commit/dd3d1a87c2dadbadecc9ac3722941a8992bc8131), [`9d553746`](https://github.com/thirdweb-dev/js/commit/9d553746b025ac489f9b8ee357372c9d01c835e1), [`64528263`](https://github.com/thirdweb-dev/js/commit/64528263f42bd2c564aad5e777f9f6dbba30af54), [`d5fafdde`](https://github.com/thirdweb-dev/js/commit/d5fafddea58bc307c9b514a1c9578cafd18b5861), [`c29042b7`](https://github.com/thirdweb-dev/js/commit/c29042b71e266cb11d70d67f0fe2ffcc0fc1f5fa), [`b5b7e524`](https://github.com/thirdweb-dev/js/commit/b5b7e5243df83e3ab60d0917c099fb6967b63439), [`b6df6b89`](https://github.com/thirdweb-dev/js/commit/b6df6b895723947427c515411a7a833edaa324c6), [`94bdcc14`](https://github.com/thirdweb-dev/js/commit/94bdcc142a7fe1e9f53273560404fa6b5ac3a7c4), [`48906a9d`](https://github.com/thirdweb-dev/js/commit/48906a9d8ef2cfdd9ac489822a72d50cbd825628), [`43f188c8`](https://github.com/thirdweb-dev/js/commit/43f188c8a7ec02f394604120b414a039a2650525), [`ea5b9c3e`](https://github.com/thirdweb-dev/js/commit/ea5b9c3ecdd588461fb00f0e9da463de4a30ed1d), [`def6d400`](https://github.com/thirdweb-dev/js/commit/def6d400ab463bda3118d4c9cb00e5cc25a415c2)]:
- @thirdweb-dev/chains@0.1.53
- @thirdweb-dev/wallets@1.3.0
- @thirdweb-dev/sdk@3.10.62
- @thirdweb-dev/auth@3.2.43
## 0.12.4
### Patch Changes
- Updated dependencies [[`3fd39cea`](https://github.com/thirdweb-dev/js/commit/3fd39cea0df71f80255106329db62660f2fd6e3a), [`3f3c63c0`](https://github.com/thirdweb-dev/js/commit/3f3c63c01e34242ae1f074e62b51787b305c059e), [`48295c06`](https://github.com/thirdweb-dev/js/commit/48295c060499371035980d08e362d9858d0fc18b), [`48295c06`](https://github.com/thirdweb-dev/js/commit/48295c060499371035980d08e362d9858d0fc18b)]:
- @thirdweb-dev/sdk@3.10.61
- @thirdweb-dev/wallets@1.2.1
- @thirdweb-dev/chains@0.1.52
- @thirdweb-dev/auth@3.2.42
## 0.12.3
### Patch Changes
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "thirdweb",
"main": "dist/cli/index.js",
"version": "0.12.3",
"version": "0.12.5",
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/cli",
"author": "thirdweb eng <[email protected]>",
"license": "Apache-2.0",
"engines": {
"node": ">=16.0.0 <19"
"node": ">=16.0.0"
},
"devDependencies": {
"@gmrchk/cli-testing-library": "^0.1.2",
+70
View File
@@ -1,5 +1,75 @@
# @thirdweb-dev/react-core
## 3.16.0
### Patch Changes
- [#1618](https://github.com/thirdweb-dev/js/pull/1618) [`64528263`](https://github.com/thirdweb-dev/js/commit/64528263f42bd2c564aad5e777f9f6dbba30af54) Thanks [@MananTank](https://github.com/MananTank)! - ## New smartWallet() API (Breaking Change)
### Before
In the previous API, adding a smart wallet created it's own new entry called "Smart wallet" in the ConnectWallet Modal and you had to pass in the personal wallets which was shown to the user when they clicked on the "Smart wallet".
```tsx
<ThirdwebProvider
supportedWallets={[
smartWalet({
personalWallets: [metamaskWallet(), coinbaseWallet()],
factoryAddress: "....",
gassless: true,
}),
]}
/>
```
### After
Since most users don't know what a smart wallet is, this was confusing. So with the new API, you can just use smart wallet under the hood for any wallet you want and it will just show up as that wallet and not a "smart wallet" in ConnectWallet Modal to improve the user experience.
Once the user is connected, the ConnectWallet Details button shows to the user that they are infact connected to a smart wallet.
```tsx
const config = {
factoryAddress: "....",
gassless: true,
}
<ThirdwebProvider
supportedWallets={[
smartWalet(metamaskWallet(), config),
smartWalet(coinbaseWallet(), config),
]}
/>
```
## New Features added to `ConnectWallet` component
- ENS Name + Avatar support added
- New 'Send funds' button added to ConnectWallet which users can use to send various tokens.
- New "Receive funds" button added to ConnectWallet which users scan the QR code from their wallet app on phone to send funds to their other wallet on desktop
- Added `supportedTokens` prop to customize the list of tokens for each network in for the "Send Funds" screen.
- "Transaction history" button added to ConnectWallet which opens the block explorer
- New wallet `embededWallet()` to sign in with Google / Email
- Ability to show balance of any token instead of just native token in the ConnectWallet details button using the `displayBalanceToken` prop
- Updated dependencies [[`dd3d1a87`](https://github.com/thirdweb-dev/js/commit/dd3d1a87c2dadbadecc9ac3722941a8992bc8131), [`9d553746`](https://github.com/thirdweb-dev/js/commit/9d553746b025ac489f9b8ee357372c9d01c835e1), [`64528263`](https://github.com/thirdweb-dev/js/commit/64528263f42bd2c564aad5e777f9f6dbba30af54), [`d5fafdde`](https://github.com/thirdweb-dev/js/commit/d5fafddea58bc307c9b514a1c9578cafd18b5861), [`c29042b7`](https://github.com/thirdweb-dev/js/commit/c29042b71e266cb11d70d67f0fe2ffcc0fc1f5fa), [`b5b7e524`](https://github.com/thirdweb-dev/js/commit/b5b7e5243df83e3ab60d0917c099fb6967b63439), [`b6df6b89`](https://github.com/thirdweb-dev/js/commit/b6df6b895723947427c515411a7a833edaa324c6), [`94bdcc14`](https://github.com/thirdweb-dev/js/commit/94bdcc142a7fe1e9f53273560404fa6b5ac3a7c4), [`48906a9d`](https://github.com/thirdweb-dev/js/commit/48906a9d8ef2cfdd9ac489822a72d50cbd825628), [`43f188c8`](https://github.com/thirdweb-dev/js/commit/43f188c8a7ec02f394604120b414a039a2650525), [`ea5b9c3e`](https://github.com/thirdweb-dev/js/commit/ea5b9c3ecdd588461fb00f0e9da463de4a30ed1d), [`def6d400`](https://github.com/thirdweb-dev/js/commit/def6d400ab463bda3118d4c9cb00e5cc25a415c2)]:
- @thirdweb-dev/chains@0.1.53
- @thirdweb-dev/wallets@1.3.0
- @thirdweb-dev/sdk@3.10.62
- @thirdweb-dev/auth@3.2.43
## 3.15.0
### Patch Changes
- [#1550](https://github.com/thirdweb-dev/js/pull/1550) [`3f3c63c0`](https://github.com/thirdweb-dev/js/commit/3f3c63c01e34242ae1f074e62b51787b305c059e) Thanks [@MananTank](https://github.com/MananTank)! - ConnectWallet v3 updates
- Updated dependencies [[`3fd39cea`](https://github.com/thirdweb-dev/js/commit/3fd39cea0df71f80255106329db62660f2fd6e3a), [`3f3c63c0`](https://github.com/thirdweb-dev/js/commit/3f3c63c01e34242ae1f074e62b51787b305c059e), [`48295c06`](https://github.com/thirdweb-dev/js/commit/48295c060499371035980d08e362d9858d0fc18b), [`48295c06`](https://github.com/thirdweb-dev/js/commit/48295c060499371035980d08e362d9858d0fc18b)]:
- @thirdweb-dev/sdk@3.10.61
- @thirdweb-dev/wallets@1.2.1
- @thirdweb-dev/chains@0.1.52
- @thirdweb-dev/auth@3.2.42
## 3.14.41
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/react-core",
"version": "3.14.41",
"version": "3.16.0",
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/react-core",
"author": "thirdweb eng <[email protected]>",
"license": "Apache-2.0",
@@ -90,6 +90,7 @@ type ThirdwebWalletContextData = {
*/
getWalletConfig: (walletInstance: WalletInstance) => WalletConfig | undefined;
activeChainSetExplicitly: boolean;
clientId?: string;
};
const ThirdwebWalletContext = /* @__PURE__ */ createContext<
@@ -483,6 +484,7 @@ export function ThirdwebWalletProvider(
return walletInstanceToConfig.get(walletInstance);
},
activeChainSetExplicitly: props.activeChainSetExplicitly,
clientId: props.clientId,
}}
>
{props.children}
@@ -17,6 +17,7 @@ export type WalletClass<I extends WalletInstance = WalletInstance> = {
};
export type WalletConfig<I extends WalletInstance = WalletInstance> = {
category?: "socialLogin" | "walletLogin";
id: string;
meta: (typeof AbstractClientWallet)["meta"];
create: (options: WalletOptions) => I;
@@ -39,6 +40,11 @@ export type WalletConfig<I extends WalletInstance = WalletInstance> = {
* * ConnectWallet modal will reopen once the personal wallet is connected so that you can render UI for connecting your wallet as the next step
*/
personalWallets?: WalletConfig[];
/**
* If true, this wallet will be tagged as "recommended" in ConnectWallet Modal and will be shown at the top of the list
*/
recommended?: boolean;
};
export type ConnectUIProps<I extends WalletInstance = WalletInstance> = {
@@ -127,6 +133,13 @@ export type ConnectUIProps<I extends WalletInstance = WalletInstance> = {
* List of all supported wallets including your wallet.
*/
supportedWallets: WalletConfig[];
/**
* Size of the modal
*
* This is always `compact` on React Native
*/
modalSize: "compact" | "wide";
};
export type SelectUIProps<I extends WalletInstance = WalletInstance> = {
@@ -156,4 +169,11 @@ export type SelectUIProps<I extends WalletInstance = WalletInstance> = {
* theme of the connect wallet modal
*/
theme: "dark" | "light";
/**
* Size of the modal
*
* This is always `compact` on React Native
*/
modalSize: "compact" | "wide";
};
@@ -1,5 +1,9 @@
# @thirdweb-dev/react-native-compat
## 0.2.62
## 0.2.61
## 0.2.60
## 0.2.59
+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.60",
"version": "0.2.62",
"author": "thirdweb eng <[email protected]>",
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/react-native-compat",
"license": "Apache-2.0",
+84
View File
@@ -1,5 +1,89 @@
# @thirdweb-dev/react-native
## 0.2.62
### Patch Changes
- [#1644](https://github.com/thirdweb-dev/js/pull/1644) [`2ce89cfe`](https://github.com/thirdweb-dev/js/commit/2ce89cfea905a669511acfcca43be2044579ab90) Thanks [@iketw](https://github.com/iketw)! - Update ipfs json upload to new endpoint
- [#1632](https://github.com/thirdweb-dev/js/pull/1632) [`cbbb8ccb`](https://github.com/thirdweb-dev/js/commit/cbbb8ccbe3435b6cc7b3f6ab83d412626e9550de) Thanks [@iketw](https://github.com/iketw)! - Coinbase url listener should only listen for the Coinbase callbackURL
- [#1635](https://github.com/thirdweb-dev/js/pull/1635) [`57af96c2`](https://github.com/thirdweb-dev/js/commit/57af96c28845d5e292970655e2ff967169f3724c) Thanks [@iketw](https://github.com/iketw)! - Correctly return json uploads results
- Updated dependencies [[`64528263`](https://github.com/thirdweb-dev/js/commit/64528263f42bd2c564aad5e777f9f6dbba30af54), [`dd3d1a87`](https://github.com/thirdweb-dev/js/commit/dd3d1a87c2dadbadecc9ac3722941a8992bc8131), [`9d553746`](https://github.com/thirdweb-dev/js/commit/9d553746b025ac489f9b8ee357372c9d01c835e1), [`64528263`](https://github.com/thirdweb-dev/js/commit/64528263f42bd2c564aad5e777f9f6dbba30af54), [`d5fafdde`](https://github.com/thirdweb-dev/js/commit/d5fafddea58bc307c9b514a1c9578cafd18b5861), [`c29042b7`](https://github.com/thirdweb-dev/js/commit/c29042b71e266cb11d70d67f0fe2ffcc0fc1f5fa), [`b5b7e524`](https://github.com/thirdweb-dev/js/commit/b5b7e5243df83e3ab60d0917c099fb6967b63439), [`b6df6b89`](https://github.com/thirdweb-dev/js/commit/b6df6b895723947427c515411a7a833edaa324c6), [`94bdcc14`](https://github.com/thirdweb-dev/js/commit/94bdcc142a7fe1e9f53273560404fa6b5ac3a7c4), [`48906a9d`](https://github.com/thirdweb-dev/js/commit/48906a9d8ef2cfdd9ac489822a72d50cbd825628), [`43f188c8`](https://github.com/thirdweb-dev/js/commit/43f188c8a7ec02f394604120b414a039a2650525), [`ea5b9c3e`](https://github.com/thirdweb-dev/js/commit/ea5b9c3ecdd588461fb00f0e9da463de4a30ed1d), [`def6d400`](https://github.com/thirdweb-dev/js/commit/def6d400ab463bda3118d4c9cb00e5cc25a415c2)]:
- @thirdweb-dev/react-core@3.16.0
- @thirdweb-dev/chains@0.1.53
- @thirdweb-dev/wallets@1.3.0
- @thirdweb-dev/sdk@3.10.62
## 0.2.61
### Patch Changes
- [#1592](https://github.com/thirdweb-dev/js/pull/1592) [`1fe867e2`](https://github.com/thirdweb-dev/js/commit/1fe867e20510a43d2a19ba553709aeaca1d46bb3) Thanks [@iketw](https://github.com/iketw)! - Improved ConnectWallet UX/DevX :)
1. Devs can now pass a `recommended` field to `supportedWallets` to recommend wallets to users.
When recommending a wallet, this wallet will show up at the top of the wallets list.
```javascript
import { metamaskWallet, ThirdwebProvider } from "@thirdweb-dev/react-native";
<ThirdwebProvider
clientId="your-client-id"
supportedWallets={[
metamaskWallet({
recommended: true,
}),
]}
>
<App />
</ThirdwebProvider>;
```
2. Better theme customization
You can now more easily customize the theme of the ConnectWallet component:
```javascript
import { ConnectWallet, darkTheme } from "@thirdweb-dev/react-native";
<ConnectWallet
theme={darkTheme({
colors: {
textPrimary: "yellow",
textSecondary: "blue",
},
})}
buttonTitle="Enter web3"
modalTitle="Sign in"
/>;
```
Note that you can still pass `light` or `dark` if you want to use one of the predefined thems :)
3. You can now pass your Privacy Policy and Terms of Service urls, they will show up
at the bottom of the modal:
```javascript
import { ConnectWallet, darkTheme } from "@thirdweb-dev/react-native";
<ConnectWallet
privacyPolicyUrl="https://your-privacy-policy"
termsOfServiceUrl="https://your-terms-of-service"
/>;
```
4. Fixed a bug when you only specify an email wallet. In this case the app shouldn't auto connect, it should show the email field.
- [#1550](https://github.com/thirdweb-dev/js/pull/1550) [`3f3c63c0`](https://github.com/thirdweb-dev/js/commit/3f3c63c01e34242ae1f074e62b51787b305c059e) Thanks [@MananTank](https://github.com/MananTank)! - ConnectWallet v3 updates
- Updated dependencies [[`3fd39cea`](https://github.com/thirdweb-dev/js/commit/3fd39cea0df71f80255106329db62660f2fd6e3a), [`3f3c63c0`](https://github.com/thirdweb-dev/js/commit/3f3c63c01e34242ae1f074e62b51787b305c059e), [`48295c06`](https://github.com/thirdweb-dev/js/commit/48295c060499371035980d08e362d9858d0fc18b), [`48295c06`](https://github.com/thirdweb-dev/js/commit/48295c060499371035980d08e362d9858d0fc18b)]:
- @thirdweb-dev/sdk@3.10.61
- @thirdweb-dev/wallets@1.2.1
- @thirdweb-dev/react-core@3.15.0
- @thirdweb-dev/chains@0.1.52
## 0.2.60
### Patch Changes
+2 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/react-native",
"version": "0.2.60",
"version": "0.2.62",
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/react-native",
"author": "thirdweb eng <[email protected]>",
"license": "Apache-2.0",
@@ -42,6 +42,7 @@
"expo-secure-store": "^12.1.1",
"react-native-mmkv": "2.5.1",
"react-native-modal": "^13.0.1",
"react-native-qrcode-svg": "6.2.0",
"react-native-svg": "^13.9.0",
"tiny-invariant": "^1.2.0"
},
@@ -6,6 +6,7 @@ import {
} from "@thirdweb-dev/storage";
import { IpfsUploaderOptions, UploadDataValue } from "./types";
import * as Application from "expo-application";
import { Platform } from "react-native";
const APP_BUNDLE_ID = Application.applicationId;
const METADATA_NAME = "Storage React Native SDK";
@@ -38,6 +39,11 @@ export class IpfsUploader implements IStorageUploader<IpfsUploadBatchOptions> {
keyvalues: { ...options?.metadata },
};
// this is on purpose because we can't import package.json as a module as it is outside rootDir
// eslint-disable-next-line @typescript-eslint/no-var-requires
const { version, name: packageName } = require("../../../package.json");
const platform = `react-native-${Platform.OS}`;
if ("uri" in data[0] && "type" in data[0] && "name" in data[0]) {
// then it's an array of files
return new Promise(async (resolve, reject) => {
@@ -142,6 +148,10 @@ export class IpfsUploader implements IStorageUploader<IpfsUploadBatchOptions> {
xhr.setRequestHeader("x-client-id", this.clientId);
}
xhr.setRequestHeader("x-sdk-version", version);
xhr.setRequestHeader("x-sdk-name", packageName);
xhr.setRequestHeader("x-sdk-platform", platform);
xhr.send(form);
});
} else {
@@ -153,21 +163,34 @@ export class IpfsUploader implements IStorageUploader<IpfsUploadBatchOptions> {
});
try {
const res = await fetch(`${TW_UPLOAD_SERVER_URL}/ipfs/pin-json`, {
method: "POST",
headers: {
"x-bundle-id": APP_BUNDLE_ID || "", // only empty on web
...(this.clientId ? { "x-client-id": this.clientId } : {}),
"Content-Type": "application/json",
const res = await fetch(
`${TW_UPLOAD_SERVER_URL}/ipfs/batch-pin-json`,
{
method: "POST",
headers: {
"x-bundle-id": APP_BUNDLE_ID || "", // only empty on web
...(this.clientId ? { "x-client-id": this.clientId } : {}),
"Content-Type": "application/json",
"x-sdk-version": version,
"x-sdk-name": packageName,
"x-sdk-platform": platform,
},
body: fetchBody,
},
body: fetchBody,
});
);
if (res.ok) {
const ipfs = await res.json();
const cid = ipfs.IpfsHash;
const ipfsResults = await res.json();
return resolve([`ipfs://${cid}`]);
const results = ipfsResults.results.map(
(ipfs: { IpfsHash: string; PinSize: number }) => {
const cid = ipfs.IpfsHash;
return `ipfs://${cid}`;
},
);
return resolve(results);
}
} catch (error) {
reject(error);
@@ -0,0 +1,19 @@
import Svg, { Path } from "react-native-svg";
import { IconStyleProp } from "./types";
import React from "react";
const CheckIcon = ({ width, height, color }: IconStyleProp) => {
return (
<Svg width={width} height={height} viewBox="0 0 18 13" fill="none">
<Path
d="M17 1L6 12L1 7"
stroke={color}
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
/>
</Svg>
);
};
export default CheckIcon;
@@ -0,0 +1,24 @@
import { IconStyleProp } from "./types";
import Svg, { Line, Path } from "react-native-svg";
const DownloadIcon = ({ width, height, color = "white" }: IconStyleProp) => {
return (
<Svg width={width} height={height} viewBox="0 0 18 18" fill="none">
<Path
d="M9.05088 12.9506C9.26009 13.1598 9.59929 13.1598 9.80849 12.9506L13.2178 9.54132C13.427 9.33211 13.427 8.99291 13.2178 8.78371C13.0085 8.5745 12.6694 8.5745 12.4601 8.78371L9.42969 11.8142L6.39923 8.78371C6.19002 8.5745 5.85082 8.5745 5.64162 8.78371C5.43241 8.99292 5.43241 9.33211 5.64162 9.54132L9.05088 12.9506ZM9.9654 3.28606C9.9654 2.9902 9.72555 2.75035 9.42969 2.75035C9.13382 2.75035 8.89397 2.9902 8.89397 3.28606L9.9654 3.28606ZM9.9654 12.5718L9.9654 3.28606L8.89397 3.28606L8.89397 12.5718L9.9654 12.5718Z"
fill={color}
/>
<Line
x1="5.68025"
y1="14.1791"
x2="13.1802"
y2="14.1791"
stroke={color}
stroke-width="1.07143"
stroke-linecap="round"
/>
</Svg>
);
};
export default DownloadIcon;
@@ -0,0 +1,33 @@
import { IconStyleProp } from "./types";
import Svg, { Path, Rect } from "react-native-svg";
const Email = ({ width, height }: IconStyleProp) => {
return (
<Svg width={width} height={height} viewBox="0 0 18 18" fill="none">
<Rect
x="0.633333"
y="0.633333"
width="16.4667"
height="16.4667"
rx="4.43333"
fill="#3385FF"
/>
<Rect x="2.2168" y="4.43359" width="13.3" height="8.86667" fill="white" />
<Path
d="M14.7775 2.95557H2.95532C2.14254 2.95557 1.48493 3.62057 1.48493 4.43334L1.47754 13.3C1.47754 14.1128 2.14254 14.7778 2.95532 14.7778H14.7775C15.5903 14.7778 16.2553 14.1128 16.2553 13.3V4.43334C16.2553 3.62057 15.5903 2.95557 14.7775 2.95557ZM14.7775 5.91112L8.86643 9.60557L2.95532 5.91112V4.43334L8.86643 8.12779L14.7775 4.43334V5.91112Z"
fill="#3385FF"
/>
<Rect
x="0.633333"
y="0.633333"
width="16.4667"
height="16.4667"
rx="4.43333"
stroke="#1177FD"
stroke-width="1.26667"
/>
</Svg>
);
};
export default Email;
@@ -1,7 +1,11 @@
import { IconStyleProp } from "./types";
import Svg, { Path } from "react-native-svg";
const RightArrowIcon = ({ width, height, color }: IconStyleProp) => {
const RightArrowIcon = ({
width,
height,
color = "#646D7A",
}: IconStyleProp) => {
return (
<Svg width={width} height={height} viewBox="0 0 5 8" fill="none">
<Path
@@ -0,0 +1,15 @@
import { IconStyleProp } from "./types";
import Svg, { Path } from "react-native-svg";
const SendIcon = ({ width, height, color = "white" }: IconStyleProp) => {
return (
<Svg width={width} height={height} viewBox="0 0 18 18" fill="none">
<Path
d="M15.378 3.05027C15.2429 2.9161 15.0734 2.82178 14.8882 2.77768C14.7029 2.73357 14.5091 2.74139 14.328 2.80027L3.87803 6.28598C3.68423 6.34739 3.51296 6.46486 3.38588 6.62355C3.2588 6.78224 3.18159 6.97502 3.16401 7.17757C3.14643 7.38011 3.18927 7.58332 3.28711 7.76153C3.38496 7.93974 3.53342 8.08496 3.71375 8.17884L8.04946 10.3217L10.1923 14.6717C10.2784 14.842 10.4102 14.9849 10.573 15.0846C10.7357 15.1842 10.9229 15.2366 11.1137 15.236H11.1852C11.3895 15.2209 11.5846 15.1448 11.7451 15.0174C11.9056 14.89 12.0241 14.7173 12.0852 14.5217L15.6209 4.10027C15.684 3.92022 15.6947 3.72594 15.6517 3.54005C15.6087 3.35416 15.5138 3.18431 15.378 3.05027ZM4.32089 7.2717L13.4423 4.22884L8.37803 9.29312L4.32089 7.2717ZM11.1637 14.1074L9.13518 10.0503L14.1995 4.98598L11.1637 14.1074Z"
fill={color}
/>
</Svg>
);
};
export default SendIcon;
@@ -0,0 +1,113 @@
export const LOCAL_WALLET_ICON = `<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="16" cy="16" r="16" fill="url(#paint0_linear_2103_3904)"/>
<rect x="14" y="22" width="4" height="3" fill="#E6DEFC"/>
<path d="M11 26C10.45 26 9.979 25.8218 9.587 25.4655C9.195 25.1091 8.99934 24.6812 9 24.1818V7.81818C9 7.31818 9.196 6.89 9.588 6.53364C9.98 6.17727 10.4507 5.9994 11 6H21C21.55 6 22.021 6.17818 22.413 6.53455C22.805 6.89091 23.0007 7.31879 23 7.81818V24.1818C23 24.6818 22.804 25.11 22.412 25.4664C22.02 25.8227 21.5493 26.0006 21 26H11ZM16 23.7273C16.2833 23.7273 16.521 23.64 16.713 23.4655C16.905 23.2909 17.0007 23.0751 17 22.8182C17 22.5606 16.904 22.3445 16.712 22.17C16.52 21.9955 16.2827 21.9085 16 21.9091C15.7167 21.9091 15.479 21.9964 15.287 22.1709C15.095 22.3455 14.9993 22.5612 15 22.8182C15 23.0758 15.096 23.2918 15.288 23.4664C15.48 23.6409 15.7173 23.7279 16 23.7273ZM11 17.6364C11 18.7409 11.8954 19.6364 13 19.6364H19C20.1046 19.6364 21 18.7409 21 17.6364V12.5455C21 11.4409 20.1046 10.5455 19 10.5455H13C11.8954 10.5455 11 11.4409 11 12.5455V17.6364Z" fill="black"/>
<path d="M11 26C10.45 26 9.979 25.8218 9.587 25.4655C9.195 25.1091 8.99934 24.6812 9 24.1818V7.81818C9 7.31818 9.196 6.89 9.588 6.53364C9.98 6.17727 10.4507 5.9994 11 6H21C21.55 6 22.021 6.17818 22.413 6.53455C22.805 6.89091 23.0007 7.31879 23 7.81818V24.1818C23 24.6818 22.804 25.11 22.412 25.4664C22.02 25.8227 21.5493 26.0006 21 26H11ZM16 23.7273C16.2833 23.7273 16.521 23.64 16.713 23.4655C16.905 23.2909 17.0007 23.0751 17 22.8182C17 22.5606 16.904 22.3445 16.712 22.17C16.52 21.9955 16.2827 21.9085 16 21.9091C15.7167 21.9091 15.479 21.9964 15.287 22.1709C15.095 22.3455 14.9993 22.5612 15 22.8182C15 23.0758 15.096 23.2918 15.288 23.4664C15.48 23.6409 15.7173 23.7279 16 23.7273ZM11 17.6364C11 18.7409 11.8954 19.6364 13 19.6364H19C20.1046 19.6364 21 18.7409 21 17.6364V12.5455C21 11.4409 20.1046 10.5455 19 10.5455H13C11.8954 10.5455 11 11.4409 11 12.5455V17.6364Z" fill="url(#paint1_linear_2103_3904)"/>
<rect x="11" y="8" width="10" height="16" rx="1.5" fill="#BB0FAE"/>
<rect x="13" y="9" width="6" height="2" rx="1" fill="#F8F5FF"/>
<defs>
<linearGradient id="paint0_linear_2103_3904" x1="15" y1="-9.12696e-08" x2="15" y2="35.5" gradientUnits="userSpaceOnUse">
<stop stop-color="#D311AA"/>
<stop offset="1" stop-color="#8409B7"/>
</linearGradient>
<linearGradient id="paint1_linear_2103_3904" x1="16" y1="6" x2="16" y2="26" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="#E1D8FB"/>
</linearGradient>
</defs>
</svg>`;
export const COINBASE_ICON = `<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="48" height="48" rx="8" fill="#0555FF"/>
<g clip-path="url(#clip0_3303_8634)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.3312 4H35.6672C40.2704 4 44 8.0128 44 12.9632V35.0368C44 39.9872 40.2704 44 35.6688 44H12.3312C7.7296 44 4 39.9872 4 35.0368V12.9632C4 8.0128 7.7296 4 12.3312 4Z" fill="#0052FF"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M24.0003 9.79346C31.8467 9.79346 38.2067 16.1535 38.2067 23.9999C38.2067 31.8463 31.8467 38.2063 24.0003 38.2063C16.1539 38.2063 9.79395 31.8463 9.79395 23.9999C9.79395 16.1535 16.1539 9.79346 24.0003 9.79346Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M20.5006 19.459H27.4974C28.0734 19.459 28.539 19.9614 28.539 20.579V27.419C28.539 28.0382 28.0718 28.539 27.4974 28.539H20.5006C19.9246 28.539 19.459 28.0366 19.459 27.419V20.579C19.459 19.9614 19.9262 19.459 20.5006 19.459Z" fill="#0052FF"/>
</g>
<defs>
<clipPath id="clip0_3303_8634">
<rect width="40" height="40" fill="white" transform="translate(4 4)"/>
</clipPath>
</defs>
</svg>`;
export const TRUST_ICON = `<svg width="80" height="80" viewBox="0 0 80 80" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="80" height="80" rx="12" fill="white"/>
<path d="M40.5303 17.1425C48.2477 23.6857 57.156 23.3518 59.7003 23.3707C58.8698 60.2513 54.6835 52.9058 40.1887 63.1412C25.8474 52.6916 21.579 59.9744 21.2964 23.0855C23.8142 23.1042 32.7165 23.5704 40.5303 17.1425Z" stroke="#3375BB" stroke-width="7" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
</svg>`;
export const METAMASK_ICON = `<svg width="80" height="80" viewBox="0 0 80 80" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="80" height="80" rx="12" fill="#FFE6CE"/>
<path d="M64.9712 14.179L43.2903 30.2817L47.2996 20.7814L64.9712 14.179Z" fill="#E2761B" stroke="#E2761B" stroke-width="0.124514" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M14.9198 14.179L36.4264 30.4343L32.6132 20.7814L14.9198 14.179ZM57.1704 51.5051L51.3961 60.3518L63.751 63.751L67.3027 51.7012L57.1704 51.5051ZM12.6319 51.7012L16.1619 63.751L28.5167 60.3518L22.7424 51.5051L12.6319 51.7012Z" fill="#E4761B" stroke="#E4761B" stroke-width="0.124514" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M27.8194 36.5572L24.3766 41.765L36.6444 42.3097L36.2086 29.1268L27.8194 36.5572ZM52.0716 36.5572L43.5735 28.9743L43.2903 42.3097L55.5362 41.765L52.0716 36.5572ZM28.5167 60.3517L35.8817 56.7564L29.5191 51.7883L28.5167 60.3517ZM44.0093 56.7564L51.3961 60.3517L50.372 51.7883L44.0093 56.7564Z" fill="#E4761B" stroke="#E4761B" stroke-width="0.124514" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M51.3961 60.3517L44.0093 56.7564L44.5977 61.572L44.5323 63.5984L51.3961 60.3517ZM28.5167 60.3517L35.3805 63.5984L35.337 61.572L35.8817 56.7564L28.5167 60.3517Z" fill="#D7C1B3" stroke="#D7C1B3" stroke-width="0.124514" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M35.4895 48.607L29.3447 46.7984L33.6809 44.8156L35.4895 48.607ZM44.4015 48.607L46.2101 44.8156L50.5681 46.7984L44.4015 48.607Z" fill="#233447" stroke="#233447" stroke-width="0.124514" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M28.5167 60.3517L29.5626 51.505L22.7424 51.7011L28.5167 60.3517ZM50.3502 51.505L51.3961 60.3517L57.1704 51.7011L50.3502 51.505ZM55.5362 41.765L43.2903 42.3097L44.4233 48.607L46.2319 44.8155L50.5899 46.7984L55.5362 41.765ZM29.3447 46.7984L33.7027 44.8155L35.4895 48.607L36.6443 42.3097L24.3766 41.765L29.3447 46.7984Z" fill="#CD6116" stroke="#CD6116" stroke-width="0.124514" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M24.3766 41.765L29.5191 51.7883L29.3447 46.7984L24.3766 41.765ZM50.5899 46.7984L50.372 51.7883L55.5362 41.765L50.5899 46.7984ZM36.6444 42.3097L35.4895 48.607L36.9276 56.0373L37.2545 46.2537L36.6444 42.3097ZM43.2903 42.3097L42.7019 46.2319L42.9634 56.0373L44.4233 48.607L43.2903 42.3097Z" fill="#E4751F" stroke="#E4751F" stroke-width="0.124514" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M44.4233 48.607L42.9634 56.0374L44.0093 56.7564L50.372 51.7883L50.5899 46.7985L44.4233 48.607ZM29.3447 46.7985L29.519 51.7883L35.8817 56.7564L36.9276 56.0374L35.4895 48.607L29.3447 46.7985Z" fill="#F6851B" stroke="#F6851B" stroke-width="0.124514" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M44.5323 63.5984L44.5977 61.572L44.0529 61.0926H35.8381L35.337 61.572L35.3805 63.5984L28.5167 60.3517L30.9136 62.3128L35.7728 65.6903H44.1183L48.9992 62.3128L51.3961 60.3517L44.5323 63.5984Z" fill="#C0AD9E" stroke="#C0AD9E" stroke-width="0.124514" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M44.0093 56.7564L42.9634 56.0374H36.9276L35.8817 56.7564L35.337 61.572L35.8381 61.0926H44.0529L44.5977 61.572L44.0093 56.7564Z" fill="#161616" stroke="#161616" stroke-width="0.124514" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M65.8864 31.3277L67.7385 22.4374L64.9712 14.179L44.0093 29.737L52.0716 36.5572L63.4677 39.8911L65.9953 36.9494L64.9058 36.165L66.649 34.5743L65.2981 33.5284L67.0412 32.1992L65.8864 31.3277ZM12.1743 22.4374L14.0265 31.3277L12.8498 32.1992L14.593 33.5284L13.2638 34.5743L15.007 36.165L13.9175 36.9494L16.4233 39.8911L27.8195 36.5572L35.8817 29.737L14.9198 14.179L12.1743 22.4374Z" fill="#763D16" stroke="#763D16" stroke-width="0.124514" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M63.4677 39.891L52.0716 36.5572L55.5362 41.765L50.372 51.7883L57.1704 51.7012H67.3027L63.4677 39.891ZM27.8194 36.5572L16.4233 39.891L12.6319 51.7012H22.7424L29.5191 51.7883L24.3766 41.765L27.8194 36.5572ZM43.2903 42.3097L44.0093 29.737L47.3214 20.7813H32.6132L35.8817 29.737L36.6443 42.3097L36.9058 46.2755L36.9276 56.0373H42.9634L43.007 46.2755L43.2903 42.3097Z" fill="#F6851B" stroke="#F6851B" stroke-width="0.124514" stroke-linecap="round" stroke-linejoin="round"/>
</svg>`;
export const RAINBOW_ICON = `<svg width="80" height="80" viewBox="0 0 80 80" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_5_149)">
<path d="M80 0H0V80H80V0Z" fill="url(#paint0_linear_5_149)"/>
<path d="M13.3333 25.3334H17.3333C37.9519 25.3334 54.6666 42.0481 54.6666 62.6667V66.6667H62.6666C64.8758 66.6667 66.6666 64.8758 66.6666 62.6667C66.6666 35.4206 44.5794 13.3334 17.3333 13.3334C15.1242 13.3334 13.3333 15.1242 13.3333 17.3334V25.3334Z" fill="url(#paint1_radial_5_149)"/>
<path d="M56 62.6666H66.6667C66.6667 64.8758 64.8758 66.6666 62.6667 66.6666H56V62.6666Z" fill="url(#paint2_linear_5_149)"/>
<path d="M17.3333 13.3334V24H13.3333V17.3334C13.3333 15.1242 15.1242 13.3334 17.3333 13.3334Z" fill="url(#paint3_linear_5_149)"/>
<path d="M13.3333 24H17.3333C38.6883 24 56 41.3117 56 62.6667V66.6667H44V62.6667C44 47.9391 32.0609 36 17.3333 36H13.3333V24Z" fill="url(#paint4_radial_5_149)"/>
<path d="M45.3333 62.6666H56V66.6666H45.3333V62.6666Z" fill="url(#paint5_linear_5_149)"/>
<path d="M13.3333 34.6667V24H17.3333V34.6667H13.3333Z" fill="url(#paint6_linear_5_149)"/>
<path d="M13.3333 41.3333C13.3333 43.5424 15.1242 45.3333 17.3333 45.3333C26.9062 45.3333 34.6666 53.0937 34.6666 62.6666C34.6666 64.8758 36.4575 66.6666 38.6666 66.6666H45.3333V62.6666C45.3333 47.2026 32.7973 34.6666 17.3333 34.6666H13.3333V41.3333Z" fill="url(#paint7_radial_5_149)"/>
<path d="M34.6667 62.6666H45.3334V66.6666H38.6667C36.4576 66.6666 34.6667 64.8758 34.6667 62.6666Z" fill="url(#paint8_radial_5_149)"/>
<path d="M17.3333 45.3333C15.1242 45.3333 13.3333 43.5424 13.3333 41.3333V34.6666H17.3333V45.3333Z" fill="url(#paint9_radial_5_149)"/>
</g>
<defs>
<linearGradient id="paint0_linear_5_149" x1="40" y1="0" x2="40" y2="80" gradientUnits="userSpaceOnUse">
<stop stop-color="#174299"/>
<stop offset="1" stop-color="#001E59"/>
</linearGradient>
<radialGradient id="paint1_radial_5_149" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(17.3333 62.6667) rotate(-90) scale(49.3333)">
<stop offset="0.770277" stop-color="#FF4000"/>
<stop offset="1" stop-color="#8754C9"/>
</radialGradient>
<linearGradient id="paint2_linear_5_149" x1="55.3333" y1="64.6666" x2="66.6667" y2="64.6666" gradientUnits="userSpaceOnUse">
<stop stop-color="#FF4000"/>
<stop offset="1" stop-color="#8754C9"/>
</linearGradient>
<linearGradient id="paint3_linear_5_149" x1="15.3333" y1="13.3334" x2="15.3333" y2="24.6667" gradientUnits="userSpaceOnUse">
<stop stop-color="#8754C9"/>
<stop offset="1" stop-color="#FF4000"/>
</linearGradient>
<radialGradient id="paint4_radial_5_149" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(17.3333 62.6667) rotate(-90) scale(38.6667)">
<stop offset="0.723929" stop-color="#FFF700"/>
<stop offset="1" stop-color="#FF9901"/>
</radialGradient>
<linearGradient id="paint5_linear_5_149" x1="45.3333" y1="64.6666" x2="56" y2="64.6666" gradientUnits="userSpaceOnUse">
<stop stop-color="#FFF700"/>
<stop offset="1" stop-color="#FF9901"/>
</linearGradient>
<linearGradient id="paint6_linear_5_149" x1="15.3333" y1="34.6667" x2="15.3333" y2="24" gradientUnits="userSpaceOnUse">
<stop stop-color="#FFF700"/>
<stop offset="1" stop-color="#FF9901"/>
</linearGradient>
<radialGradient id="paint7_radial_5_149" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(17.3333 62.6666) rotate(-90) scale(28)">
<stop offset="0.59513" stop-color="#00AAFF"/>
<stop offset="1" stop-color="#01DA40"/>
</radialGradient>
<radialGradient id="paint8_radial_5_149" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(34 64.6666) scale(11.3333 30.2222)">
<stop stop-color="#00AAFF"/>
<stop offset="1" stop-color="#01DA40"/>
</radialGradient>
<radialGradient id="paint9_radial_5_149" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(15.3333 46) rotate(-90) scale(11.3333 214.913)">
<stop stop-color="#00AAFF"/>
<stop offset="1" stop-color="#01DA40"/>
</radialGradient>
<clipPath id="clip0_5_149">
<rect width="80" height="80" rx="12" fill="white"/>
</clipPath>
</defs>
</svg>`;
@@ -0,0 +1,35 @@
import { IconStyleProp } from "./types";
import Svg, { ClipPath, Defs, G, Path, Rect } from "react-native-svg";
const ThirdwebLogo = ({ width, height, color }: IconStyleProp) => {
return (
<Svg width={width} height={height} viewBox="0 0 26 16" fill="none">
<G clip-path="url(#clip0_3185_13519)">
<Path
d="M13.6795 0.5C13.8386 0.531779 13.9887 0.595721 14.1196 0.6875C14.2673 0.797724 14.3804 0.944328 14.4467 1.11141C14.8623 2.10272 15.279 3.09348 15.6968 4.0837L17.089 7.39022C17.3322 7.96902 17.5736 8.5462 17.8202 9.12337C17.8727 9.24121 17.8998 9.36791 17.8998 9.49592C17.8998 9.62393 17.8727 9.75064 17.8202 9.86848C17.1147 11.5446 16.4086 13.2217 15.7019 14.9C15.6444 15.0572 15.542 15.196 15.4062 15.3009C15.2703 15.4057 15.1065 15.4724 14.933 15.4935C14.7188 15.5315 14.4972 15.4982 14.3057 15.399C14.1143 15.2998 13.9645 15.1409 13.8816 14.9489C13.4843 14.0196 13.0956 13.087 12.7034 12.1543C12.0367 10.575 11.3717 8.99457 10.7085 7.41304C10.1159 6.00543 9.52401 4.59891 8.93265 3.19348C8.73572 2.72717 8.54564 2.25761 8.34015 1.79293C8.28363 1.66705 8.25686 1.53087 8.26173 1.39405C8.26661 1.25722 8.30301 1.12309 8.36836 1.00114C8.43371 0.879187 8.52641 0.772402 8.6399 0.688339C8.75339 0.604275 8.8849 0.544988 9.02512 0.514673C9.04225 0.514673 9.05766 0.504891 9.07478 0.5H13.6795Z"
fill={color}
/>
<Path
d="M5.41171 0.5C5.46479 0.514674 5.51617 0.526087 5.56754 0.542392C5.70064 0.581915 5.82278 0.64928 5.925 0.739555C6.02722 0.829829 6.10694 0.940727 6.15833 1.06413C6.61498 2.13913 7.06821 3.21413 7.51801 4.28913C7.96781 5.36413 8.42446 6.45109 8.88796 7.55L9.56095 9.14456C9.61135 9.25806 9.63731 9.38004 9.63731 9.50326C9.63731 9.62648 9.61135 9.74846 9.56095 9.86196C9.31094 10.4587 9.05978 11.0565 8.80748 11.6554L7.42554 14.931C7.35297 15.1012 7.22895 15.2471 7.0692 15.3501C6.90946 15.4532 6.72117 15.5087 6.52822 15.5098C6.32467 15.5125 6.12519 15.4554 5.95696 15.3463C5.78872 15.2372 5.65991 15.0814 5.58809 14.9C5.15998 13.8712 4.73187 12.844 4.29348 11.8168L2.53823 7.64946L0.753866 3.41033C0.529536 2.87554 0.305206 2.33913 0.0688893 1.80435C-0.0308499 1.58161 -0.0356895 1.33067 0.0553906 1.10459C0.146471 0.878515 0.326331 0.695028 0.556936 0.592935C0.641014 0.560457 0.727427 0.533759 0.815514 0.513044L5.41171 0.5Z"
fill={color}
/>
<Path
d="M25.9999 9.64348C25.8766 10.0478 25.6968 10.4293 25.5255 10.8239C25.0609 11.9315 24.5934 13.0397 24.123 14.1484C24.0151 14.4109 23.9055 14.6554 23.8011 14.9114C23.7264 15.091 23.595 15.244 23.4251 15.3495C23.2552 15.4549 23.0551 15.5075 22.8524 15.5C22.6589 15.495 22.4714 15.4353 22.3137 15.3284C22.156 15.2216 22.0354 15.0724 21.9671 14.9C21.2889 13.2848 20.6097 11.6696 19.9292 10.0543C19.3425 8.66522 18.7534 7.27283 18.162 5.87717C17.5912 4.51087 17.0204 3.14456 16.4496 1.77826C16.3942 1.65338 16.3684 1.51844 16.374 1.383C16.3795 1.24756 16.4163 1.11494 16.4816 0.994528C16.547 0.874117 16.6393 0.768861 16.7522 0.686219C16.865 0.603577 16.9955 0.545569 17.1345 0.516304L17.1911 0.5H21.7924L21.8352 0.513044C21.9973 0.545507 22.1481 0.616718 22.2733 0.719974C22.3985 0.82323 22.4941 0.955132 22.551 1.10326C22.8181 1.73913 23.0864 2.37554 23.3558 3.0125C24.0579 4.68206 24.76 6.35163 25.4621 8.02119C25.6471 8.46141 25.8526 8.89348 26.0033 9.34511L25.9999 9.64348Z"
fill={color}
/>
</G>
<Defs>
<ClipPath id="clip0_3185_13519">
<Rect
width={width}
height={height}
fill={color}
transform="translate(0 0.5)"
/>
</ClipPath>
</Defs>
</Svg>
);
};
export default ThirdwebLogo;
@@ -0,0 +1,43 @@
import { IconStyleProp } from "./types";
import Svg, { Path } from "react-native-svg";
const TransactionIcon = ({
width,
height,
color = "#646D7A",
}: IconStyleProp) => {
return (
<Svg width={width} height={height} viewBox="0 0 16 16" fill="none">
<Path
d="M11.3333 6.6665H2"
stroke={color}
stroke-width="1.33333"
stroke-linecap="round"
stroke-linejoin="round"
/>
<Path
d="M14 4H2"
stroke={color}
stroke-width="1.33333"
stroke-linecap="round"
stroke-linejoin="round"
/>
<Path
d="M14 9.3335H2"
stroke={color}
stroke-width="1.33333"
stroke-linecap="round"
stroke-linejoin="round"
/>
<Path
d="M11.3333 12H2"
stroke={color}
stroke-width="1.33333"
stroke-linecap="round"
stroke-linejoin="round"
/>
</Svg>
);
};
export default TransactionIcon;
@@ -1,4 +1,3 @@
import { ThemeProvider } from "../styles/ThemeProvider";
import {
ConnectWalletDetailsProps,
WalletDetailsButton,
@@ -9,12 +8,15 @@ import {
useSwitchChain,
useWalletContext,
} from "@thirdweb-dev/react-core";
import { useEffect, useRef, useState } from "react";
import { useEffect, useMemo, useRef, useState } from "react";
import { ActivityIndicator, Animated, StyleSheet } from "react-native";
import { ConnectWalletButton } from "./ConnectWalletFlow/ConnectWalletButton";
import { ConnectWalletButtonProps } from "./ConnectWalletFlow/ConnectWalletButton";
import BaseButton from "./base/BaseButton";
import Text from "./base/Text";
import { useUIContext } from "../providers/ui-context-provider";
import { ThemeProvider } from "../styles/ThemeProvider";
import { SupportedTokens, defaultTokens } from "./SendFunds/defaultTokens";
export type ConnectWalletProps = {
/**
@@ -44,6 +46,25 @@ export type ConnectWalletProps = {
* @default false
*/
switchToActiveChain?: boolean;
/**
* Override the default supported tokens for each network
*
* These tokens will be displayed in "Send Funds" Modal
*/
supportedTokens?: SupportedTokens;
/**
* Show balance of ERC20 token instead of the native token in the "Connected" button when connected to certain network
*
* @example
* ```tsx
* <ConnectWallet balanceToken={{
* 1: "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599" // show USDC balance when connected to Ethereum mainnet
* }} />
* ```
*/
displayBalanceToken?: Record<number, string>;
} & ConnectWalletButtonProps;
export const ConnectWallet = ({
@@ -54,6 +75,9 @@ export const ConnectWallet = ({
extraRows,
hideTestnetFaucet,
switchToActiveChain,
termsOfServiceUrl,
privacyPolicyUrl,
supportedTokens,
}: ConnectWalletProps) => {
const fadeAnim = useRef(new Animated.Value(0)).current;
const address = useAddress();
@@ -62,6 +86,7 @@ export const ConnectWallet = ({
const { activeChain } = useWalletContext();
const switchChain = useSwitchChain();
const [switching, setSwitching] = useState(false);
const setTheme = useUIContext().setTheme;
useEffect(() => {
Animated.timing(fadeAnim, {
@@ -71,6 +96,26 @@ export const ConnectWallet = ({
}).start();
}, [fadeAnim]);
useEffect(() => {
if (theme) {
setTheme(theme);
}
}, [setTheme, theme]);
const supportedTokensMemo = useMemo(() => {
if (!supportedTokens) {
return defaultTokens;
}
const tokens = { ...defaultTokens };
for (const k in supportedTokens) {
const key = Number(k);
tokens[key] = supportedTokens[key];
}
return tokens;
}, [supportedTokens]);
return (
<ThemeProvider theme={theme}>
<Animated.View style={{ opacity: fadeAnim }}>
@@ -105,6 +150,7 @@ export const ConnectWallet = ({
detailsButton={detailsButton}
extraRows={extraRows}
hideTestnetFaucet={hideTestnetFaucet}
supportedTokens={supportedTokensMemo}
/>
)
) : (
@@ -112,6 +158,8 @@ export const ConnectWallet = ({
modalTitle={modalTitle}
buttonTitle={buttonTitle}
theme={theme}
termsOfServiceUrl={termsOfServiceUrl}
privacyPolicyUrl={privacyPolicyUrl}
/>
)}
</Animated.View>
@@ -113,17 +113,21 @@ const ConnectAppField = () => {
{!appMeta && showWCInput ? (
<Box
flexDirection="row"
mb="sm"
mt="xs"
borderColor="border"
borderWidth={1}
borderRadius="md"
>
<TextInput
onChangeText={onAddressChangeText}
flex={1}
value={wcUri}
placeholder={"wc://..."}
placeholderTextColor={theme.colors.textSecondary}
textInputProps={{
onChangeText: onAddressChangeText,
value: wcUri,
placeholder: "wc://...",
placeholderTextColor: theme.colors.textSecondary,
}}
containerProps={{
flex: 1,
}}
/>
<BaseButton
onPress={() => onWCPress()}
@@ -153,13 +157,13 @@ const ConnectAppField = () => {
<BaseButton
backgroundColor="background"
borderColor="border"
mb="sm"
mt="xs"
justifyContent="space-between"
style={styles.exportWallet}
onPress={onConnectDappPress}
>
<>
{appMeta ? (
{appMeta?.iconUrl ? (
<WalletIcon size={32} iconUri={appMeta.iconUrl} />
) : (
<WalletConnectIcon width={16} height={16} />
@@ -18,11 +18,15 @@ import { useSmartWallet } from "../../providers/context-provider";
import BaseButton from "../base/BaseButton";
import { SmartWalletAdditionalActions } from "./SmartWalletAdditionalActions";
import Text from "../base/Text";
import { useAppTheme } from "../../styles/hooks";
import { LocalWalletImportModal } from "../ConnectWalletFlow/LocalWalletImportModal";
import { IconTextButton } from "../base/IconTextButton";
import MoneyIcon from "../../assets/money";
import { TWModal } from "../base/modal/TWModal";
import { ThemeProvider } from "../../styles/ThemeProvider";
import TransactionIcon from "../../assets/transaction";
import { ReceiveButton } from "../ReceiveButton";
import { SendButton } from "../SendFunds/SendButton";
import { SupportedTokens } from "../SendFunds/defaultTokens";
const MODAL_HEIGHT = Dimensions.get("window").height * 0.7;
const DEVICE_WIDTH = Dimensions.get("window").width;
@@ -33,14 +37,17 @@ export const ConnectWalletDetailsModal = ({
extraRows,
address,
hideTestnetFaucet,
supportedTokens,
displayBalanceToken,
}: {
isVisible: boolean;
onClosePress: () => void;
extraRows?: React.FC;
address?: string;
hideTestnetFaucet?: boolean;
supportedTokens: SupportedTokens;
displayBalanceToken?: Record<number, string>;
}) => {
const theme = useAppTheme();
const [isExportModalVisible, setIsExportModalVisible] = useState(false);
const activeWallet = useWallet();
const chain = useChain();
@@ -50,6 +57,11 @@ export const ConnectWalletDetailsModal = ({
const [isImportModalVisible, setIsImportModalVisible] = useState(false);
const setConnectedWallet = useSetConnectedWallet();
const tokenAddress =
chain?.chainId && displayBalanceToken
? displayBalanceToken[chain?.chainId]
: undefined;
useEffect(() => {
const timeout = setTimeout(() => {
if (addressCopied) {
@@ -126,11 +138,7 @@ export const ConnectWalletDetailsModal = ({
<Text variant="bodySmall">Backup wallet</Text>
</View>
</>
<RightArrowIcon
height={10}
width={10}
color={theme.colors.iconPrimary}
/>
<RightArrowIcon height={10} width={10} />
</BaseButton>
<BaseButton
@@ -147,11 +155,7 @@ export const ConnectWalletDetailsModal = ({
<Text variant="bodySmall">Import wallet</Text>
</View>
</>
<RightArrowIcon
height={10}
width={10}
color={theme.colors.iconPrimary}
/>
<RightArrowIcon height={10} width={10} />
</BaseButton>
<LocalWalletImportModal
@@ -178,48 +182,64 @@ export const ConnectWalletDetailsModal = ({
onExportLocalWalletPress,
onWalletImported,
smartWallet,
theme.colors.iconPrimary,
]);
return (
<TWModal isVisible={isVisible} onBackdropPress={onClosePress}>
<View
style={[styles.modal, { backgroundColor: theme.colors.background }]}
>
<Box style={styles.contentContainer}>
<ExportLocalWalletModal
isVisible={isExportModalVisible}
onClose={onExportModalClose}
/>
<WalletDetailsModalHeader
address={address}
onDisconnectPress={onDisconnectPress}
onAddressCopied={onAddressCopied}
/>
<View style={styles.currentNetwork}>
<Text variant="bodySmallSecondary">Current Network</Text>
</View>
<NetworkButton chain={chain} enableSwitchModal={true} />
{!hideTestnetFaucet && chain?.testnet && chain?.faucets?.length ? (
<IconTextButton
mt="xs"
text="Request Testnet Funds"
icon={<MoneyIcon height={10} width={10} />}
onPress={() => {
if (chain?.faucets?.[0]) {
Linking.openURL(chain.faucets[0]);
}
}}
<ThemeProvider>
<TWModal isVisible={isVisible} onBackdropPress={onClosePress}>
<Box backgroundColor="background" style={styles.modal}>
<Box style={styles.contentContainer}>
<ExportLocalWalletModal
isVisible={isExportModalVisible}
onClose={onExportModalClose}
/>
) : null}
{getAdditionalActions()}
{extraRows ? extraRows({}) : null}
{addressCopied === true ? (
<Toast text={"Address copied to clipboard"} />
) : null}
<WalletDetailsModalHeader
tokenAddress={tokenAddress}
address={address}
onDisconnectPress={onDisconnectPress}
onAddressCopied={onAddressCopied}
/>
<Box flexDirection="row" justifyContent="space-evenly" mt="md">
<SendButton supportedTokens={supportedTokens} />
<ReceiveButton />
</Box>
<View style={styles.currentNetwork}>
<Text variant="bodySmallSecondary">Current Network</Text>
</View>
<NetworkButton chain={chain} enableSwitchModal={true} />
{!hideTestnetFaucet && chain?.testnet && chain?.faucets?.length ? (
<IconTextButton
mt="xs"
text="Request Testnet Funds"
icon={<MoneyIcon height={16} width={16} />}
onPress={() => {
if (chain?.faucets?.[0]) {
Linking.openURL(chain.faucets[0]);
}
}}
/>
) : null}
{chain?.explorers && chain?.explorers?.[0] && (
<IconTextButton
mt="xs"
text="View Transaction History"
icon={<TransactionIcon height={16} width={16} />}
onPress={() => {
Linking.openURL(
chain?.explorers?.[0].url + "/address/" + address,
);
}}
/>
)}
{getAdditionalActions()}
{extraRows ? extraRows({}) : null}
{addressCopied === true ? (
<Toast text={"Address copied to clipboard"} />
) : null}
</Box>
</Box>
</View>
</TWModal>
</TWModal>
</ThemeProvider>
);
};
@@ -1,28 +1,26 @@
import { StyleSheet, View } from "react-native";
import PocketWalletIcon from "../../assets/wallet";
import BaseButton from "../base/BaseButton";
import { WalletIcon } from "../base/WalletIcon";
import {
AbstractClientWallet,
SmartWallet,
walletIds,
} from "@thirdweb-dev/wallets";
import { AddressDisplay } from "../base/AddressDisplay";
import Text from "../base/Text";
import { usePersonalWalletAddress } from "../../wallets/hooks/usePersonalWalletAddress";
import { useWalletContext, useWallet } from "@thirdweb-dev/react-core";
import { useEffect, useState } from "react";
import { useSmartWallet } from "../../providers/context-provider";
import RightArrowIcon from "../../assets/right-arrow";
import ConnectAppField from "./ConnectAppField";
import { useAppTheme } from "../../styles/hooks";
import DisconnectIcon from "../../assets/disconnect";
import { IconTextButton } from "../base/IconTextButton";
export const SmartWalletAdditionalActions = ({
onExportPress,
}: {
onExportPress: () => void;
}) => {
const personalWalletAddress = usePersonalWalletAddress();
const { setConnectedWallet } = useWalletContext();
const [smartWallet, setSmartWallet] = useSmartWallet();
const [smartWalletAddress, setSmartWalletAddress] = useState<string>("");
@@ -62,38 +60,22 @@ export const SmartWalletAdditionalActions = ({
return (
<>
<View style={styles.currentNetwork}>
<Text variant="bodySmallSecondary">
{showSmartWallet ? "Smart Wallet" : "Personal Wallet"}
</Text>
</View>
<BaseButton
backgroundColor="background"
borderColor="border"
justifyContent="space-between"
mb="md"
style={styles.walletDetails}
<IconTextButton
mt="xs"
text={
showSmartWallet
? "Switch to Smart Wallet"
: "Switch to Personal Wallet"
}
icon={
<DisconnectIcon
width={14}
height={14}
color={theme.colors.iconPrimary}
/>
}
onPress={onWalletPress}
>
<>
{wallet?.getMeta().iconURL ? (
<WalletIcon size={32} iconUri={wallet?.getMeta().iconURL || ""} />
) : null}
<View style={styles.walletInfo}>
<AddressDisplay
variant="bodyLarge"
address={
showSmartWallet ? smartWalletAddress : personalWalletAddress
}
/>
</View>
</>
<RightArrowIcon
height={10}
width={10}
color={theme.colors.iconPrimary}
/>
</BaseButton>
/>
{!showSmartWallet && smartWallet?.enableConnectApp ? (
<ConnectAppField />
) : null}
@@ -104,6 +86,7 @@ export const SmartWalletAdditionalActions = ({
backgroundColor="background"
borderColor="border"
mb="sm"
mt="xs"
justifyContent="space-between"
style={styles.exportWallet}
onPress={onExportPress}
@@ -1,21 +1,39 @@
import { AddressDisplay } from "../base/AddressDisplay";
import BaseButton from "../base/BaseButton";
import { ChainIcon } from "../base/ChainIcon";
import Text from "../base/Text";
import { WalletIcon } from "../base/WalletIcon";
import { useWallet, useChain } from "@thirdweb-dev/react-core";
import { StyleSheet, View } from "react-native";
import { useWallet } from "@thirdweb-dev/react-core";
import { StyleSheet } from "react-native";
import { LocalWallet } from "@thirdweb-dev/wallets";
import Box from "../base/Box";
import { ConnectWalletDetailsModal } from "./ConnectWalletDetailsModal";
import { useState } from "react";
import { TextBalance } from "../base/TextBalance";
import { SupportedTokens } from "../SendFunds/defaultTokens";
export type ConnectWalletDetailsProps = {
address?: string;
detailsButton?: React.FC<{ onPress: () => void }>;
extraRows?: React.FC;
hideTestnetFaucet?: boolean;
/**
* Override the default supported tokens for each network
*
* These tokens will be displayed in "Send Funds" Modal
*/
supportedTokens: SupportedTokens;
/**
* Show balance of ERC20 token instead of the native token in the "Connected" button when connected to certain network
*
* @example
* ```tsx
* <ConnectWallet balanceToken={{
* 1: "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599" // show USDC balance when connected to Ethereum mainnet
* }} />
* ```
*/
displayBalanceToken?: Record<number, string>;
};
export const WalletDetailsButton = ({
@@ -23,21 +41,13 @@ export const WalletDetailsButton = ({
detailsButton,
extraRows,
hideTestnetFaucet,
supportedTokens,
displayBalanceToken,
}: ConnectWalletDetailsProps) => {
const activeWallet = useWallet();
const chain = useChain();
const [isModalVisible, setIsModalVisible] = useState(false);
const onPress = () => {
// setModalState({
// view: "WalletDetails",
// data: {
// address: address,
// },
// isOpen: true,
// isSheet: true,
// caller: "ConnectWalletDetails",
// });
setIsModalVisible(!isModalVisible);
};
@@ -49,6 +59,8 @@ export const WalletDetailsButton = ({
extraRows={extraRows}
address={address}
hideTestnetFaucet={hideTestnetFaucet}
supportedTokens={supportedTokens}
displayBalanceToken={displayBalanceToken}
/>
{detailsButton ? (
detailsButton({ onPress })
@@ -59,28 +71,25 @@ export const WalletDetailsButton = ({
style={styles.walletDetails}
onPress={onPress}
>
<Box flex={1} flexDirection="row" justifyContent="space-between">
<View>
<ChainIcon size={32} chainIconUrl={chain?.icon?.url} />
</View>
<Box justifyContent="center" alignItems="flex-start">
<TextBalance textVariant="bodySmall" />
<Box flex={1} flexDirection="row" justifyContent="flex-start">
<WalletIcon
size={32}
iconUri={activeWallet?.getMeta().iconURL || ""}
/>
<Box ml="md" justifyContent="center" alignItems="flex-start">
{activeWallet?.walletId === LocalWallet.id ? (
<Text variant="error">Guest</Text>
<Text variant="bodySmall" color="red">
Guest
</Text>
) : (
<AddressDisplay
variant="bodySmallSecondary"
variant="bodySmall"
extraShort={false}
address={address}
/>
)}
<TextBalance textVariant="bodySmallSecondary" />
</Box>
<View>
<WalletIcon
size={32}
iconUri={activeWallet?.getMeta().iconURL || ""}
/>
</View>
</Box>
</BaseButton>
)}
@@ -16,12 +16,14 @@ interface WalletDetailsModalHeaderProps {
onDisconnectPress: () => void;
onAddressCopied?: () => void;
loading?: boolean;
tokenAddress?: string;
}
export const WalletDetailsModalHeader = ({
address,
onDisconnectPress,
onAddressCopied,
tokenAddress,
}: WalletDetailsModalHeaderProps) => {
const theme = useAppTheme();
const activeWallet = useWallet();
@@ -66,7 +68,10 @@ export const WalletDetailsModalHeader = ({
color={theme.colors.textSecondary}
/>
</Box>
<TextBalance textVariant="bodySmallSecondary" />
<TextBalance
textVariant="bodySmallSecondary"
tokenAddress={tokenAddress}
/>
</BaseButton>
{showLoading ? (
<ActivityIndicator size="small" color={theme.colors.iconHighlight} />
@@ -1,11 +1,15 @@
import { walletIds } from "@thirdweb-dev/wallets";
import { localWallet } from "../../../wallets/wallets/local-wallet";
import { ModalFooter } from "../../base/modal/ModalFooter";
import { ModalHeaderTextClose } from "../../base/modal/ModalHeaderTextClose";
import { ChooseWalletContent } from "./ChooseWalletContent";
import { WalletConfig } from "@thirdweb-dev/react-core";
import { ReactNode, useState } from "react";
import { View } from "react-native";
import Box from "../../base/Box";
import Text from "../../base/Text";
import ThirdwebLogo from "../../../assets/thirdweb-logo";
import { useAppTheme } from "../../../styles/hooks";
import { ChooseWalletContent } from "./ChooseWalletContent";
import { BaseButton, ImageSvgUri } from "../../base";
import { ActivityIndicator, Linking } from "react-native";
import { useTheme } from "@shopify/restyle";
export type ChooseWalletProps = {
headerText?: ReactNode | string;
@@ -14,7 +18,8 @@ export type ChooseWalletProps = {
onClose: () => void;
wallets: WalletConfig<any>[];
excludeWalletIds?: string[];
showGuestWalletAsButton?: boolean;
termsOfServiceUrl?: string;
privacyPolicyUrl?: string;
};
export function ChooseWallet({
@@ -24,40 +29,258 @@ export function ChooseWallet({
onChooseWallet,
onClose,
excludeWalletIds = [],
showGuestWalletAsButton = false,
termsOfServiceUrl,
privacyPolicyUrl,
}: ChooseWalletProps) {
const theme = useAppTheme();
const themeLightDark = useTheme();
const [isConnecting, setIsConnecting] = useState(false);
const [isConnectAWalletEnabled, setIsConnectAWalletEnabled] = useState(false);
const guestWallet = wallets.find((w) => w.id === walletIds.localWallet);
const emailWallet = wallets.find((w) => w.id === walletIds.magicLink);
const connectionWallets = wallets
.filter(
(wallet) =>
wallet.id !== walletIds.magicLink &&
wallet.id !== walletIds.localWallet,
)
.slice(0, 2);
const showToSPrivacyPolicy = termsOfServiceUrl || privacyPolicyUrl;
const onContinueAsGuestPress = () => {
setIsConnecting(true);
onChooseWallet(localWallet());
setTimeout(() => {
if (guestWallet) {
onChooseWallet(guestWallet);
}
}, 0);
};
const onConnectAWalletPress = () => {
setIsConnectAWalletEnabled(true);
};
const onBackPress = () => {
setIsConnectAWalletEnabled(false);
};
const onGetStartedPress = () => {
Linking.openURL("https://ethereum.org/en/wallets/find-wallet/");
};
const onToSPressed = () => {
if (termsOfServiceUrl) {
Linking.openURL(termsOfServiceUrl);
}
};
const onPrivacyPolicyPress = () => {
if (privacyPolicyUrl) {
Linking.openURL(privacyPolicyUrl);
}
};
return (
<View>
<Box flexDirection="column">
<ModalHeaderTextClose
paddingHorizontal="xl"
onBackPress={isConnectAWalletEnabled ? onBackPress : undefined}
onClose={onClose}
headerText={headerText ? headerText : "Choose your wallet"}
headerText={
headerText ? (
headerText
) : (
<Box
flexDirection="row"
alignItems="center"
alignContent="center"
justifyContent="center"
>
<ThirdwebLogo
width={26}
height={15}
color={theme.colors.backgroundInverted}
/>
<Text
variant="header"
ml="xxs"
fontWeight="700"
fontSize={20}
lineHeight={24}
>
Connect
</Text>
</Box>
)
}
subHeaderText={subHeaderText}
/>
<ChooseWalletContent
wallets={wallets}
excludeWalletIds={
showGuestWalletAsButton
? excludeWalletIds
: [...excludeWalletIds, walletIds.localWallet]
}
onChooseWallet={onChooseWallet}
/>
{guestWallet && !showGuestWalletAsButton ? (
<ModalFooter
footer={"Continue as Guest"}
isLoading={isConnecting}
onPress={onContinueAsGuestPress}
{!emailWallet || isConnectAWalletEnabled ? (
<>
<ChooseWalletContent
wallets={wallets}
excludeWalletIds={[
...excludeWalletIds,
walletIds.localWallet,
walletIds.magicLink,
]}
onChooseWallet={onChooseWallet}
/>
<Box
pt="md"
flexGrow={0}
borderTopColor="border"
borderTopWidth={1}
paddingHorizontal="xl"
flexDirection="row"
justifyContent="space-between"
alignItems="center"
>
<Text variant="bodySmall">New to wallets?</Text>
<BaseButton onPress={onGetStartedPress}>
<Text variant="link">Get started</Text>
</BaseButton>
</Box>
</>
) : emailWallet.selectUI ? (
<emailWallet.selectUI
modalSize="compact"
theme={themeLightDark}
supportedWallets={wallets}
onSelect={(data: any) => {
onChooseWallet(emailWallet, data);
}}
walletConfig={emailWallet}
/>
) : null}
</View>
{emailWallet &&
!isConnectAWalletEnabled &&
(guestWallet || connectionWallets.length > 0) ? (
<Box
mb="md"
mt="md"
marginHorizontal="xl"
flexDirection="row"
justifyContent="center"
alignItems="center"
>
<Box height={1} flex={1} backgroundColor="border" />
<Text variant="subHeader" textAlign="center" marginHorizontal="xxs">
OR
</Text>
<Box height={1} flex={1} backgroundColor="border" />
</Box>
) : null}
{emailWallet &&
!isConnectAWalletEnabled &&
connectionWallets.length > 0 ? (
<BaseButton
marginHorizontal="xl"
justifyContent="center"
borderRadius="lg"
paddingVertical="xmd"
borderColor="border"
flexDirection="row"
alignItems="center"
borderWidth={1}
onPress={onConnectAWalletPress}
>
{connectionWallets.map((wallet) => {
return (
<Box key={wallet.meta.name} mr="xxs">
<ImageSvgUri
height={20}
width={20}
imageUrl={wallet.meta.iconURL}
/>
</Box>
);
})}
<Text variant="bodySmall" fontWeight="700">
Connect a wallet
</Text>
</BaseButton>
) : null}
{guestWallet ? (
<BaseButton
marginHorizontal="xl"
mt="sm"
paddingVertical="xmd"
justifyContent="center"
borderRadius="lg"
borderColor="border"
borderWidth={1}
onPress={onContinueAsGuestPress}
>
{isConnecting ? (
<ActivityIndicator size={"small"} />
) : (
<Text variant="bodySmall" fontWeight="700">
Continue as guest
</Text>
)}
</BaseButton>
) : null}
{showToSPrivacyPolicy ? (
<Box
mt="md"
pt="md"
height={50}
marginHorizontal={!isConnectAWalletEnabled ? "xl" : "none"}
alignItems="center"
>
<Text variant="bodySmallSecondary" fontSize={10} lineHeight={14}>
By connecting, you agree to the
</Text>
<Box flexDirection="row" alignItems="center" justifyContent="center">
{termsOfServiceUrl ? (
<BaseButton
onPress={onToSPressed}
flexDirection="row"
padding="none"
>
<Text
variant="link"
fontSize={10}
padding="none"
lineHeight={14}
>
Terms of Service
</Text>
</BaseButton>
) : null}
{termsOfServiceUrl && privacyPolicyUrl ? (
<Text
variant="bodySmallSecondary"
fontSize={10}
padding="none"
lineHeight={14}
>
{" & "}
</Text>
) : null}
{privacyPolicyUrl ? (
<BaseButton
onPress={onPrivacyPolicyPress}
flexDirection="row"
padding="none"
>
<Text
variant="link"
fontSize={10}
padding="none"
lineHeight={14}
>
Privacy Policy
</Text>
</BaseButton>
) : null}
</Box>
</Box>
) : null}
</Box>
);
}
@@ -1,6 +1,6 @@
import { useMemo } from "react";
import type { WalletConfig } from "@thirdweb-dev/react-core";
import { StyleSheet, View, FlatList } from "react-native";
import { Dimensions, ScrollView, View } from "react-native";
import { WalletButton } from "../../base/WalletButton";
import Box from "../../base/Box";
import { useTheme } from "@shopify/restyle";
@@ -11,29 +11,44 @@ interface ChooseWalletContentProps {
onChooseWallet: (wallet: WalletConfig, data?: any) => void;
}
const MAX_HEIGHT = Dimensions.get("window").height * 0.3;
export const ChooseWalletContent = ({
wallets,
excludeWalletIds,
onChooseWallet,
}: ChooseWalletContentProps) => {
const walletsToDisplay = useMemo(() => {
return wallets.filter(
(w) => !!!excludeWalletIds?.find((ewId) => ewId === w.id),
);
}, [wallets, excludeWalletIds]);
const theme = useTheme();
const walletsToDisplay = useMemo(() => {
const filteredWallets = wallets.filter(
(w) => !!!excludeWalletIds?.find((ewId) => ewId === w.id),
);
const trueItems = filteredWallets.filter(
(item) => item.recommended === true,
);
const falseItems = filteredWallets.filter(
(item) => item.recommended !== true,
);
const sortedWallets = [...trueItems, ...falseItems];
return sortedWallets;
}, [wallets, excludeWalletIds]);
return (
<View style={styles.explorerContainer}>
<FlatList
keyExtractor={(item) => item.meta.name}
data={walletsToDisplay}
renderItem={({ item, index }) => {
<View style={{ flexDirection: "column", maxHeight: MAX_HEIGHT }}>
<ScrollView
style={{
marginVertical: 16,
paddingHorizontal: 16,
}}
>
{walletsToDisplay.map((item, index) => {
const marginBottom =
index === walletsToDisplay.length - 1 ? "none" : "xxs";
return (
<>
<Box key={item.id}>
{item.selectUI ? (
<Box
mb={marginBottom}
@@ -43,6 +58,7 @@ export const ChooseWalletContent = ({
borderRadius="sm"
>
<item.selectUI
modalSize="compact"
theme={theme}
supportedWallets={wallets}
onSelect={(data) => {
@@ -55,26 +71,15 @@ export const ChooseWalletContent = ({
<WalletButton
walletIconUrl={item.meta.iconURL}
name={item.meta.name}
recommended={item.recommended}
onPress={() => onChooseWallet(item)}
mb={marginBottom}
/>
)}
</>
</Box>
);
}}
/>
})}
</ScrollView>
</View>
);
};
const styles = StyleSheet.create({
explorerContainer: {
display: "flex",
flexDirection: "row",
flexWrap: "wrap",
justifyContent: "center",
alignItems: "center",
paddingHorizontal: 4,
marginTop: 25,
},
});
@@ -0,0 +1,93 @@
import { useMemo } from "react";
import type { WalletConfig } from "@thirdweb-dev/react-core";
import { FlatList, Dimensions } from "react-native";
import Box from "../../base/Box";
import { useTheme } from "@shopify/restyle";
import { WalletButtonSquare } from "../../base/WalletButtonSquare";
import Text from "../../base/Text";
const SCREEN_WIDTH = Dimensions.get("screen").width;
const SCREEN_HEIGHT = Dimensions.get("screen").height;
const MAX_HEIGHT = SCREEN_HEIGHT * 0.4;
const NUM_COLUMNS = 3;
const TILE_SIZE = SCREEN_WIDTH / NUM_COLUMNS;
interface ChooseWalletContentProps {
wallets: WalletConfig[];
excludeWalletIds?: string[];
onChooseWallet: (wallet: WalletConfig, data?: any) => void;
}
export const ChooseWalletContentSquare = ({
wallets,
excludeWalletIds,
onChooseWallet,
}: ChooseWalletContentProps) => {
const walletsToDisplay = useMemo(() => {
return wallets.filter(
(w) => !!!excludeWalletIds?.find((ewId) => ewId === w.id),
);
}, [wallets, excludeWalletIds]);
const theme = useTheme();
return (
<Box marginTop="sm" flexDirection="column">
{walletsToDisplay.map((wallet) => {
if (!wallet.selectUI) {
return null;
}
return (
<Box
key={wallet.id}
flexDirection="row"
justifyContent="space-between"
alignItems="center"
borderRadius="sm"
>
<wallet.selectUI
modalSize="compact"
theme={theme}
supportedWallets={wallets}
onSelect={(data) => {
onChooseWallet(wallet, data);
}}
walletConfig={wallet}
/>
</Box>
);
})}
<FlatList
keyExtractor={(item) => item.meta.name}
data={walletsToDisplay}
numColumns={NUM_COLUMNS}
style={{ maxHeight: MAX_HEIGHT }}
fadingEdgeLength={10}
showsVerticalScrollIndicator={false}
renderItem={({ item }) => {
return !item.selectUI ? (
<WalletButtonSquare
walletIconUrl={item.meta.iconURL}
name={item.meta.name}
size={TILE_SIZE}
onPress={() => onChooseWallet(item)}
/>
) : null;
}}
/>
<Box
height={60}
borderTopColor="border"
borderTopWidth={1}
borderBottomColor="border"
borderBottomWidth={1}
flexDirection="row"
justifyContent="space-between"
alignItems="center"
>
<Text variant="bodySmall">New to wallets?</Text>
<Text variant="link">Get started</Text>
</Box>
</Box>
);
};
@@ -4,6 +4,7 @@ import { useConnectionStatus, useWallets } from "@thirdweb-dev/react-core";
import { useState, useEffect } from "react";
import { useModalState } from "../../providers/ui-context-provider";
import { ThemeProvider, ThemeProviderProps } from "../../styles/ThemeProvider";
import { useAppTheme } from "../../styles/hooks";
export type ConnectWalletButtonProps = {
theme?: ThemeProviderProps["theme"];
@@ -17,13 +18,25 @@ export type ConnectWalletButtonProps = {
* @default "Choose your wallet"
*/
modalTitle?: string;
/**
* Set a custom terms of service url
*/
termsOfServiceUrl?: string;
/**
* Set a custom privacy policy url
*/
privacyPolicyUrl?: string;
};
export const ConnectWalletButton = ({
modalTitle,
termsOfServiceUrl,
privacyPolicyUrl,
buttonTitle,
theme,
}: ConnectWalletButtonProps) => {
const appTheme = useAppTheme();
const connectionStatus = useConnectionStatus();
const isWalletConnecting = connectionStatus === "connecting";
@@ -59,6 +72,8 @@ export const ConnectWalletButton = ({
view: "ConnectWalletFlow",
data: {
modalTitle,
termsOfServiceUrl,
privacyPolicyUrl,
walletConfig:
supportedWallets.length === 1 ? supportedWallets[0] : undefined,
},
@@ -67,7 +82,7 @@ export const ConnectWalletButton = ({
};
return (
<ThemeProvider theme={theme}>
<ThemeProvider theme={theme ? theme : appTheme}>
<BaseButton
backgroundColor="buttonBackgroundColor"
onPress={onConnectWalletPress}
@@ -3,19 +3,22 @@ import { ChooseWallet } from "./ChooseWallet/ChooseWallet";
import { ConnectingWallet } from "./ConnectingWallet/ConnectingWallet";
import { WalletConfig, useConnect, useWallets } from "@thirdweb-dev/react-core";
import { useCallback, useEffect, useState } from "react";
import { SmartWallet, walletIds } from "@thirdweb-dev/wallets";
import { SmartWalletFlow } from "./SmartWallet/SmartWalletFlow";
import { walletIds } from "@thirdweb-dev/wallets";
import { useColorScheme } from "react-native";
import { useModalState } from "../../providers/ui-context-provider";
import {
CLOSE_MODAL_STATE,
ConnectWalletFlowModal,
} from "../../utils/modalTypes";
import Box from "../base/Box";
import { ThemeProvider } from "../../styles/ThemeProvider";
import { useAppTheme } from "../../styles/hooks";
export const ConnectWalletFlow = () => {
const { modalState, setModalState } = useModalState();
const { modalTitle, walletConfig } = (modalState as ConnectWalletFlowModal)
.data;
const { modalTitle, privacyPolicyUrl, termsOfServiceUrl, walletConfig } = (
modalState as ConnectWalletFlowModal
).data;
const [modalVisible, setModalVisible] = useState(false);
const [activeWallet, setActiveWallet] = useState<WalletConfig | undefined>();
@@ -23,6 +26,7 @@ export const ConnectWalletFlow = () => {
const [selectionData, setSelectionData] = useState<any>();
const supportedWallets = useWallets();
const theme = useColorScheme();
const appTheme = useAppTheme();
const connect = useConnect();
const onClose = useCallback(
@@ -55,10 +59,8 @@ export const ConnectWalletFlow = () => {
setActiveWallet(() => wallet);
setSelectionData(data);
// If not smart wallet (sw has it's own flow, need to migrate it to connectUI)
// &&
// If the wallet has no custom connect UI, then connect it
if (wallet.id !== SmartWallet.id && !wallet.connectUI) {
if (!wallet.connectUI) {
connectActiveWallet(wallet, data);
}
},
@@ -67,7 +69,7 @@ export const ConnectWalletFlow = () => {
useEffect(() => {
// case when only one wallet is passed in supportedWallets
if (walletConfig) {
if (walletConfig && !walletConfig.connectUI) {
onChooseWallet(walletConfig);
}
}, [onChooseWallet, walletConfig]);
@@ -80,10 +82,6 @@ export const ConnectWalletFlow = () => {
resetModal();
}, []);
const onConnected = useCallback(() => {
onClose(true);
}, [onClose]);
const resetModal = () => {
setActiveWallet(undefined);
setIsConnecting(false);
@@ -94,14 +92,10 @@ export const ConnectWalletFlow = () => {
}, [onClose]);
const getComponentForWallet = useCallback(() => {
switch (activeWallet?.id) {
case SmartWallet.id:
return <SmartWalletFlow onClose={onClose} onConnect={onConnected} />;
}
if (activeWallet?.connectUI) {
return (
<activeWallet.connectUI
modalSize="compact"
theme={theme || "dark"}
goBack={onBackPress}
close={handleClose}
@@ -119,40 +113,43 @@ export const ConnectWalletFlow = () => {
handleClose,
modalVisible,
onBackPress,
onClose,
onConnected,
selectionData,
supportedWallets,
theme,
]);
return (
<>
{activeWallet ? (
isConnecting ? (
<ConnectingWallet
content={
activeWallet.id === walletIds.localWallet ? (
<Text variant="bodySmallSecondary" mt="md">
Creating, encrypting and securing your device wallet.
</Text>
) : undefined
}
wallet={activeWallet}
onClose={onClose}
onBackPress={onBackPress}
/>
<ThemeProvider theme={appTheme}>
<Box flexDirection="column">
{activeWallet ? (
isConnecting ? (
<ConnectingWallet
subHeaderText=""
content={
activeWallet.id === walletIds.localWallet ? (
<Text variant="bodySmallSecondary" mt="md">
Creating, encrypting and securing your device wallet.
</Text>
) : undefined
}
wallet={activeWallet}
onClose={onClose}
onBackPress={onBackPress}
/>
) : (
getComponentForWallet()
)
) : (
getComponentForWallet()
)
) : (
<ChooseWallet
headerText={modalTitle}
wallets={supportedWallets}
onChooseWallet={onChooseWallet}
onClose={onClose}
/>
)}
</>
<ChooseWallet
headerText={modalTitle}
privacyPolicyUrl={privacyPolicyUrl}
termsOfServiceUrl={termsOfServiceUrl}
wallets={supportedWallets}
onChooseWallet={onChooseWallet}
onClose={onClose}
/>
)}
</Box>
</ThemeProvider>
);
};
@@ -1,10 +1,10 @@
import { useAppTheme } from "../../../styles/hooks";
import WalletLoadingThumbnail from "../../../wallets/wallets/wallet-connect/WalletLoadingThumbnail";
import { Box, ImageSvgUri } from "../../base";
import Text from "../../base/Text";
import { ModalFooter } from "../../base/modal/ModalFooter";
import { ConnectWalletHeader } from "./ConnectingWalletHeader";
import type { WalletConfig } from "@thirdweb-dev/react-core";
import { ReactNode } from "react";
import { ActivityIndicator, Linking, StyleSheet, View } from "react-native";
import { StyleSheet, View } from "react-native";
export type ConnectingWalletProps = {
subHeaderText?: string;
@@ -12,7 +12,7 @@ export type ConnectingWalletProps = {
content?: ReactNode;
onClose: () => void;
onBackPress: () => void;
wallet: WalletConfig;
wallet: WalletConfig<any>;
};
export function ConnectingWallet({
@@ -23,51 +23,43 @@ export function ConnectingWallet({
onClose,
onBackPress,
}: ConnectingWalletProps) {
const theme = useAppTheme();
const onFooterPress = () => {
Linking.openURL("https://support.thirdweb.com/");
};
return (
<View>
<Box paddingHorizontal="xl">
<ConnectWalletHeader
onBackPress={onBackPress}
walletLogoUrl={wallet.meta.iconURL}
subHeaderText={subHeaderText}
onClose={onClose}
/>
<WalletLoadingThumbnail imageSize={100}>
<ImageSvgUri height={80} width={80} imageUrl={wallet.meta.iconURL} />
</WalletLoadingThumbnail>
<View style={styles.connectingContainer}>
<ActivityIndicator size="small" color={theme.colors.linkPrimary} />
{content ? (
content
) : (
<Text variant="bodySmallSecondary" mt="md">
Connect your wallet through the {wallet.meta.name} application.
</Text>
<>
<Text variant="header" mt="lg">
Connecting your wallet
</Text>
<Text variant="bodySmallSecondary" mt="lg" textAlign="center">
{`Login and connect your wallet through the ${wallet.meta.name} pop-up`}
</Text>
</>
)}
</View>
{footer ? (
footer
) : (
<ModalFooter
footer={`Having troubles connecting to ${wallet.meta.name}?`}
onPress={onFooterPress}
/>
)}
</View>
{footer ? footer : null}
</Box>
);
}
const styles = StyleSheet.create({
connectingContainer: {
display: "flex",
flexDirection: "column",
flexWrap: "wrap",
justifyContent: "center",
alignContent: "center",
alignItems: "center",
paddingHorizontal: 4,
marginBottom: 24,
marginTop: 18,
},
});
@@ -33,9 +33,7 @@ export const ConnectWalletHeader = ({
onPress={onBackPress}
color={theme.colors.iconPrimary}
/>
) : (
<View />
)}
) : null}
{walletLogoUrl ? (
<ImageSvgUri width={56} height={56} imageUrl={walletLogoUrl} />
) : null}
@@ -196,10 +196,12 @@ export const LocalWalletImportModal = ({
---- Or Private key or Mnemonic ----
</Text>
<TextInput
secureTextEntry={true}
placeholder={"Private key / Mnemonic"}
placeholderTextColor={theme.colors.textSecondary}
onChangeText={onPrivateKeyEntered}
textInputProps={{
secureTextEntry: true,
placeholder: "Private key / Mnemonic",
placeholderTextColor: theme.colors.textSecondary,
onChangeText: onPrivateKeyEntered,
}}
/>
<Box
flexDirection="row"
@@ -1,121 +1,83 @@
import { useCallback, useEffect, useState } from "react";
import {
ConnectUIProps,
WalletConfig,
WalletInstance,
useConnect,
useCreateWalletInstance,
useSupportedWallet,
useWalletContext,
useWallets,
} from "@thirdweb-dev/react-core";
import { SmartWalletObj } from "../../../wallets/wallets/smart-wallet";
import { localWallet } from "../../../wallets/wallets/local-wallet";
import { ChooseWallet } from "../ChooseWallet/ChooseWallet";
import { LocalWalletFlow } from "../LocalWalletFlow";
import { ModalHeaderTextClose } from "../../base/modal/ModalHeaderTextClose";
import { ActivityIndicator, Linking, useColorScheme } from "react-native";
import { StyleSheet, View } from "react-native";
import BaseButton from "../../base/BaseButton";
import Text from "../../base/Text";
import { walletIds } from "@thirdweb-dev/wallets";
import { useAppTheme } from "../../../styles/hooks";
import { DEFAULT_WALLETS } from "../../../constants/wallets";
import { SmartWallet } from "@thirdweb-dev/wallets";
import WalletLoadingThumbnail from "../../../wallets/wallets/wallet-connect/WalletLoadingThumbnail";
import ImageSvgUri from "../../base/ImageSvgUri";
import Box from "../../base/Box";
import { ConnectWalletHeader } from "../ConnectingWallet/ConnectingWalletHeader";
export const SmartWalletFlow = ({
onClose,
onConnect,
}: {
onClose: () => void;
onConnect: () => void;
}) => {
const [showLocalWalletFlow, setShowLocalWalletFlow] = useState<boolean>();
close,
goBack,
walletConfig,
}: ConnectUIProps<SmartWallet>) => {
const [connectedPersonalWallet, setConnectedPersonalWallet] =
useState<WalletInstance>();
const [isConnecting, setIsConnecting] = useState(false);
const [personalWalletChainId, setPersonalWalletChaindId] = useState<
number | undefined
>();
const theme = useAppTheme();
const createWalletInstance = useCreateWalletInstance();
const walletObj = useSupportedWallet(walletIds.smartWallet) as SmartWalletObj;
const connect = useConnect();
const targetChain = useWalletContext().activeChain;
const colorScheme = useColorScheme();
const supportedWallets = useWallets();
const mismatch = personalWalletChainId
? personalWalletChainId !== targetChain.chainId
: false;
const connectSmartWallet = useCallback(
async (personalWallet: WalletInstance) => {
const eoaWalletChainId = await personalWallet.getChainId();
if (eoaWalletChainId === targetChain.chainId) {
await connect(walletConfig, {
personalWallet: personalWallet,
});
close();
} else {
setPersonalWalletChaindId(eoaWalletChainId);
}
},
[close, connect, targetChain.chainId, walletConfig],
);
const connectPersonalWallet = useCallback(
async (wallet: WalletConfig) => {
setIsConnecting(true);
const walletInstance = createWalletInstance(wallet);
await walletInstance.connect();
setConnectedPersonalWallet(walletInstance);
},
[createWalletInstance],
);
const onChoosePersonalWallet = useCallback(
async (wallet: WalletConfig) => {
// if (wallet.id === LocalWallet.id) {
// setShowLocalWalletFlow(true);
// } else {
connectPersonalWallet(wallet);
// }
connectSmartWallet(walletInstance);
},
[connectPersonalWallet],
[connectSmartWallet, createWalletInstance],
);
useEffect(() => {
if (walletObj.personalWallets?.length === 1) {
onChoosePersonalWallet(walletObj.personalWallets[0]);
if (walletConfig.personalWallets?.[0]) {
connectPersonalWallet(walletConfig.personalWallets[0]);
}
}, [onChoosePersonalWallet, walletObj.personalWallets]);
useEffect(() => {
(async () => {
if (connectedPersonalWallet) {
const chainId = await connectedPersonalWallet.getChainId();
setPersonalWalletChaindId(chainId);
}
})();
}, [connectedPersonalWallet]);
const connectSmartWallet = useCallback(
async (personalWallet: WalletInstance) => {
if (!mismatch && personalWalletChainId) {
await connect(walletObj, {
personalWallet: personalWallet,
});
onConnect();
}
},
[connect, mismatch, onConnect, personalWalletChainId, walletObj],
);
useEffect(() => {
if (connectedPersonalWallet) {
if (!mismatch) {
connectSmartWallet(connectedPersonalWallet);
}
}
}, [connectSmartWallet, connectedPersonalWallet, mismatch]);
const onConnectedLocalWallet = async (wallet: WalletInstance) => {
setIsConnecting(true);
connectSmartWallet(wallet);
};
const onLocalWalletBackPress = () => {
setShowLocalWalletFlow(false);
};
}, [connectPersonalWallet, walletConfig.personalWallets]);
const onConnectingClosePress = () => {
connectedPersonalWallet?.disconnect();
reset();
close();
};
const onConnectingBackPress = () => {
connectedPersonalWallet?.disconnect();
reset();
goBack();
};
const onSwitchNetworkPress = async () => {
@@ -124,88 +86,74 @@ export const SmartWalletFlow = ({
};
const reset = () => {
setIsConnecting(false);
setConnectedPersonalWallet(undefined);
setPersonalWalletChaindId(undefined);
};
const onLearnMorePress = () => {
Linking.openURL("https://portal.thirdweb.com/wallet/smart-wallet");
};
if (isConnecting) {
return (
<>
<ModalHeaderTextClose
onClose={onConnectingClosePress}
headerText={mismatch ? "Network Mismatch" : "Connecting smart wallet"}
subHeaderText={
mismatch
? "There's a network mismatch between your contract and your wallet"
: ""
}
/>
{mismatch ? null : (
<ActivityIndicator size="large" color={theme.colors.linkPrimary} />
)}
{mismatch === true ? (
<BaseButton
marginVertical="sm"
flexDirection="row"
justifyContent="flex-start"
alignItems="center"
paddingHorizontal="md"
paddingVertical="sm"
borderRadius="sm"
backgroundColor="backgroundHighlight"
onPress={onSwitchNetworkPress}
>
<Text variant="bodyLarge">Switch Network</Text>
</BaseButton>
) : null}
</>
);
}
if (showLocalWalletFlow) {
return (
<LocalWalletFlow
theme={colorScheme || "dark"}
close={onClose}
goBack={onLocalWalletBackPress}
onConnected={onConnectedLocalWallet}
isOpen={false}
open={() => {}}
walletConfig={localWallet()}
selectionData={undefined} // TODO
setSelectionData={() => {}} // TODO
supportedWallets={supportedWallets} // TODO - pass personal wallets instead
/>
);
}
return (
<ChooseWallet
headerText={"Link key"}
subHeaderText={
<Text variant="subHeader">
{
"Choose a personal wallet that acts as your account's key. This controls access to your account. "
}
<Text
variant="subHeader"
color="linkPrimary"
onPress={onLearnMorePress}
>
Learn more.
<>
<Box paddingHorizontal="xl">
<ConnectWalletHeader
subHeaderText=""
onBackPress={onConnectingBackPress}
onClose={onConnectingClosePress}
/>
<WalletLoadingThumbnail imageSize={100}>
<ImageSvgUri
height={80}
width={80}
imageUrl={walletConfig.meta.iconURL}
/>
</WalletLoadingThumbnail>
<View style={styles.connectingContainer}>
<>
<Text variant="header" mt="lg">
{mismatch
? "Network Mismatch"
: `Connecting ${walletConfig.meta.name}`}
</Text>
{mismatch ? (
<Text variant="bodySmallSecondary" mt="lg" textAlign="center">
{
"There's a network mismatch between your contract and your wallet"
}
</Text>
) : null}
</>
</View>
</Box>
{mismatch === true ? (
<BaseButton
marginVertical="sm"
mt="md"
marginHorizontal="xl"
flexDirection="row"
justifyContent="flex-start"
alignItems="center"
paddingHorizontal="md"
paddingVertical="sm"
borderRadius="sm"
backgroundColor="backgroundHighlight"
onPress={onSwitchNetworkPress}
>
<Text variant="bodyLarge" textAlign="center">
Switch Network
</Text>
</Text>
}
wallets={walletObj.personalWallets || DEFAULT_WALLETS}
onChooseWallet={onChoosePersonalWallet}
onClose={onClose}
/>
</BaseButton>
) : null}
</>
);
};
const styles = StyleSheet.create({
connectingContainer: {
flexDirection: "column",
justifyContent: "center",
alignContent: "center",
alignItems: "center",
paddingHorizontal: 4,
marginBottom: 24,
marginTop: 18,
},
});
@@ -4,17 +4,15 @@ import { Dimensions, StyleSheet, View } from "react-native";
import { useMemo } from "react";
import { CLOSE_MODAL_STATE } from "../utils/modalTypes";
import { ThemeProvider } from "../styles/ThemeProvider";
import { useAppTheme } from "../styles/hooks";
import { SessionRequestModal } from "./ConnectWalletDetails/SessionRequestModal";
import { SessionProposalModal } from "./ConnectWalletDetails/SessionProposalModal";
import { TWModal } from "./base/modal/TWModal";
import Box from "./base/Box";
const MODAL_HEIGHT = Dimensions.get("window").height * 0.7;
const MODAL_HEIGHT = Dimensions.get("window").height * 0.6;
const DEVICE_WIDTH = Dimensions.get("window").width;
export const MainModal = () => {
const theme = useAppTheme();
const { modalState, setModalState } = useModalState();
const { isOpen, isSheet } = modalState;
@@ -39,14 +37,12 @@ export const MainModal = () => {
};
return (
<ThemeProvider theme={theme}>
<ThemeProvider>
<TWModal isVisible={isOpen} onBackdropPress={onBackdropPress}>
{isSheet ? (
<View
style={[styles.modal, { backgroundColor: theme.colors.background }]}
>
<Box backgroundColor="background" style={styles.modal}>
<View style={styles.contentContainer}>{view}</View>
</View>
</Box>
) : (
view
)}
@@ -63,8 +59,8 @@ const styles = StyleSheet.create({
width: DEVICE_WIDTH,
borderTopLeftRadius: 24,
borderTopRightRadius: 24,
padding: 24,
paddingBottom: 40,
paddingTop: 32,
paddingBottom: 28,
},
contentContainer: {
maxHeight: MODAL_HEIGHT,
@@ -31,6 +31,8 @@ export const PasswordInput = ({
secureTextEntry={!showPassword}
textContentType="none"
returnKeyType={"done"}
placeholder="Password"
placeholderTextColor={theme.colors.textSecondary}
clearTextOnFocus={false}
autoCapitalize="none"
autoCorrect={false}
@@ -0,0 +1,156 @@
import React, { useEffect, useState } from "react";
import { IconTextButton } from "./base/IconTextButton";
import { useAddress } from "@thirdweb-dev/react-core";
import { Dimensions, KeyboardAvoidingView } from "react-native";
import { useAppTheme } from "../styles/hooks";
import {
TWModal,
Box,
ModalHeaderTextClose,
Text,
AddressDisplay,
BaseButton,
Toast,
} from "./base";
import DownloadIcon from "../assets/download";
import CopyIcon from "../assets/copy";
import QRCode from "react-native-qrcode-svg";
// type SendButtonProps = {
// chain?: Chain;
// onPress?: () => void;
// switchChainOnPress?: boolean;
// onChainSwitched?: () => void;
// };
export const ReceiveButton = () => {
const theme = useAppTheme();
const [isModalVisible, setIsModalVisible] = useState(false);
const onClose = () => {
setIsModalVisible(false);
};
const onReceivePress = () => {
setIsModalVisible(true);
};
return (
<>
<IconTextButton
flex={1}
ml="xs"
text="Receive"
justifyContent="center"
icon={
<DownloadIcon
height={16}
width={16}
color={theme.colors.textPrimary}
/>
}
onPress={onReceivePress}
/>
<ReceiveFundsModal isVisible={isModalVisible} onClose={onClose} />
</>
);
};
const MODAL_HEIGHT = Dimensions.get("window").height * 0.7;
export type ReceiveFundsModalProps = {
isVisible: boolean;
onClose: () => void;
};
export const ReceiveFundsModal = ({
isVisible,
onClose,
}: ReceiveFundsModalProps) => {
const theme = useAppTheme();
const address = useAddress();
const [addressCopied, setAddressCopied] = useState(false);
useEffect(() => {
const timeout = setTimeout(() => {
if (addressCopied) {
setAddressCopied(false);
}
}, 2000);
return () => clearTimeout(timeout);
}, [addressCopied]);
const onCloseInternal = () => {
onClose();
};
const onAddressPress = () => {
setAddressCopied(true);
};
return (
<TWModal isVisible={isVisible} backdropOpacity={0.7}>
<KeyboardAvoidingView behavior="padding">
<Box
flexDirection="column"
backgroundColor="background"
maxHeight={MODAL_HEIGHT}
borderRadius="md"
p="lg"
>
<ModalHeaderTextClose
onBackPress={onCloseInternal}
headerText="Receive Funds"
/>
<Text
variant="bodySmallSecondary"
textAlign="center"
mt="md"
marginHorizontal="xmd"
>
Paste the wallet address or scan the QR code to send funds to this
wallet.
</Text>
<Text mt="lg" variant="bodySmallSecondary">
Current Network
</Text>
<BaseButton
mt="xs"
flexDirection="row"
alignItems="center"
justifyContent="space-between"
borderColor="border"
borderWidth={1}
borderRadius="md"
onPress={onAddressPress}
p="md"
>
<AddressDisplay address={address} variant="bodySmall" />
<CopyIcon width={14} height={14} color={theme.colors.iconPrimary} />
</BaseButton>
<Text mt="lg" variant="bodySmallSecondary">
QR Code
</Text>
<Box
mt="xs"
flexDirection="row"
alignItems="center"
justifyContent="center"
borderColor="border"
backgroundColor="white"
borderWidth={1}
borderRadius="md"
p="md"
>
<QRCode value={address} size={150} />
</Box>
{addressCopied === true ? (
<Toast text={"Address copied to clipboard"} />
) : null}
</Box>
</KeyboardAvoidingView>
</TWModal>
);
};
@@ -0,0 +1,42 @@
import { useBalance, useChain } from "@thirdweb-dev/react-core";
import { TokenInfo } from "./defaultTokens";
import { BaseButton, Box, ChainIcon, ImageSvgUri, Text } from "../base";
import LoadingTextAnimation from "../base/LoadingTextAnimation";
export function SelectTokenButton(props: {
token?: TokenInfo;
onPress: () => void;
}) {
const balanceQuery = useBalance(props.token?.address);
const chain = useChain();
const tokenName = props.token?.name || balanceQuery.data?.name;
return (
<BaseButton
flexDirection="row"
alignItems="center"
onPress={props.onPress}
mb="md"
>
{props.token?.icon ? (
<ImageSvgUri width={32} height={32} imageUrl={props.token.icon} />
) : (
<ChainIcon chainIconUrl={chain?.icon?.url} size={32} active={false} />
)}
<Box ml="md" alignItems="flex-start" justifyContent="center" height={36}>
<Text variant="bodyLarge">{tokenName}</Text>
{!balanceQuery.data ? (
<LoadingTextAnimation
text="Fetching..."
textVariant={"bodySmallSecondary"}
/>
) : (
<Text variant="bodySmallSecondary" fontSize={12}>
{Number(balanceQuery.data?.displayValue).toFixed(3)}{" "}
{balanceQuery.data.symbol}
</Text>
)}
</Box>
</BaseButton>
);
}
@@ -0,0 +1,360 @@
import { useAppTheme } from "../../styles/hooks";
import React, { useEffect, useMemo, useState } from "react";
import SendIcon from "../../assets/send";
import {
BaseButton,
Box,
ChainIcon,
IconTextButton,
ImageSvgUri,
ModalHeaderTextClose,
TWModal,
Text,
} from "../base";
import {
ActivityIndicator,
Dimensions,
KeyboardAvoidingView,
TextInput as TextInputRN,
} from "react-native";
import {
TransactionResult,
useBalance,
useChain,
useChainId,
useWallet,
} from "@thirdweb-dev/react-core";
import { SupportedTokens, TokenInfo } from "./defaultTokens";
import { useMutation } from "@tanstack/react-query";
import { utils } from "ethers";
import LoadingTextAnimation from "../base/LoadingTextAnimation";
import CheckIcon from "../../assets/check";
import { TokenSelector } from "./TokenSelector";
export const SendButton = ({
supportedTokens,
}: {
supportedTokens: SupportedTokens;
}) => {
const theme = useAppTheme();
const [isModalVisible, setIsModalVisible] = useState(false);
const onClose = () => {
setIsModalVisible(false);
};
const onSendPress = () => {
setIsModalVisible(true);
};
return (
<>
<IconTextButton
flex={1}
text="Send"
justifyContent="center"
icon={
<SendIcon height={16} width={16} color={theme.colors.textPrimary} />
}
onPress={onSendPress}
/>
<SendFundsModal
isVisible={isModalVisible}
onClose={onClose}
supportedTokens={supportedTokens}
/>
</>
);
};
const MODAL_HEIGHT = Dimensions.get("window").height * 0.7;
type TXError = Error & { data?: { message?: string } };
export type SendFundsModalProps = {
isVisible: boolean;
onClose: () => void;
supportedTokens: SupportedTokens;
};
export const SendFundsModal = ({
isVisible,
onClose,
supportedTokens,
}: SendFundsModalProps) => {
const [token, setToken] = useState<TokenInfo | undefined>();
const [showTokenSelector, setShowTokenSelector] = useState(false);
const onTokenSelectorPress = () => {
setShowTokenSelector(true);
};
return (
<TWModal isVisible={isVisible} backdropOpacity={0.7}>
<KeyboardAvoidingView behavior="padding">
{showTokenSelector ? (
<TokenSelector
supportedTokens={supportedTokens}
onBack={() => {
setShowTokenSelector(false);
}}
onTokenSelect={(_token) => {
setToken(_token);
setShowTokenSelector(false);
}}
/>
) : (
<SendFundsForm
onClose={onClose}
token={token}
onTokenSelectorPress={onTokenSelectorPress}
/>
)}
</KeyboardAvoidingView>
</TWModal>
);
};
const SendFundsForm = ({
onClose,
onTokenSelectorPress,
token,
}: {
onClose: () => void;
onTokenSelectorPress: () => void;
token?: TokenInfo;
}) => {
const theme = useAppTheme();
const chain = useChain();
const wallet = useWallet();
const chainId = useChainId();
const [receiverAddress, setReceiverAddress] = useState("");
const [amount, setAmount] = useState("");
const [showIcon, setShowIcon] = useState(false);
const tokenAddress = token?.address;
const balanceQuery = useBalance(
tokenAddress && utils.isAddress(tokenAddress) ? tokenAddress : undefined,
);
const tokenName = token?.name || balanceQuery?.data?.name;
const tokenSymbol = token?.symbol || balanceQuery.data?.symbol;
// Ethereum or Rinkeby or Goerli
const isENSSupprted = chainId === 1 || chainId === 5 || chainId === 4;
const isValidReceieverAddress = useMemo(() => {
const isENS = receiverAddress.endsWith(".eth");
if (!isENSSupprted && isENS) {
return false;
}
return isENS || utils.isAddress(receiverAddress);
}, [receiverAddress, isENSSupprted]);
const showInvalidAddressError = receiverAddress && !isValidReceieverAddress;
const sendTokenMutation = useMutation<TransactionResult | undefined, TXError>(
async () => {
if (!wallet) {
return;
}
return wallet.transfer(receiverAddress, amount, tokenAddress);
},
);
useEffect(() => {
if (sendTokenMutation.isSuccess) {
setShowIcon(true);
setTimeout(() => {
setShowIcon(false);
}, 1000);
}
}, [sendTokenMutation.isSuccess]);
const onCloseInternal = () => {
onClose();
};
function getErrorMessage(error?: TXError) {
const message = error?.data?.message || error?.message;
if (!message) {
return "Transaction Failed";
}
if (message.includes("user rejected")) {
return "Transaction Rejected";
}
if (message.includes("insufficient funds")) {
return "Insufficient funds";
}
return "Transaction Failed";
}
return (
<Box
flexDirection="column"
backgroundColor="background"
maxHeight={MODAL_HEIGHT}
borderRadius="md"
p="lg"
>
<ModalHeaderTextClose
onBackPress={onCloseInternal}
headerText="Send Funds"
/>
<Text mt="lg" variant="bodySmallSecondary">
Select Token
</Text>
<BaseButton
mt="xs"
borderRadius="md"
borderWidth={0.5}
paddingHorizontal="md"
paddingVertical="sm"
flexDirection="row"
alignItems="center"
justifyContent="space-between"
borderColor="border"
onPress={onTokenSelectorPress}
>
<Box flexDirection="row" alignItems="center">
{token?.icon ? (
<ImageSvgUri width={32} height={32} imageUrl={token.icon} />
) : (
<ChainIcon
chainIconUrl={chain?.icon?.url}
size={32}
active={false}
/>
)}
<Box
ml="md"
alignItems="flex-start"
justifyContent="center"
height={36}
>
<Text variant="bodySmall">{tokenName}</Text>
{!balanceQuery.data ? (
<LoadingTextAnimation
text="Fetching..."
textVariant={"bodySmallSecondary"}
/>
) : (
<Text variant="bodySmallSecondary" fontSize={12}>
{Number(balanceQuery.data?.displayValue).toFixed(3)}{" "}
{balanceQuery.data.symbol}
</Text>
)}
</Box>
</Box>
</BaseButton>
<Text mt="md" variant="bodySmallSecondary">
Send to
</Text>
<Box
mt="xs"
flexDirection="row"
alignItems="center"
justifyContent="space-between"
borderColor={showInvalidAddressError ? "red" : "border"}
borderWidth={1}
borderRadius="md"
pr="xs"
>
<TextInputRN
editable={!sendTokenMutation.isLoading}
returnKeyType={"done"}
clearTextOnFocus={false}
onChangeText={setReceiverAddress}
style={{
color: theme.colors.textPrimary,
textAlign: "left",
flex: 1,
height: 40,
paddingHorizontal: 16,
}}
value={receiverAddress}
placeholder={isENSSupprted ? `0x... / ENS name` : "0x..."}
placeholderTextColor={theme.colors.textSecondary}
autoCapitalize="none"
autoCorrect={false}
/>
</Box>
<Text mt="md" variant="bodySmallSecondary">
Amount
</Text>
<Box
mt="xs"
flexDirection="row"
alignItems="center"
justifyContent="space-between"
borderColor="border"
borderWidth={1}
borderRadius="md"
pr="xs"
>
<TextInputRN
editable={!sendTokenMutation.isLoading}
returnKeyType={"done"}
keyboardType="numeric"
clearTextOnFocus={false}
style={{
color: theme.colors.textPrimary,
textAlign: "left",
flex: 1,
height: 40,
paddingHorizontal: 16,
}}
onChangeText={setAmount}
placeholder="0"
placeholderTextColor={theme.colors.textPrimary}
autoCapitalize="none"
autoCorrect={false}
/>
<Text variant="bodySmallSecondary">{tokenSymbol}</Text>
</Box>
<BaseButton
mt="md"
backgroundColor="accentButtonColor"
flexDirection="row"
alignItems="center"
justifyContent="center"
alignContent="center"
borderRadius="md"
borderWidth={0.5}
paddingVertical="md"
onPress={async () => {
if (!receiverAddress || !wallet || !amount) {
return;
}
await sendTokenMutation.mutateAsync();
}}
>
{sendTokenMutation.isLoading ? (
<ActivityIndicator size="small" color={theme.colors.textPrimary} />
) : showIcon ? (
<CheckIcon width={20} height={13} color={theme.colors.textPrimary} />
) : (
<Text variant="bodySmall" mr="sm">
Send
</Text>
)}
</BaseButton>
{sendTokenMutation.isError && (
<Text mt="sm" variant="error">
{getErrorMessage(sendTokenMutation.error || undefined)}
</Text>
)}
</Box>
);
};
@@ -0,0 +1,206 @@
import {
NATIVE_TOKEN_ADDRESS,
useBalance,
useChain,
useChainId,
} from "@thirdweb-dev/react-core";
import React, { useState } from "react";
import { TokenInfo, SupportedTokens } from "./defaultTokens";
import { utils } from "ethers";
import Box from "../base/Box";
import { ModalHeaderTextClose } from "../base/modal/ModalHeaderTextClose";
import {
ActivityIndicator,
Dimensions,
ScrollView,
TextInput,
} from "react-native";
import { useAppTheme } from "../../styles/hooks";
import { SelectTokenButton } from "./SelectTokenButton";
import CloseIcon from "../../assets/close";
import Text from "../base/Text";
export function useToken(tokenAddress: string): {
isLoading: boolean;
data: TokenInfo | undefined;
} {
const balanceQuery = useBalance(
utils.isAddress(tokenAddress) ? tokenAddress : undefined,
);
const chain = useChain();
if (!utils.isAddress(tokenAddress)) {
return {
isLoading: false,
data: undefined,
};
}
if (balanceQuery.isLoading) {
return {
isLoading: true,
data: undefined,
};
}
if (balanceQuery.data) {
return {
isLoading: false,
data: {
name: balanceQuery.data.name,
symbol: balanceQuery.data.symbol,
address: tokenAddress,
icon: chain?.icon?.url || "",
},
};
}
return {
isLoading: false,
data: undefined,
};
}
function useNativeToken(): TokenInfo | undefined {
const balanceQuery = useBalance();
const chain = useChain();
if (balanceQuery.data) {
return {
name: balanceQuery.data.name,
symbol: balanceQuery.data.symbol,
address: NATIVE_TOKEN_ADDRESS,
icon: chain?.icon?.url || "",
};
}
}
const MODAL_HEIGHT = Dimensions.get("window").height * 0.5;
export function TokenSelector(props: {
onTokenSelect: (token?: TokenInfo) => void;
onBack: () => void;
supportedTokens: SupportedTokens;
}) {
const theme = useAppTheme();
const [input, setInput] = useState("");
const chainId = useChainId();
const nativeTokenInfo = useNativeToken();
const { data: foundToken, isLoading: findingToken } = useToken(input);
let tokenList = (chainId ? props.supportedTokens[chainId] : undefined) || [];
if (nativeTokenInfo) {
tokenList = [nativeTokenInfo, ...tokenList];
}
if (foundToken) {
tokenList = [foundToken, ...tokenList];
}
const filteredList = input
? tokenList.filter((t) => {
const inputStr = input.toLowerCase();
return (
t.name.toLowerCase().includes(inputStr) ||
t.symbol.toLowerCase().includes(inputStr) ||
t.address.includes(input)
);
})
: tokenList;
return (
<Box
flexDirection="column"
backgroundColor="background"
maxHeight={MODAL_HEIGHT}
borderRadius="md"
p="lg"
>
<Box>
<ModalHeaderTextClose
onBackPress={props.onBack}
headerText="Select a Token"
/>
<Box
mt="xs"
mb="md"
flexDirection="row"
alignItems="center"
justifyContent="space-between"
borderColor="border"
borderWidth={1}
borderRadius="md"
pr="xs"
>
<TextInput
returnKeyType={"done"}
clearTextOnFocus={false}
onChangeText={setInput}
style={{
color: theme.colors.textPrimary,
textAlign: "left",
flex: 1,
height: 40,
paddingHorizontal: 16,
}}
placeholder="Search or paste token address"
placeholderTextColor={theme.colors.textSecondary}
autoCapitalize="none"
autoCorrect={false}
/>
</Box>
</Box>
{filteredList.length > 0 && (
<Box
flexDirection="column"
paddingTop="none"
maxHeight={MODAL_HEIGHT - 150}
>
<ScrollView>
{filteredList.map((token) => {
return (
<SelectTokenButton
onPress={() => props.onTokenSelect(token)}
token={token}
key={token.address}
/>
);
})}
</ScrollView>
</Box>
)}
{findingToken && (
<Box
p="lg"
flexDirection="column"
gap="md"
alignItems="center"
justifyContent="center"
minHeight={150}
paddingTop="none"
>
<ActivityIndicator size="large" color={theme.colors.textSecondary} />
</Box>
)}
{filteredList.length === 0 && !findingToken && (
<Box
p="lg"
flexDirection="column"
gap="md"
alignItems="center"
justifyContent="center"
minHeight={150}
paddingTop="none"
>
<CloseIcon width={16} height={16} />
<Text variant="bodySmall">No Tokens found</Text>
</Box>
)}
</Box>
);
}
@@ -0,0 +1,306 @@
export type TokenInfo = {
name: string;
symbol: string;
address: string;
icon: string;
};
const wrappedEthIcon =
'<svg width="2500" height="2500" viewBox="0 0 32 32"><g fill="none" fill-rule="evenodd"><circle cx="16" cy="16" r="16" fill="#627EEA"/><g fill="#FFF" fill-rule="nonzero"><path fill-opacity=".602" d="M16.498 4v8.87l7.497 3.35z"/><path d="M16.498 4L9 16.22l7.498-3.35z"/><path fill-opacity=".602" d="M16.498 21.968v6.027L24 17.616z"/><path d="M16.498 27.995v-6.028L9 17.616z"/><path fill-opacity=".2" d="M16.498 20.573l7.497-4.353-7.497-3.348z"/><path fill-opacity=".602" d="M9 16.22l7.498 4.353v-7.701z"/></g></g></svg>';
const tetherUsdIcon =
'<svg id="Layer_1" data-name="Layer 1" viewBox="0 0 339.43 295.27"><title>tether-usdt-logo</title><path d="M62.15,1.45l-61.89,130a2.52,2.52,0,0,0,.54,2.94L167.95,294.56a2.55,2.55,0,0,0,3.53,0L338.63,134.4a2.52,2.52,0,0,0,.54-2.94l-61.89-130A2.5,2.5,0,0,0,275,0H64.45a2.5,2.5,0,0,0-2.3,1.45h0Z" style="fill:#50af95;fill-rule:evenodd"/><path d="M191.19,144.8v0c-1.2.09-7.4,0.46-21.23,0.46-11,0-18.81-.33-21.55-0.46v0c-42.51-1.87-74.24-9.27-74.24-18.13s31.73-16.25,74.24-18.15v28.91c2.78,0.2,10.74.67,21.74,0.67,13.2,0,19.81-.55,21-0.66v-28.9c42.42,1.89,74.08,9.29,74.08,18.13s-31.65,16.24-74.08,18.12h0Zm0-39.25V79.68h59.2V40.23H89.21V79.68H148.4v25.86c-48.11,2.21-84.29,11.74-84.29,23.16s36.18,20.94,84.29,23.16v82.9h42.78V151.83c48-2.21,84.12-11.73,84.12-23.14s-36.09-20.93-84.12-23.15h0Zm0,0h0Z" style="fill:#fff;fill-rule:evenodd"/></svg>';
const usdcIcon = `<svg data-name="86977684-12db-4850-8f30-233a7c267d11" viewBox="0 0 2000 2000">
<path d="M1000 2000c554.17 0 1000-445.83 1000-1000S1554.17 0 1000 0 0 445.83 0 1000s445.83 1000 1000 1000z" fill="#2775ca"/>
<path d="M1275 1158.33c0-145.83-87.5-195.83-262.5-216.66-125-16.67-150-50-150-108.34s41.67-95.83 125-95.83c75 0 116.67 25 137.5 87.5 4.17 12.5 16.67 20.83 29.17 20.83h66.66c16.67 0 29.17-12.5 29.17-29.16v-4.17c-16.67-91.67-91.67-162.5-187.5-170.83v-100c0-16.67-12.5-29.17-33.33-33.34h-62.5c-16.67 0-29.17 12.5-33.34 33.34v95.83c-125 16.67-204.16 100-204.16 204.17 0 137.5 83.33 191.66 258.33 212.5 116.67 20.83 154.17 45.83 154.17 112.5s-58.34 112.5-137.5 112.5c-108.34 0-145.84-45.84-158.34-108.34-4.16-16.66-16.66-25-29.16-25h-70.84c-16.66 0-29.16 12.5-29.16 29.17v4.17c16.66 104.16 83.33 179.16 220.83 200v100c0 16.66 12.5 29.16 33.33 33.33h62.5c16.67 0 29.17-12.5 33.34-33.33v-100c125-20.84 208.33-108.34 208.33-220.84z" fill="#fff"/>
<path d="M787.5 1595.83c-325-116.66-491.67-479.16-370.83-800 62.5-175 200-308.33 370.83-370.83 16.67-8.33 25-20.83 25-41.67V325c0-16.67-8.33-29.17-25-33.33-4.17 0-12.5 0-16.67 4.16-395.83 125-612.5 545.84-487.5 941.67 75 233.33 254.17 412.5 487.5 487.5 16.67 8.33 33.34 0 37.5-16.67 4.17-4.16 4.17-8.33 4.17-16.66v-58.34c0-12.5-12.5-29.16-25-37.5zM1229.17 295.83c-16.67-8.33-33.34 0-37.5 16.67-4.17 4.17-4.17 8.33-4.17 16.67v58.33c0 16.67 12.5 33.33 25 41.67 325 116.66 491.67 479.16 370.83 800-62.5 175-200 308.33-370.83 370.83-16.67 8.33-25 20.83-25 41.67V1700c0 16.67 8.33 29.17 25 33.33 4.17 0 12.5 0 16.67-4.16 395.83-125 612.5-545.84 487.5-941.67-75-237.5-258.34-416.67-487.5-491.67z" fill="#fff"/>
</svg>
`;
const wrappedBtcIcon =
'<svg viewBox="0 0 109.26 109.26"><title>wrapped-bitcoin-wbtc</title><g id="Layer_2" data-name="Layer 2"><g id="Layer_1-2" data-name="Layer 1"><g id="Page-1"><g id="wbtc_colour" data-name="wbtc colour"><path id="Shape" fill="#5a5564" d="M89.09,22.93l-3,3a42.47,42.47,0,0,1,0,57.32l3,3a46.76,46.76,0,0,0,0-63.39Z"/><path id="Shape-2" data-name="Shape" fill="#5a5564" d="M26,23.19a42.47,42.47,0,0,1,57.32,0l3-3a46.76,46.76,0,0,0-63.39,0Z"/><path id="Shape-3" data-name="Shape" fill="#5a5564" d="M23.19,83.28a42.47,42.47,0,0,1,0-57.29l-3-3a46.76,46.76,0,0,0,0,63.39Z"/><path id="Shape-4" data-name="Shape" fill="#5a5564" d="M83.28,86.05a42.47,42.47,0,0,1-57.32,0l-3,3a46.76,46.76,0,0,0,63.39,0Z"/><path id="Shape-5" data-name="Shape" fill="#f09242" d="M73.57,44.62c-.6-6.26-6-8.36-12.83-9V27H55.46v8.46c-1.39,0-2.81,0-4.22,0V27H46v8.68H35.29v5.65s3.9-.07,3.84,0a2.73,2.73,0,0,1,3,2.32V67.41a1.85,1.85,0,0,1-.64,1.29,1.83,1.83,0,0,1-1.36.46c.07.06-3.84,0-3.84,0l-1,6.31H45.9v8.82h5.28V75.6H55.4v8.65h5.29V75.53c8.92-.54,15.14-2.74,15.92-11.09.63-6.72-2.53-9.72-7.58-10.93C72.1,52,74,49.2,73.57,44.62ZM66.17,63.4c0,6.56-11.24,5.81-14.82,5.81V57.57C54.93,57.58,66.17,56.55,66.17,63.4ZM63.72,47c0,6-9.38,5.27-12.36,5.27V41.69C54.34,41.69,63.72,40.75,63.72,47Z"/><path id="Shape-6" data-name="Shape" fill="#282138" d="M54.62,109.26a54.63,54.63,0,1,1,54.64-54.64A54.63,54.63,0,0,1,54.62,109.26Zm0-105A50.34,50.34,0,1,0,105,54.62,50.34,50.34,0,0,0,54.62,4.26Z"/></g></g></g></g></svg>';
const maticIcon = `<svg width="1024" height="1024" viewBox="0 0 1024 1024" fill="none" >
<circle cx="512" cy="512" r="512" fill="#8247E5"/>
<path d="M681.469 402.456C669.189 395.312 653.224 395.312 639.716 402.456L543.928 457.228L478.842 492.949L383.055 547.721C370.774 554.865 354.81 554.865 341.301 547.721L265.162 504.856C252.882 497.712 244.286 484.614 244.286 470.325V385.786C244.286 371.498 251.654 358.4 265.162 351.256L340.073 309.581C352.353 302.437 368.318 302.437 381.827 309.581L456.737 351.256C469.018 358.4 477.614 371.498 477.614 385.786V440.558L542.7 403.646V348.874C542.7 334.586 535.332 321.488 521.824 314.344L383.055 235.758C370.774 228.614 354.81 228.614 341.301 235.758L200.076 314.344C186.567 321.488 179.199 334.586 179.199 348.874V507.237C179.199 521.525 186.567 534.623 200.076 541.767L341.301 620.353C353.582 627.498 369.546 627.498 383.055 620.353L478.842 566.772L543.928 529.86L639.716 476.279C651.996 469.135 667.961 469.135 681.469 476.279L756.38 517.953C768.66 525.098 777.257 538.195 777.257 552.484V637.023C777.257 651.312 769.888 664.409 756.38 671.553L681.469 714.419C669.189 721.563 653.224 721.563 639.716 714.419L564.805 672.744C552.525 665.6 543.928 652.502 543.928 638.214V583.442L478.842 620.353V675.125C478.842 689.414 486.21 702.512 499.719 709.656L640.944 788.242C653.224 795.386 669.189 795.386 682.697 788.242L823.922 709.656C836.203 702.512 844.799 689.414 844.799 675.125V516.763C844.799 502.474 837.431 489.377 823.922 482.232L681.469 402.456Z" fill="white"/>
</svg>
`;
const binanceCoinIcon =
'<svg width="32" height="32"><g fill="none"><circle cx="16" cy="16" r="16" fill="#F3BA2F"/><path fill="#FFF" d="M12.116 14.404L16 10.52l3.886 3.886 2.26-2.26L16 6l-6.144 6.144 2.26 2.26zM6 16l2.26-2.26L10.52 16l-2.26 2.26L6 16zm6.116 1.596L16 21.48l3.886-3.886 2.26 2.259L16 26l-6.144-6.144-.003-.003 2.263-2.257zM21.48 16l2.26-2.26L26 16l-2.26 2.26L21.48 16zm-3.188-.002h.002v.002L16 18.294l-2.291-2.29-.004-.004.004-.003.401-.402.195-.195L16 13.706l2.293 2.293z"/></g></svg>';
const BUSDIcon =
'<svg viewBox="0 0 336.41 337.42"><title>Asset 1</title><g id="Layer_2" data-name="Layer 2"><g id="Layer_1-2" data-name="Layer 1"><path fill="#f0b90b" stroke="#f0b90b" d="M168.2.71l41.5,42.5L105.2,147.71l-41.5-41.5Z"/><path fill="#f0b90b" stroke="#f0b90b" d="M231.2,63.71l41.5,42.5L105.2,273.71l-41.5-41.5Z"/><path fill="#f0b90b" stroke="#f0b90b" d="M42.2,126.71l41.5,42.5-41.5,41.5L.7,169.21Z"/><path fill="#f0b90b" stroke="#f0b90b" d="M294.2,126.71l41.5,42.5L168.2,336.71l-41.5-41.5Z"/></g></g></svg>';
const fantomIcon =
'<svg viewBox="0 0 32 32"><defs><mask id="mask" x="10" y="6" width="93.1" height="20" maskUnits="userSpaceOnUse"><g id="a"><path fill="#fff" fill-rule="evenodd" d="M10,6h93.1V26H10Z"/></g></mask></defs><title>fa</title><g id="Layer_2" data-name="Layer 2"><g id="Layer_1-2" data-name="Layer 1"><circle fill="#13b5ec" cx="16" cy="16" r="16"/><g mask="url(#mask)"><path fill="#fff" fill-rule="evenodd" d="M17.2,12.9l3.6-2.1V15Zm3.6,9L16,24.7l-4.8-2.8V17L16,19.8,20.8,17ZM11.2,10.8l3.6,2.1L11.2,15Zm5.4,3.1L20.2,16l-3.6,2.1Zm-1.2,4.2L11.8,16l3.6-2.1Zm4.8-8.3L16,12.2,11.8,9.8,16,7.3ZM10,9.4V22.5l6,3.4,6-3.4V9.4L16,6Z"/></g></g></g></svg>';
export type SupportedTokens = Record<number, TokenInfo[]>;
export const defaultTokens: SupportedTokens = {
"1": [
{
address: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
name: "Wrapped Ether",
symbol: "WETH",
icon: wrappedEthIcon,
},
{
address: "0xdAC17F958D2ee523a2206206994597C13D831ec7",
name: "Tether USD",
symbol: "USDT",
icon: tetherUsdIcon,
},
{
address: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
name: "USD Coin",
symbol: "USDC",
icon: usdcIcon,
},
{
address: "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599",
name: "Wrapped Bitcoin",
symbol: "WBTC",
icon: wrappedBtcIcon,
},
{
address: "0x7D1AfA7B718fb893dB30A3aBc0Cfc608AaCfeBB0",
name: "Polygon",
symbol: "WMATIC",
icon: maticIcon,
},
],
"5": [
{
address: "0xb4fbf271143f4fbf7b91a5ded31805e42b2208d6",
name: "Wrapped Ether",
symbol: "WETH",
icon: wrappedEthIcon,
},
{
address: "0x07865c6E87B9F70255377e024ace6630C1Eaa37F",
name: "USD Coin",
symbol: "USDC",
icon: usdcIcon,
},
],
"10": [
{
address: "0x4200000000000000000000000000000000000006",
name: "Wrapped Ether",
symbol: "WETH",
icon: wrappedEthIcon,
},
],
"56": [
{
address: "0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c",
name: "Wrapped Binance Chain Token",
symbol: "WBNB",
icon: binanceCoinIcon,
},
{
address: "0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56",
name: "Binance USD",
symbol: "BUSD",
icon: BUSDIcon,
},
],
"97": [
{
address: "0xae13d989daC2f0dEbFf460aC112a837C89BAa7cd",
name: "Wrapped Binance Chain Testnet Token",
symbol: "WBNB",
icon: binanceCoinIcon,
},
{
address: "0xed24fc36d5ee211ea25a80239fb8c4cfd80f12ee",
name: "Binance USD",
symbol: "BUSD",
icon: BUSDIcon,
},
],
"137": [
{
address: "0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270",
name: "Wrapped Matic",
symbol: "WMATIC",
icon: maticIcon,
},
{
address: "0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619",
name: "Wrapped Ether",
symbol: "WETH",
icon: wrappedEthIcon,
},
{
address: "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174",
name: "USD Coin",
symbol: "USDC",
icon: usdcIcon,
},
{
address: "0xc2132D05D31c914a87C6611C10748AEb04B58e8F",
name: "Tether USD",
symbol: "USDT",
icon: tetherUsdIcon,
},
{
address: "0x1BFD67037B42Cf73acF2047067bd4F2C47D9BfD6",
name: "Wrapped BTC",
symbol: "WBTC",
icon: wrappedBtcIcon,
},
],
"250": [
{
address: "0x21be370D5312f44cB42ce377BC9b8a0cEF1A4C83",
name: "Wrapped Fantom",
symbol: "WFTM",
icon: fantomIcon,
},
{
name: "Wrapped Ether",
address: "0x74b23882a30290451A17c44f4F05243b6b58C76d",
symbol: "WETH",
icon: wrappedEthIcon,
},
{
name: "USD Coin",
address: "0x04068DA6C83AFCFA0e13ba15A6696662335D5B75",
symbol: "USDC",
icon: usdcIcon,
},
{
name: "Wrapped Bitcoin",
address: "0x321162Cd933E2Be498Cd2267a90534A804051b11",
symbol: "WBTC",
icon: wrappedBtcIcon,
},
],
"420": [
{
address: "0x4200000000000000000000000000000000000006",
name: "Wrapped Ether",
symbol: "WETH",
icon: wrappedEthIcon,
},
],
"4002": [
{
address: "0xf1277d1Ed8AD466beddF92ef448A132661956621",
name: "Wrapped Fantom",
symbol: "WFTM",
icon: fantomIcon,
},
],
"42161": [
{
address: "0x82af49447d8a07e3bd95bd0d56f35241523fbab1",
name: "Wrapped Ether",
symbol: "WETH",
icon: wrappedEthIcon,
},
{
address: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831",
name: "USD Coin",
symbol: "USDC",
icon: usdcIcon,
},
],
"43113": [
{
address: "0xd00ae08403B9bbb9124bB305C09058E32C39A48c",
name: "Wrapped AVAX",
symbol: "WAVAX",
icon: `<svg width="1503" height="1504" viewBox="0 0 1503 1504" fill="none">
<rect x="287" y="258" width="928" height="844" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M1502.5 752C1502.5 1166.77 1166.27 1503 751.5 1503C336.734 1503 0.5 1166.77 0.5 752C0.5 337.234 336.734 1 751.5 1C1166.27 1 1502.5 337.234 1502.5 752ZM538.688 1050.86H392.94C362.314 1050.86 347.186 1050.86 337.962 1044.96C327.999 1038.5 321.911 1027.8 321.173 1015.99C320.619 1005.11 328.184 991.822 343.312 965.255L703.182 330.935C718.495 303.999 726.243 290.531 736.021 285.55C746.537 280.2 759.083 280.2 769.599 285.55C779.377 290.531 787.126 303.999 802.438 330.935L876.42 460.079L876.797 460.738C893.336 489.635 901.723 504.289 905.385 519.669C909.443 536.458 909.443 554.169 905.385 570.958C901.695 586.455 893.393 601.215 876.604 630.549L687.573 964.702L687.084 965.558C670.436 994.693 661.999 1009.46 650.306 1020.6C637.576 1032.78 622.263 1041.63 605.474 1046.62C590.161 1050.86 573.004 1050.86 538.688 1050.86ZM906.75 1050.86H1115.59C1146.4 1050.86 1161.9 1050.86 1171.13 1044.78C1181.09 1038.32 1187.36 1027.43 1187.92 1015.63C1188.45 1005.1 1181.05 992.33 1166.55 967.307C1166.05 966.455 1165.55 965.588 1165.04 964.706L1060.43 785.75L1059.24 783.735C1044.54 758.877 1037.12 746.324 1027.59 741.472C1017.08 736.121 1004.71 736.121 994.199 741.472C984.605 746.453 976.857 759.552 961.544 785.934L857.306 964.891L856.949 965.507C841.69 991.847 834.064 1005.01 834.614 1015.81C835.352 1027.62 841.44 1038.5 851.402 1044.96C860.443 1050.86 875.94 1050.86 906.75 1050.86Z" fill="#E84142"/>
</svg>
`,
},
{
address: "0x5425890298aed601595a70AB815c96711a31Bc65",
name: "USD Coin",
symbol: "USDC",
icon: usdcIcon,
},
],
"43114": [
{
address: "0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7",
name: "Wrapped AVAX",
symbol: "WAVAX",
icon: `<svg width="1503" height="1504" viewBox="0 0 1503 1504" fill="none">
<rect x="287" y="258" width="928" height="844" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M1502.5 752C1502.5 1166.77 1166.27 1503 751.5 1503C336.734 1503 0.5 1166.77 0.5 752C0.5 337.234 336.734 1 751.5 1C1166.27 1 1502.5 337.234 1502.5 752ZM538.688 1050.86H392.94C362.314 1050.86 347.186 1050.86 337.962 1044.96C327.999 1038.5 321.911 1027.8 321.173 1015.99C320.619 1005.11 328.184 991.822 343.312 965.255L703.182 330.935C718.495 303.999 726.243 290.531 736.021 285.55C746.537 280.2 759.083 280.2 769.599 285.55C779.377 290.531 787.126 303.999 802.438 330.935L876.42 460.079L876.797 460.738C893.336 489.635 901.723 504.289 905.385 519.669C909.443 536.458 909.443 554.169 905.385 570.958C901.695 586.455 893.393 601.215 876.604 630.549L687.573 964.702L687.084 965.558C670.436 994.693 661.999 1009.46 650.306 1020.6C637.576 1032.78 622.263 1041.63 605.474 1046.62C590.161 1050.86 573.004 1050.86 538.688 1050.86ZM906.75 1050.86H1115.59C1146.4 1050.86 1161.9 1050.86 1171.13 1044.78C1181.09 1038.32 1187.36 1027.43 1187.92 1015.63C1188.45 1005.1 1181.05 992.33 1166.55 967.307C1166.05 966.455 1165.55 965.588 1165.04 964.706L1060.43 785.75L1059.24 783.735C1044.54 758.877 1037.12 746.324 1027.59 741.472C1017.08 736.121 1004.71 736.121 994.199 741.472C984.605 746.453 976.857 759.552 961.544 785.934L857.306 964.891L856.949 965.507C841.69 991.847 834.064 1005.01 834.614 1015.81C835.352 1027.62 841.44 1038.5 851.402 1044.96C860.443 1050.86 875.94 1050.86 906.75 1050.86Z" fill="#E84142"/>
</svg>
`,
},
{
address: "0x49D5c2BdFfac6CE2BFdB6640F4F80f226bc10bAB",
name: "Wrapped Ether",
symbol: "WETH",
icon: wrappedEthIcon,
},
{
address: "0xc7198437980c041c805A1EDcbA50c1Ce5db95118",
name: "Tether USD",
symbol: "USDT",
icon: tetherUsdIcon,
},
{
address: "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E",
name: "USD Coin",
symbol: "USDC",
icon: usdcIcon,
},
{
address: "0x50b7545627a5162F82A992c33b87aDc75187B218",
name: "Wrapped BTC",
symbol: "WBTC",
icon: wrappedBtcIcon,
},
],
"80001": [
{
address: "0x9c3C9283D3e44854697Cd22D3Faa240Cfb032889",
name: "Wrapped Matic",
symbol: "WMATIC",
icon: maticIcon,
},
{
name: "Wrapped Ether",
address: "0xa6fa4fb5f76172d178d61b04b0ecd319c5d1c0aa",
symbol: "WETH",
icon: wrappedEthIcon,
},
{
address: "0x0FA8781a83E46826621b3BC094Ea2A0212e71B23",
name: "USD Coin",
symbol: "USDC",
icon: usdcIcon,
},
{
name: "Tether USD",
address: "0x3813e82e6f7098b9583FC0F33a962D02018B6803",
symbol: "USDT",
icon: tetherUsdIcon,
},
],
"421613": [
{
address: "0xe39Ab88f8A4777030A534146A9Ca3B52bd5D43A3",
name: "Wrapped Ether",
symbol: "WETH",
icon: wrappedEthIcon,
},
{
address: "0xfd064A18f3BF249cf1f87FC203E90D8f650f2d63",
name: "USD Coin",
symbol: "USDC",
icon: usdcIcon,
},
],
};
@@ -18,6 +18,7 @@ import type { CallOverrides, ContractInterface } from "ethers";
import { PropsWithChildren, useEffect } from "react";
import { ActivityIndicator, StyleSheet } from "react-native";
import invariant from "tiny-invariant";
import { useUIContext } from "../providers/ui-context-provider";
type ActionFn = (contract: SmartContract) => Promise<any>;
@@ -82,11 +83,18 @@ export const Web3Button = <TAction extends ActionFn>({
const needToSwitchChain =
sdkChainId && walletChainId && sdkChainId !== walletChainId;
const connectionStatus = useConnectionStatus();
const setTheme = useUIContext().setTheme;
const queryClient = useQueryClient();
const { contract } = useContract(contractAddress, contractAbi || "custom");
useEffect(() => {
if (theme) {
setTheme(theme);
}
}, [setTheme, theme]);
const actionMutation = useMutation(
async () => {
invariant(contract, "contract is not ready yet");
@@ -153,7 +161,7 @@ export const Web3Button = <TAction extends ActionFn>({
}
return (
<ThemeProvider theme={theme}>
<ThemeProvider>
<BaseButton
backgroundColor="buttonBackgroundColor"
onPress={() => {
@@ -22,7 +22,11 @@ export const IconTextButton = ({
<BaseButton
p={p || "sm"}
backgroundColor="background"
style={styles.networkContainer}
borderRadius="md"
borderWidth={0.5}
flexDirection="row"
justifyContent="flex-start"
alignItems="center"
onPress={onPress}
borderColor="border"
{...args}
@@ -38,14 +42,6 @@ export const IconTextButton = ({
const styles = StyleSheet.create({
networkText: {
marginLeft: 16,
},
networkContainer: {
borderRadius: 8,
borderWidth: 0.5,
display: "flex",
flexDirection: "row",
justifyContent: "flex-start",
alignItems: "center",
marginLeft: 8,
},
});
@@ -1,7 +1,7 @@
import { useStorage } from "@thirdweb-dev/react-core";
import { useState } from "react";
import { Image } from "react-native";
import { SvgUri } from "react-native-svg";
import { SvgUri, SvgXml } from "react-native-svg";
import { isAppBundleIdPresentInGlobal } from "../../utils/global";
import Box from "./Box";
@@ -17,6 +17,12 @@ const ImageSvgUri = ({
imageAlt?: string;
}) => {
const storage = useStorage();
const [error, setError] = useState(false);
if (imageUrl.startsWith("<svg")) {
return <SvgXml width={width} height={height} xml={imageUrl} />;
}
const resolvedImageUrl = storage
? storage.resolveScheme(imageUrl) +
(isAppBundleIdPresentInGlobal()
@@ -24,8 +30,6 @@ const ImageSvgUri = ({
: "")
: imageUrl.replace("ipfs://", "https://ipfs.io/ipfs/");
const [error, setError] = useState(false);
if (!resolvedImageUrl || resolvedImageUrl === "") {
return null;
}
@@ -5,10 +5,11 @@ import { baseTheme } from "../../styles/theme";
type TextBalance = {
textVariant: keyof typeof baseTheme.textVariants;
tokenAddress?: string;
};
export const TextBalance = ({ textVariant }: TextBalance) => {
const balanceQuery = useBalance();
export const TextBalance = ({ textVariant, tokenAddress }: TextBalance) => {
const balanceQuery = useBalance(tokenAddress);
return !balanceQuery.data ? (
<LoadingTextAnimation text="Fetching..." textVariant={textVariant} />
@@ -2,12 +2,14 @@ import { StyleSheet, TextInput as TextInputRN } from "react-native";
import Box from "./Box";
import { useTheme } from "@shopify/restyle";
type TextInputProps = (typeof Box)["arguments"] & TextInputRN["props"];
type TextInputProps = {
containerProps?: (typeof Box)["arguments"];
textInputProps?: TextInputRN["props"];
};
export const TextInput = ({
onChangeText,
placeholder,
...props
containerProps,
textInputProps,
}: TextInputProps) => {
const theme = useTheme();
@@ -19,17 +21,15 @@ export const TextInput = ({
borderWidth={1}
borderRadius="md"
pr="xs"
{...props}
{...containerProps}
>
<TextInputRN
style={{ ...styles.textInput, color: theme.colors.textPrimary }}
returnKeyType={"done"}
clearTextOnFocus={false}
placeholder={placeholder}
autoCapitalize="none"
autoCorrect={false}
onChangeText={onChangeText}
{...props}
{...textInputProps}
/>
</Box>
);
@@ -40,6 +40,5 @@ const styles = StyleSheet.create({
textAlign: "left",
flex: 1,
height: 40,
paddingLeft: 5,
},
});
@@ -62,7 +62,6 @@ const styles = StyleSheet.create({
alignContent: "center",
alignItems: "center",
justifyContent: "center",
alignSelf: "stretch",
height: TOAST_HEIGHT,
marginHorizontal: 10,
padding: 10,
@@ -10,6 +10,7 @@ type WalletButtonProps = {
walletIconUrl: string;
name: string;
labelText?: string;
recommended?: boolean;
} & React.ComponentProps<typeof BaseButton>;
export const WalletButton = ({
@@ -18,6 +19,7 @@ export const WalletButton = ({
walletIconUrl,
name,
labelText,
recommended,
}: WalletButtonProps) => {
return (
<BaseButton
@@ -28,14 +30,15 @@ export const WalletButton = ({
paddingHorizontal="md"
paddingVertical="sm"
borderRadius="sm"
backgroundColor="backgroundHighlight"
backgroundColor="background"
onPress={onPress}
>
<Box flexDirection="row" justifyContent="flex-start" alignItems="center">
<ImageSvgUri imageUrl={walletIconUrl} width={32} height={32} />
<Text ml="md" variant="bodyLarge">
{name}
</Text>
<ImageSvgUri imageUrl={walletIconUrl} width={48} height={48} />
<Box ml="sm" alignItems="flex-start">
<Text variant="bodyLarge">{name}</Text>
{recommended ? <Text variant="link">Recommended</Text> : null}
</Box>
</Box>
{labelText ? <Label text={labelText} /> : <View />}
</BaseButton>
@@ -0,0 +1,53 @@
import { useAppTheme } from "../../styles/hooks";
import BaseButton from "./BaseButton";
import Box from "./Box";
import ImageSvgUri from "./ImageSvgUri";
import Text from "./Text";
type WalletButtonSquareProps = {
onPress: () => void;
walletIconUrl: string;
size: number;
name: string;
} & (typeof BaseButton)["arguments"];
export const WalletButtonSquare = ({
onPress,
walletIconUrl,
name,
size,
}: WalletButtonSquareProps) => {
const theme = useAppTheme();
const marginVertical = theme.spacing.xs;
return (
<BaseButton
flexDirection="column"
alignItems="center"
justifyContent="center"
marginVertical="xxs"
borderWidth={0}
borderRadius="sm"
width={size - marginVertical * 2}
height={size - marginVertical * 2}
onPress={onPress}
>
<Box borderWidth={0} borderRadius="lg">
<ImageSvgUri
imageUrl={walletIconUrl}
width={size * 0.6}
height={size * 0.6}
/>
</Box>
<Text
variant="bodySmallSecondary"
numberOfLines={1}
mt="xs"
fontSize={14}
lineHeight={16}
>
{name}
</Text>
</BaseButton>
);
};
@@ -1,3 +1,4 @@
import { View } from "react-native";
import { Icon } from "../../../assets/icon";
import { useAppTheme } from "../../../styles/hooks";
import Box from "../Box";
@@ -5,15 +6,17 @@ import Text from "../Text";
import { ReactNode } from "react";
interface ModalHeaderTextCloseProps {
onClose: () => void;
onClose?: () => void;
headerText?: ReactNode | string;
subHeaderText?: ReactNode | string;
onBackPress?: () => void;
}
export const ModalHeaderTextClose = ({
headerText,
subHeaderText,
onClose,
onBackPress,
...props
}: ModalHeaderTextCloseProps & React.ComponentProps<typeof Box>) => {
const theme = useAppTheme();
@@ -25,18 +28,31 @@ export const ModalHeaderTextClose = ({
justifyContent={headerText ? "space-between" : "flex-end"}
{...props}
>
{onBackPress ? (
<Icon
type="back"
width={16}
height={16}
onPress={onBackPress}
color={theme.colors.iconPrimary}
/>
) : null}
{typeof headerText === "string" ? (
<Text variant="header">{headerText}</Text>
) : (
headerText
)}
<Icon
type="close"
width={14}
height={14}
color={theme.colors.iconSecondary}
onPress={onClose}
/>
{onClose ? (
<Icon
type="close"
width={16}
height={16}
color={theme.colors.iconSecondary}
onPress={onClose}
/>
) : (
<View />
)}
</Box>
<Box flexDirection="row" justifyContent="space-between" mt="md">
{typeof subHeaderText === "string" ? (
@@ -16,6 +16,7 @@ import { ThemeProvider } from "../styles/ThemeProvider";
import { SafeAreaProvider } from "react-native-safe-area-context";
import { walletIds } from "@thirdweb-dev/wallets";
import { ThirdwebStorage } from "../../core/storage/storage";
import { useColorScheme } from "react-native";
interface ThirdwebProviderProps<TChains extends Chain[]>
extends Omit<
@@ -70,8 +71,16 @@ export const ThirdwebProvider = <
sdkOptions,
...restProps
}: PropsWithChildren<ThirdwebProviderProps<TChains>>) => {
const colorScheme = useColorScheme();
const coinbaseWalletObj = supportedWallets.find(
(w) => w.id === walletIds.coinbase,
);
useCoinbaseWalletListener(
!!supportedWallets.find((w) => w.id === walletIds.coinbase),
!!coinbaseWalletObj,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
coinbaseWalletObj?.config?.callbackURL,
);
const hasMagicConfig = useMemo(
@@ -102,7 +111,9 @@ export const ThirdwebProvider = <
{...sdkOptions}
{...restProps}
>
<ThemeProvider theme={theme}>
<ThemeProvider
theme={theme ? theme : colorScheme === "dark" ? "dark" : "light"}
>
<UIContextProvider>
{hasMagicConfig ? (
<SafeAreaProvider>
@@ -1,7 +1,7 @@
import { createContext, useContext, useMemo, useState } from "react";
import { ModalState } from "../utils/modalTypes";
import { ThemeProviderProps } from "../styles/ThemeProvider";
import { Theme, darkTheme, lightTheme } from "../styles/theme";
import { Theme, _darkTheme, _lightTheme } from "../styles/theme";
import { useAppTheme } from "../styles/hooks";
import { useTheme } from "@shopify/restyle";
import { PropsWithChildren } from "react";
@@ -22,7 +22,7 @@ const UIContext = createContext<UIContextType>({
caller: "init",
},
setModalState: () => undefined,
theme: darkTheme(),
theme: "light",
setTheme: () => undefined,
});
@@ -65,9 +65,9 @@ export const useModalState = (): {
const getThemeObj = (theme?: ThemeProviderProps["theme"]) => {
if (theme === "dark" || !theme) {
return darkTheme();
return _darkTheme;
} else if (theme === "light") {
return lightTheme();
return _lightTheme;
} else {
return theme;
}
@@ -3,6 +3,7 @@ import { ThemeProvider as ShopifyThemeProvider } from "@shopify/restyle";
import { ThirdwebThemeContext } from "@thirdweb-dev/react-core";
import { PropsWithChildren, useContext } from "react";
import { useColorScheme } from "react-native";
import { useUIContext } from "../providers/ui-context-provider";
export type ThemeProviderProps = {
theme?: Theme | "light" | "dark";
@@ -14,7 +15,9 @@ export function ThemeProvider({
}: PropsWithChildren<ThemeProviderProps>) {
const isDarkMode = useColorScheme() === "dark";
const themeFromCore = useContext(ThirdwebThemeContext);
const theme_ = theme || themeFromCore || (isDarkMode ? "dark" : "light");
const themeFromRN = useUIContext().theme;
const theme_ =
theme || themeFromRN || themeFromCore || (isDarkMode ? "dark" : "light");
return (
<ShopifyThemeProvider
theme={
+14 -2
View File
@@ -2,6 +2,10 @@ export type Palette = {
buttonBackgroundColor: string;
buttonTextColor: string;
accentButtonColor: string;
accentButtonTextColor: string;
backgroundInverted: string;
background: string;
backgroundHighlight: string;
@@ -39,6 +43,10 @@ export const paletteLight: Palette = {
buttonBackgroundColor: "white",
buttonTextColor: "black",
accentButtonColor: "#3385FF",
accentButtonTextColor: "white",
backgroundInverted: "#131417",
background: "#F7F7F9",
backgroundHighlight: "#FFFFFF",
@@ -58,9 +66,13 @@ export const paletteLight: Palette = {
};
export const paletteDark: Palette = {
buttonBackgroundColor: "white",
buttonTextColor: "black",
buttonBackgroundColor: "black",
buttonTextColor: "white",
accentButtonColor: "#3385FF",
accentButtonTextColor: "white",
backgroundInverted: "white",
background: "#131417",
backgroundHighlight: "#232429",
+21 -24
View File
@@ -1,3 +1,4 @@
import { DeepPartial } from "../types/deepPartial";
import { paletteDark, paletteLight } from "./colors";
import { textVariants } from "./textVariants";
import { createTheme } from "@shopify/restyle";
@@ -9,17 +10,11 @@ export const baseTheme = {
xs: 8,
sm: 12,
md: 16,
xmd: 20,
lg: 24,
xl: 32,
xxl: 48,
},
fontSize: {
xs: 12,
sm: 14,
md: 16,
lg: 20,
xl: 24,
},
borderRadii: {
xs: 4,
sm: 6,
@@ -28,30 +23,24 @@ export const baseTheme = {
xl: 20,
xxl: 32,
},
iconSize: {
sm: 16,
md: 24,
lg: 32,
xl: 48,
},
textVariants,
};
const darkTheme_ = createTheme({
export const _darkTheme = createTheme({
...baseTheme,
colors: {
...paletteDark,
},
});
const lightTheme_: Theme = {
export const _lightTheme: Theme = {
...baseTheme,
colors: {
...paletteLight,
},
};
export type Theme = typeof darkTheme_;
export type Theme = typeof _darkTheme;
export type ButtonTheme = {
buttonBackgroundColor?: string;
@@ -61,27 +50,35 @@ export type ButtonTheme = {
export const darkTheme = ({
buttonBackgroundColor = paletteDark.buttonBackgroundColor,
buttonTextColor = paletteDark.buttonTextColor,
}: ButtonTheme = {}) => {
colors,
...props
}: ButtonTheme & DeepPartial<Theme> = _darkTheme): Theme => {
return {
...darkTheme_,
..._darkTheme,
colors: {
...darkTheme_.colors,
..._darkTheme.colors,
...colors,
buttonBackgroundColor,
buttonTextColor,
},
};
...props,
} as Theme;
};
export const lightTheme = ({
buttonBackgroundColor = paletteLight.buttonBackgroundColor,
buttonTextColor = paletteLight.buttonTextColor,
}: ButtonTheme = {}) => {
colors,
...props
}: ButtonTheme & DeepPartial<Theme> = _lightTheme): Theme => {
return {
...lightTheme_,
..._lightTheme,
colors: {
...lightTheme_.colors,
..._lightTheme.colors,
...colors,
buttonBackgroundColor,
buttonTextColor,
},
};
...props,
} as Theme;
};
@@ -0,0 +1,3 @@
export type DeepPartial<T> = {
[P in keyof T]?: DeepPartial<T[P]>;
};
@@ -52,6 +52,8 @@ export type ClosedModal = {
// connect wallet flow
export type ConnectWalletFlowData = {
modalTitle?: string;
termsOfServiceUrl?: string;
privacyPolicyUrl?: string;
walletConfig?: WalletConfig;
};
@@ -11,15 +11,14 @@ import { useCoinbaseWalletListener } from "./useCoinbaseWalletListener";
* @param callbackURL The Universal Link used by Coinbase Wallet to return responses to your application.
* @returns connect function to connect to the Coinbase wallet
*/
export function useCoinbaseWallet(callbackURL: string) {
export function useCoinbaseWallet(callbackURL: URL) {
const connect = useConnect();
useCoinbaseWalletListener(true);
useCoinbaseWalletListener(true, callbackURL);
return useCallback(
async (connectOptions?: { chainId?: number; callbackURL: string }) => {
async (connectOptions?: { chainId?: number }) => {
const { coinbaseWallet } = await import("../wallets/coinbase-wallet");
return connect(
// @ts-expect-error - Passing a URL object to callbackURL crashes the function @fixme: @manan
coinbaseWallet({ callbackURL: callbackURL }),
connectOptions,
);
@@ -7,16 +7,24 @@ import { Linking } from "react-native";
*
* This hook sets up the UniversalLinks listener.
*/
export function useCoinbaseWalletListener(enable: boolean) {
export function useCoinbaseWalletListener(enable: boolean, callbackURL?: URL) {
useEffect(() => {
if (!enable) {
return;
}
const sub = Linking.addEventListener("url", ({ url }) => {
// @ts-expect-error - Passing a URL object to handleResponse crashes the function
handleResponse(url);
const incomingUrl = new URL(url);
if (
callbackURL &&
incomingUrl.host === callbackURL.host &&
incomingUrl.protocol === callbackURL.protocol &&
incomingUrl.hostname === callbackURL.hostname
) {
// @ts-expect-error - Passing a URL object to handleResponse crashes the function
handleResponse(url);
}
});
return () => sub?.remove();
}, [enable]);
}, [callbackURL, enable]);
}
@@ -0,0 +1,12 @@
import type { WalletConfig } from "@thirdweb-dev/react-core";
import {
SmartWallet,
SmartWalletConfig as SmartWalletConfigWallets,
} from "@thirdweb-dev/wallets";
export type SmartWalletConfig = Omit<
SmartWalletConfigWallets,
"chain" | "clientId" | "secretKey"
>;
export type SmartWalletObj = WalletConfig<SmartWallet>;
@@ -3,12 +3,12 @@ import {
Connector,
} from "@thirdweb-dev/wallets";
import { ethers, utils } from "ethers";
import { LOCAL_WALLET_ICON } from "../../assets/svgs";
export class LocalWallet extends LocalWalletCore {
static meta = {
name: "Guest Wallet",
iconURL:
"ipfs://QmQAyJG3y2wZf9u6JXxn8U9Kd1ZVfjtQkf5aua8FcWr8Gm/local-wallet-mobile.svg",
iconURL: LOCAL_WALLET_ICON,
};
getMeta() {

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