Compare commits

...
152 changed files with 3409 additions and 700 deletions
+7
View File
@@ -1,5 +1,12 @@
# @thirdweb-dev/auth
## 4.1.4
### Patch Changes
- Updated dependencies [[`20d0f53b`](https://github.com/thirdweb-dev/js/commit/20d0f53b46a2975a0a14ddaa4a377f2f758804db)]:
- @thirdweb-dev/wallets@2.1.1
## 4.1.3
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/auth",
"version": "4.1.3",
"version": "4.1.4",
"main": "dist/thirdweb-dev-auth.cjs.js",
"module": "dist/thirdweb-dev-auth.esm.js",
"browser": {
+6
View File
@@ -1,5 +1,11 @@
# @thirdweb-dev/chains
## 0.1.58
### Patch Changes
- [#1911](https://github.com/thirdweb-dev/js/pull/1911) [`0dcb15b7`](https://github.com/thirdweb-dev/js/commit/0dcb15b7e647acd038b0ec8b1a2b200808aae00e) Thanks [@iketw](https://github.com/iketw)! - Syncs Chains
## 0.1.57
### Patch Changes
+13 -1
View File
@@ -2,7 +2,19 @@ import type { Chain } from "../src/types";
export default {
"chain": "FUSE",
"chainId": 122,
"explorers": [],
"explorers": [
{
"name": "blockscout",
"url": "https://explorer.fuse.io",
"standard": "EIP3091",
"icon": {
"url": "ipfs://QmYtUimyqHkkFxYdbXXRbUqNg2VLPUg6Uu2C2nmFWowiZM",
"width": 551,
"height": 540,
"format": "png"
}
}
],
"faucets": [],
"features": [],
"icon": {
+8 -4
View File
@@ -9,7 +9,10 @@ export default {
"standard": "EIP3091"
}
],
"faucets": [],
"faucets": [
"https://faucet.avax.network/?subnet=beam",
"https://faucet.onbeam.com"
],
"features": [
{
"name": "EIP1559"
@@ -24,15 +27,16 @@ export default {
"infoURL": "https://www.onbeam.com",
"name": "Beam Testnet",
"nativeCurrency": {
"name": "Merit Circle",
"symbol": "MC",
"name": "Beam",
"symbol": "BEAM",
"decimals": 18
},
"networkId": 13337,
"rpc": [
"https://beam-testnet.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://13337.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://subnets.avax.network/beam/testnet/rpc"
"https://subnets.avax.network/beam/testnet/rpc",
"wss://subnets.avax.network/beam/testnet/ws"
],
"shortName": "beam-testnet",
"slug": "beam-testnet",
+1
View File
@@ -26,5 +26,6 @@ export default {
"shortName": "hmy-s2",
"slip44": 1023,
"slug": "harmony-shard-2",
"status": "deprecated",
"testnet": false
} as const satisfies Chain;
+1
View File
@@ -26,5 +26,6 @@ export default {
"shortName": "hmy-s3",
"slip44": 1023,
"slug": "harmony-shard-3",
"status": "deprecated",
"testnet": false
} as const satisfies Chain;
+34
View File
@@ -0,0 +1,34 @@
import type { Chain } from "../src/types";
export default {
"chain": "Roll",
"chainId": 1748,
"explorers": [
{
"name": "Blockscout",
"url": "https://roll.calderaexplorer.xyz/",
"standard": "EIP3091"
}
],
"faucets": [],
"features": [],
"icon": {
"url": "ipfs://QmSNKW3HsFekHrTdgt7aAukExwYeYzHZEbG49ZDLYRfkKV/roll_logo%20-%20Ellie%20Li.png",
"width": 3600,
"height": 3600,
"format": ".png"
},
"infoURL": "https://tryroll.com/",
"name": "Roll Testnet",
"nativeCurrency": {
"name": "Ether",
"symbol": "ETH",
"decimals": 18
},
"networkId": 1748,
"redFlags": [],
"rpc": [],
"shortName": "Roll",
"slug": "roll-testnet",
"testnet": true,
"title": "Roll Testnet"
} as const satisfies Chain;
+6 -6
View File
@@ -1,6 +1,6 @@
import type { Chain } from "../src/types";
export default {
"chain": "okbchain",
"chain": "xgon",
"chainId": 195,
"explorers": [
{
@@ -14,20 +14,20 @@ export default {
],
"features": [],
"infoURL": "https://www.okx.com/okbc/docs/dev/quick-start/introduction/introduction-to-okbchain",
"name": "OKBChain Testnet",
"name": "Xgon Testnet",
"nativeCurrency": {
"name": "OKBChain Global Utility Token in testnet",
"name": "Xgon Global Utility Token in testnet",
"symbol": "OKB",
"decimals": 18
},
"networkId": 195,
"rpc": [
"https://okbchain-testnet.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://xgon-testnet.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://195.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://okbtestrpc.okbchain.org"
"https://testrpc.xgon.io"
],
"shortName": "tokb",
"slug": "okbchain-testnet",
"slug": "xgon-testnet",
"status": "active",
"testnet": true
} as const satisfies Chain;
+5 -5
View File
@@ -1,21 +1,21 @@
import type { Chain } from "../src/types";
export default {
"chain": "okbchain",
"chain": "xgon",
"chainId": 196,
"explorers": [],
"faucets": [],
"features": [],
"infoURL": "https://www.okex.com/okc",
"name": "OKBChain Mainnet",
"infoURL": "https://www.okx.com/okbc/docs/dev/quick-start/introduction/introduction-to-okbchain",
"name": "Xgon Mainnet",
"nativeCurrency": {
"name": "OKBChain Global Utility Token",
"name": "Xgon Global Utility Token",
"symbol": "OKB",
"decimals": 18
},
"networkId": 196,
"rpc": [],
"shortName": "okb",
"slug": "okbchain",
"slug": "xgon",
"status": "incubating",
"testnet": false
} as const satisfies Chain;
+41
View File
@@ -0,0 +1,41 @@
import type { Chain } from "../src/types";
export default {
"chain": "VinuChain Testnet",
"chainId": 206,
"explorers": [
{
"name": "VinuScan Testnet",
"url": "https://testnet.vinuscan.com",
"standard": "none",
"icon": {
"url": "ipfs://bafkreiayq7a5qncxzlilfnvpgzt4pwfxbof2qrqr2nyd2suztfummpty4i",
"width": 2000,
"height": 2000,
"format": "jpg"
}
}
],
"faucets": [],
"icon": {
"url": "ipfs://bafkreidtie4srt52ry67wd3dpxhklmug2ozrawvf3jdlljveinp3frutra",
"width": 2000,
"height": 392,
"format": "png"
},
"infoURL": "https://vitainu.org",
"name": "VinuChain Testnet",
"nativeCurrency": {
"name": "VinuChain",
"symbol": "VС",
"decimals": 18
},
"networkId": 206,
"rpc": [
"https://vinuchain-testnet.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://206.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://vinufoundation-rpc.com"
],
"shortName": "VCTEST",
"slug": "vinuchain-testnet",
"testnet": true
} as const satisfies Chain;
+3 -3
View File
@@ -17,9 +17,9 @@ export default {
],
"faucets": [],
"icon": {
"url": "ipfs://bafkreidtie4srt52ry67wd3dpxhklmug2ozrawvf3jdlljveinp3frutra",
"width": 2000,
"height": 392,
"url": "ipfs://bafybeiderli6uaaxk7eboyf7g4abnybgrkbglmqbkmf25z57ode7zdzmvy",
"width": 613,
"height": 613,
"format": "png"
},
"infoURL": "https://vitainu.org",
-2
View File
@@ -34,10 +34,8 @@ export default {
"https://kava.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://2222.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://evm.kava.io",
"https://evm2.kava.io",
"https://kava-rpc.gateway.pokt.network",
"wss://wevm.kava.io",
"wss://wevm2.kava.io",
"https://kava-evm.publicnode.com",
"wss://kava-evm.publicnode.com"
],
+6 -4
View File
@@ -10,11 +10,12 @@ export default {
}
],
"faucets": [],
"features": [],
"icon": {
"url": "ipfs://QmT84suD2ZmTSraJBfeHhTNst2vXctQijNCztok9XiVcUR",
"width": 3600,
"height": 3600,
"format": "png"
"url": "ipfs://QmYcGHTPheBwLv9zoJfQAJ7NB6MCtXe2zV7XqPyiTJg3WP/Oasys_Large%20Logo%20Black.png",
"width": 1250,
"height": 1250,
"format": ".png"
},
"infoURL": "https://oasys.games",
"name": "Oasys Mainnet",
@@ -24,6 +25,7 @@ export default {
"decimals": 18
},
"networkId": 248,
"redFlags": [],
"rpc": [
"https://oasys.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://248.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
+39
View File
@@ -0,0 +1,39 @@
import type { Chain } from "../src/types";
export default {
"chain": "MCH Verse",
"chainId": 29548,
"explorers": [
{
"name": "MCH Verse Explorer",
"url": "https://explorer.oasys.mycryptoheroes.net",
"standard": "EIP3091"
}
],
"faucets": [],
"icon": {
"url": "ipfs://QmZZnwR1y6cU1sare2TQmwqkNDLXQxD4GdPrmHLmUoPtbU",
"width": 4000,
"height": 4000,
"format": "png"
},
"infoURL": "https://www.mycryptoheroes.net/verse",
"name": "MCH Verse Mainnet",
"nativeCurrency": {
"name": "OAS",
"symbol": "OAS",
"decimals": 18
},
"networkId": 29548,
"parent": {
"type": "L2",
"chain": "eip155-248"
},
"rpc": [
"https://mch-verse.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://29548.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://rpc.oasys.mycryptoheroes.net"
],
"shortName": "MCHV",
"slug": "mch-verse",
"testnet": false
} as const satisfies Chain;
+45
View File
@@ -0,0 +1,45 @@
import type { Chain } from "../src/types";
export default {
"chain": "N3-Test",
"chainId": 333333,
"explorers": [
{
"name": "Nativ3 Test Explorer",
"url": "https://scantest.nativ3.network",
"standard": "EIP3091"
}
],
"faucets": [],
"icon": {
"url": "ipfs://QmVzJDndPui6qBSeJWe5kMLA56C3KpVhqqqk9xvVKE1DGb",
"width": 256,
"height": 256,
"format": "png"
},
"infoURL": "https://nativ3.network",
"name": "Nativ3 Testnet",
"nativeCurrency": {
"name": "USNT",
"symbol": "USNT",
"decimals": 18
},
"networkId": 333333,
"parent": {
"type": "L2",
"chain": "eip155-421613",
"bridges": [
{
"url": "https://bridgetest.nativ3.network"
}
]
},
"rpc": [
"https://nativ3-testnet.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://333333.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://rpctest.nativ3.network",
"wss://wstest.nativ3.network"
],
"shortName": "N3-Test",
"slug": "nativ3-testnet",
"testnet": true
} as const satisfies Chain;
+10 -10
View File
@@ -1,24 +1,24 @@
import type { Chain } from "../src/types";
export default {
"chain": "BTC",
"chain": "BOTANIX",
"chainId": 3636,
"explorers": [
{
"name": "Botanix",
"url": "https://testnet.btxtestchain.com",
"name": "3xpl",
"url": "https://3xpl.com/botanix",
"standard": "EIP3091"
}
],
"faucets": [
"https://faucet.btxtestchain.com"
"https://faucet.botanixlabs.dev"
],
"icon": {
"url": "ipfs://Qmf2iSjcrZwUDKhCVY9ZzfbSV2He2HSssbcG2yMz1mDerm",
"width": 32,
"height": 32,
"url": "ipfs://QmVE5s2pXiqdMnAcxhAmWkZYhpFB5CysypeLyPKzT4rGYe",
"width": 512,
"height": 512,
"format": "png"
},
"infoURL": "https://btxtestchain.com",
"infoURL": "https://botanixlabs.xyz",
"name": "Botanix Testnet",
"nativeCurrency": {
"name": "Botanix",
@@ -29,9 +29,9 @@ export default {
"rpc": [
"https://botanix-testnet.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://3636.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://testnet-rpc.btxtestchain.com"
"https://node.botanixlabs.dev"
],
"shortName": "BTCt",
"shortName": "BTNX",
"slug": "botanix-testnet",
"testnet": true
} as const satisfies Chain;
+3 -3
View File
@@ -13,9 +13,9 @@ export default {
"https://faucet.btxtestchain.com"
],
"icon": {
"url": "ipfs://Qmf2iSjcrZwUDKhCVY9ZzfbSV2He2HSssbcG2yMz1mDerm",
"width": 32,
"height": 32,
"url": "ipfs://QmVE5s2pXiqdMnAcxhAmWkZYhpFB5CysypeLyPKzT4rGYe",
"width": 512,
"height": 512,
"format": "png"
},
"infoURL": "https://btxtestchain.com",
+45
View File
@@ -0,0 +1,45 @@
import type { Chain } from "../src/types";
export default {
"chain": "Nativ3",
"chainId": 399,
"explorers": [
{
"name": "N3scan",
"url": "https://scan.nativ3.network",
"standard": "EIP3091"
}
],
"faucets": [],
"icon": {
"url": "ipfs://QmVzJDndPui6qBSeJWe5kMLA56C3KpVhqqqk9xvVKE1DGb",
"width": 256,
"height": 256,
"format": "png"
},
"infoURL": "https://nativ3.network",
"name": "Nativ3 Mainnet",
"nativeCurrency": {
"name": "USNT",
"symbol": "USNT",
"decimals": 18
},
"networkId": 399,
"parent": {
"type": "L2",
"chain": "eip155-42161",
"bridges": [
{
"url": "https://bridge.nativ3.network"
}
]
},
"rpc": [
"https://nativ3.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://399.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://rpc.nativ3.network",
"wss://ws.nativ3.network"
],
"shortName": "N3",
"slug": "nativ3",
"testnet": false
} as const satisfies Chain;
+7 -4
View File
@@ -9,7 +9,9 @@ export default {
"standard": "EIP3091"
}
],
"faucets": [],
"faucets": [
"https://faucet.onbeam.com"
],
"features": [
{
"name": "EIP1559"
@@ -24,15 +26,16 @@ export default {
"infoURL": "https://www.onbeam.com",
"name": "Beam",
"nativeCurrency": {
"name": "Merit Circle",
"symbol": "MC",
"name": "Beam",
"symbol": "BEAM",
"decimals": 18
},
"networkId": 4337,
"rpc": [
"https://beam.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://4337.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://subnets.avax.network/beam/mainnet/rpc"
"https://subnets.avax.network/beam/mainnet/rpc",
"wss://subnets.avax.network/beam/mainnet/ws"
],
"shortName": "beam",
"slug": "beam",
+31
View File
@@ -0,0 +1,31 @@
import type { Chain } from "../src/types";
export default {
"chain": "ETH",
"chainId": 5003,
"explorers": [
{
"name": "blockscout",
"url": "https://explorer.sepolia.mantle.xyz",
"standard": "EIP3091"
}
],
"faucets": [
"https://faucet.sepolia.mantle.xyz"
],
"infoURL": "https://mantle.xyz",
"name": "Mantle Sepolia Testnet",
"nativeCurrency": {
"name": "Sepolia Mantle",
"symbol": "MNT",
"decimals": 18
},
"networkId": 5003,
"rpc": [
"https://mantle-sepolia-testnet.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://5003.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://rpc.sepolia.mantle.xyz"
],
"shortName": "mnt-sep",
"slug": "mantle-sepolia-testnet",
"testnet": true
} as const satisfies Chain;
+49
View File
@@ -0,0 +1,49 @@
import type { Chain } from "../src/types";
export default {
"chain": "Electroneum",
"chainId": 52014,
"explorers": [
{
"name": "blockscout",
"url": "https://blockexplorer.electroneum.com",
"standard": "EIP3091",
"icon": {
"url": "ipfs://QmVgFqXA3kkCrVYGcWFF7Mhx8JUSe9vSCauNamuKWSvCym",
"width": 1000,
"height": 1000,
"format": "png"
}
}
],
"faucets": [],
"features": [
{
"name": "EIP155"
},
{
"name": "EIP1559"
}
],
"icon": {
"url": "ipfs://QmVgFqXA3kkCrVYGcWFF7Mhx8JUSe9vSCauNamuKWSvCym",
"width": 1000,
"height": 1000,
"format": "png"
},
"infoURL": "https://electroneum.com",
"name": "Electroneum Mainnet",
"nativeCurrency": {
"name": "Electroneum",
"symbol": "ETN",
"decimals": 18
},
"networkId": 52014,
"rpc": [
"https://electroneum.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://52014.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://api.electroneum.com"
],
"shortName": "etn-mainnet",
"slug": "electroneum",
"testnet": false
} as const satisfies Chain;
+49
View File
@@ -0,0 +1,49 @@
import type { Chain } from "../src/types";
export default {
"chain": "Electroneum",
"chainId": 5201420,
"explorers": [
{
"name": "blockscout",
"url": "https://blockexplorer.thesecurityteam.rocks",
"standard": "EIP3091",
"icon": {
"url": "ipfs://QmVgFqXA3kkCrVYGcWFF7Mhx8JUSe9vSCauNamuKWSvCym",
"width": 1000,
"height": 1000,
"format": "png"
}
}
],
"faucets": [],
"features": [
{
"name": "EIP155"
},
{
"name": "EIP1559"
}
],
"icon": {
"url": "ipfs://QmVgFqXA3kkCrVYGcWFF7Mhx8JUSe9vSCauNamuKWSvCym",
"width": 1000,
"height": 1000,
"format": "png"
},
"infoURL": "https://electroneum.com",
"name": "Electroneum Testnet",
"nativeCurrency": {
"name": "Electroneum",
"symbol": "ETN",
"decimals": 18
},
"networkId": 5201420,
"rpc": [
"https://electroneum-testnet.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://5201420.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://api.electroneum.com"
],
"shortName": "etn-testnet",
"slug": "electroneum-testnet",
"testnet": true
} as const satisfies Chain;
+1 -2
View File
@@ -25,8 +25,7 @@ export default {
"https://570.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://rpc.rollux.com",
"https://rollux.public-rpc.com",
"wss://rpc.rollux.com/wss",
"https://rpc.ankr.com/rollux/${ANKR_API_KEY}"
"wss://rpc.rollux.com/wss"
],
"shortName": "sys-rollux",
"slug": "rollux",
+6
View File
@@ -13,6 +13,12 @@ export default {
"https://faucet.triangleplatform.com/frame"
],
"features": [],
"icon": {
"url": "ipfs://QmTS1P9sh49zg5xL5BUsRLWDz3vVoJCtCNf9yN1TTnsuQ3",
"width": 321,
"height": 320,
"format": "png"
},
"infoURL": "https://frame.xyz",
"name": "Frame Testnet",
"nativeCurrency": {
+36
View File
@@ -0,0 +1,36 @@
import type { Chain } from "../src/types";
export default {
"chain": "TWT",
"chainId": 894538,
"explorers": [
{
"name": "explorer",
"url": "https://subnets-test.avax.network/thirdweb",
"standard": "EIP3091"
}
],
"faucets": [],
"features": [],
"icon": {
"url": "ipfs://QmXDGoJKuuwQKzY1Y7ZhgULg8hcmveu6PVaDaYQzEjKwkQ/Thirdweb-Icon-Black-BG.png",
"width": 512,
"height": 512,
"format": "png"
},
"name": "thirdweb test subnet",
"nativeCurrency": {
"name": "TWT",
"symbol": "TWT",
"decimals": 18
},
"networkId": 894538,
"redFlags": [],
"rpc": [
"https://thirdweb-test-subnet.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://894538.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://subnets.avax.network/thirdweb/testnet/rpc"
],
"shortName": "twt",
"slug": "thirdweb-test-subnet",
"testnet": true
} as const satisfies Chain;
+42
View File
@@ -0,0 +1,42 @@
import type { Chain } from "../src/types";
export default {
"chain": "XCAP",
"chainId": 9322252,
"explorers": [
{
"name": "blockscout",
"url": "https://xcap-mainnet.explorer.xcap.network",
"standard": "EIP3091",
"icon": {
"url": "ipfs://QmYtUimyqHkkFxYdbXXRbUqNg2VLPUg6Uu2C2nmFWowiZM",
"width": 551,
"height": 540,
"format": "png"
}
}
],
"faucets": [],
"icon": {
"url": "ipfs://QmeTj6tfaw9qf9wnEUQh7PnCpNWyvm56e7kY35kANRWNxj",
"width": 1600,
"height": 1600,
"format": "png"
},
"infoURL": "https://xcap.network/",
"name": "XCAP",
"nativeCurrency": {
"name": "Gas",
"symbol": "GAS",
"decimals": 18
},
"networkId": 9322252,
"rpc": [
"https://xcap.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://9322252.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://xcap-mainnet.relay.xcap.network/znzvh2ueyvm2yts5fv5gnul395jbkfb2/rpc1"
],
"shortName": "xcap",
"slug": "xcap",
"testnet": false,
"title": "XCAP Mainnet"
} as const satisfies Chain;
+42
View File
@@ -0,0 +1,42 @@
import type { Chain } from "../src/types";
export default {
"chain": "XCAP",
"chainId": 9322253,
"explorers": [
{
"name": "blockscout",
"url": "https://xcap-milvine.explorer.xcap.network",
"standard": "EIP3091",
"icon": {
"url": "ipfs://QmYtUimyqHkkFxYdbXXRbUqNg2VLPUg6Uu2C2nmFWowiZM",
"width": 551,
"height": 540,
"format": "png"
}
}
],
"faucets": [],
"icon": {
"url": "ipfs://QmeTj6tfaw9qf9wnEUQh7PnCpNWyvm56e7kY35kANRWNxj",
"width": 1600,
"height": 1600,
"format": "png"
},
"infoURL": "https://xcap.network/",
"name": "Milvine",
"nativeCurrency": {
"name": "Gas",
"symbol": "GAS",
"decimals": 18
},
"networkId": 9322253,
"rpc": [
"https://milvine.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://9322253.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://xcap-milvine.relay.xcap.network/zj5l55ftsgi027kz4nf14vs8d89inego/rpc1"
],
"shortName": "milv",
"slug": "milvine",
"testnet": true,
"title": "XCAP Testnet Milvine"
} as const satisfies Chain;
+35
View File
@@ -0,0 +1,35 @@
import type { Chain } from "../src/types";
export default {
"chain": "Ecrox Chain",
"chainId": 988207,
"explorers": [
{
"name": "Ecrox Chain Explorer",
"url": "https://ecroxscan.com",
"standard": "EIP3091"
}
],
"faucets": [],
"icon": {
"url": "ipfs://bafkreibnqdlwgotu4hwx6oeu4ye3huobr3eaiuvv55n5tolyxya2q7edmq",
"width": 300,
"height": 300,
"format": "png"
},
"infoURL": "https://ecroxcoin.io/",
"name": "Ecrox Chain Mainnet",
"nativeCurrency": {
"name": "ECROX COIN",
"symbol": "ECROX",
"decimals": 18
},
"networkId": 988207,
"rpc": [
"https://ecrox-chain.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://988207.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://mainnet-rpc.ecroxscan.com/"
],
"shortName": "ecrox",
"slug": "ecrox-chain",
"testnet": false
} as const satisfies Chain;
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/chains",
"version": "0.1.57",
"version": "0.1.58",
"main": "dist/thirdweb-dev-chains.cjs.js",
"module": "dist/thirdweb-dev-chains.esm.js",
"browser": {
File diff suppressed because one or more lines are too long
+10
View File
@@ -1,5 +1,15 @@
# thirdweb
## 0.13.12
### Patch Changes
- Updated dependencies [[`0dcb15b7`](https://github.com/thirdweb-dev/js/commit/0dcb15b7e647acd038b0ec8b1a2b200808aae00e), [`fefeaeef`](https://github.com/thirdweb-dev/js/commit/fefeaeef18f52a75462c43da8b85a77975d32e01), [`20d0f53b`](https://github.com/thirdweb-dev/js/commit/20d0f53b46a2975a0a14ddaa4a377f2f758804db), [`91c2352b`](https://github.com/thirdweb-dev/js/commit/91c2352b03d7cf1c3cbbbc98846f248b0f707ea7)]:
- @thirdweb-dev/chains@0.1.58
- @thirdweb-dev/sdk@4.0.11
- @thirdweb-dev/wallets@2.1.1
- @thirdweb-dev/auth@4.1.4
## 0.13.11
### Patch Changes
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "thirdweb",
"main": "dist/cli/index.js",
"version": "0.13.11",
"version": "0.13.12",
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/cli",
"author": "thirdweb eng <[email protected]>",
"license": "Apache-2.0",
+10
View File
@@ -1,5 +1,15 @@
# @thirdweb-dev/react-core
## 4.1.2
### Patch Changes
- Updated dependencies [[`0dcb15b7`](https://github.com/thirdweb-dev/js/commit/0dcb15b7e647acd038b0ec8b1a2b200808aae00e), [`fefeaeef`](https://github.com/thirdweb-dev/js/commit/fefeaeef18f52a75462c43da8b85a77975d32e01), [`20d0f53b`](https://github.com/thirdweb-dev/js/commit/20d0f53b46a2975a0a14ddaa4a377f2f758804db), [`91c2352b`](https://github.com/thirdweb-dev/js/commit/91c2352b03d7cf1c3cbbbc98846f248b0f707ea7)]:
- @thirdweb-dev/chains@0.1.58
- @thirdweb-dev/sdk@4.0.11
- @thirdweb-dev/wallets@2.1.1
- @thirdweb-dev/auth@4.1.4
## 4.1.1
## 4.1.0
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/react-core",
"version": "4.1.1",
"version": "4.1.2",
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/react-core",
"author": "thirdweb eng <[email protected]>",
"license": "Apache-2.0",
@@ -3,6 +3,7 @@ import invariant from "tiny-invariant";
import type {
BloctoWallet,
CoinbaseWallet,
CoreWallet,
EmbeddedWallet,
FrameWallet,
LocalWallet,
@@ -26,6 +27,7 @@ export type WalletId = (typeof walletIds)[keyof typeof walletIds];
type WalletIdToWalletTypeMap = {
metamask: MetaMaskWallet;
coinbase: CoinbaseWallet;
coreWallet: CoreWallet;
rainbowWallet: RainbowWallet;
blocto: BloctoWallet;
frame: FrameWallet;
@@ -1,5 +1,7 @@
# @thirdweb-dev/react-native-compat
## 0.4.2
## 0.4.1
## 0.4.0
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@thirdweb-dev/react-native-compat",
"description": "Shims for Thirdweb in React Native Projects",
"version": "0.4.1",
"version": "0.4.2",
"author": "thirdweb eng <[email protected]>",
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/react-native-compat",
"license": "Apache-2.0",
+30
View File
@@ -1,5 +1,35 @@
# @thirdweb-dev/react-native
## 0.4.2
### Patch Changes
- [#1893](https://github.com/thirdweb-dev/js/pull/1893) [`8e104b44`](https://github.com/thirdweb-dev/js/commit/8e104b443e4a11908be660656c525ad237115835) Thanks [@iketw](https://github.com/iketw)! - Updates Japanese translations
- [#1893](https://github.com/thirdweb-dev/js/pull/1893) [`8e104b44`](https://github.com/thirdweb-dev/js/commit/8e104b443e4a11908be660656c525ad237115835) Thanks [@iketw](https://github.com/iketw)! - Adds support for Japanese and Spanish
You can now pass 'ja' or 'es' to our ThirdwebProvider:
```
import { ThirdwebProvider } from '@thirdweb-dev/react-native';
<ThirdwebProvider locale={'ja'}>
<App />
</ ThirdwebProvider>
```
- [#1892](https://github.com/thirdweb-dev/js/pull/1892) [`59e55410`](https://github.com/thirdweb-dev/js/commit/59e55410942f3cb08570623f990487868aaed107) Thanks [@iketw](https://github.com/iketw)! - Deprecates magicLink in favor of embedded wallet
- [#1900](https://github.com/thirdweb-dev/js/pull/1900) [`9f454d1e`](https://github.com/thirdweb-dev/js/commit/9f454d1e08ebf5280a3266791d1843ddd7e24242) Thanks [@iketw](https://github.com/iketw)! - Export Locale types and improve modal title customization
- [#1909](https://github.com/thirdweb-dev/js/pull/1909) [`ba49a824`](https://github.com/thirdweb-dev/js/commit/ba49a82463487735dc19bf33ab2f7a3e9cce08ac) Thanks [@iketw](https://github.com/iketw)! - UI improvements
- Updated dependencies [[`0dcb15b7`](https://github.com/thirdweb-dev/js/commit/0dcb15b7e647acd038b0ec8b1a2b200808aae00e), [`fefeaeef`](https://github.com/thirdweb-dev/js/commit/fefeaeef18f52a75462c43da8b85a77975d32e01), [`20d0f53b`](https://github.com/thirdweb-dev/js/commit/20d0f53b46a2975a0a14ddaa4a377f2f758804db), [`91c2352b`](https://github.com/thirdweb-dev/js/commit/91c2352b03d7cf1c3cbbbc98846f248b0f707ea7)]:
- @thirdweb-dev/[email protected]
- @thirdweb-dev/[email protected]
- @thirdweb-dev/[email protected]
- @thirdweb-dev/[email protected]
## 0.4.1
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/react-native",
"version": "0.4.1",
"version": "0.4.2",
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/react-native",
"author": "thirdweb eng <[email protected]>",
"license": "Apache-2.0",
@@ -14,7 +14,11 @@ import { ConnectWalletButton } from "./ConnectWalletFlow/ConnectWalletButton";
import { ConnectWalletButtonProps } from "./ConnectWalletFlow/ConnectWalletButton";
import BaseButton from "./base/BaseButton";
import Text from "./base/Text";
import { useLocale, useUIContext } from "../providers/ui-context-provider";
import {
useGlobalTheme,
useLocale,
useUIContext,
} from "../providers/ui-context-provider";
import { ThemeProvider } from "../styles/ThemeProvider";
import { SupportedTokens, defaultTokens } from "./SendFunds/defaultTokens";
@@ -81,6 +85,7 @@ export const ConnectWallet = ({
privacyPolicyUrl,
supportedTokens,
}: ConnectWalletProps) => {
const globalTheme = useGlobalTheme();
const l = useLocale();
const fadeAnim = useRef(new Animated.Value(0)).current;
const address = useAddress();
@@ -140,7 +145,10 @@ export const ConnectWallet = ({
style={styles.connectWalletButton}
>
{switching ? (
<ActivityIndicator size="small" color="buttonTextColor" />
<ActivityIndicator
size="small"
color={globalTheme.colors.buttonTextColor}
/>
) : (
<Text variant="bodyLarge" color="buttonTextColor">
{l.common.switch_network}
@@ -84,9 +84,9 @@ const ConnectAppField = () => {
const onQRCodeScan = (data: string) => {
if (wcUriRef.current !== data && data.startsWith("wc:")) {
wcUriRef.current = data;
setWCUri(data);
setShowQRCodeScan(false);
wcUriRef.current = data;
onWCPress(data);
}
@@ -180,7 +180,7 @@ export const ExportLocalWalletModal = ({
onPress={onContinuePress}
>
{isExporting ? (
<ActivityIndicator size="small" color="buttonTextColor" />
<ActivityIndicator size="small" color="black" />
) : (
<Text variant="bodySmall" color="black">
{l.connect_wallet_details.backup}
@@ -1,6 +1,7 @@
import { RNCamera } from "react-native-camera";
import { TWModal } from "../base/modal/TWModal";
import { ModalHeaderTextClose } from "../base/modal/ModalHeaderTextClose";
import { SafeAreaView } from "react-native";
export const QRCodeScan = ({
isVisible,
@@ -13,21 +14,26 @@ export const QRCodeScan = ({
}) => {
return (
<TWModal isVisible={isVisible} backdropOpacity={1}>
<ModalHeaderTextClose headerText={"Scan to Connect"} onClose={onClose} />
<RNCamera
style={{ flex: 1 }}
type={RNCamera.Constants.Type.back}
captureAudio={false}
androidCameraPermissionOptions={{
title: "Permission to use camera",
message: "We need your permission to use your camera",
buttonPositive: "Ok",
buttonNegative: "Cancel",
}}
onBarCodeRead={({ data }) => {
onQRCodeScan(data);
}}
/>
<SafeAreaView style={{ flex: 1, backgroundColor: "transparent" }}>
<ModalHeaderTextClose
headerText={"Scan to Connect"}
onClose={onClose}
/>
<RNCamera
style={{ flex: 1 }}
type={RNCamera.Constants.Type.back}
captureAudio={false}
androidCameraPermissionOptions={{
title: "Permission to use camera",
message: "We need your permission to use your camera",
buttonPositive: "Ok",
buttonNegative: "Cancel",
}}
onBarCodeRead={({ data }) => {
onQRCodeScan(data);
}}
/>
</SafeAreaView>
</TWModal>
);
};
@@ -104,35 +104,31 @@ export function ChooseWallet({
onBackPress={isConnectAWalletEnabled ? onBackPress : undefined}
onClose={onClose}
headerText={
headerText ? (
headerText
) : (
<Box
flexDirection="row"
alignItems="center"
alignContent="center"
justifyContent="center"
>
{modalTitleIconUrl !== undefined ? (
modalTitleIconUrl.length === 0 ? null : (
<ImageSvgUri
width={26}
height={15}
imageUrl={modalTitleIconUrl}
/>
)
) : (
<ThirdwebLogo
<Box
flexDirection="row"
alignItems="center"
alignContent="center"
justifyContent="center"
>
{modalTitleIconUrl !== undefined ? (
modalTitleIconUrl.length === 0 ? null : (
<ImageSvgUri
width={26}
height={15}
color={theme.colors.backgroundInverted}
imageUrl={modalTitleIconUrl}
/>
)}
<Text variant="headerBold" ml="xxs" fontSize={20} lineHeight={24}>
{l.connect_wallet_details.connect}
</Text>
</Box>
)
)
) : (
<ThirdwebLogo
width={26}
height={15}
color={theme.colors.backgroundInverted}
/>
)}
<Text variant="headerBold" ml="xxs" fontSize={20} lineHeight={24}>
{headerText ? headerText : l.connect_wallet_details.connect}
</Text>
</Box>
}
subHeaderText={subHeaderText}
/>
@@ -104,7 +104,10 @@ export const ConnectWalletButton = ({
>
<Text variant="bodyLargeBold" color="buttonTextColor">
{showButtonSpinner ? (
<ActivityIndicator size="small" color="buttonTextColor" />
<ActivityIndicator
size="small"
color={appTheme.colors.buttonTextColor}
/>
) : buttonTitle ? (
buttonTitle
) : (
@@ -74,7 +74,7 @@ export function LocalWalletFlow({
data-test="create-new-wallet-button"
>
{isCreatingWallet ? (
<ActivityIndicator size="small" color="buttonTextColor" />
<ActivityIndicator size="small" color="black" />
) : (
<Text variant="bodyLarge" color="black">
{l.local_wallet.create_new_wallet}
@@ -228,7 +228,7 @@ export const LocalWalletImportModal = ({
onPress={onImportPress}
>
{isImporting ? (
<ActivityIndicator size="small" color="buttonTextColor" />
<ActivityIndicator size="small" color="black" />
) : (
<Text variant="bodySmall" color="black">
{l.common.import}
@@ -18,7 +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";
import { useGlobalTheme, useUIContext } from "../providers/ui-context-provider";
type ActionFn = (contract: SmartContract) => Promise<any>;
@@ -74,6 +74,7 @@ export const Web3Button = <TAction extends ActionFn>({
theme,
connectWalletProps,
}: PropsWithChildren<Web3ButtonProps<TAction>>) => {
const globalTheme = useGlobalTheme();
const address = useAddress();
const activeWallet = useWallet();
const walletChainId = useChainId();
@@ -155,7 +156,12 @@ export const Web3Button = <TAction extends ActionFn>({
connectionStatus === "connecting" ||
connectionStatus === "unknown"
) {
content = <ActivityIndicator size="small" color="buttonTextColor" />;
content = (
<ActivityIndicator
size="small"
color={globalTheme.colors.buttonTextColor}
/>
);
buttonLoading = true;
}
}
+22 -22
View File
@@ -4,11 +4,11 @@ export const _ja = {
},
connect_wallet_details: {
additional_actions: "追加のアクション",
address_copied_clipboard: "アドレスがクリップボードにコピーされました",
address_copied_clipboard: "アドレスがクリップボードにコピーされました。",
backup_wallet: "ウォレットのバックアップ",
backup_personal_wallet: "個人のウォレットのバックアップ",
import_wallet: "ウォレットのインポート",
connected_to_smart_wallet: "スマートウォレットに接続されています",
connected_to_smart_wallet: "スマートウォレットに接続されています。",
current_network: "現在のネットワーク",
backup: "バックアップ",
connect_to_app: "アプリに接続",
@@ -23,30 +23,30 @@ export const _ja = {
continue_as_guest: "ゲストとして続行",
tos: "利用規約",
privacy_policy: "プライバシーポリシー",
by_connecting_you_agree: "接続することで、あなたは以下に同意します",
by_connecting_you_agree: "同意の上、接続してください。",
copy_address_or_scan:
"ウォレットのアドレスをコピーするか、QRコードをスキャンして資金をこのウォレットに送信します",
"ウォレットのアドレスをコピーするか、QRコードをスキャンして資金をこのウォレットに送信します。",
request_testnet_funds: "テストネットの資金をリクエスト",
your_address: "あなたのアドレス",
qr_code: "QRコード",
select_token: "トークンを選択",
send_to: "送信先",
send_funds: "資金を送る",
no_tokens_found: "トークンが見つかりません",
no_tokens_found: "トークンが見つかりません。",
confirm_in_wallet: "ウォレットで確認",
select_network: "ネットワークを選択",
switch_to: "に切り替える",
switch_to: "切り替え先",
no_supported_chains_detected:
"サポートされているチェーンが検出されませんでした",
"サポートされているチェーンが検出されませんでした。",
recommended: "推奨",
network_mismatch:
"あなたのコントラクトとウォレットの間にネットワークの不一致があります",
"あなたのコントラクトとウォレットの間にネットワークの不一致があります。",
},
connecting_wallet: {
creating_encrypting: "デバイスのウォレットを作成、暗号化、保護しています",
connecting_your_wallet: "ウォレットを接続しています",
creating_encrypting: "デバイスのウォレットを作成、暗号化、保護しています。",
connecting_your_wallet: "ウォレットを接続しています。",
connecting_through_pop_up:
"ウォレットのポップアップを通じてアプリにログインして接続します",
"ウォレットのポップアップを通じてアプリにログインして接続します。",
},
local_wallet: {
guest_wallet: "ゲストウォレット",
@@ -55,15 +55,15 @@ export const _ja = {
private_key_mnemonic: "または、秘密鍵またはニーモニック",
private_key_mnemonic_placeholder: "秘密鍵 / ニーモニック",
application_can_authorize_transactions:
"アプリケーションは、承認なしにウォレットを代わりにトランザクションを許可することができます 信頼できるアプリケーションのみに接続することをおすすめします",
double_check_password: "パスワードまたは秘密鍵を再度確認してください",
"アプリケーションは、承認なしにウォレットを代わりにトランザクションを許可することができます。信頼できるアプリケーションのみに接続することをおすすめします。",
double_check_password: "パスワードまたは秘密鍵を再度確認してください。",
error_accessing_file:
"ファイルにアクセスできませんでしたもう一度試してください",
"ファイルにアクセスできませんでしたもう一度試してください。",
wallet_address: "ウォレットのアドレス",
this_will_download_json:
"これにより、パスワードで暗号化されたウォレット情報を含むJSONファイルがデバイスにダウンロードされます",
"これにより、パスワードで暗号化されたウォレット情報を含むJSONファイルがデバイスにダウンロードされます。",
this_is_a_temporary_wallet:
"これは一時的なゲストウォレットです アクセスを失わないようにバックアップをダウンロードしてください",
"これは一時的なゲストウォレットです。アクセスを失わないようにバックアップをダウンロードしてください。",
},
smart_wallet: {
switch_to_smart: "スマートウォレットに切り替え",
@@ -76,17 +76,17 @@ export const _ja = {
sign_in: "サインイン",
sign_in_google: "Googleでサインイン",
enter_your_email: "メールアドレスを入力",
forgot_password: "パスワードを忘れた",
forgot_password: "パスワードを忘れた場合",
enter_account_recovery_code: "アカウント回復コードを入力",
backup_your_account: "アカウントのバックアップ",
create_password: "パスワードを作成する",
create_password: "パスワードを作成",
enter_password: "パスワードを入力",
set_password_message: "アカウントの保護のためパスワードを設定してください",
enter_password_for_email: "メールのためのパスワードを入力",
make_sure_you_save_it: "必ず保存してください",
set_password_message: "パスワードを設定してアカウントを保護",
enter_password_for_email: "メールアドレスに対するパスワードを入力",
make_sure_you_save_it: "必ず保存してください。",
},
wallet_connect: {
no_results_found: "結果が見つかりません",
no_results_found: "結果が見つかりません。",
search_wallets: "ウォレットを検索",
},
common: {
+1
View File
@@ -49,6 +49,7 @@ export { createSecureStorage } from "../core/SecureStorage";
export { ThirdwebStorage } from "../core/storage/storage";
export * from "./i18n/strings";
export * from "./i18n/types";
// ui components
export * from "./components/base";
@@ -6,6 +6,14 @@ import {
import type { MagicConnector as MagicConnectorType } from "../connectors/magic/magic-connector";
import { MagicConnector } from "../connectors/magic/magic-connector";
/**
* @deprecated We have deprecated magicLink in favor of our embeddedWallet which adds support for more sign in methods.
* To learn more, please see:
*
* Our release notes: https://portal.thirdweb.com/embedded-wallet
*
* Our documentation: https://portal.thirdweb.com/react-native/react-native.embeddedwallet
*/
export class MagicLink extends AbstractClientWallet<
MagicLinkOptions,
MagicConnectorOptions
@@ -4,6 +4,7 @@ import { ActivityIndicator, Keyboard } from "react-native";
import {
SelectUIProps,
useCreateWalletInstance,
useWallets,
} from "@thirdweb-dev/react-core";
import Box from "../../../components/base/Box";
import Text from "../../../components/base/Text";
@@ -33,6 +34,7 @@ export const EmailSelectionUI: React.FC<
const [isFetching, setIsFetching] = useState<boolean>(false);
const createWalletInstance = useCreateWalletInstance();
const [emailWallet, setEmailWallet] = useState<EmbeddedWallet | null>(null);
const supportedWallets = useWallets();
const isEmailEnabled = auth?.options.includes("email");
const isGoogleEnabled = auth?.options.includes("google");
@@ -107,7 +109,7 @@ export const EmailSelectionUI: React.FC<
walletIconUrl={GOOGLE_ICON}
onPress={onGoogleSignInPress}
/>
{isEmailEnabled ? (
{isEmailEnabled && supportedWallets.length === 1 ? (
<Box
mb="md"
mt="md"
@@ -125,7 +127,9 @@ export const EmailSelectionUI: React.FC<
</Text>
<Box height={1} flex={1} backgroundColor="border" />
</Box>
) : null}
) : (
<Box mt="md" />
)}
</Box>
) : null}
{isEmailEnabled ? (
@@ -21,6 +21,18 @@ import React from "react";
import { ConnectingWallet } from "../../components/ConnectWalletFlow/ConnectingWallet/ConnectingWallet";
import { useGlobalTheme, useLocale } from "../../providers/ui-context-provider";
/**
*
* @param magicLinkOptions Options available to create a magic link wallet
* @returns A WalletConfig<MagicLink> object
*
* @deprecated We have deprecated magicLink in favor of our embeddedWallet which adds support for more sign in methods.
* To learn more, please see:
*
* Our release notes: https://portal.thirdweb.com/embedded-wallet
*
* Our documentation: https://portal.thirdweb.com/react-native/react-native.embeddedwallet
*/
export const magicLink = (
magicLinkOptions: MagicLinkOptions & { recommended?: boolean },
): WalletConfig<MagicLink> => {
+2 -1
View File
@@ -1,9 +1,10 @@
module.exports = {
root: true,
extends: ["thirdweb"],
extends: ["thirdweb", "plugin:i18next/recommended"],
plugins: ["better-tree-shaking"],
rules: {
"@next/next/no-img-element": "off",
"better-tree-shaking/no-top-level-side-effects": "error",
"i18next/no-literal-string": 2,
},
};
+81
View File
@@ -1,5 +1,86 @@
# @thirdweb-dev/react
## 4.1.2
### Patch Changes
- [#1906](https://github.com/thirdweb-dev/js/pull/1906) [`b90d3af5`](https://github.com/thirdweb-dev/js/commit/b90d3af5b8cfb762b38a88d7997879be9b5744bf) Thanks [@kien-ngo](https://github.com/kien-ngo)! - Add `hideSwitchToPersonalWallet` prop on `ConnectWallet` component to hide the "switch to Personal wallet" option in the ConnectWallet dropdown which is shown when wallet is connected to either Smart Wallet or Safe
```tsx
<ConnectWallet hideSwitchToPersonalWallet={true} > // default: false
```
- [#1891](https://github.com/thirdweb-dev/js/pull/1891) [`20d0f53b`](https://github.com/thirdweb-dev/js/commit/20d0f53b46a2975a0a14ddaa4a377f2f758804db) Thanks [@kien-ngo](https://github.com/kien-ngo)! - Add Core wallet (Avalanche)
- [#1847](https://github.com/thirdweb-dev/js/pull/1847) [`c34ac468`](https://github.com/thirdweb-dev/js/commit/c34ac468fbf983a6806cd88828c77eb6d7231e78) Thanks [@MananTank](https://github.com/MananTank)! - Add Localization API to change language used in components `ConnectWallet` and `Web3Button` and/or override the default texts
### Japanese
```tsx
import { ja } from "@thirdweb-dev/react";
const japanese = ja();
<ThirdwebProvider locale={japanese}>
<App />
</ThirdwebProvider>;
```
### Spanish
```tsx
import { es } from "@thirdweb-dev/react";
const spanish = es();
<ThirdwebProvider locale={spanish}>
<App />
</ThirdwebProvider>;
```
### English ( default )
```tsx
import { en } from "@thirdweb-dev/react";
const english = en();
<ThirdwebProvider locale={english}>
<App />
</ThirdwebProvider>;
```
This API also allows overriding the default texts in the locale object. You can override all the texts or just some parts
```tsx
import { en } from "@thirdweb-dev/react";
// override some texts
const english = en({
connectWallet: {
confirmInWallet: "Confirm in your wallet",
},
wallets: {
metamaskWallet: {
connectionScreen: {
inProgress: "Awaiting Confirmation",
instruction: "Accept connection request in your MetaMask wallet",
},
},
},
});
<ThirdwebProvider locale={english}>
<App />
</ThirdwebProvider>;
```
- Updated dependencies [[`0dcb15b7`](https://github.com/thirdweb-dev/js/commit/0dcb15b7e647acd038b0ec8b1a2b200808aae00e), [`fefeaeef`](https://github.com/thirdweb-dev/js/commit/fefeaeef18f52a75462c43da8b85a77975d32e01), [`20d0f53b`](https://github.com/thirdweb-dev/js/commit/20d0f53b46a2975a0a14ddaa4a377f2f758804db), [`91c2352b`](https://github.com/thirdweb-dev/js/commit/91c2352b03d7cf1c3cbbbc98846f248b0f707ea7)]:
- @thirdweb-dev/[email protected]
- @thirdweb-dev/[email protected]
- @thirdweb-dev/[email protected]
- @thirdweb-dev/[email protected]
## 4.1.1
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/react",
"version": "4.1.1",
"version": "4.1.2",
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/react",
"author": "thirdweb eng <[email protected]>",
"license": "Apache-2.0",
+6 -3
View File
@@ -12,6 +12,10 @@ export const DragNDrop: React.FC<{
extension: string;
accept: string;
onUpload: (file: File) => void;
locale: {
wrongFileError: string;
uploadedSuccessfully: string;
};
}> = (props) => {
const [error, setError] = useState(false);
const [uploaded, setUploaded] = useState<File | undefined>();
@@ -105,8 +109,7 @@ export const DragNDrop: React.FC<{
<Spacer y="lg" />
{error ? (
<Text color="danger" size="sm">
{" "}
Please upload a {props.extension} file{" "}
{props.locale.wrongFileError}
</Text>
) : (
<Text size="sm"> {props.extension} </Text>
@@ -115,7 +118,7 @@ export const DragNDrop: React.FC<{
) : (
<>
<Text weight={600} color="primaryText" center multiline>
{uploaded.name} uploaded successfully
{uploaded.name} {props.locale.uploadedSuccessfully}
</Text>
<Spacer y="md" />
<Container color="success">
+5 -5
View File
@@ -7,10 +7,10 @@ import {
iconSize,
} from "../design-system";
import {
widemodalMaxHeight,
wideModalMaxHeight,
modalMaxWidthCompact,
modalMaxWidthWide,
compactmodalMaxHeight,
compactModalMaxHeight,
modalCloseFadeOutDuration,
} from "../wallet/ConnectWallet/constants";
import { Overlay } from "./Overlay";
@@ -88,7 +88,7 @@ export const Modal: React.FC<{
? { width: 0, height: 0, overflow: "hidden", opacity: 0 }
: {
height:
props.size === "compact" ? "auto" : widemodalMaxHeight,
props.size === "compact" ? "auto" : wideModalMaxHeight,
maxWidth:
props.size === "compact"
? modalMaxWidthCompact
@@ -97,7 +97,7 @@ export const Modal: React.FC<{
}
>
{props.size === "compact" ? (
<DynamicHeight maxHeight={compactmodalMaxHeight}>
<DynamicHeight maxHeight={compactModalMaxHeight}>
{props.children}{" "}
</DynamicHeight>
) : (
@@ -174,7 +174,7 @@ const DialogContent = styled.div<{ theme?: Theme }>`
box-sizing: border-box;
animation: ${modalAnimationDesktop} 300ms ease;
box-shadow: ${shadow.lg};
line-height: 1;
line-height: normal;
border: 1px solid ${(p) => p.theme.colors.borderColor};
outline: none;
overflow: hidden;
+2
View File
@@ -16,6 +16,8 @@ export const QRCode: React.FC<{
<div
style={{
position: "relative",
display: "flex",
justifyContent: "center",
}}
>
{props.qrCodeUri ? (
+1 -1
View File
@@ -46,7 +46,7 @@ const TooltipContent = /* @__PURE__ */ (() => styled(RadixTooltip.Content)<{
background: ${(p) => p.theme.colors.tooltipBg};
color: ${(p) => p.theme.colors.tooltipText};
border-radius: ${radius.sm};
line-height: 1;
line-height: normal;
padding: ${spacing.sm} ${spacing.md};
font-size: ${fontSize.sm};
box-shadow: ${shadow.sm};
+1 -1
View File
@@ -18,7 +18,7 @@ export const Button = styled.button<{
font-weight: 500;
box-sizing: border-box;
-webkit-tap-highlight-color: transparent;
line-height: 1;
line-height: normal;
flex-shrink: 0;
transition: border 200ms ease;
gap: ${(p) => (p.gap && spacing[p.gap]) || 0};
+2 -2
View File
@@ -16,7 +16,7 @@ export const Text = styled.span<{
margin: 0;
display: ${(p) => (p.inline ? "inline" : "block")};
font-weight: ${(p) => p.weight || 500};
line-height: ${(p) => (p.multiline ? 1.5 : 1)};
line-height: ${(p) => (p.multiline ? 1.5 : "normal")};
${(p) => (p.center ? `text-align: center;` : "")};
text-wrap: ${(p) => (p.balance ? "balance" : "inherit")};
max-width: 100%;
@@ -41,7 +41,7 @@ export const Link = styled.a<{
text-align: ${(p) => (p.center ? "center" : "left")};
display: ${(p) => (p.inline ? "inline" : "block")};
font-weight: ${(p) => p.weight || 500};
line-height: 1;
line-height: normal;
transition: color 0.2s ease;
&:hover {
@@ -22,6 +22,7 @@ import { PropsWithChildren, useState } from "react";
import invariant from "tiny-invariant";
import { CustomThemeProvider } from "../../../design-system/CustomThemeProvider";
import { useTheme } from "@emotion/react";
import { useTWLocale } from "../../providers/locale-provider";
type ActionFn = (contract: SmartContract) => any;
@@ -48,7 +49,11 @@ interface Web3ButtonProps<TActionFn extends ActionFn> {
style?: React.CSSProperties;
connectWallet?: Omit<
ConnectWalletProps,
"detailsBtn" | "hideTestnetFaucet" | "switchToActiveChain" | "theme"
| "detailsBtn"
| "hideTestnetFaucet"
| "switchToActiveChain"
| "theme"
| "hideSwitchToPersonalWallet"
>;
}
@@ -104,6 +109,8 @@ export const Web3Button = <TAction extends ActionFn>(
const contextTheme = useTheme() as ThemeObjectOrType;
const theme = props.theme || contextTheme || "dark";
const locale = useTWLocale();
const [confirmStatus, setConfirmStatus] = useState<"idle" | "waiting">(
"idle",
);
@@ -185,7 +192,7 @@ export const Web3Button = <TAction extends ActionFn>(
{confirmStatus === "waiting" ? (
<Spinner size="sm" color={"primaryButtonText"} />
) : (
"Switch Network"
locale.connectWallet.switchNetwork
)}
</Button>
);
@@ -193,7 +200,7 @@ export const Web3Button = <TAction extends ActionFn>(
if (requiresConfirmation) {
button = (
<Popover
content={<span>Confirm in Wallet</span>}
content={<span>{locale.connectWallet.confirmInWallet}</span>}
open={confirmStatus === "waiting"}
onOpenChange={(isOpen) => {
if (!isOpen) {
+1
View File
@@ -69,4 +69,5 @@ export {
SignerWallet,
InjectedWallet,
setWalletAnalyticsEnabled,
CoreWallet,
} from "@thirdweb-dev/wallets";
+323
View File
@@ -0,0 +1,323 @@
import { DeepPartial, immutableOverride } from "../utils/applyOverrides";
// wallets that connect via extension and QR scan
function extensionAndQRScanScreens(walletName: string) {
return {
connectionScreen: {
inProgress: "Awaiting Confirmation",
failed: "Connection failed",
instruction: `Accept the connection request in ${walletName} wallet`,
retry: "Try Again",
},
getStartedScreen: {
instruction: `Scan the QR code to download ${walletName} app`,
},
scanScreen: {
instruction: `Scan the QR code with ${walletName} wallet app to connect`,
},
getStartedLink: `Don't have ${walletName} wallet?`,
};
}
export function enDefault() {
return {
connectWallet: {
signIn: "Sign in",
defaultButtonTitle: "Connect Wallet",
connecting: "Connecting",
switchNetwork: "Switch Network",
switchingNetwork: "Switching Network",
defaultModalTitle: "Connect",
recommended: "Recommended",
installed: "Installed",
continueAsGuest: "Continue as guest",
connectAWallet: "Connect a wallet",
newToWallets: "New to wallets?",
getStarted: "Get started",
guest: "Guest",
send: "Send",
receive: "Receive",
currentNetwork: "Current network",
switchAccount: "Switch Account",
requestTestnetFunds: "Request Testnet Funds",
transactionHistory: "Transaction History",
backupWallet: "Backup Wallet",
guestWalletWarning:
"This is a temporary guest wallet. Backup if you don't want to lose access to it",
switchTo: "Switch to", // Used in "Switch to <Wallet-Name>"
connectedToSmartWallet: "Connected To Smart Wallet",
confirmInWallet: "Confirm in wallet",
disconnectWallet: "Disconnect Wallet",
copyAddress: "Copy Address",
personalWallet: "Personal Wallet",
smartWallet: "Smart Wallet",
or: "OR",
download: {
chrome: "Download Chrome Extension",
android: "Download on Google Play",
iOS: "Download on App Store",
},
welcomeScreen: {
defaultTitle: "Your gateway to the decentralized world",
defaultSubtitle: "Connect a wallet to get started",
},
agreement: {
prefix: "By connecting, you agree to the",
termsOfService: "Terms of Service",
and: "&",
privacyPolicy: "Privacy Policy",
},
networkSelector: {
title: "Select Network",
mainnets: "Mainnets",
testnets: "Testnets",
allNetworks: "All",
addCustomNetwork: "Add Custom Network",
inputPlaceholder: "Search Network or Chain ID",
categoryLabel: {
recentlyUsed: "Recently Used",
popular: "Popular",
others: "All Networks",
},
loading: "Loading",
failedToSwitch: "Failed to switch network",
},
receiveFundsScreen: {
title: "Receive Funds",
instruction: "Copy the wallet address to send funds to this wallet",
},
sendFundsScreen: {
title: "Send Funds",
submitButton: "Send",
token: "Token",
sendTo: "Send to",
amount: "Amount",
successMessage: "Transaction Successful",
invalidAddress: "Invalid Address",
noTokensFound: "No Tokens Found",
searchToken: "Search or Paste token address",
transactionFailed: "Transaction Failed",
transactionRejected: "Transaction Rejected",
insufficientFunds: "Insufficient Funds",
selectTokenTitle: "Select a Token",
sending: "Sending",
},
signatureScreen: {
instructionScreen: {
title: "Sign in",
instruction:
"Please sign the message request in your wallet to continue",
signInButton: "Sign in",
},
signingScreen: {
title: "Signing In",
prompt: "Sign the signature request in your wallet",
promptForSafe:
"Sign signature request in your wallet & approve transaction in Safe",
approveTransactionInSafe: "Approve transaction in Safe",
tryAgain: "Try Again",
failedToSignIn: "Failed to Sign in",
},
},
},
wallets: {
walletConnect: {
scanInstruction: "Scan this with your wallet app to connect",
},
smartWallet: {
connecting: "Connecting to Smart Wallet",
failedToConnect: "Failed to connect to Smart Wallet",
wrongNetworkScreen: {
title: "Wrong Network",
subtitle: "Your wallet is not connected to the required network",
failedToSwitch: "Failed to switch network",
},
},
safeWallet: {
connectWalletScreen: {
title: "Link personal wallet",
subtitle: "Connect your personal wallet to use Safe.",
learnMoreLink: "Learn more",
},
accountDetailsScreen: {
title: "Enter your safe details",
findSafeAddressIn: "You can find your safe address in", // You can find your safe address in + <dashboardLink>
dashboardLink: "Safe Dashboard", // <dashboardLink>
network: "Safe Network",
selectNetworkPlaceholder: "Network your safe is deployed to",
invalidChainConfig:
"Can not use Safe: No Safe supported chains are configured in App",
failedToConnect:
"Could not connect to Safe. Make sure safe address and network are correct.",
failedToSwitch: "Failed to switch network",
switchNetwork: "Switch Network",
switchingNetwork: "Switching Network",
connectToSafe: "Connect to Safe",
connecting: "Connecting",
mainnets: "Mainnets",
testnets: "Testnets",
safeAddress: "Safe Address",
},
},
coinbaseWallet: extensionAndQRScanScreens("Coinbase"),
metamaskWallet: extensionAndQRScanScreens("MetaMask"),
okxWallet: extensionAndQRScanScreens("OKX"),
phantomWallet: extensionAndQRScanScreens("Phantom"),
rainbowWallet: extensionAndQRScanScreens("Rainbow"),
trustWallet: extensionAndQRScanScreens("Trust"),
zerionWallet: extensionAndQRScanScreens("Zerion"),
coreWallet: extensionAndQRScanScreens("Core"),
paperWallet: {
signIn: "Sign in",
signInWithGoogle: "Sign in with Google",
emailPlaceholder: "Enter your email address",
submitEmail: "Continue",
invalidEmail: "Invalid email address",
emailRequired: "Email address is required",
googleLoginScreen: {
title: "Sign in",
instruction: "Select your Google account in the pop-up",
failed: "Failed to sign in",
retry: "Retry",
},
emailLoginScreen: {
title: "Sign in",
enterCodeSendTo: "Enter the verification code sent to", // Enter the verification code sent to + <email>
newDeviceDetected: "New device detected",
enterRecoveryCode:
"Enter the recovery code emailed to you when you first signed up",
invalidCode: "Invalid verification code",
invalidCodeOrRecoveryCode:
"Invalid verification code or Recovery code",
verify: "Verify",
failedToSendCode: "Failed to send verification code",
sendingCode: "Sending verification code",
resendCode: "Resend verification code",
},
},
embeddedWallet: {
signInWithGoogle: "Sign in with Google",
emailPlaceholder: "Enter your email address",
submitEmail: "Continue",
signIn: "Sign in",
emailRequired: "Email address is required",
invalidEmail: "Invalid email address",
googleLoginScreen: {
title: "Sign in",
instruction: "Select your Google account in the pop-up",
failed: "Failed to sign in",
retry: "Retry",
},
emailLoginScreen: {
title: "Sign in",
enterCodeSendTo: "Enter the verification code sent to",
newDeviceDetected: "New device detected",
enterRecoveryCode:
"Enter the recovery code emailed to you when you first signed up",
invalidCode: "Invalid verification code",
invalidCodeOrRecoveryCode:
"Invalid verification code or Recovery code",
verify: "Verify",
failedToSendCode: "Failed to send verification code",
sendingCode: "Sending verification code",
resendCode: "Resend verification code",
},
createPassword: {
title: "Create Password",
instruction:
"Set a password for your account. You will need this password when connecting from a new device.",
saveInstruction: "Make sure to save it",
inputPlaceholder: "Enter your password",
confirmation: "I have saved my password",
submitButton: "Set Password",
failedToSetPassword: "Failed to set password",
},
enterPassword: {
title: "Enter Password",
instruction: "Enter the password for your account",
inputPlaceholder: "Enter your password",
submitButton: "Verify",
wrongPassword: "Wrong password",
},
},
magicLink: {
signIn: "Sign in",
loginWith: "Login with",
submitEmail: "Continue",
loginWithEmailOrPhone: "Login with email or phone number",
emailOrPhoneRequired: "email or phone number is required",
loginWithPhone: "Login with phone number",
phoneRequired: "phone number is required",
invalidEmail: "Invalid email address",
invalidPhone: "Invalid phone number",
invalidEmailOrPhone: "Invalid email address or phone number",
countryCodeMissing: "Phone number must start with a country code",
emailPlaceholder: "Enter your email address",
emailRequired: "Email address is required",
},
localWallet: {
passwordLabel: "Password",
confirmPasswordLabel: "Confirm Password",
enterYourPassword: "Enter your password",
warningScreen: {
title: "Warning",
warning:
"Your current wallet will be deleted if you create a new wallet. Backup wallet to your device before creating a new wallet",
backupWallet: "Backup Wallet",
},
reconnectScreen: {
title: "Connect to saved wallet",
savedWallet: "Saved Wallet",
continue: "Continue",
createNewWallet: "Create a new wallet",
},
createScreen: {
instruction:
"Choose a password for your wallet. You'll be able to access and export this wallet with the same password",
importWallet: "Import Wallet",
createNewWallet: "Create new wallet",
connecting: "Connecting",
},
exportScreen: {
description1:
"This will download a JSON file containing the wallet information onto your device encrypted with the password",
description2:
"You can use this JSON file to import the account in MetaMask using the same password",
walletAddress: "Wallet Address",
download: "Download",
title: "Backup Wallet",
},
importScreen: {
title: "Import Wallet",
description1:
"The application can authorize any transactions on behalf of the wallet without any approvals",
description2: "We recommend only connecting to trusted applications",
import: "Import",
uploadJSON: "Please upload a JSON file",
uploadedSuccessfully: "Uploaded successfully",
},
},
frameWallet: {
...extensionAndQRScanScreens("Frame"),
connectionFailedScreen: {
title: "Failed to connect to Frame",
description:
"Make sure the desktop app is installed and running. You can download Frame from the link below. Make sure to refresh this page once Frame is running.",
downloadFrame: "Download Frame",
supportLink: "Still having troubles connecting?",
},
},
},
};
}
export type ThirdwebLocale = ReturnType<typeof enDefault>;
export function en(overrides?: DeepPartial<ThirdwebLocale>) {
const defaultObj = enDefault();
if (!overrides) {
return defaultObj;
}
return immutableOverride(defaultObj, overrides);
}
+330
View File
@@ -0,0 +1,330 @@
import { DeepPartial, immutableOverride } from "../utils/applyOverrides";
import { ThirdwebLocale } from "./en";
// ...
function extensionAndQRScanScreens(walletName: string) {
return {
connectionScreen: {
inProgress: "Esperando confirmación",
failed: "Conexión fallida",
instruction: `Acepta la solicitud de conexión en tu cartera ${walletName}`,
retry: "Intentar de nuevo",
},
getStartedScreen: {
instruction: `Escanea el código QR para descargar la aplicación ${walletName}`,
},
scanScreen: {
instruction: `Escanea el código QR con la aplicación de cartera ${walletName} para conectarte`,
},
getStartedLink: `¿No tienes la cartera ${walletName}?`,
};
}
export function esDefault(): ThirdwebLocale {
return {
connectWallet: {
signIn: "Iniciar sesión",
defaultButtonTitle: "Conectar cartera",
connecting: "Conectando",
switchNetwork: "Cambiar red",
switchingNetwork: "Cambiando de red",
defaultModalTitle: "Conectar",
recommended: "Recomendado",
installed: "Instalado",
continueAsGuest: "Continuar como invitado",
connectAWallet: "Conectar una cartera",
newToWallets: "¿Nuevo en carteras?",
getStarted: "Comenzar",
guest: "Invitado",
send: "Enviar",
receive: "Recibir",
currentNetwork: "Red actual",
switchAccount: "Cambiar cuenta",
requestTestnetFunds: "Solicitar fondos de Testnet",
transactionHistory: "Historial de transacciones",
backupWallet: "Respaldar cartera",
guestWalletWarning:
"Esta es una cartera de invitado temporal. Haz una copia de seguridad si no quieres perder el acceso a ella",
switchTo: "Cambiar a",
connectedToSmartWallet: "Conectado a la cartera inteligente",
confirmInWallet: "Confirmar en cartera",
disconnectWallet: "Desconectar cartera",
copyAddress: "Copiar dirección",
personalWallet: "Cartera personal",
smartWallet: "Cartera inteligente",
or: "O",
download: {
chrome: "Descargar extensión para Chrome",
android: "Descargar en Google Play",
iOS: "Descargar en App Store",
},
welcomeScreen: {
defaultTitle: "Tu puerta de entrada al mundo descentralizado",
defaultSubtitle: "Conecta una cartera para empezar",
},
agreement: {
prefix: "Al conectar, aceptas los",
termsOfService: "Términos de servicio",
and: "y",
privacyPolicy: "Política de privacidad",
},
networkSelector: {
title: "Seleccionar red",
mainnets: "Redes principales",
testnets: "Redes de prueba",
allNetworks: "Todas",
addCustomNetwork: "Agregar red personalizada",
inputPlaceholder: "Buscar red o ID de cadena",
categoryLabel: {
recentlyUsed: "Usadas recientemente",
popular: "Populares",
others: "Todas las redes",
},
loading: "Cargando",
failedToSwitch: "Error al cambiar de red",
},
receiveFundsScreen: {
title: "Recibir fondos",
instruction:
"Copia la dirección de la cartera para enviar fondos a esta cartera",
},
sendFundsScreen: {
title: "Enviar fondos",
submitButton: "Enviar",
token: "Token",
sendTo: "Enviar a",
amount: "Cantidad",
successMessage: "Transacción exitosa",
invalidAddress: "Dirección inválida",
noTokensFound: "No se encontraron tokens",
searchToken: "Buscar o pegar la dirección del token",
transactionFailed: "Transacción fallida",
transactionRejected: "Transacción rechazada",
insufficientFunds: "Fondos insuficientes",
selectTokenTitle: "Seleccione un Token",
sending: "Enviando",
},
signatureScreen: {
instructionScreen: {
title: "Iniciar sesión",
instruction:
"Por favor, firma la solicitud de mensaje en tu cartera para continuar",
signInButton: "Iniciar sesión",
},
signingScreen: {
title: "Iniciando sesión",
prompt: "Firma la solicitud de firma en tu cartera",
promptForSafe:
"Firma la solicitud de firma en tu cartera y aprueba la transacción en Safe",
approveTransactionInSafe: "Aprobar transacción en Safe",
tryAgain: "Intentar de nuevo",
failedToSignIn: "Error al iniciar sesión",
},
},
},
wallets: {
walletConnect: {
scanInstruction:
"Escanea esto con tu aplicación de cartera para conectar",
},
smartWallet: {
connecting: "Conectando a Smart Wallet",
failedToConnect: "Error al conectar con Smart Wallet",
wrongNetworkScreen: {
title: "Red incorrecta",
subtitle: "Tu cartera no está conectada a la red requerida",
failedToSwitch: "Error al cambiar de red",
},
},
safeWallet: {
connectWalletScreen: {
title: "Vincular cartera personal",
subtitle: "Conecta tu cartera personal para usar Safe",
learnMoreLink: "Saber más",
},
accountDetailsScreen: {
title: "Introduce los detalles de tu safe",
findSafeAddressIn: "Puedes encontrar tu dirección de safe en",
dashboardLink: "Tablero de Safe",
network: "Red de Safe",
selectNetworkPlaceholder: "Red a la que se ha desplegado tu safe",
invalidChainConfig:
"No se puede usar Safe: No hay cadenas compatibles con Safe configuradas en la aplicación",
failedToConnect:
"No se pudo conectar con Safe. Asegúrate de que la dirección y red del safe son correctas",
failedToSwitch: "Error al cambiar de red",
switchNetwork: "Cambiar de red",
switchingNetwork: "Cambiando de red",
connectToSafe: "Conectar a Safe",
connecting: "Conectando",
mainnets: "Redes principales",
testnets: "Redes de prueba",
safeAddress: "Dirección de Safe",
},
},
coinbaseWallet: extensionAndQRScanScreens("Coinbase"),
metamaskWallet: extensionAndQRScanScreens("MetaMask"),
okxWallet: extensionAndQRScanScreens("OKX"),
coreWallet: extensionAndQRScanScreens("Core"),
phantomWallet: extensionAndQRScanScreens("Phantom"),
rainbowWallet: extensionAndQRScanScreens("Rainbow"),
trustWallet: extensionAndQRScanScreens("Trust"),
zerionWallet: extensionAndQRScanScreens("Zerion"),
paperWallet: {
signIn: "Iniciar sesión",
signInWithGoogle: "Iniciar sesión con Google",
emailPlaceholder: "Ingresa tu dirección de correo electrónico",
submitEmail: "Continuar",
invalidEmail: "Dirección de correo electrónico inválida",
emailRequired: "Se requiere dirección de correo electrónico",
googleLoginScreen: {
title: "Iniciar sesión",
instruction: "Selecciona tu cuenta de Google en la ventana emergente",
failed: "Error al iniciar sesión",
retry: "Reintentar",
},
emailLoginScreen: {
title: "Iniciar sesión",
enterCodeSendTo: "Ingresa el código de verificación enviado a",
newDeviceDetected: "Nuevo dispositivo detectado",
enterRecoveryCode:
"Ingresa el código de recuperación que se te envió por correo electrónico cuando te registraste por primera vez",
invalidCode: "Código de verificación inválido",
invalidCodeOrRecoveryCode:
"Código de verificación o de recuperación inválido",
verify: "Verificar",
failedToSendCode: "Error al enviar el código de verificación",
sendingCode: "Enviando código de verificación",
resendCode: "Reenviar código de verificación",
},
},
embeddedWallet: {
signInWithGoogle: "Iniciar sesión con Google",
emailPlaceholder: "Ingresa tu dirección de correo electrónico",
submitEmail: "Continuar",
signIn: "Iniciar sesión",
emailRequired: "Se requiere dirección de correo electrónico",
invalidEmail: "Dirección de correo electrónico inválida",
googleLoginScreen: {
title: "Iniciar sesión",
instruction: "Selecciona tu cuenta de Google en la ventana emergente",
failed: "Error al iniciar sesión",
retry: "Reintentar",
},
emailLoginScreen: {
title: "Iniciar sesión",
enterCodeSendTo: "Ingresa el código de verificación enviado a",
newDeviceDetected: "Nuevo dispositivo detectado",
enterRecoveryCode:
"Ingresa el código de recuperación que se te envió por correo electrónico cuando te registraste por primera vez",
invalidCode: "Código de verificación inválido",
invalidCodeOrRecoveryCode:
"Código de verificación o de recuperación inválido",
verify: "Verificar",
failedToSendCode: "Error al enviar el código de verificación",
sendingCode: "Enviando código de verificación",
resendCode: "Reenviar código de verificación",
},
createPassword: {
title: "Crear contraseña",
instruction:
"Establezca una contraseña para su cuenta. Necesitará esta contraseña cuando se conecte desde un nuevo dispositivo.",
saveInstruction: "Asegúrese de guardarla",
inputPlaceholder: "Ingrese su contraseña",
confirmation: "He guardado mi contraseña",
submitButton: "Establecer contraseña",
failedToSetPassword: "Error al establecer la contraseña",
},
enterPassword: {
title: "Ingrese la contraseña",
instruction: "Ingrese la contraseña de su cuenta",
inputPlaceholder: "Ingrese su contraseña",
submitButton: "Verificar",
wrongPassword: "Contraseña incorrecta",
},
},
magicLink: {
signIn: "Iniciar sesión",
loginWith: "Iniciar sesión con",
submitEmail: "Continuar",
loginWithEmailOrPhone:
"Iniciar sesión con correo electrónico o número de teléfono",
emailOrPhoneRequired:
"Se requiere correo electrónico o número de teléfono",
loginWithPhone: "Iniciar sesión con número de teléfono",
phoneRequired: "Se requiere número de teléfono",
invalidEmail: "Dirección de correo electrónico inválida",
invalidPhone: "Número de teléfono inválido",
invalidEmailOrPhone:
"Dirección de correo electrónico o número de teléfono inválido",
countryCodeMissing:
"El número de teléfono debe comenzar con un código de país",
emailPlaceholder: "Ingresa tu dirección de correo electrónico",
emailRequired: "Se requiere dirección de correo electrónico",
},
localWallet: {
passwordLabel: "Contraseña",
confirmPasswordLabel: "Confirmar contraseña",
enterYourPassword: "Ingresa tu contraseña",
warningScreen: {
title: "Advertencia",
warning:
"Tu cartera actual se eliminará si creas una nueva. Haz una copia de seguridad de la cartera en tu dispositivo antes de crear una nueva",
backupWallet: "Copia de seguridad de la cartera",
},
reconnectScreen: {
title: "Conectar a cartera guardada",
savedWallet: "Cartera guardada",
continue: "Continuar",
createNewWallet: "Crear una nueva cartera",
},
createScreen: {
instruction:
"Elige una contraseña para tu cartera. Podrás acceder y exportar esta cartera con la misma contraseña",
importWallet: "Importar cartera",
createNewWallet: "Crear nueva cartera",
connecting: "Conectando",
},
exportScreen: {
description1:
"Esto descargará un archivo JSON que contiene la información de la cartera en tu dispositivo cifrado con la contraseña",
description2:
"Puedes usar este archivo JSON para importar la cuenta en MetaMask usando la misma contraseña",
walletAddress: "Dirección de la cartera",
download: "Descargar",
title: "Respaldar cartera",
},
importScreen: {
title: "Importar cartera",
description1:
"La aplicación puede autorizar cualquier transacción en nombre de la cartera sin ninguna aprobación",
description2:
"Recomendamos conectar solo con aplicaciones de confianza",
import: "Importar",
uploadJSON: "Por favor sube un archivo JSON",
uploadedSuccessfully: "Subido con éxito",
},
},
frameWallet: {
...extensionAndQRScanScreens("Frame"),
connectionFailedScreen: {
title: "Fallo al conectar con Frame",
description:
"Asegúrate de que la aplicación de escritorio esté instalada y en funcionamiento. Puedes descargar Frame desde el enlace de abajo. Asegúrate de actualizar esta página una vez que Frame esté funcionando.",
downloadFrame: "Descargar Frame",
supportLink: "¿Sigues teniendo problemas para conectar?",
},
},
},
};
}
export function es(overrides?: DeepPartial<ThirdwebLocale>) {
const defaultObj = esDefault();
if (!overrides) {
return defaultObj;
}
return immutableOverride(defaultObj, overrides);
}
+324
View File
@@ -0,0 +1,324 @@
import { DeepPartial, immutableOverride } from "../utils/applyOverrides";
import { ThirdwebLocale } from "./en";
// wallets that connect via extension and QR scan
function extensionAndQRScanScreens(walletName: string) {
return {
connectionScreen: {
inProgress: "確認待ち",
failed: "接続に失敗しました",
instruction: `${walletName}のウォレットで接続リクエストを承認してください`,
retry: "再試行",
},
getStartedScreen: {
instruction: `QRコードをスキャンして${walletName}アプリをダウンロードしてください`,
},
scanScreen: {
instruction: `${walletName}のウォレットアプリでQRコードをスキャンして接続してください`,
},
getStartedLink: `${walletName}のウォレットを持っていませんか?`,
};
}
export function jaDefault(): ThirdwebLocale {
return {
connectWallet: {
signIn: "サインイン",
defaultButtonTitle: "ウォレット接続",
connecting: "接続中",
switchNetwork: "ネットワークを切り替える",
switchingNetwork: "ネットワークの切替中",
defaultModalTitle: "接続",
recommended: "推奨",
installed: "インストール済み",
continueAsGuest: "ゲストとして続ける",
connectAWallet: "ウォレットを接続する",
newToWallets: "ウォレットは初めてですか?",
getStarted: "始める",
guest: "ゲスト",
send: "送る",
receive: "受け取る",
currentNetwork: "現在のネットワーク",
switchAccount: "アカウントを切り替える",
requestTestnetFunds: "テストネットの資金をリクエストする",
transactionHistory: "取引履歴",
backupWallet: "ウォレットのバックアップ",
guestWalletWarning:
"これは一時的なゲストウォレットです。アクセスできなくなることを防ぐため、バックアップをしてください",
switchTo: "切り替え先", // Used in "Switch to <Wallet-Name>"
connectedToSmartWallet: "スマートウォレットに接続済み",
confirmInWallet: "ウォレットで確認",
disconnectWallet: "ウォレットの切断",
copyAddress: "アドレスをコピー",
personalWallet: "パーソナルウォレット",
smartWallet: "スマートウォレット",
or: "または",
download: {
chrome: "Chrome拡張をダウンロード",
android: "Google Playでダウンロード",
iOS: "App Storeでダウンロード",
},
welcomeScreen: {
defaultTitle: "分散型世界へのゲートウェイ",
defaultSubtitle: "始めるためにウォレットを接続してください",
},
agreement: {
prefix: "接続することで、以下に同意したことになります:",
termsOfService: "利用規約",
and: "および",
privacyPolicy: "プライバシーポリシー",
},
networkSelector: {
title: "ネットワークの選択",
mainnets: "メインネット",
testnets: "テストネット",
allNetworks: "すべて",
addCustomNetwork: "カスタムネットワークを追加",
inputPlaceholder: "ネットワーク名またはチェーンIDを検索",
categoryLabel: {
recentlyUsed: "最近使用したもの",
popular: "人気",
others: "全てのネットワーク",
},
loading: "読み込み中",
failedToSwitch: "ネットワークの切替に失敗しました",
},
receiveFundsScreen: {
title: "資金を受け取る",
instruction:
"このウォレットに資金を送るためのウォレットアドレスをコピーしてください",
},
sendFundsScreen: {
title: "資金の送付",
submitButton: "送信",
token: "トークン",
sendTo: "送信先",
amount: "金額",
successMessage: "取引成功",
invalidAddress: "無効なアドレス",
noTokensFound: "トークンが見つかりません",
searchToken: "トークンのアドレスを検索するか、貼り付けてください",
transactionFailed: "取引に失敗しました",
transactionRejected: "取引が拒否されました",
insufficientFunds: "資金が不足しています",
selectTokenTitle: "トークンを選択",
sending: "送信中",
},
signatureScreen: {
instructionScreen: {
title: "サインイン",
instruction:
"続行するためにウォレットでメッセージリクエストにサインしてください",
signInButton: "サインイン",
},
signingScreen: {
title: "サインイン中",
prompt: "ウォレットで署名リクエストにサインしてください",
promptForSafe:
"ウォレットで署名リクエストにサインし、Safeで取引を承認してください",
approveTransactionInSafe: "Safeで取引を承認",
tryAgain: "再試行",
failedToSignIn: "サインインに失敗しました",
},
},
},
wallets: {
walletConnect: {
scanInstruction:
"接続するためにウォレットアプリでこちらをスキャンしてください",
},
smartWallet: {
connecting: "スマートウォレットに接続中",
failedToConnect: "スマートウォレットに接続できませんでした",
wrongNetworkScreen: {
title: "異なるネットワーク",
subtitle: "ウォレットが必要なネットワークに接続されていません",
failedToSwitch: "ネットワークの切り替えに失敗しました",
},
},
safeWallet: {
connectWalletScreen: {
title: "パーソナルウォレットのリンク",
subtitle:
"Safeを使用するためにパーソナルウォレットを接続してください。",
learnMoreLink: "もっと詳しく",
},
accountDetailsScreen: {
title: "Safeの詳細を入力してください",
findSafeAddressIn: "Safeのアドレスは以下で見つけることができます", // You can find your safe address in + <dashboardLink>
dashboardLink: "Safeダッシュボード", // <dashboardLink>
network: "Safeネットワーク",
selectNetworkPlaceholder: "Safeがデプロイされているネットワーク",
invalidChainConfig:
"アプリにSafeをサポートするチェーンが設定されていないため、Safeを使用できません",
failedToConnect:
"Safeに接続できませんでした。Safeアドレスとネットワークが正しいことを確認してください。",
failedToSwitch: "ネットワークの切り替えに失敗しました",
switchNetwork: "ネットワークを切り替える",
switchingNetwork: "ネットワークを切り替え中",
connectToSafe: "Safeに接続",
connecting: "接続中",
mainnets: "メインネット",
testnets: "テストネット",
safeAddress: "Safeアドレス",
},
},
coinbaseWallet: extensionAndQRScanScreens("Coinbase"),
metamaskWallet: extensionAndQRScanScreens("MetaMask"),
okxWallet: extensionAndQRScanScreens("OKX"),
coreWallet: extensionAndQRScanScreens("Core"),
phantomWallet: extensionAndQRScanScreens("Phantom"),
rainbowWallet: extensionAndQRScanScreens("Rainbow"),
trustWallet: extensionAndQRScanScreens("Trust"),
zerionWallet: extensionAndQRScanScreens("Zerion"),
paperWallet: {
signIn: "サインイン",
signInWithGoogle: "Googleでサインイン",
emailPlaceholder: "メールアドレスを入力してください",
submitEmail: "続ける",
invalidEmail: "無効なメールアドレス",
emailRequired: "メールアドレスが必要です",
googleLoginScreen: {
title: "サインイン",
instruction: "ポップアップ内でGoogleアカウントを選択してください",
failed: "サインインに失敗しました",
retry: "再試行",
},
emailLoginScreen: {
title: "サインイン",
enterCodeSendTo: "送信された確認コードを入力してください", // Enter the verification code sent to + <email>
newDeviceDetected: "新しいデバイスが検出されました",
enterRecoveryCode:
"初回サインアップ時にメールで送信されたリカバリーコードを入力してください",
invalidCode: "無効な確認コード",
invalidCodeOrRecoveryCode: "無効な確認コードまたはリカバリーコード",
verify: "確認",
failedToSendCode: "確認コードの送信に失敗しました",
sendingCode: "確認コードを送信中",
resendCode: "確認コードを再送",
},
},
embeddedWallet: {
signInWithGoogle: "Googleでサインイン",
emailPlaceholder: "メールアドレスを入力してください",
submitEmail: "続ける",
emailRequired: "メールアドレスが必要です",
invalidEmail: "無効なメールアドレス",
signIn: "サインイン",
googleLoginScreen: {
title: "サインイン",
instruction: "ポップアップ内でGoogleアカウントを選択してください",
failed: "サインインに失敗しました",
retry: "再試行",
},
emailLoginScreen: {
title: "サインイン",
enterCodeSendTo: "送信された確認コードを入力してください",
newDeviceDetected: "新しいデバイスが検出されました",
enterRecoveryCode:
"初回サインアップ時にメールで送信されたリカバリーコードを入力してください",
invalidCode: "無効な確認コード",
invalidCodeOrRecoveryCode: "無効な確認コードまたはリカバリーコード",
verify: "確認",
failedToSendCode: "確認コードの送信に失敗しました",
sendingCode: "確認コードを送信中",
resendCode: "確認コードを再送",
},
createPassword: {
title: "パスワードを作成",
instruction:
"アカウントのパスワードを設定してください。新しいデバイスから接続する際にこのパスワードが必要となります。",
saveInstruction: "パスワードは必ず保存してください。",
inputPlaceholder: "パスワードを入力してください",
confirmation: "パスワードを保存しました。",
submitButton: "パスワードを設定",
failedToSetPassword: "パスワードの設定に失敗しました。",
},
enterPassword: {
title: "パスワードを入力",
instruction: "アカウントのパスワードを入力してください",
inputPlaceholder: "パスワードを入力してください",
submitButton: "確認",
wrongPassword: "パスワードが違います",
},
},
magicLink: {
signIn: "サインイン",
loginWith: "次でログイン:",
submitEmail: "続ける",
loginWithEmailOrPhone: "メールアドレスまたは電話番号でログイン",
emailOrPhoneRequired: "メールアドレスまたは電話番号が必要です",
loginWithPhone: "電話番号でログイン",
phoneRequired: "電話番号が必要です",
invalidEmail: "無効なメールアドレス",
invalidPhone: "無効な電話番号",
invalidEmailOrPhone: "無効なメールアドレスまたは電話番号",
countryCodeMissing: "電話番号は国コードから始める必要があります",
emailPlaceholder: "メールアドレスを入力してください",
emailRequired: "メールアドレスが必要です",
},
localWallet: {
passwordLabel: "パスワード",
confirmPasswordLabel: "パスワードを確認",
enterYourPassword: "パスワードを入力してください",
warningScreen: {
title: "警告",
warning:
"新しいウォレットを作成すると、現在のウォレットは削除されます。新しいウォレットを作成する前に、ウォレットのバックアップをデバイスに保存してください",
backupWallet: "ウォレットのバックアップ",
},
reconnectScreen: {
title: "保存されたウォレットへの接続",
savedWallet: "保存されたウォレット",
continue: "続ける",
createNewWallet: "新しいウォレットを作成",
},
createScreen: {
instruction:
"ウォレットのパスワードを選択してください。このパスワードで、このウォレットにアクセスしたり、同じパスワードでエクスポートすることができます",
importWallet: "ウォレットをインポート",
createNewWallet: "新しいウォレットを作成",
connecting: "接続中",
},
exportScreen: {
description1:
"この操作は、ウォレット情報を含む、パスワードで暗号化されたJSONファイルをデバイスにダウンロードします",
description2:
"このJSONファイルを使用して、同じパスワードでMetaMaskにアカウントをインポートすることができます",
walletAddress: "ウォレットアドレス",
download: "ダウンロード",
title: "ウォレットのバックアップ",
},
importScreen: {
title: "ウォレットをインポート",
description1:
"アプリケーションは、ウォレットの代わりに任意の取引を承認なしで認証することができます",
description2:
"信頼できるアプリケーションにのみ接続することをお勧めします",
import: "インポート",
uploadJSON: "JSONファイルをアップロードしてください",
uploadedSuccessfully: "正常にアップロードされました",
},
},
frameWallet: {
...extensionAndQRScanScreens("Frame"),
connectionFailedScreen: {
title: "Frameに接続できませんでした",
description:
"デスクトップアプリがインストールされていて実行中であることを確認してください。以下のリンクからFrameをダウンロードすることができます。Frameが実行されている場合は、このページをリフレッシュしてください。",
downloadFrame: "Frameをダウンロード",
supportLink: "まだ接続に問題がありますか?",
},
},
},
};
}
export function ja(overrides?: DeepPartial<ThirdwebLocale>) {
const defaultObj = jaDefault();
if (!overrides) {
return defaultObj;
}
return immutableOverride(defaultObj, overrides);
}
@@ -0,0 +1,9 @@
import { createContext, useContext } from "react";
import { ThirdwebLocale, enDefault } from "../locales/en";
export const ThirdwebLocaleContext = /* @__PURE__ */ (() =>
createContext<ThirdwebLocale>(enDefault()))();
export function useTWLocale() {
return useContext(ThirdwebLocaleContext);
}
@@ -12,6 +12,8 @@ import { defaultWallets } from "../../wallet/wallets/defaultWallets";
import { CustomThemeProvider } from "../../design-system/CustomThemeProvider";
import { signerWallet } from "../../wallet/wallets/signerWallet";
import { Signer } from "ethers";
import { ThirdwebLocale, en } from "../locales/en";
import { ThirdwebLocaleContext } from "./locale-provider";
import { walletIds } from "@thirdweb-dev/wallets";
interface ThirdwebProviderProps<TChains extends Chain[]>
@@ -41,6 +43,8 @@ interface ThirdwebProviderProps<TChains extends Chain[]>
theme?: ThemeObjectOrType;
signer?: Signer;
locale?: ThirdwebLocale;
}
/**
@@ -105,18 +109,20 @@ export const ThirdwebProvider = <
}, [restProps.clientId, wallets]);
return (
<WalletUIStatesProvider theme={theme}>
<CustomThemeProvider theme={theme}>
<ThirdwebProviderCore
{...restProps}
theme={typeof theme === "string" ? theme : theme.type}
supportedWallets={wallets}
signerWallet={signerWalletConfig}
>
{children}
<ConnectModal />
</ThirdwebProviderCore>
</CustomThemeProvider>
</WalletUIStatesProvider>
<ThirdwebLocaleContext.Provider value={restProps.locale || en()}>
<WalletUIStatesProvider theme={theme}>
<CustomThemeProvider theme={theme}>
<ThirdwebProviderCore
{...restProps}
theme={typeof theme === "string" ? theme : theme.type}
supportedWallets={wallets}
signerWallet={signerWalletConfig}
>
{children}
<ConnectModal />
</ThirdwebProviderCore>
</CustomThemeProvider>
</WalletUIStatesProvider>
</ThirdwebLocaleContext.Provider>
);
};
@@ -1,8 +1,8 @@
import { useState, createContext, useContext } from "react";
import { defaultModalTitle } from "../../wallet/ConnectWallet/constants";
import { isMobile } from "../utils/isMobile";
import { Theme } from "../../design-system";
import { WelcomeScreen } from "../../wallet/ConnectWallet/screens/types";
import { useTWLocale } from "./locale-provider";
type BoolSetter = (value: boolean) => void;
@@ -51,9 +51,10 @@ export const WalletUIStatesProvider = (
) => {
const [isWalletModalOpen, setIsWalletModalOpen] = useState(false);
const _isMobile = isMobile();
const locale = useTWLocale();
const [modalConfig, setModalConfig] = useState<ModalConfig>({
title: props.title || defaultModalTitle,
title: props.title || locale.connectWallet.defaultModalTitle,
theme: props.theme || "dark",
data: undefined,
modalSize: (_isMobile ? "compact" : props.modalSize) || "wide",
@@ -0,0 +1,33 @@
export type DeepPartial<T extends Record<string, any>> = {
[P in keyof T]?: DeepPartial<T[P]>;
};
/**
* Create a new object with given overrides object applied on top of given base object without mutating the base and overrides object
*
* @param defaultObj the object to use as the base object
* @param overrides the object to use as the overrides
*/
export function immutableOverride<T extends Record<string, any>>(
defaultObj: T,
overrides: DeepPartial<T>,
): T {
const output = { ...defaultObj };
for (const key in overrides) {
const value = overrides[key];
// only apply overrides if it is not undefined
if (value !== undefined) {
// partially apply overrides object
if (typeof value === "object" && value !== null) {
output[key] = immutableOverride(defaultObj[key], value);
}
// completely override non-object values
else {
output[key] = value;
}
}
}
return output;
}
+6 -1
View File
@@ -19,9 +19,14 @@ export { walletConnect } from "./wallet/wallets/walletConnect/walletConnect";
export { walletConnectV1 } from "./wallet/wallets/walletConnectV1";
export { zerionWallet } from "./wallet/wallets/zerion/zerionWallet";
export { okxWallet } from "./wallet/wallets/okx/okxWallet";
export { coreWallet } from "./wallet/wallets/coreWallet/coreWallet";
export { darkTheme, lightTheme } from "./design-system/index";
export type { Theme, ThemeOverrides } from "./design-system/index";
// at the moment we'll re-export everything from the evm package
export * from "./evm";
// locales
export { en } from "./evm/locales/en";
export { ja } from "./evm/locales/ja";
export { es } from "./evm/locales/es";
@@ -22,7 +22,7 @@ import { Button } from "../../components/buttons";
import { Spinner } from "../../components/Spinner";
import styled from "@emotion/styled";
import type { NetworkSelectorProps } from "./NetworkSelector";
import { defaultModalTitle, onModalUnmount } from "./constants";
import { onModalUnmount } from "./constants";
import { isMobile } from "../../evm/utils/isMobile";
import { CustomThemeProvider } from "../../design-system/CustomThemeProvider";
import { WelcomeScreen } from "./screens/types";
@@ -33,6 +33,7 @@ import { Container } from "../../components/basic";
import { LockIcon } from "./icons/LockIcon";
import { SignatureScreen } from "./SignatureScreen";
import { Modal } from "../../components/Modal";
import { useTWLocale } from "../../evm/providers/locale-provider";
export type ConnectWalletProps = {
className?: string;
@@ -134,6 +135,13 @@ export type ConnectWalletProps = {
* ```
*/
displayBalanceToken?: Record<number, string>;
/**
* Hide the "switch to Personal wallet" option in the dropdown which is shown when wallet is connected to either Smart Wallet or Safe
*
* @default false
*/
hideSwitchToPersonalWallet?: boolean;
};
const TW_CONNECT_WALLET = "tw-connect-wallet";
@@ -148,12 +156,13 @@ export const ConnectWallet: React.FC<ConnectWalletProps> = (props) => {
const contextTheme = useTheme() as ThemeObjectOrType;
const theme = props.theme || contextTheme || "dark";
const connectionStatus = useConnectionStatus();
const locale = useTWLocale();
const walletConfigs = useWallets();
const isLoading =
connectionStatus === "connecting" || connectionStatus === "unknown";
const btnTitle = props.btnTitle || "Connect Wallet";
const btnTitle = props.btnTitle || locale.connectWallet.defaultButtonTitle;
const setIsWalletModalOpen = useSetIsWalletModalOpen();
const setModalConfig = useContext(SetModalConfigCtx);
@@ -241,7 +250,9 @@ export const ConnectWallet: React.FC<ConnectWalletProps> = (props) => {
...props.style,
}}
aria-label={
connectionStatus === "connecting" ? "Connecting" : btnTitle
connectionStatus === "connecting"
? locale.connectWallet.connecting
: btnTitle
}
onClick={() => {
let modalSize = props.modalSize || "wide";
@@ -251,7 +262,8 @@ export const ConnectWallet: React.FC<ConnectWalletProps> = (props) => {
}
setModalConfig({
title: props.modalTitle || defaultModalTitle,
title:
props.modalTitle || locale.connectWallet.defaultModalTitle,
theme,
data: undefined,
modalSize,
@@ -310,7 +322,7 @@ export const ConnectWallet: React.FC<ConnectWalletProps> = (props) => {
>
<Container flex="row" center="y" gap="sm">
<LockIcon size={iconSize.sm} />
<span> Sign in </span>
<span> {locale.connectWallet.signIn} </span>
</Container>
</Button>
);
@@ -334,6 +346,7 @@ export const ConnectWallet: React.FC<ConnectWalletProps> = (props) => {
props?.auth?.onLogout?.();
}
}}
hideSwitchToPersonalWallet={props.hideSwitchToPersonalWallet}
/>
);
})()}
@@ -348,6 +361,7 @@ function SwitchNetworkButton(props: {
const { activeChain } = useWalletContext();
const switchChain = useSwitchChain();
const [switching, setSwitching] = useState(false);
const locale = useTWLocale();
return (
<AnimatedButton
@@ -372,12 +386,12 @@ function SwitchNetworkButton(props: {
minWidth: "140px",
...props.style,
}}
aria-label={switching ? "Switching Network" : undefined}
aria-label={switching ? locale.connectWallet.switchingNetwork : undefined}
>
{switching ? (
<Spinner size="sm" color="primaryButtonText" />
) : (
"Switch Network"
locale.connectWallet.switchNetwork
)}
</AnimatedButton>
);
@@ -60,6 +60,7 @@ import { SendFunds } from "./SendFunds";
import { SupportedTokens } from "./defaultTokens";
import { ReceiveFunds } from "./ReceiveFunds";
import { smartWalletIcon } from "./icons/dataUris";
import { useTWLocale } from "../../evm/providers/locale-provider";
export type DropDownPosition = {
side: "top" | "bottom" | "left" | "right";
@@ -82,7 +83,9 @@ export const ConnectedWalletDetails: React.FC<{
theme: "light" | "dark" | Theme;
supportedTokens: SupportedTokens;
displayBalanceToken?: Record<number, string>;
hideSwitchToPersonalWallet?: boolean;
}> = (props) => {
const locale = useTWLocale().connectWallet;
const chain = useChain();
const walletChainId = useChainId();
@@ -164,7 +167,7 @@ export const ConnectedWalletDetails: React.FC<{
}}
/>
<Container flex="column" gap="xs">
<Container flex="column" gap="xxs">
{/* Address */}
{activeWallet?.walletId === walletIds.localWallet ? (
<Text
@@ -174,7 +177,7 @@ export const ConnectedWalletDetails: React.FC<{
minWidth: "70px",
}}
>
Guest
{locale.guest}
</Text>
) : addressOrENS ? (
<Text
@@ -206,44 +209,44 @@ export const ConnectedWalletDetails: React.FC<{
</WalletInfoButton>
);
const networkSwitcherButton = (
<ToolTip
tip={
disableSwitchChain ? "Network Switching is disabled" : "Switch Network"
}
let networkSwitcherButton = (
<MenuButton
type="button"
disabled={disableSwitchChain}
onClick={() => {
setIsDropdownOpen(false);
setShowNetworkSelector(true);
}}
>
<MenuButton
type="button"
disabled={disableSwitchChain}
onClick={() => {
setIsDropdownOpen(false);
setShowNetworkSelector(true);
<div
style={{
display: "flex",
alignItems: "center",
position: "relative",
}}
>
<div
style={{
display: "flex",
alignItems: "center",
position: "relative",
}}
>
<ChainIcon chain={chain} size={iconSize.md} active />
</div>
<Text size="sm" color="primaryText" multiline>
{chain?.name || `Unknown chain #${walletChainId}`}
</Text>
<StyledChevronRightIcon
width={iconSize.sm}
height={iconSize.sm}
style={{
flexShrink: 0,
marginLeft: "auto",
}}
/>
</MenuButton>
</ToolTip>
<ChainIcon chain={chain} size={iconSize.md} active />
</div>
<Text size="sm" color="primaryText" multiline>
{chain?.name || `Unknown chain #${walletChainId}`}
</Text>
<StyledChevronRightIcon
width={iconSize.sm}
height={iconSize.sm}
style={{
flexShrink: 0,
marginLeft: "auto",
}}
/>
</MenuButton>
);
if (!disableSwitchChain) {
networkSwitcherButton = (
<ToolTip tip={locale.switchNetwork}>{networkSwitcherButton}</ToolTip>
);
}
const content = (
<div>
{/* Balance and Account Address */}
@@ -285,14 +288,14 @@ export const ConnectedWalletDetails: React.FC<{
>
<CopyIcon
text={address || ""}
tip="Copy Address"
tip={locale.copyAddress}
side="bottom"
/>
</IconButton>
</div>
<ToolTip
tip="Disconnect Wallet"
tip={locale.disconnectWallet}
side="bottom"
align={"end"}
sideOffset={10}
@@ -326,7 +329,7 @@ export const ConnectedWalletDetails: React.FC<{
<ConnectedToSmartWallet />
{/* Send and Recive */}
{/* Send and Receive */}
<Container
style={{
display: "grid",
@@ -355,7 +358,7 @@ export const ConnectedWalletDetails: React.FC<{
transform: "translateY(-10%) rotate(-45deg) ",
}}
/>
Send
{locale.send}
</Button>
<Button
@@ -372,7 +375,8 @@ export const ConnectedWalletDetails: React.FC<{
setIsDropdownOpen(false);
}}
>
<PinBottomIcon width={iconSize.sm} height={iconSize.sm} /> Receive{" "}
<PinBottomIcon width={iconSize.sm} height={iconSize.sm} />{" "}
{locale.receive}{" "}
</Button>
</Container>
@@ -380,7 +384,7 @@ export const ConnectedWalletDetails: React.FC<{
{/* Network Switcher */}
<div>
<DropdownLabel>Current Network</DropdownLabel>
<DropdownLabel>{locale.currentNetwork}</DropdownLabel>
<Spacer y="xs" />
{networkSwitcherButton}
</div>
@@ -389,22 +393,24 @@ export const ConnectedWalletDetails: React.FC<{
<Container flex="column" gap="sm">
{/* Switch to Personal Wallet for Safe */}
{personalWallet && personalWalletConfig && (
<WalletSwitcher
wallet={personalWallet}
name="Personal Wallet"
onSwitch={() => {
setWrapperWallet(activeWallet);
}}
/>
)}
{personalWallet &&
personalWalletConfig &&
!props.hideSwitchToPersonalWallet && (
<WalletSwitcher
wallet={personalWallet}
name={locale.personalWallet}
onSwitch={() => {
setWrapperWallet(activeWallet);
}}
/>
)}
{/* Switch to Wrapper Wallet */}
{wrapperWalletConfig && wrapperWallet && (
<WalletSwitcher
name={
wrapperWallet.walletId === walletIds.smartWallet
? "Smart Wallet"
? locale.smartWallet
: wrapperWalletConfig.meta.name
}
wallet={wrapperWallet}
@@ -433,7 +439,7 @@ export const ConnectedWalletDetails: React.FC<{
<Container color="secondaryText">
<ShuffleIcon width={iconSize.sm} height={iconSize.sm} />
</Container>
Switch Account
{locale.switchAccount}
</MenuButton>
)}
@@ -462,7 +468,7 @@ export const ConnectedWalletDetails: React.FC<{
<Container flex="row" center="both" color="secondaryText">
<FundsIcon size={iconSize.sm} />
</Container>
Request Testnet Funds
{locale.requestTestnetFunds}
</MenuLink>
)}
@@ -481,7 +487,7 @@ export const ConnectedWalletDetails: React.FC<{
<Container flex="row" center="both" color="secondaryText">
<TextAlignLeftIcon width={iconSize.sm} height={iconSize.sm} />
</Container>
Transaction History
{locale.transactionHistory}
</MenuLink>
)}
@@ -500,13 +506,11 @@ export const ConnectedWalletDetails: React.FC<{
<Container flex="row" center="both" color="secondaryText">
<PinBottomIcon width={iconSize.sm} height={iconSize.sm} />
</Container>
Backup wallet{" "}
{locale.backupWallet}{" "}
</MenuButton>
<Spacer y="sm" />
<Text size="xs" center multiline color="danger">
This is a temporary guest wallet <br />
Backup if you {`don't `}
want to lose access to it
<Spacer y="md" />
<Text size="xs" center multiline color="danger" balance>
{locale.guestWalletWarning}
</Text>
</div>
)}
@@ -596,7 +600,7 @@ const dropdownContentFade = keyframes`
const DropDownContent = /* @__PURE__ */ (() => styled(DropdownMenu.Content)<{
theme?: Theme;
}>`
width: 320px;
width: 360px;
box-sizing: border-box;
max-width: 100%;
border-radius: ${radius.lg};
@@ -607,7 +611,7 @@ const DropDownContent = /* @__PURE__ */ (() => styled(DropdownMenu.Content)<{
background-color: ${(props) => props.theme.colors.dropdownBg};
--bg: ${(props) => props.theme.colors.dropdownBg};
z-index: 1000000;
line-height: 1;
line-height: normal;
`)();
const WalletInfoButton = styled.button<{ theme?: Theme }>`
@@ -623,7 +627,7 @@ const WalletInfoButton = styled.button<{ theme?: Theme }>`
gap: ${spacing.sm};
box-sizing: border-box;
-webkit-tap-highlight-color: transparent;
line-height: 1;
line-height: normal;
animation: ${fadeInAnimation} 300ms ease;
${media.mobile} {
@@ -727,6 +731,7 @@ function WalletSwitcher({
name: string;
}) {
const walletContext = useWalletContext();
const locale = useTWLocale().connectWallet;
return (
<MenuButton
@@ -742,7 +747,7 @@ function WalletSwitcher({
<Container color="secondaryText">
<EnterIcon width={iconSize.sm} height={iconSize.sm} />
</Container>
Switch to {name}
{locale.switchTo} {name}
</MenuButton>
);
}
@@ -758,6 +763,7 @@ function ConnectedToSmartWallet() {
const activeWallet = useWallet();
const chain = useChain();
const address = useAddress();
const locale = useTWLocale().connectWallet;
const [isSmartWalletDeployed, setIsSmartWalletDeployed] = useState(false);
@@ -784,7 +790,7 @@ function ConnectedToSmartWallet() {
>
<Container flex="row" gap="xs" center="y">
<ActiveDot />
Connected to Smart Wallet
{locale.connectedToSmartWallet}
</Container>
{isSmartWalletDeployed && (
<ChevronRightIcon width={iconSize.sm} height={iconSize.sm} />
@@ -18,7 +18,7 @@ import {
import { useCallback, useEffect, useContext, useState } from "react";
import {
reservedScreens,
compactmodalMaxHeight,
compactModalMaxHeight as compactModalMaxHeight,
onModalUnmount,
} from "../constants";
import { HeadlessConnectUI } from "../../wallets/headlessConnectUI";
@@ -173,7 +173,7 @@ export const ConnectModalContent = (props: {
scrollY
relative
style={{
maxHeight: compactmodalMaxHeight,
maxHeight: compactModalMaxHeight,
}}
>
{screen === reservedScreens.signIn && (
@@ -9,10 +9,9 @@ import {
WalletUIStatesProvider,
} from "../../../evm/providers/wallet-ui-states-provider";
import {
widemodalMaxHeight,
wideModalMaxHeight,
modalMaxWidthCompact,
modalMaxWidthWide,
defaultModalTitle,
defaultTheme,
} from "../constants";
import { ConnectModalContent } from "./ConnectModal";
@@ -23,6 +22,7 @@ import { DynamicHeight } from "../../../components/DynamicHeight";
import { CustomThemeProvider } from "../../../design-system/CustomThemeProvider";
import { ComponentProps, useContext, useEffect } from "react";
import { ConnectWalletProps } from "../ConnectWallet";
import { useTWLocale } from "../../../evm/providers/locale-provider";
export const ConnectModalInline = (
props: Omit<
@@ -34,6 +34,7 @@ export const ConnectModalInline = (
| "hideTestnetFaucet"
| "switchToActiveChain"
| "supportedTokens"
| "hideSwitchToPersonalWallet"
> & {
onModalHide?: () => void;
},
@@ -87,7 +88,7 @@ export const ConnectModalInline = (
<ConnectModalInlineContainer
className={props.className}
style={{
height: modalSize === "compact" ? "auto" : widemodalMaxHeight,
height: modalSize === "compact" ? "auto" : wideModalMaxHeight,
maxWidth:
modalSize === "compact"
? modalMaxWidthCompact
@@ -110,12 +111,13 @@ function SyncedWalletUIStates(
props: ComponentProps<typeof WalletUIStatesProvider>,
) {
const setModalConfig = useContext(SetModalConfigCtx);
const locale = useTWLocale();
// update modalConfig on props change
useEffect(() => {
setModalConfig((c) => ({
...c,
title: props.title || defaultModalTitle,
title: props.title || locale.connectWallet.defaultModalTitle,
theme: props.theme || "dark",
modalSize: (isMobile() ? "compact" : props.modalSize) || "wide",
termsOfServiceUrl: props.termsOfServiceUrl,
@@ -132,6 +134,7 @@ function SyncedWalletUIStates(
props.welcomeScreen,
props.titleIconUrl,
setModalConfig,
locale.connectWallet.defaultModalTitle,
]);
return <WalletUIStatesProvider {...props} />;
@@ -147,7 +150,7 @@ const ConnectModalInlineContainer = styled.div<{ theme?: Theme }>`
box-shadow: ${shadow.lg};
position: relative;
border: 1px solid ${(p) => p.theme.colors.borderColor};
line-height: 1;
line-height: normal;
overflow: hidden;
font-family: ${(p) => p.theme.fontFamily};
& *::selection {
@@ -1,11 +1,13 @@
import { Container } from "../../../components/basic";
import { Link, Text } from "../../../components/text";
import { useTWLocale } from "../../../evm/providers/locale-provider";
export function TOS(props: {
termsOfServiceUrl?: string;
privacyPolicyUrl?: string;
}) {
const { termsOfServiceUrl, privacyPolicyUrl } = props;
const locale = useTWLocale().connectWallet.agreement;
if (!termsOfServiceUrl && !privacyPolicyUrl) {
return null;
@@ -25,7 +27,7 @@ export function TOS(props: {
maxWidth: "250px",
}}
>
By connecting, you agree to the{" "}
{locale.prefix}{" "}
{termsOfServiceUrl && (
<Link
inline
@@ -37,13 +39,13 @@ export function TOS(props: {
}}
>
{" "}
Terms of Service{" "}
{locale.termsOfService}{" "}
</Link>
)}
{bothGiven && "& "}
{bothGiven && locale.and + " "}
{privacyPolicyUrl && (
<Link inline size="xs" href={privacyPolicyUrl} target="_blank">
Privacy Policy
{locale.privacyPolicy}
</Link>
)}
</Text>
@@ -30,6 +30,7 @@ import { Text } from "../../components/text";
import { ModalTitle } from "../../components/modalElements";
import { CustomThemeProvider } from "../../design-system/CustomThemeProvider";
import { useTheme } from "@emotion/react";
import { useTWLocale } from "../../evm/providers/locale-provider";
type RenderChain = React.FC<{
chain: Chain;
@@ -72,6 +73,7 @@ export const NetworkSelector: React.FC<NetworkSelectorProps> = (props) => {
const theme = props.theme || themeFromContext || "dark";
const supportedChains = useSupportedChains();
const chains = props.chains || supportedChains;
const locale = useTWLocale().connectWallet.networkSelector;
const _recentChains = props.recentChains;
@@ -149,7 +151,7 @@ export const NetworkSelector: React.FC<NetworkSelectorProps> = (props) => {
>
<Container>
<Container p="lg">
<ModalTitle>Select Network</ModalTitle>
<ModalTitle>{locale.title}</ModalTitle>
</Container>
<Tabs.Root className="TabsRoot" defaultValue="all">
@@ -163,13 +165,13 @@ export const NetworkSelector: React.FC<NetworkSelectorProps> = (props) => {
}}
>
<TabButton className="TabsTrigger" value="all">
All
{locale.allNetworks}
</TabButton>
<TabButton className="TabsTrigger" value="mainnet">
Mainnets
{locale.mainnets}
</TabButton>
<TabButton className="TabsTrigger" value="testnet">
Testnets
{locale.testnets}
</TabButton>
</Tabs.List>
</Container>
@@ -198,7 +200,7 @@ export const NetworkSelector: React.FC<NetworkSelectorProps> = (props) => {
}}
tabIndex={-1}
variant="outline"
placeholder="Search Network or Chain ID"
placeholder={locale.inputPlaceholder}
value={searchTerm}
onChange={(e) => {
setSearchTerm(e.target.value);
@@ -296,7 +298,7 @@ export const NetworkSelector: React.FC<NetworkSelectorProps> = (props) => {
boxShadow: "none",
}}
>
Add Custom Network
{locale.addCustomNetwork}
</Button>
</Container>
</>
@@ -344,6 +346,7 @@ const NetworkTab = (props: {
() => filterChainByType(props.popularChains || [], props.type),
[props.type, props.popularChains],
);
const locale = useTWLocale().connectWallet.networkSelector.categoryLabel;
return (
<Container
@@ -356,7 +359,7 @@ const NetworkTab = (props: {
>
{recentChains.length > 0 && (
<div>
<SectionLabel>Recently Used</SectionLabel>
<SectionLabel>{locale.recentlyUsed}</SectionLabel>
<Spacer y="sm" />
<NetworkList
chains={recentChains}
@@ -370,7 +373,7 @@ const NetworkTab = (props: {
{popularChains.length > 0 && (
<div>
<SectionLabel>Popular</SectionLabel>
<SectionLabel>{locale.popular}</SectionLabel>
<Spacer y="sm" />
<NetworkList
chains={popularChains}
@@ -385,7 +388,7 @@ const NetworkTab = (props: {
{/* separator */}
{(popularChains.length > 0 || recentChains.length > 0) && (
<>
<SectionLabel>All Networks</SectionLabel>
<SectionLabel>{locale.others}</SectionLabel>
<Spacer y="sm" />
</>
)}
@@ -410,6 +413,8 @@ const NetworkList = /* @__PURE__ */ memo(function NetworkList(props: {
const activeChainId = useChainId();
const [switchingChainId, setSwitchingChainId] = useState(-1);
const [errorSwitchingChainId, setErrorSwitchingChainId] = useState(-1);
const twLocale = useTWLocale();
const locale = twLocale.connectWallet.networkSelector;
const close = props.close;
@@ -456,7 +461,7 @@ const NetworkList = /* @__PURE__ */ memo(function NetworkList(props: {
}}
>
{/* Don't put a spinner here - it's gonna freeze */}
<Text>Loading</Text>
<Text>{locale.loading}</Text>
</Container>
);
}
@@ -513,7 +518,7 @@ const NetworkList = /* @__PURE__ */ memo(function NetworkList(props: {
{confirming && (
<>
<Text size="xs" color="accentText">
Confirm in Wallet
{twLocale.connectWallet.confirmInWallet}
</Text>
<Spinner size="xs" color="accentText" />
</>
@@ -522,7 +527,7 @@ const NetworkList = /* @__PURE__ */ memo(function NetworkList(props: {
{switchingFailed && (
<Container animate="fadein">
<Text size="xs" color="danger">
Failed to Switch Network
{locale.failedToSwitch}
</Text>
</Container>
)}
@@ -9,14 +9,16 @@ import { Theme, iconSize, radius, spacing } from "../../design-system";
import { QRCode } from "../../components/QRCode";
import { Img } from "../../components/Img";
import { useClipboard } from "../../evm/components/hooks/useCopyClipboard";
import { useTWLocale } from "../../evm/providers/locale-provider";
export function ReceiveFunds(props: { iconUrl: string }) {
const address = useAddress();
const { hasCopied, onCopy } = useClipboard(address || "");
const locale = useTWLocale().connectWallet.receiveFundsScreen;
return (
<Container p="lg">
<ModalHeader title="Receive Funds" />
<ModalHeader title={locale.title} />
<Spacer y="xl" />
@@ -35,7 +37,7 @@ export function ReceiveFunds(props: { iconUrl: string }) {
</Container>
<Spacer y="xl" />
<WalletAddressContianer onClick={onCopy}>
<WalletAddressContainer onClick={onCopy}>
<Text color="primaryText" size="md">
{shortenString(address || "")}
</Text>
@@ -44,19 +46,18 @@ export function ReceiveFunds(props: { iconUrl: string }) {
tip="Copy address"
hasCopied={hasCopied}
/>
</WalletAddressContianer>
</WalletAddressContainer>
<Spacer y="lg" />
<Text multiline center>
Copy the wallet address to <br />
send funds to this wallet
<Text multiline center balance>
{locale.instruction}
</Text>
</Container>
);
}
const WalletAddressContianer = styled.button<{ theme?: Theme }>`
const WalletAddressContainer = styled.button<{ theme?: Theme }>`
all: unset;
width: 100%;
box-sizing: border-box;
@@ -23,6 +23,7 @@ import { SupportedTokens, TokenInfo, defaultTokens } from "./defaultTokens";
import { Img } from "../../components/Img";
import styled from "@emotion/styled";
import { NATIVE_TOKEN_ADDRESS } from "@thirdweb-dev/sdk";
import { useTWLocale } from "../../evm/providers/locale-provider";
// TODO - use a better way to fetch token Info instead of useBalance
@@ -50,7 +51,7 @@ export function SendFunds(props: { supportedTokens: SupportedTokens }) {
}
const [token, setToken] = useState<TokenInfo | undefined>(defaultToken);
const [receieverAddress, setReceieverAddress] = useState("");
const [receiverAddress, setReceiverAddress] = useState("");
const [amount, setAmount] = useState("0");
if (screen === "tokenSelector") {
@@ -74,8 +75,8 @@ export function SendFunds(props: { supportedTokens: SupportedTokens }) {
onTokenSelect={() => {
setScreen("tokenSelector");
}}
receieverAddress={receieverAddress}
setReceieverAddress={setReceieverAddress}
receiverAddress={receiverAddress}
setReceiverAddress={setReceiverAddress}
amount={amount}
setAmount={setAmount}
/>
@@ -85,37 +86,33 @@ export function SendFunds(props: { supportedTokens: SupportedTokens }) {
export function SendFundsForm(props: {
onTokenSelect: () => void;
token?: TokenInfo;
receieverAddress: string;
setReceieverAddress: (value: string) => void;
receiverAddress: string;
setReceiverAddress: (value: string) => void;
amount: string;
setAmount: (value: string) => void;
}) {
const locale = useTWLocale().connectWallet.sendFundsScreen;
const tokenAddress = props.token?.address;
const balanceQuery = useBalance(tokenAddress);
const {
receieverAddress: receiverAddress,
setReceieverAddress,
amount,
setAmount,
} = props;
const { receiverAddress, setReceiverAddress, amount, setAmount } = props;
const chain = useChain();
const chainId = useChainId();
const wallet = useWallet();
// Ethereum or Rinkeby or Goerli
const isENSSupprted = chainId === 1 || chainId === 5 || chainId === 4;
const isENSSupported = chainId === 1 || chainId === 5 || chainId === 4;
const isValidReceiverAddress = useMemo(() => {
const isENS = receiverAddress.endsWith(".eth");
if (!isENSSupprted && isENS) {
if (!isENSSupported && isENS) {
return false;
}
return isENS || utils.isAddress(receiverAddress);
}, [receiverAddress, isENSSupprted]);
}, [receiverAddress, isENSSupported]);
const showInvalidAddressError = receiverAddress && !isValidReceiverAddress;
@@ -129,11 +126,29 @@ export function SendFundsForm(props: {
},
);
function getErrorMessage(error?: TXError) {
const message = error?.data?.message || error?.message;
if (!message) {
return locale.transactionFailed;
}
if (message.includes("user rejected")) {
return locale.transactionRejected;
}
if (message.includes("insufficient funds")) {
return locale.insufficientFunds;
}
return locale.transactionFailed;
}
if (sendTokenMutation.isError) {
return (
<Container p="lg" animate="fadein">
<ModalHeader
title="Send Funds"
title={locale.title}
onBack={() => {
sendTokenMutation.reset();
}}
@@ -160,7 +175,7 @@ export function SendFundsForm(props: {
return (
<Container p="lg" animate="fadein">
<ModalHeader
title="Send Funds"
title={locale.title}
onBack={() => {
sendTokenMutation.reset();
}}
@@ -176,7 +191,7 @@ export function SendFundsForm(props: {
color="success"
>
<CheckCircledIcon width={iconSize.xl} height={iconSize.xl} />
<Text color="success"> Transaction Successful </Text>
<Text color="success"> {locale.successMessage} </Text>
</Container>
</Container>
);
@@ -187,7 +202,7 @@ export function SendFundsForm(props: {
return (
<Container p="lg" animate="fadein">
<ModalTitle>Send Funds</ModalTitle>
<ModalTitle>{locale.title}</ModalTitle>
<Spacer y="xl" />
<form
@@ -197,7 +212,7 @@ export function SendFundsForm(props: {
>
{/* Token */}
<Label htmlFor="token" color="secondaryText">
Token
{locale.token}
</Label>
<Spacer y="sm" />
<Button
@@ -242,18 +257,18 @@ export function SendFundsForm(props: {
{/* Send to */}
<Label htmlFor="receiever" color="secondaryText">
Send to
{locale.sendTo}
</Label>
<Spacer y="sm" />
<Input
data-error={showInvalidAddressError}
required
id="receiever"
placeholder={isENSSupprted ? `0x... / ENS name` : "0x..."}
placeholder={isENSSupported ? `0x... / ENS name` : "0x..."}
variant="outline"
value={receiverAddress}
onChange={(e) => {
setReceieverAddress(e.target.value);
setReceiverAddress(e.target.value);
}}
/>
@@ -261,8 +276,7 @@ export function SendFundsForm(props: {
<>
<Spacer y="xs" />
<Text color="danger" size="sm">
{" "}
Invalid Address{" "}
{locale.invalidAddress}
</Text>
</>
)}
@@ -271,7 +285,7 @@ export function SendFundsForm(props: {
{/* Amount */}
<Label htmlFor="amount" color="secondaryText">
Amount
{locale.amount}
</Label>
<Spacer y="sm" />
<Container relative>
@@ -310,7 +324,7 @@ export function SendFundsForm(props: {
padding: spacing.md,
}}
>
{sendTokenMutation.isLoading ? "Sending" : "Send"}
{sendTokenMutation.isLoading ? locale.sending : locale.submitButton}
{sendTokenMutation.isLoading && (
<Spinner size="sm" color="accentButtonText" />
)}
@@ -320,24 +334,6 @@ export function SendFundsForm(props: {
);
}
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";
}
function useNativeToken(): TokenInfo | undefined {
const balanceQuery = useBalance();
const chain = useChain();
@@ -400,6 +396,7 @@ export function TokenSelector(props: {
const chainId = useChainId();
const nativeTokenInfo = useNativeToken();
const { data: foundToken, isLoading: findingToken } = useToken(input);
const locale = useTWLocale().connectWallet.sendFundsScreen;
let tokenList = (chainId ? props.supportedTokens[chainId] : undefined) || [];
@@ -425,10 +422,10 @@ export function TokenSelector(props: {
return (
<Container animate="fadein">
<Container p="lg">
<ModalHeader onBack={props.onBack} title="Select a Token" />
<ModalHeader onBack={props.onBack} title={locale.selectTokenTitle} />
<Spacer y="xl" />
<Input
placeholder="Search or paste token address"
placeholder={locale.searchToken}
variant="outline"
value={input}
onChange={(e) => {
@@ -492,7 +489,7 @@ export function TokenSelector(props: {
color="secondaryText"
>
<CrossCircledIcon width={iconSize.lg} height={iconSize.lg} />
No Tokens found
{locale.noTokensFound}
</Container>
)}
</Container>
@@ -28,6 +28,7 @@ import { safeChainIdToSlug } from "../wallets/safe/safeChainSlug";
import { TOS } from "./Modal/TOS";
import { keyframes } from "@emotion/react";
import { Spinner } from "../../components/Spinner";
import { useTWLocale } from "../../evm/providers/locale-provider";
type Status = "signing" | "failed" | "idle";
@@ -37,6 +38,7 @@ export const SignatureScreen: React.FC<{
termsOfServiceUrl?: string;
privacyPolicyUrl?: string;
}> = ({ onDone, modalSize, termsOfServiceUrl, privacyPolicyUrl }) => {
const locale = useTWLocale().connectWallet.signatureScreen;
const walletConfig = useWalletConfig();
const wallet = useWallet();
const { auth } = useContext(ModalConfigCtx);
@@ -99,7 +101,7 @@ export const SignatureScreen: React.FC<{
paddingBottom: 0,
}}
>
<ModalHeader title="Sign in" />
<ModalHeader title={locale.instructionScreen.title} />
</Container>
<Container
@@ -126,9 +128,8 @@ export const SignatureScreen: React.FC<{
</Container>
)}
<Text center multiline>
Please sign the message request <br />
in your wallet to continue
<Text center multiline balance>
{locale.instructionScreen.instruction}
</Text>
<Spacer y="md" />
<Button
@@ -141,7 +142,7 @@ export const SignatureScreen: React.FC<{
padding: spacing.md,
}}
>
Sign in
{locale.instructionScreen.signInButton}
</Button>
</>
) : (
@@ -164,13 +165,11 @@ export const SignatureScreen: React.FC<{
</Text>
{status === "signing" && (
<Text center multiline>
<Text center multiline balance>
{isSafeWallet ? (
<SafeWalletInstruction />
) : (
<>
Sign the signature request <br /> in your wallet
</>
<>{locale.signingScreen.prompt}</>
)}
</Text>
)}
@@ -193,7 +192,7 @@ export const SignatureScreen: React.FC<{
}}
>
{" "}
Approve transaction in Safe{" "}
{locale.signingScreen.approveTransactionInSafe}
<ExternalLinkIcon
width={iconSize.sm}
height={iconSize.sm}
@@ -216,7 +215,7 @@ export const SignatureScreen: React.FC<{
}}
>
<ReloadIcon width={iconSize.sm} height={iconSize.sm} />
Try Again
{locale.signingScreen.tryAgain}
</Button>
</Container>
)}
@@ -238,6 +237,7 @@ export const SignatureScreen: React.FC<{
};
function SafeWalletInstruction() {
const locale = useTWLocale().connectWallet.signatureScreen.signingScreen;
const { getWalletConfig } = useWalletContext();
const wallet = useWallet();
const personalWallet = wallet?.getPersonalWallet() as
@@ -249,12 +249,9 @@ function SafeWalletInstruction() {
return (
<>
{isSafePersonalWalletHeadless ? (
<>Approve transaction in Safe</>
<>{locale.approveTransactionInSafe}</>
) : (
<>
Sign signature request in your wallet <br /> & approve transaction in
Safe
</>
<>{locale.promptForSafe}</>
)}
</>
);
@@ -267,6 +264,7 @@ function HeadlessSignIn({
signIn: () => void;
status: Status;
}) {
const locale = useTWLocale().connectWallet.signatureScreen.signingScreen;
const mounted = useRef(false);
useEffect(() => {
if (mounted.current) {
@@ -278,7 +276,7 @@ function HeadlessSignIn({
return (
<Container p="lg" fullHeight flex="column" animate="fadein">
<ModalHeader title="Signing in" />
<ModalHeader title={locale.title} />
<Container
expand
flex="row"
@@ -296,7 +294,7 @@ function HeadlessSignIn({
animate="fadein"
>
<CrossCircledIcon width={iconSize.xl} height={iconSize.xl} />
<Text color="danger"> Failed to Sign in </Text>
<Text color="danger"> {locale.failedToSignIn} </Text>
</Container>
) : (
<Spinner size="xl" color="accentText" />
@@ -26,6 +26,7 @@ import { Link, Text } from "../../components/text";
import { Spacer } from "../../components/Spacer";
import { TextDivider } from "../../components/TextDivider";
import { TOS } from "./Modal/TOS";
import { useTWLocale } from "../../evm/providers/locale-provider";
export const WalletSelector: React.FC<{
walletConfigs: WalletConfig[];
@@ -39,6 +40,7 @@ export const WalletSelector: React.FC<{
const [isWalletGroupExpanded, setIsWalletGroupExpanded] = useState(false);
const disconnect = useDisconnect();
const connectionStatus = useConnectionStatus();
const locale = useTWLocale().connectWallet;
const localWalletConfig = props.walletConfigs.find(
(w) => w.id === walletIds.localWallet,
@@ -73,7 +75,7 @@ export const WalletSelector: React.FC<{
}}
data-test="continue-as-guest-button"
>
Continue as Guest
{locale.continueAsGuest}
</Button>
);
@@ -148,7 +150,7 @@ export const WalletSelector: React.FC<{
/>
))}
</Container>
Connect a wallet
{locale.connectAWallet}
</Button>
);
@@ -160,7 +162,7 @@ export const WalletSelector: React.FC<{
}}
>
<Text color="secondaryText" size="sm" weight={500}>
New to wallets?
{locale.newToWallets}
</Text>
<Link
weight={500}
@@ -168,7 +170,7 @@ export const WalletSelector: React.FC<{
target="_blank"
href="https://blog.thirdweb.com/web3-wallet/"
>
Get started
{locale.getStarted}
</Link>
</Container>
);
@@ -246,7 +248,7 @@ export const WalletSelector: React.FC<{
/>
{eoaWallets.length > 0 && (
<>
<TextDivider text="OR" />
<TextDivider text={locale.or} />
<Spacer y="lg" />
</>
)}
@@ -420,6 +422,7 @@ export function WalletEntryButton(props: {
}) {
const { walletConfig, selectWallet } = props;
const isRecommended = walletConfig.recommended;
const locale = useTWLocale().connectWallet;
return (
<WalletButton
type="button"
@@ -439,11 +442,13 @@ export function WalletEntryButton(props: {
{walletConfig.meta.name}
</Text>
{isRecommended && <Text size="sm">Recommended</Text>}
{isRecommended && <Text size="sm">{locale.recommended}</Text>}
{!isRecommended &&
walletConfig.isInstalled &&
walletConfig.isInstalled() && <Text size="sm">Installed</Text>}
walletConfig.isInstalled() && (
<Text size="sm">{locale.installed}</Text>
)}
</Container>
</WalletButton>
);
@@ -7,10 +7,8 @@ export const reservedScreens = {
export const modalMaxWidthCompact = "360px";
export const modalMaxWidthWide = "730px";
export const defaultModalTitle = "Connect";
export const widemodalMaxHeight = "570px";
export const compactmodalMaxHeight = "600px";
export const wideModalMaxHeight = "570px";
export const compactModalMaxHeight = "600px";
export const defaultTheme = "dark";
@@ -1,7 +1,6 @@
import { Spacer } from "../../../components/Spacer";
import { Container, ModalHeader, Line } from "../../../components/basic";
import { iconSize, spacing } from "../../../design-system";
import { isMobile } from "../../../evm/utils/isMobile";
import { Button } from "../../../components/buttons";
import { Text } from "../../../components/text";
import { useContext } from "react";
@@ -17,7 +16,15 @@ export const ConnectingScreen: React.FC<{
hideBackButton: boolean;
errorConnecting: boolean;
onRetry: () => void;
locale: {
getStartedLink: string;
tryAgain: string;
instruction: string;
failed: string;
inProgress: string;
};
}> = (props) => {
const { locale } = props;
const modalConfig = useContext(ModalConfigCtx);
return (
<Container animate="fadein" fullHeight flex="column">
@@ -57,23 +64,14 @@ export const ConnectingScreen: React.FC<{
}}
>
<Text center color="primaryText" size="lg" weight={600}>
{props.errorConnecting
? "Connection Failed"
: "Awaiting Confirmation"}
{props.errorConnecting ? locale.failed : locale.inProgress}
</Text>
<Spacer y="md" />
{!props.errorConnecting ? (
<Text
multiline
style={{
textAlign: "center",
}}
>
Login and connect your wallet
<br /> through the {props.walletName}{" "}
{isMobile() ? "application" : "pop-up"}
<Text balance center multiline>
{locale.instruction}
</Text>
) : (
<Container flex="row" center="x" animate="fadein">
@@ -82,11 +80,12 @@ export const ConnectingScreen: React.FC<{
variant="accent"
onClick={props.onRetry}
style={{
gap: spacing.sm,
gap: spacing.xs,
alignItems: "center",
}}
>
<ReloadIcon width={iconSize.sm} height={iconSize.sm} /> Try Agin{" "}
<ReloadIcon width={iconSize.sm} height={iconSize.sm} />{" "}
{locale.tryAgain}
</Button>
</Container>
)}
@@ -98,7 +97,7 @@ export const ConnectingScreen: React.FC<{
<Container flex="row" center="x" p="lg">
<Button variant="link" onClick={props.onGetStarted}>
Don{`'`}t have {props.walletName}?
{locale.getStartedLink}
</Button>
</Container>
</Container>
@@ -1,22 +1,18 @@
import { Img } from "../../../components/Img";
import { QRCode } from "../../../components/QRCode";
import { Spacer } from "../../../components/Spacer";
import {
Container,
ModalHeader,
ScreenBottomContainer,
} from "../../../components/basic";
import { Container, ModalHeader } from "../../../components/basic";
import { iconSize, radius, spacing } from "../../../design-system";
import type { Theme } from "../../../design-system/index";
import { isMobile } from "../../../evm/utils/isMobile";
import { openWindow } from "../../utils/openWindow";
import styled from "@emotion/styled";
import { useContext, useState } from "react";
import { useState } from "react";
import { AppleIcon } from "../icons/AppleIcon";
import { ChromeIcon } from "../icons/ChromeIcon";
import { PlayStoreIcon } from "../icons/PlayStoreIcon";
import { Text } from "../../../components/text";
import { ModalConfigCtx } from "../../../evm/providers/wallet-ui-states-provider";
import { useTWLocale } from "../../../evm/providers/locale-provider";
export const GetStartedScreen: React.FC<{
onBack?: () => void;
@@ -28,6 +24,9 @@ export const GetStartedScreen: React.FC<{
header?: React.ReactNode;
footer?: React.ReactNode;
showBack?: boolean;
locale: {
scanToDownload: string;
};
}> = ({
walletName,
walletIconURL,
@@ -37,14 +36,16 @@ export const GetStartedScreen: React.FC<{
header,
footer,
onBack,
locale: localeProp,
}) => {
const [showScreen, setShowScreen] = useState<
"base" | "android-scan" | "ios-scan"
>("base");
const locale = useTWLocale().connectWallet.download;
const isScanScreen =
showScreen === "android-scan" || showScreen === "ios-scan";
const isCompact = useContext(ModalConfigCtx).modalSize === "compact";
const handleBack = onBack
? () => {
@@ -67,6 +68,9 @@ export const GetStartedScreen: React.FC<{
walletName={walletName}
walletIconURL={walletIconURL}
onBack={handleBack}
locale={{
scanToDownload: localeProp.scanToDownload,
}}
/>
)}
@@ -78,6 +82,9 @@ export const GetStartedScreen: React.FC<{
walletName={walletName}
walletIconURL={walletIconURL}
onBack={handleBack}
locale={{
scanToDownload: localeProp.scanToDownload,
}}
/>
)}
@@ -104,7 +111,7 @@ export const GetStartedScreen: React.FC<{
}}
>
<ChromeIcon size={iconSize.lg} />
<span>Download Chrome Extension</span>
<span>{locale.chrome}</span>
</ButtonLink>
)}
@@ -121,7 +128,7 @@ export const GetStartedScreen: React.FC<{
}}
>
<PlayStoreIcon size={iconSize.lg} />
<span>Download on Google Play</span>
<span>{locale.android}</span>
</ButtonLink>
)}
@@ -138,7 +145,7 @@ export const GetStartedScreen: React.FC<{
}}
>
<AppleIcon size={iconSize.lg} />
<span>Download on App Store</span>
<span>{locale.iOS}</span>
</ButtonLink>
)}
</Container>
@@ -148,25 +155,6 @@ export const GetStartedScreen: React.FC<{
{!isScanScreen && footer}
</Container>
{showScreen === "base" && (
<>
{isCompact && <Spacer y="xs" />}
<ScreenBottomContainer
style={
isCompact
? undefined
: {
borderTop: "none",
}
}
>
<Text size="sm" center>
Get started with {walletName}
</Text>
</ScreenBottomContainer>
</>
)}
</Container>
);
};
@@ -178,6 +166,9 @@ const InstallScanScreen: React.FC<{
platformIcon: React.ReactNode;
walletIconURL: string;
onBack?: () => void;
locale: {
scanToDownload: string;
};
}> = (props) => {
return (
<Container animate="fadein" expand>
@@ -205,9 +196,8 @@ const InstallScanScreen: React.FC<{
<Spacer y="xl" />
<Text multiline center>
Scan with your phone to download <br /> {props.walletName} from{" "}
{props.platform}
<Text multiline center balance>
{props.locale.scanToDownload}
</Text>
<Spacer y="xs" />
@@ -19,11 +19,10 @@ export const ScanScreen: React.FC<{
walletName: string;
walletIconURL: string;
hideBackButton: boolean;
qrScanInstruction: string;
getStartedLink: string;
}> = (props) => {
const modalConfig = useContext(ModalConfigCtx);
const walletName = props.walletName.toLowerCase().includes("wallet")
? props.walletName
: `${props.walletName} wallet`;
return (
<Container fullHeight flex="column" animate="fadein">
<Container p="lg">
@@ -51,8 +50,8 @@ export const ScanScreen: React.FC<{
<Spacer y="lg" />
<Text center multiline>
Scan this with {walletName} <br /> or camera app to connect
<Text center multiline balance>
{props.qrScanInstruction}
</Text>
</div>
</Container>
@@ -72,7 +71,7 @@ export const ScanScreen: React.FC<{
textAlign: "center",
}}
>
{`Don't`} have {walletName}?
{props.getStartedLink}
</Button>
</ScreenBottomContainer>
</Container>
@@ -9,6 +9,7 @@ import styled from "@emotion/styled";
import { Theme } from "../../../design-system";
import { keyframes } from "@emotion/react";
import { Img } from "../../../components/Img";
import { useTWLocale } from "../../../evm/providers/locale-provider";
export function StartScreen() {
const {
@@ -16,6 +17,7 @@ export function StartScreen() {
privacyPolicyUrl,
welcomeScreen: WelcomeScreen,
} = useContext(ModalConfigCtx);
const locale = useTWLocale().connectWallet;
if (WelcomeScreen) {
if (typeof WelcomeScreen === "function") {
@@ -25,11 +27,11 @@ export function StartScreen() {
const title =
(typeof WelcomeScreen === "object" ? WelcomeScreen?.title : undefined) ||
"Your gateway to the decentralized world";
locale.welcomeScreen.defaultTitle;
const subtitle =
(typeof WelcomeScreen === "object" ? WelcomeScreen?.subtitle : undefined) ||
"Connect a wallet to get started";
locale.welcomeScreen.defaultSubtitle;
const img =
typeof WelcomeScreen === "object" ? WelcomeScreen?.img : undefined;
@@ -86,7 +88,7 @@ export function StartScreen() {
center
href="https://blog.thirdweb.com/web3-wallet/"
>
New to wallets?
{locale.newToWallets}
</Link>
{showTOS && (
@@ -23,6 +23,13 @@ export const WCOpenURI: React.FC<{
onRetry: () => void;
hideBackButton: boolean;
onGetStarted: () => void;
locale: {
getStartedLink: string;
tryAgain: string;
instruction: string;
failed: string;
inProgress: string;
};
}> = ({
onBack,
onConnected,
@@ -32,6 +39,7 @@ export const WCOpenURI: React.FC<{
errorConnecting,
hideBackButton,
onGetStarted,
locale,
}) => {
const createInstance = useCreateWalletInstance();
const { setConnectedWallet, chainToConnect, setConnectionStatus } =
@@ -77,6 +85,7 @@ export const WCOpenURI: React.FC<{
return (
<ConnectingScreen
locale={locale}
hideBackButton={hideBackButton}
onBack={onBack}
walletName={walletConfig.meta.name}
@@ -84,7 +93,6 @@ export const WCOpenURI: React.FC<{
errorConnecting={errorConnecting}
onRetry={onRetry}
onGetStarted={onGetStarted}
// supportLink={supportLink}
/>
);
};
@@ -11,6 +11,7 @@ export function InputSelectionUI(props: {
type: string;
errorMessage?: (input: string) => string | undefined;
emptyErrorMessage?: string;
submitButtonText: string;
}) {
const [input, setInput] = useState("");
const [error, setError] = useState<string | undefined>();
@@ -83,7 +84,7 @@ export function InputSelectionUI(props: {
<Spacer y="md" />
<Button variant="accent" onClick={handleSelect} fullWidth>
Continue
{props.submitButtonText}
</Button>
</div>
);
@@ -6,6 +6,7 @@ import { GetStartedScreen } from "../../ConnectWallet/screens/GetStartedScreen";
import { CoinbaseScan } from "./CoinbaseScan";
import type { CoinbaseWallet } from "@thirdweb-dev/wallets";
import { wait } from "../../../utils/wait";
import { useTWLocale } from "../../../evm/providers/locale-provider";
export const CoinbaseConnectUI = ({
walletConfig,
@@ -18,6 +19,7 @@ export const CoinbaseConnectUI = ({
const [screen, setScreen] = useState<
"connecting" | "loading" | "scanning" | "get-started"
>("loading");
const locale = useTWLocale().wallets.coinbaseWallet;
const [errorConnecting, setErrorConnecting] = useState(false);
const hideBackButton = supportedWallets.length === 1;
@@ -71,6 +73,13 @@ export const CoinbaseConnectUI = ({
if (screen === "connecting" || screen === "loading") {
return (
<ConnectingScreen
locale={{
getStartedLink: locale.getStartedLink,
instruction: locale.connectionScreen.instruction,
tryAgain: locale.connectionScreen.retry,
inProgress: locale.connectionScreen.inProgress,
failed: locale.connectionScreen.failed,
}}
errorConnecting={errorConnecting}
onGetStarted={() => setScreen("get-started")}
onRetry={connectToExtension}
@@ -85,6 +94,9 @@ export const CoinbaseConnectUI = ({
if (screen === "get-started") {
return (
<GetStartedScreen
locale={{
scanToDownload: locale.getStartedScreen.instruction,
}}
walletIconURL={meta.iconURL}
walletName={meta.name}
chromeExtensionLink={meta.urls?.chrome}
@@ -6,6 +6,7 @@ import {
} from "@thirdweb-dev/react-core";
import { useEffect, useRef, useState } from "react";
import { ScanScreen } from "../../ConnectWallet/screens/ScanScreen";
import { useTWLocale } from "../../../evm/providers/locale-provider";
export const CoinbaseScan: React.FC<{
onBack: () => void;
@@ -14,6 +15,7 @@ export const CoinbaseScan: React.FC<{
walletConfig: WalletConfig<CoinbaseWallet>;
hideBackButton: boolean;
}> = ({ walletConfig, onConnected, onGetStarted, onBack, hideBackButton }) => {
const locale = useTWLocale().wallets.coinbaseWallet;
const createInstance = useCreateWalletInstance();
const [qrCodeUri, setQrCodeUri] = useState<string | undefined>(undefined);
const { setConnectedWallet, chainToConnect, setConnectionStatus } =
@@ -59,12 +61,14 @@ export const CoinbaseScan: React.FC<{
return (
<ScanScreen
qrScanInstruction={locale.scanScreen.instruction}
onBack={onBack}
onGetStarted={onGetStarted}
qrCodeUri={qrCodeUri}
walletName={walletConfig.meta.name}
walletIconURL={walletConfig.meta.iconURL}
hideBackButton={hideBackButton}
getStartedLink={locale.getStartedLink}
/>
);
};
@@ -0,0 +1,148 @@
import { ConnectUIProps, useConnect } from "@thirdweb-dev/react-core";
import { ConnectingScreen } from "../../ConnectWallet/screens/ConnectingScreen";
import { isMobile } from "../../../evm/utils/isMobile";
import { useCallback, useEffect, useRef, useState } from "react";
import { CoreWalletScan } from "./CoreWalletScan";
import { GetStartedScreen } from "../../ConnectWallet/screens/GetStartedScreen";
import type { CoreWallet } from "@thirdweb-dev/wallets";
import { wait } from "../../../utils/wait";
import { useTWLocale } from "../../../evm/providers/locale-provider";
import { WCOpenURI } from "../../ConnectWallet/screens/WCOpenUri";
import { coreWalletUris } from "./coreWalletUris";
export const CoreWalletConnectUI = (props: ConnectUIProps<CoreWallet>) => {
const [screen, setScreen] = useState<
"connecting" | "scanning" | "get-started" | "open-wc-uri"
>("connecting");
const locale = useTWLocale().wallets.coreWallet;
const connectingLocale = {
getStartedLink: locale.getStartedLink,
instruction: locale.connectionScreen.instruction,
tryAgain: locale.connectionScreen.retry,
inProgress: locale.connectionScreen.inProgress,
failed: locale.connectionScreen.failed,
};
const { walletConfig, connected } = props;
const connect = useConnect();
const [errorConnecting, setErrorConnecting] = useState(false);
const hideBackButton = props.supportedWallets.length === 1;
const connectToExtension = useCallback(async () => {
try {
connectPrompted.current = true;
setErrorConnecting(false);
setScreen("connecting");
await wait(1000);
await connect(walletConfig);
connected();
} catch (e) {
setErrorConnecting(true);
console.error(e);
}
}, [connected, connect, walletConfig]);
const connectPrompted = useRef(false);
useEffect(() => {
if (connectPrompted.current) {
return;
}
const isInstalled = walletConfig.isInstalled
? walletConfig.isInstalled()
: false;
// if loading
(async () => {
if (isInstalled) {
connectToExtension();
}
// if wallet is not injected
else {
// on mobile, open the Core Mobile via wallet connect
if (isMobile()) {
setScreen("open-wc-uri");
} else {
// on desktop, show the Core app scan qr code
setScreen("scanning");
}
}
})();
}, [connectToExtension, walletConfig]);
if (screen === "connecting") {
return (
<ConnectingScreen
locale={{
getStartedLink: locale.getStartedLink,
instruction: locale.connectionScreen.instruction,
tryAgain: locale.connectionScreen.retry,
inProgress: locale.connectionScreen.inProgress,
failed: locale.connectionScreen.failed,
}}
errorConnecting={errorConnecting}
onGetStarted={() => {
setScreen("get-started");
}}
onRetry={connectToExtension}
hideBackButton={hideBackButton}
onBack={props.goBack}
walletName={walletConfig.meta.name}
walletIconURL={walletConfig.meta.iconURL}
/>
);
}
if (screen === "open-wc-uri") {
return (
<WCOpenURI
locale={connectingLocale}
onRetry={() => {
// NOOP - TODO make onRetry optional
}}
errorConnecting={errorConnecting}
onGetStarted={() => {
setScreen("get-started");
}}
hideBackButton={hideBackButton}
onBack={props.goBack}
onConnected={connected}
walletConfig={walletConfig}
appUriPrefix={coreWalletUris}
/>
);
}
if (screen === "get-started") {
return (
<GetStartedScreen
locale={{
scanToDownload: locale.getStartedScreen.instruction,
}}
walletIconURL={walletConfig.meta.iconURL}
walletName={walletConfig.meta.name}
chromeExtensionLink={walletConfig.meta.urls?.chrome}
googlePlayStoreLink={walletConfig.meta.urls?.android}
appleStoreLink={walletConfig.meta.urls?.ios}
onBack={props.goBack}
/>
);
}
if (screen === "scanning") {
return (
<CoreWalletScan
onBack={props.goBack}
onConnected={props.connected}
onGetStarted={() => {
setScreen("get-started");
}}
hideBackButton={hideBackButton}
walletConfig={walletConfig}
/>
);
}
return null;
};
@@ -0,0 +1,65 @@
import { ScanScreen } from "../../ConnectWallet/screens/ScanScreen";
import {
useCreateWalletInstance,
useWalletContext,
} from "@thirdweb-dev/react-core";
import { useEffect, useRef, useState } from "react";
import type { CoreWallet } from "@thirdweb-dev/wallets";
import type { WalletConfig } from "@thirdweb-dev/react-core";
import { useTWLocale } from "../../../evm/providers/locale-provider";
export const CoreWalletScan: React.FC<{
onBack: () => void;
onGetStarted: () => void;
onConnected: () => void;
walletConfig: WalletConfig<CoreWallet>;
hideBackButton: boolean;
}> = ({ onBack, onConnected, onGetStarted, walletConfig, hideBackButton }) => {
const locale = useTWLocale().wallets.coreWallet;
const createInstance = useCreateWalletInstance();
const [qrCodeUri, setQrCodeUri] = useState<string | undefined>();
const { setConnectedWallet, chainToConnect, setConnectionStatus } =
useWalletContext();
const scanStarted = useRef(false);
useEffect(() => {
if (scanStarted.current) {
return;
}
scanStarted.current = true;
const wallet = createInstance(walletConfig);
setConnectionStatus("connecting");
wallet.connectWithQrCode({
chainId: chainToConnect?.chainId,
onQrCodeUri(uri) {
setQrCodeUri(uri);
},
onConnected() {
setConnectedWallet(wallet);
onConnected();
},
});
}, [
createInstance,
setConnectedWallet,
chainToConnect,
onConnected,
walletConfig,
setConnectionStatus,
]);
return (
<ScanScreen
qrScanInstruction={locale.scanScreen.instruction}
onBack={onBack}
onGetStarted={onGetStarted}
qrCodeUri={qrCodeUri}
walletName={walletConfig.meta.name}
walletIconURL={walletConfig.meta.iconURL}
hideBackButton={hideBackButton}
getStartedLink={locale.getStartedLink}
/>
);
};
File diff suppressed because one or more lines are too long
@@ -0,0 +1,5 @@
export const coreWalletUris = {
ios: "core://",
android: "core://",
other: "core://",
};
@@ -16,12 +16,15 @@ import type { EmbeddedWalletLoginType, AuthOption } from "./types";
import { TextDivider } from "../../../components/TextDivider";
import { openGoogleSignInWindow } from "../../utils/openGoogleSignInWindow";
import { useTheme } from "@emotion/react";
import { useTWLocale } from "../../../evm/providers/locale-provider";
export const EmbeddedWalletFormUI = (props: {
onSelect: (loginType: EmbeddedWalletLoginType) => void;
walletConfig: WalletConfig<EmbeddedWallet>;
authOptions: AuthOption[];
}) => {
const twLocale = useTWLocale();
const locale = twLocale.wallets.embeddedWallet;
const createWalletInstance = useCreateWalletInstance();
const setConnectionStatus = useSetConnectionStatus();
const setConnectedWallet = useSetConnectedWallet();
@@ -70,17 +73,17 @@ export const EmbeddedWalletFormUI = (props: {
}}
>
<GoogleIcon size={iconSize.md} />
Sign in with Google
{locale.signInWithGoogle}
</SocialButton>
</>
)}
{enableGoogleLogin && enableEmailLogin && (
<TextDivider text="OR" py="lg" />
<TextDivider text={twLocale.connectWallet.or} py="lg" />
)}
{enableEmailLogin && (
<InputSelectionUI
onSelect={(email) => props.onSelect({ email })}
placeholder="Enter your email address"
placeholder={locale.emailPlaceholder}
name="email"
type="email"
errorMessage={(_input) => {
@@ -89,10 +92,11 @@ export const EmbeddedWalletFormUI = (props: {
/^([a-z0-9_\.-]+)@([\da-z\.-]+)\.([a-z\.]{2,})$/g;
const isValidEmail = emailRegex.test(input);
if (!isValidEmail) {
return "Invalid email address";
return locale.invalidEmail;
}
}}
emptyErrorMessage="email address is required"
emptyErrorMessage={locale.emailRequired}
submitButtonText={locale.submitEmail}
/>
)}
</div>
@@ -106,6 +110,7 @@ export const EmbeddedWalletFormUIScreen: React.FC<{
walletConfig: WalletConfig<EmbeddedWallet>;
authOptions: AuthOption[];
}> = (props) => {
const locale = useTWLocale().wallets.embeddedWallet.emailLoginScreen;
const isCompact = props.modalSize === "compact";
return (
<Container
@@ -117,7 +122,7 @@ export const EmbeddedWalletFormUIScreen: React.FC<{
minHeight: "250px",
}}
>
<ModalHeader onBack={props.onBack} title="Sign in" />
<ModalHeader onBack={props.onBack} title={locale.title} />
{isCompact ? <Spacer y="xl" /> : null}
<Container
@@ -17,10 +17,12 @@ import { Theme, iconSize } from "../../../design-system";
import { GoogleIcon } from "../../ConnectWallet/icons/GoogleIcon";
import { openGoogleSignInWindow } from "../../utils/openGoogleSignInWindow";
import { useTheme } from "@emotion/react";
import { useTWLocale } from "../../../evm/providers/locale-provider";
export const EmbeddedWalletGoogleLogin = (
props: ConnectUIProps<EmbeddedWallet>,
) => {
const locale = useTWLocale().wallets.embeddedWallet.googleLoginScreen;
const { goBack, modalSize } = props;
const createWalletInstance = useCreateWalletInstance();
const setConnectionStatus = useSetConnectionStatus();
@@ -76,7 +78,7 @@ export const EmbeddedWalletGoogleLogin = (
title={
<Container flex="row" center="both" gap="xs">
<GoogleIcon size={iconSize.md} />
<ModalTitle> Sign in </ModalTitle>
<ModalTitle> {locale.title} </ModalTitle>
</Container>
}
onBack={goBack}
@@ -102,7 +104,7 @@ export const EmbeddedWalletGoogleLogin = (
maxWidth: "250px",
}}
>
Select your Google account in the pop-up
{locale.instruction}
</Text>
<Spacer y="xl" />
<Container center="x" flex="row">
@@ -115,11 +117,10 @@ export const EmbeddedWalletGoogleLogin = (
{connectionStatus === "disconnected" && (
<Container animate="fadein">
<Text color="danger">Failed to sign in</Text>
<Text color="danger">{locale.failed}</Text>
<Spacer y="lg" />
<Button variant="primary" onClick={googleLogin}>
{" "}
Retry{" "}
{locale.retry}
</Button>
<Spacer y="xxl" />
</Container>
@@ -12,6 +12,7 @@ import { Text } from "../../../components/text";
import { Theme, fontSize } from "../../../design-system";
import { CreatePassword } from "./USER_MANAGED/CreatePassword";
import { EnterPasswordOrRecovery } from "./USER_MANAGED/EnterPassword";
import { useTWLocale } from "../../../evm/providers/locale-provider";
type EmbeddedWalletOTPLoginUIProps = ConnectUIProps<EmbeddedWallet>;
@@ -27,6 +28,7 @@ export const EmbeddedWalletOTPLoginUI: React.FC<
> = (props) => {
const email = props.selectionData;
const isWideModal = props.modalSize === "wide";
const locale = useTWLocale().wallets.embeddedWallet.emailLoginScreen;
const [otpInput, setOtpInput] = useState("");
const { createWalletInstance, setConnectedWallet, setConnectionStatus } =
@@ -225,7 +227,7 @@ export const EmbeddedWalletOTPLoginUI: React.FC<
}}
>
{!isWideModal && <Spacer y="xl" />}
<Text>Enter the verification code sent to</Text>
<Text>{locale.enterCodeSendTo}</Text>
<Spacer y="sm" />
<Text color="primaryText">{email}</Text>
<Spacer y="xl" />
@@ -249,7 +251,7 @@ export const EmbeddedWalletOTPLoginUI: React.FC<
<FadeIn>
<Spacer y="md" />
<Text size="sm" color="danger" center>
Invalid verification code
{locale.invalidCode}
</Text>
</FadeIn>
)}
@@ -273,7 +275,7 @@ export const EmbeddedWalletOTPLoginUI: React.FC<
width: "100%",
}}
>
Verify
{locale.verify}
</Button>
</Container>
)}
@@ -287,7 +289,7 @@ export const EmbeddedWalletOTPLoginUI: React.FC<
{emailStatus === "error" && (
<>
<Text size="sm" center color="danger">
Failed to send verification code
{locale.failedToSendCode}
</Text>
</>
)}
@@ -301,14 +303,14 @@ export const EmbeddedWalletOTPLoginUI: React.FC<
textAlign: "center",
}}
>
<Text size="sm">Sending verification code</Text>
<Text size="sm">{locale.sendingCode}</Text>
<Spinner size="xs" color="secondaryText" />
</Container>
)}
{typeof emailStatus === "object" && (
<LinkButton onClick={sendEmail} type="button">
Resend verification code
{locale.resendCode}
</LinkButton>
)}
</Container>
@@ -1,3 +1,5 @@
/* eslint-disable i18next/no-literal-string */
import { useState } from "react";
import { Spacer } from "../../../../components/Spacer";
import { Spinner } from "../../../../components/Spinner";

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