Compare commits

..
Author SHA1 Message Date
Jonas DanielsandGitHub 758cbdde0a Version Packages (#2743) 2024-04-10 08:44:09 -07:00
github-actions[bot]andGitHub d65713a7af chore(chains): update chains (#2740) 2024-04-10 08:32:45 -07:00
ce144ff521 [SDK] Add test cases for sign-transaction types (#2737)
Co-authored-by: Jonas Daniels <[email protected]>
2024-04-09 23:32:50 +00:00
d836889f46 [v4] Add Buy with Crypto APIs and UI in ConnectWallet component (#2579)
Signed-off-by: Jonas Daniels <[email protected]>
Co-authored-by: Jonas Daniels <[email protected]>
Co-authored-by: Manan Tank <[email protected]>
2024-04-09 23:07:32 +00:00
Jonas DanielsGitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>GitHub Action
1f6ee22fb9 Version Packages (#2742)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: GitHub Action <[email protected]>
2024-04-09 12:28:38 -07:00
Manan TankandGitHub 900e7d0324 fix: Remove duplicated entry for MetaMask in MM browser (#2741) 2024-04-09 19:06:00 +00:00
142 changed files with 5889 additions and 817 deletions
+7
View File
@@ -1,5 +1,12 @@
# @thirdweb-dev/auth
## 4.1.59
### Patch Changes
- Updated dependencies []:
- @thirdweb-dev/wallets@2.5.1
## 4.1.58
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/auth",
"version": "4.1.58",
"version": "4.1.59",
"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.92
### Patch Changes
- [#2740](https://github.com/thirdweb-dev/js/pull/2740) [`d65713a`](https://github.com/thirdweb-dev/js/commit/d65713a7af67bf20e5b88b72f5d43dfba9172b3b) Thanks [@github-actions](https://github.com/apps/github-actions)! - Synced Chains Package
## 0.1.91
### Patch Changes
+3 -3
View File
@@ -1,11 +1,11 @@
import type { Chain } from "../src/types";
export default {
"chain": "$HYB",
"chain": "HYB",
"chainId": 1224,
"explorers": [
{
"name": "Hybrid Testnet",
"url": "https://alphatestnet.buildonhybrid.com",
"url": "https://explorer.buildonhybrid.com",
"standard": "EIP3091"
}
],
@@ -20,7 +20,7 @@ export default {
"name": "Hybrid Testnet",
"nativeCurrency": {
"name": "Hybrid",
"symbol": "$HYB",
"symbol": "HYB",
"decimals": 18
},
"networkId": 1224,
+1
View File
@@ -27,5 +27,6 @@ export default {
],
"shortName": "Bobabeam",
"slug": "bobabeam",
"status": "deprecated",
"testnet": false
} as const satisfies Chain;
+1
View File
@@ -28,5 +28,6 @@ export default {
"shortName": "Bobabase",
"slip44": 1,
"slug": "bobabase-testnet",
"status": "deprecated",
"testnet": true
} as const satisfies Chain;
+35
View File
@@ -0,0 +1,35 @@
import type { Chain } from "../src/types";
export default {
"chain": "DIONE",
"chainId": 131313,
"explorers": [],
"faucets": [
"https://faucet.dioneprotocol.com/"
],
"features": [
{
"name": "EIP155"
}
],
"icon": {
"url": "ipfs://QmVt5Y585JqBSrkpZmYaEnX9FW7tDwfDLmUGNyDdyV2Pd2",
"width": 48,
"height": 48,
"format": "svg"
},
"infoURL": "https://www.dioneprotocol.com/",
"name": "Odyssey Chain (Testnet)",
"nativeCurrency": {
"name": "DIONE",
"symbol": "DIONE",
"decimals": 18
},
"networkId": 131313,
"rpc": [
"https://131313.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://testnode.dioneprotocol.com/ext/bc/D/rpc"
],
"shortName": "DIONE",
"slug": "odyssey-chain-testnet",
"testnet": true
} as const satisfies Chain;
+41
View File
@@ -0,0 +1,41 @@
import type { Chain } from "../src/types";
export default {
"chain": "KNB",
"chainId": 13600,
"explorers": [
{
"name": "qbitscan",
"url": "https://explorer.qbitscan.com",
"standard": "EIP3091",
"icon": {
"url": "ipfs://QmZEuR4bJaZkC497qQnsxHv23pZFcXb9dTdaDPCPo2sj9Z",
"width": 200,
"height": 200,
"format": "png"
}
}
],
"faucets": [],
"icon": {
"url": "ipfs://QmZEuR4bJaZkC497qQnsxHv23pZFcXb9dTdaDPCPo2sj9Z",
"width": 200,
"height": 200,
"format": "png"
},
"infoURL": "https://kronobit.org",
"name": "Kronobit Mainnet",
"nativeCurrency": {
"name": "Kronobit",
"symbol": "KNB",
"decimals": 18
},
"networkId": 13600,
"rpc": [
"https://13600.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://mainnet-rpc.qbitscan.com"
],
"shortName": "KNB",
"slug": "kronobit",
"testnet": false,
"title": "Kronobit Mainnet"
} as const satisfies Chain;
+9 -2
View File
@@ -2,7 +2,13 @@ import type { Chain } from "../src/types";
export default {
"chain": "Rari",
"chainId": 1380012617,
"explorers": [],
"explorers": [
{
"name": "rarichain-explorer",
"url": "https://mainnet.explorer.rarichain.org",
"standard": "EIP3091"
}
],
"faucets": [],
"features": [],
"icon": {
@@ -31,7 +37,8 @@ export default {
"redFlags": [],
"rpc": [
"https://1380012617.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://mainnet.rpc.rarichain.org/http"
"https://mainnet.rpc.rarichain.org/http",
"https://rari.calderachain.xyz/http"
],
"shortName": "rari",
"slug": "rari",
+41
View File
@@ -0,0 +1,41 @@
import type { Chain } from "../src/types";
export default {
"chain": "Titan (TKX)",
"chainId": 18888,
"explorers": [
{
"name": "Titan Explorer",
"url": "https://titan-explorer-light.titanlab.io/Titan",
"standard": "none",
"icon": {
"url": "ipfs://bafkreicyvc7t55zsejv6vwaxbmhvea5vdfkkbgqkmn65rhyyifpg3mq3ua",
"width": 512,
"height": 512,
"format": "png"
}
}
],
"faucets": [],
"icon": {
"url": "ipfs://bafkreicyvc7t55zsejv6vwaxbmhvea5vdfkkbgqkmn65rhyyifpg3mq3ua",
"width": 512,
"height": 512,
"format": "png"
},
"infoURL": "https://titanlab.io",
"name": "Titan (TKX)",
"nativeCurrency": {
"name": "Titan tkx",
"symbol": "TKX",
"decimals": 18
},
"networkId": 18888,
"rpc": [
"https://18888.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://titan-json-rpc.titanlab.io"
],
"shortName": "titan_tkx",
"slip44": 1,
"slug": "titan-tkx",
"testnet": false
} as const satisfies Chain;
+7 -1
View File
@@ -2,7 +2,13 @@ import type { Chain } from "../src/types";
export default {
"chain": "RARI-T",
"chainId": 1918988905,
"explorers": [],
"explorers": [
{
"name": "rarichain-testnet-explorer",
"url": "https://explorer.rarichain.org",
"standard": "EIP3091"
}
],
"faucets": [],
"features": [],
"infoURL": "https://rarichain.org/",
+9 -9
View File
@@ -1,34 +1,34 @@
import type { Chain } from "../src/types";
export default {
"chain": "X1",
"chain": "X Layer",
"chainId": 195,
"explorers": [
{
"name": "OKLink",
"url": "https://www.oklink.com/x1-test",
"url": "https://www.oklink.com/xlayer-test",
"standard": "EIP3091"
}
],
"faucets": [
"https://www.okx.com/x1/faucet"
"https://www.okx.com/xlayer/faucet"
],
"features": [],
"infoURL": "https://www.okx.com/x1",
"name": "X1 Testnet",
"infoURL": "https://www.okx.com/xlayer",
"name": "X Layer Testnet",
"nativeCurrency": {
"name": "X1 Global Utility Token in testnet",
"name": "X Layer Global Utility Token in testnet",
"symbol": "OKB",
"decimals": 18
},
"networkId": 195,
"rpc": [
"https://195.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://testrpc.x1.tech",
"https://x1testrpc.okx.com"
"https://testrpc.xlayer.tech",
"https://xlayertestrpc.okx.com"
],
"shortName": "tokb",
"slip44": 1,
"slug": "x1-testnet",
"slug": "x-layer-testnet",
"status": "active",
"testnet": true
} as const satisfies Chain;
+17 -7
View File
@@ -1,21 +1,31 @@
import type { Chain } from "../src/types";
export default {
"chain": "X1",
"chain": "X Layer",
"chainId": 196,
"explorers": [],
"explorers": [
{
"name": "OKLink",
"url": "https://www.oklink.com/xlayer",
"standard": "EIP3091"
}
],
"faucets": [],
"features": [],
"infoURL": "https://www.okx.com/x1",
"name": "X1 Mainnet",
"infoURL": "https://www.okx.com/xlayer",
"name": "X Layer Mainnet",
"nativeCurrency": {
"name": "X1 Global Utility Token",
"name": "X Layer Global Utility Token",
"symbol": "OKB",
"decimals": 18
},
"networkId": 196,
"rpc": [],
"rpc": [
"https://196.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://rpc.xlayer.tech",
"https://xlayerrpc.okx.com"
],
"shortName": "okb",
"slug": "x1",
"slug": "x-layer",
"status": "incubating",
"testnet": false
} as const satisfies Chain;
+33
View File
@@ -0,0 +1,33 @@
import type { Chain } from "../src/types";
export default {
"chain": "EdgelessTestnet",
"chainId": 202,
"explorers": [
{
"name": "Edgeless Explorer",
"url": "https://testnet.explorer.edgeless.network",
"standard": "EIP3091"
}
],
"faucets": [],
"features": [
{
"name": "EIP155"
}
],
"infoURL": "https://edgeless.network",
"name": "Edgeless Testnet",
"nativeCurrency": {
"name": "Edgeless Wrapped Eth",
"symbol": "EwEth",
"decimals": 18
},
"networkId": 202,
"rpc": [
"https://202.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://testnet.rpc.edgeless.network/http"
],
"shortName": "edgeless-testnet",
"slug": "edgeless-testnet",
"testnet": true
} as const satisfies Chain;
+33
View File
@@ -0,0 +1,33 @@
import type { Chain } from "../src/types";
export default {
"chain": "Edgeless",
"chainId": 2026,
"explorers": [
{
"name": "Edgeless Explorer",
"url": "https://explorer.edgeless.network",
"standard": "EIP3091"
}
],
"faucets": [],
"features": [
{
"name": "EIP155"
}
],
"infoURL": "https://edgeless.network",
"name": "Edgeless Network",
"nativeCurrency": {
"name": "Edgeless Wrapped Eth",
"symbol": "EwEth",
"decimals": 18
},
"networkId": 2026,
"rpc": [
"https://2026.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://rpc.edgeless.network/http"
],
"shortName": "edgeless",
"slug": "edgeless-network",
"testnet": false
} as const satisfies Chain;
+35
View File
@@ -0,0 +1,35 @@
import type { Chain } from "../src/types";
export default {
"chain": "HDX",
"chainId": 222222,
"explorers": [
{
"name": "blockscout",
"url": "https://explorer.evm.hydration.cloud",
"standard": "EIP3091"
}
],
"faucets": [],
"icon": {
"url": "ipfs://QmQnx3j1ZtQ9Br873FuS6612HQfosXsr4Whew8eN4ArYwE",
"width": 400,
"height": 400,
"format": "svg"
},
"infoURL": "https://hydradx.io",
"name": "HydraDX",
"nativeCurrency": {
"name": "Wrapped ETH",
"symbol": "WETH",
"decimals": 18
},
"networkId": 222222,
"rpc": [
"https://222222.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://rpc.hydradx.cloud",
"wss://rpc.hydradx.cloud"
],
"shortName": "hdx",
"slug": "hydradx",
"testnet": false
} as const satisfies Chain;
+40
View File
@@ -0,0 +1,40 @@
import type { Chain } from "../src/types";
export default {
"chain": "DreyerX",
"chainId": 23451,
"explorers": [
{
"name": "drxscan",
"url": "https://scan.dreyerx.com",
"standard": "EIP3091",
"icon": {
"url": "ipfs://QmTGjVbiSdHYhjzpeqzY6fGwGiLQuEa2hW7irD75sUSx9e",
"width": 4501,
"height": 4501,
"format": "png"
}
}
],
"faucets": [],
"icon": {
"url": "ipfs://QmTGjVbiSdHYhjzpeqzY6fGwGiLQuEa2hW7irD75sUSx9e",
"width": 4501,
"height": 4501,
"format": "png"
},
"infoURL": "https://dreyerx.com",
"name": "DreyerX Mainnet",
"nativeCurrency": {
"name": "DreyerX",
"symbol": "DRX",
"decimals": 18
},
"networkId": 23451,
"rpc": [
"https://23451.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://rpc.dreyerx.com"
],
"shortName": "dreyerx",
"slug": "dreyerx",
"testnet": false
} as const satisfies Chain;
+40
View File
@@ -0,0 +1,40 @@
import type { Chain } from "../src/types";
export default {
"chain": "DreyerX",
"chainId": 23452,
"explorers": [
{
"name": "drxscan",
"url": "https://testnet-scan.dreyerx.com",
"standard": "EIP3091",
"icon": {
"url": "ipfs://QmTGjVbiSdHYhjzpeqzY6fGwGiLQuEa2hW7irD75sUSx9e",
"width": 4501,
"height": 4501,
"format": "png"
}
}
],
"faucets": [],
"icon": {
"url": "ipfs://QmTGjVbiSdHYhjzpeqzY6fGwGiLQuEa2hW7irD75sUSx9e",
"width": 4501,
"height": 4501,
"format": "png"
},
"infoURL": "https://dreyerx.com",
"name": "DreyerX Testnet",
"nativeCurrency": {
"name": "DreyerX",
"symbol": "DRX",
"decimals": 18
},
"networkId": 23452,
"rpc": [
"https://23452.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://testnet-rpc.dreyerx.com"
],
"shortName": "dreyerx-testnet",
"slug": "dreyerx-testnet",
"testnet": true
} as const satisfies Chain;
-5
View File
@@ -7,11 +7,6 @@ export default {
"name": "Bobascan",
"url": "https://bobascan.com",
"standard": "none"
},
{
"name": "Blockscout",
"url": "https://blockexplorer.boba.network",
"standard": "none"
}
],
"faucets": [],
+43
View File
@@ -0,0 +1,43 @@
import type { Chain } from "../src/types";
export default {
"chain": "ETH",
"chainId": 28882,
"explorers": [
{
"name": "Bobascan",
"url": "https://testnet.bobascan.com",
"standard": "none"
}
],
"faucets": [
"https://www.l2faucet.com/boba"
],
"infoURL": "https://boba.network",
"name": "Boba Sepolia",
"nativeCurrency": {
"name": "Ether",
"symbol": "ETH",
"decimals": 18
},
"networkId": 28882,
"parent": {
"type": "L2",
"chain": "eip155-1",
"bridges": [
{
"url": "https://gateway.boba.network"
}
]
},
"rpc": [
"https://28882.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://sepolia.boba.network",
"https://boba-sepolia.gateway.tenderly.co",
"https://gateway.tenderly.co/public/boba-sepolia",
"wss://boba-sepolia.gateway.tenderly.co/",
"wss://gateway.tenderly.co/public/boba-sepolia"
],
"shortName": "BobaSepolia",
"slug": "boba-sepolia",
"testnet": true
} as const satisfies Chain;
+1 -1
View File
@@ -25,6 +25,6 @@ export default {
"https://subnets.avax.network/defi-kingdoms/dfk-chain/rpc"
],
"shortName": "Testnet",
"slug": "testnet",
"slug": "testnet-testnet",
"testnet": true
} as const satisfies Chain;
+30
View File
@@ -0,0 +1,30 @@
import type { Chain } from "../src/types";
export default {
"chain": "Avalanche",
"chainId": 29386,
"explorers": [],
"faucets": [],
"features": [],
"icon": {
"url": "https://images.ctfassets.net/9bazykntljf6/62CceHSYsRS4D9fgDSkLRB/877cb8f26954e1743ff535fd7fdaf78f/avacloud-placeholder.svg",
"width": 256,
"height": 256,
"format": "svg"
},
"infoURL": "https://avacloud.io",
"name": "QaUser4106 Testnet",
"nativeCurrency": {
"name": "QaUser4106 Testnet Token",
"symbol": "BBS",
"decimals": 18
},
"networkId": 29386,
"redFlags": [],
"rpc": [
"https://29386.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://subnets.avax.network/defi-kingdoms/dfk-chain/rpc"
],
"shortName": "QaUser4106 Testnet",
"slug": "qauser4106-testnet",
"testnet": true
} as const satisfies Chain;
+30
View File
@@ -0,0 +1,30 @@
import type { Chain } from "../src/types";
export default {
"chain": "KaiChain",
"chainId": 29536,
"explorers": [
{
"name": "KaiChain Explorer",
"url": "https://testnet-explorer.kaichain.net",
"standard": "EIP3091"
}
],
"faucets": [
"https://faucet.kaichain.net"
],
"infoURL": "https://kaichain.net",
"name": "KaiChain Testnet",
"nativeCurrency": {
"name": "KaiChain Testnet Native Token",
"symbol": "KEC",
"decimals": 18
},
"networkId": 29536,
"rpc": [
"https://29536.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://testnet-rpc.kaichain.net"
],
"shortName": "tkec",
"slug": "kaichain-testnet",
"testnet": true
} as const satisfies Chain;
+1
View File
@@ -27,5 +27,6 @@ export default {
],
"shortName": "Bobaopera",
"slug": "bobaopera",
"status": "deprecated",
"testnet": false
} as const satisfies Chain;
+28
View File
@@ -0,0 +1,28 @@
import type { Chain } from "../src/types";
export default {
"chain": "SatoshiVM",
"chainId": 3109,
"explorers": [],
"faucets": [],
"icon": {
"url": "ipfs://QmdXnQw9YwzEBZnciSe42MMwnH1SqhfY3FcVbseyM4k7Th",
"width": 940,
"height": 941,
"format": "png"
},
"infoURL": "https://www.satoshivm.io/",
"name": "SatoshiVM Alpha Mainnet",
"nativeCurrency": {
"name": "BTC",
"symbol": "BTC",
"decimals": 18
},
"networkId": 3109,
"rpc": [
"https://3109.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://alpha-rpc-node-http.svmscan.io"
],
"shortName": "SAVM",
"slug": "satoshivm-alpha",
"testnet": false
} as const satisfies Chain;
+28
View File
@@ -0,0 +1,28 @@
import type { Chain } from "../src/types";
export default {
"chain": "SatoshiVM",
"chainId": 3110,
"explorers": [],
"faucets": [],
"icon": {
"url": "ipfs://QmdXnQw9YwzEBZnciSe42MMwnH1SqhfY3FcVbseyM4k7Th",
"width": 940,
"height": 941,
"format": "png"
},
"infoURL": "https://www.satoshivm.io/",
"name": "SatoshiVM Testnet",
"nativeCurrency": {
"name": "BTC",
"symbol": "BTC",
"decimals": 18
},
"networkId": 3110,
"rpc": [
"https://3110.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://test-rpc-node-http.svmscan.io"
],
"shortName": "tSAVM",
"slug": "satoshivm-testnet",
"testnet": true
} as const satisfies Chain;
+33
View File
@@ -0,0 +1,33 @@
import type { Chain } from "../src/types";
export default {
"chain": "KingdomChain",
"chainId": 39916801,
"explorers": [
{
"name": "TravelSong",
"url": "https://www.beastkingdom.io/travelsong",
"standard": "EIP3091"
}
],
"faucets": [],
"features": [
{
"name": "EIP155"
}
],
"infoURL": "https://www.beastkingdom.io/",
"name": "Kingdom Chain",
"nativeCurrency": {
"name": "Kozi",
"symbol": "KOZI",
"decimals": 18
},
"networkId": 39916801,
"rpc": [
"https://39916801.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://kingdomchain.observer/rpc"
],
"shortName": "kchain",
"slug": "kingdom-chain",
"testnet": false
} as const satisfies Chain;
+45
View File
@@ -0,0 +1,45 @@
import type { Chain } from "../src/types";
export default {
"chain": "SYNDR",
"chainId": 404,
"explorers": [
{
"name": "Syndr L3 Explorer",
"url": "https://explorer.syndr.com",
"standard": "EIP3091"
}
],
"faucets": [],
"icon": {
"url": "ipfs://bafkreibsl7iuxeibovp7uddigbus3lyse2f7n4s2lomgvf33fmgyxjwq5i",
"width": 1000,
"height": 1000,
"format": "png"
},
"infoURL": "https://syndr.com",
"name": "Syndr L3",
"nativeCurrency": {
"name": "Ether",
"symbol": "ETH",
"decimals": 18
},
"networkId": 404,
"parent": {
"type": "L2",
"chain": "eip155-42161",
"bridges": [
{
"url": "https://bridge.syndr.com"
}
]
},
"rpc": [
"https://404.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://rpc.syndr.com",
"wss://rpc.syndr.com/ws"
],
"shortName": "syndr-l3",
"slug": "syndr-l3",
"testnet": false,
"title": "Syndr L3 Rollup"
} as const satisfies Chain;
+1
View File
@@ -28,5 +28,6 @@ export default {
"shortName": "BobaoperaTestnet",
"slip44": 1,
"slug": "bobaopera-testnet",
"status": "deprecated",
"testnet": true
} as const satisfies Chain;
+1 -1
View File
@@ -25,6 +25,6 @@ export default {
"https://subnets.avax.network/defi-kingdoms/dfk-chain/rpc"
],
"shortName": "Testnet",
"slug": "testnet-testnet",
"slug": "testnet-testnet-41604",
"testnet": true
} as const satisfies Chain;
+30
View File
@@ -0,0 +1,30 @@
import type { Chain } from "../src/types";
export default {
"chain": "Avalanche",
"chainId": 41720,
"explorers": [],
"faucets": [],
"features": [],
"icon": {
"url": "https://images.ctfassets.net/9bazykntljf6/62CceHSYsRS4D9fgDSkLRB/877cb8f26954e1743ff535fd7fdaf78f/avacloud-placeholder.svg",
"width": 256,
"height": 256,
"format": "svg"
},
"infoURL": "https://avacloud.io",
"name": "QR0409s1 Testnet",
"nativeCurrency": {
"name": "QR0409s1 Testnet Token",
"symbol": "ZSP",
"decimals": 18
},
"networkId": 41720,
"redFlags": [],
"rpc": [
"https://41720.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://subnets.avax.network/qr0409s1/testnet/rpc"
],
"shortName": "QR0409s1 Testnet",
"slug": "qr0409s1-testnet",
"testnet": true
} as const satisfies Chain;
+1
View File
@@ -36,5 +36,6 @@ export default {
"shortName": "BobaFujiTestnet",
"slip44": 1,
"slug": "bobafuji-testnet",
"status": "deprecated",
"testnet": true
} as const satisfies Chain;
+1
View File
@@ -27,5 +27,6 @@ export default {
],
"shortName": "bobaavax",
"slug": "boba-avax",
"status": "deprecated",
"testnet": false
} as const satisfies Chain;
+30
View File
@@ -0,0 +1,30 @@
import type { Chain } from "../src/types";
export default {
"chain": "Avalanche",
"chainId": 45979,
"explorers": [],
"faucets": [],
"features": [],
"icon": {
"url": "https://images.ctfassets.net/9bazykntljf6/62CceHSYsRS4D9fgDSkLRB/877cb8f26954e1743ff535fd7fdaf78f/avacloud-placeholder.svg",
"width": 256,
"height": 256,
"format": "svg"
},
"infoURL": "https://avacloud.io",
"name": "QaUser5277 Testnet",
"nativeCurrency": {
"name": "QaUser5277 Testnet Token",
"symbol": "LWR",
"decimals": 18
},
"networkId": 45979,
"redFlags": [],
"rpc": [
"https://45979.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://subnets.avax.network/defi-kingdoms/dfk-chain/rpc"
],
"shortName": "QaUser5277 Testnet",
"slug": "qauser5277-testnet",
"testnet": true
} as const satisfies Chain;
+30
View File
@@ -0,0 +1,30 @@
import type { Chain } from "../src/types";
export default {
"chain": "Avalanche",
"chainId": 47157,
"explorers": [],
"faucets": [],
"features": [],
"icon": {
"url": "https://images.ctfassets.net/9bazykntljf6/62CceHSYsRS4D9fgDSkLRB/877cb8f26954e1743ff535fd7fdaf78f/avacloud-placeholder.svg",
"width": 256,
"height": 256,
"format": "svg"
},
"infoURL": "https://avacloud.io",
"name": "Avaland Testnet",
"nativeCurrency": {
"name": "Avaland Testnet Token",
"symbol": "AVA",
"decimals": 18
},
"networkId": 47157,
"redFlags": [],
"rpc": [
"https://47157.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://subnets.avax.network/avalandtes/testnet/rpc"
],
"shortName": "Avaland Testnet",
"slug": "avaland-testnet",
"testnet": true
} as const satisfies Chain;
+29
View File
@@ -0,0 +1,29 @@
import type { Chain } from "../src/types";
export default {
"chain": "DoCoin",
"chainId": 526916,
"explorers": [
{
"name": "DoCoin Community Chain Explorer",
"url": "https://explorer.docoin.shop",
"standard": "EIP3091"
}
],
"faucets": [],
"infoURL": "https://docoin.network",
"name": "DoCoin Community Chain",
"nativeCurrency": {
"name": "DO",
"symbol": "DCT",
"decimals": 18
},
"networkId": 526916,
"rpc": [
"https://526916.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://rpc.docoin.shop"
],
"shortName": "DoCoin",
"slug": "docoin-community-chain",
"testnet": false,
"title": "DoCoin Community Chain"
} as const satisfies Chain;
-5
View File
@@ -7,11 +7,6 @@ export default {
"name": "Scroll Sepolia Etherscan",
"url": "https://sepolia.scrollscan.com",
"standard": "EIP3091"
},
{
"name": "Scroll Sepolia Blockscout",
"url": "https://sepolia-blockscout.scroll.io",
"standard": "EIP3091"
}
],
"faucets": [],
-6
View File
@@ -7,11 +7,6 @@ export default {
"name": "Scrollscan",
"url": "https://scrollscan.com",
"standard": "EIP3091"
},
{
"name": "Blockscout",
"url": "https://blockscout.scroll.io",
"standard": "EIP3091"
}
],
"faucets": [],
@@ -35,7 +30,6 @@ export default {
"rpc": [
"https://534352.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://rpc.scroll.io",
"https://rpc-scroll.icecreamswap.com",
"https://rpc.ankr.com/scroll",
"https://scroll-mainnet.chainstacklabs.com"
],
+36
View File
@@ -0,0 +1,36 @@
import type { Chain } from "../src/types";
export default {
"chain": "Flow",
"chainId": 545,
"explorers": [
{
"name": "Flow Diver",
"url": "https://testnet.flowdiver.io",
"standard": "none"
}
],
"faucets": [
"https://testnet-faucet.onflow.org"
],
"icon": {
"url": "ipfs://QmQFugEvsfU3ARjjJ7YRLJwSxMfTk54WWzrofekTRBKFaC",
"width": 2000,
"height": 2000,
"format": "png"
},
"infoURL": "https://developers.flow.com/evm/about",
"name": "Testnet",
"nativeCurrency": {
"name": "FLOW",
"symbol": "FLOW",
"decimals": 18
},
"networkId": 545,
"rpc": [
"https://545.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://testnet.evm.nodes.onflow.org"
],
"shortName": "flow-testnet",
"slug": "testnet",
"testnet": true
} as const satisfies Chain;
+6
View File
@@ -12,6 +12,12 @@ export default {
"faucets": [
"https://t.me/vexfaucetbot"
],
"icon": {
"url": "ipfs://QmcZiEAGFYEEax1uxYnDUh8X5YBvZqMi9K92yZoq4o2zeM",
"width": 451,
"height": 446,
"format": "png"
},
"infoURL": "https://vexanium.com",
"name": "VEX EVM TESTNET",
"nativeCurrency": {
+1 -1
View File
@@ -5,7 +5,7 @@ export default {
"explorers": [
{
"name": "Boba BNB block explorer",
"url": "https://blockexplorer.bnb.boba.network",
"url": "https://bobascan.com",
"standard": "none"
}
],
+35
View File
@@ -0,0 +1,35 @@
import type { Chain } from "../src/types";
export default {
"chain": "QIE",
"chainId": 5656,
"explorers": [
{
"name": "QIE Explorer",
"url": "https://mainnet.qiblockchain.online",
"standard": "EIP3091"
}
],
"faucets": [],
"icon": {
"url": "ipfs://QmRoNxCti6cHrPgZ48YQVSMg9g6ympfXuV8kGQJXrbbmed",
"width": 600,
"height": 600,
"format": "png"
},
"infoURL": "https://qiblockchain.online/",
"name": "QIE Blockchain",
"nativeCurrency": {
"name": "QIE Blockchain",
"symbol": "QIE",
"decimals": 18
},
"networkId": 5656,
"rpc": [
"https://5656.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://rpc-main1.qiblockchain.online/",
"https://rpc-main2.qiblockchain.online/"
],
"shortName": "QIE",
"slug": "qie-blockchain",
"testnet": false
} as const satisfies Chain;
+40
View File
@@ -0,0 +1,40 @@
import type { Chain } from "../src/types";
export default {
"chain": "Filenova",
"chainId": 5675,
"explorers": [
{
"name": "filenova testnet explorer",
"url": "https://scantest.filenova.org",
"standard": "none",
"icon": {
"url": "ipfs://Qmc4KSKiAChhtN7ZNE5gvkLTRbvtLreKU9xaQFG8BKM8RE",
"width": 1000,
"height": 1000,
"format": "png"
}
}
],
"faucets": [],
"icon": {
"url": "ipfs://Qmc4KSKiAChhtN7ZNE5gvkLTRbvtLreKU9xaQFG8BKM8RE",
"width": 1000,
"height": 1000,
"format": "png"
},
"infoURL": "https://filenova.org",
"name": "Filenova Testnet",
"nativeCurrency": {
"name": "Test Filecoin",
"symbol": "tFIL",
"decimals": 18
},
"networkId": 5675,
"rpc": [
"https://5675.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://rpctest.filenova.org"
],
"shortName": "tfilenova",
"slug": "filenova-testnet",
"testnet": true
} as const satisfies Chain;
+40
View File
@@ -0,0 +1,40 @@
import type { Chain } from "../src/types";
export default {
"chain": "Filenova",
"chainId": 579,
"explorers": [
{
"name": "filenova explorer",
"url": "https://scan.filenova.org",
"standard": "none",
"icon": {
"url": "ipfs://Qmc4KSKiAChhtN7ZNE5gvkLTRbvtLreKU9xaQFG8BKM8RE",
"width": 1000,
"height": 1000,
"format": "png"
}
}
],
"faucets": [],
"icon": {
"url": "ipfs://Qmc4KSKiAChhtN7ZNE5gvkLTRbvtLreKU9xaQFG8BKM8RE",
"width": 1000,
"height": 1000,
"format": "png"
},
"infoURL": "https://filenova.org",
"name": "Filenova Mainnet",
"nativeCurrency": {
"name": "Filecoin",
"symbol": "FIL",
"decimals": 18
},
"networkId": 579,
"rpc": [
"https://579.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://rpc.filenova.org"
],
"shortName": "filenova",
"slug": "filenova",
"testnet": false
} as const satisfies Chain;
+41
View File
@@ -0,0 +1,41 @@
import type { Chain } from "../src/types";
export default {
"chain": "Tangle",
"chainId": 5845,
"explorers": [
{
"name": "Tangle EVM Explorer",
"url": "https://explorer.tangle.tools",
"standard": "EIP3091",
"icon": {
"url": "ipfs://QmbxMNBTeQgch8t9GpWdLiS2R3wPYCzVRaX5kCQ4o5QU3w",
"width": 1600,
"height": 1600,
"format": "png"
}
}
],
"faucets": [],
"icon": {
"url": "ipfs://QmbxMNBTeQgch8t9GpWdLiS2R3wPYCzVRaX5kCQ4o5QU3w",
"width": 1600,
"height": 1600,
"format": "png"
},
"infoURL": "https://docs.tangle.tools",
"name": "Tangle",
"nativeCurrency": {
"name": "Tangle",
"symbol": "TNT",
"decimals": 18
},
"networkId": 5845,
"rpc": [
"https://5845.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://rpc.tangle.tools",
"wss://rpc.tangle.tools"
],
"shortName": "tangle",
"slug": "tangle",
"testnet": false
} as const satisfies Chain;
+30
View File
@@ -0,0 +1,30 @@
import type { Chain } from "../src/types";
export default {
"chain": "Avalanche",
"chainId": 58474,
"explorers": [],
"faucets": [],
"features": [],
"icon": {
"url": "https://images.ctfassets.net/9bazykntljf6/62CceHSYsRS4D9fgDSkLRB/877cb8f26954e1743ff535fd7fdaf78f/avacloud-placeholder.svg",
"width": 256,
"height": 256,
"format": "svg"
},
"infoURL": "https://avacloud.io",
"name": "QI0408s4 Testnet",
"nativeCurrency": {
"name": "QI0408s4 Testnet Token",
"symbol": "QCE",
"decimals": 18
},
"networkId": 58474,
"redFlags": [],
"rpc": [
"https://58474.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://subnets.avax.network/defi-kingdoms/dfk-chain/rpc"
],
"shortName": "QI0408s4 Testnet",
"slug": "qi0408s4-testnet",
"testnet": true
} as const satisfies Chain;
+3 -3
View File
@@ -36,7 +36,7 @@ export default {
"format": "svg"
},
"infoURL": "https://linea.build",
"name": "Linea Testnet",
"name": "Linea Goerli",
"nativeCurrency": {
"name": "Linea Ether",
"symbol": "ETH",
@@ -59,9 +59,9 @@ export default {
"https://linea-goerli.infura.io/v3/${INFURA_API_KEY}",
"wss://linea-goerli.infura.io/ws/v3/${INFURA_API_KEY}"
],
"shortName": "linea-testnet",
"shortName": "linea-goerli",
"slip44": 1,
"slug": "linea-testnet",
"slug": "linea-goerli",
"status": "active",
"testnet": true,
"title": "Linea Goerli Testnet"
+28 -1
View File
@@ -3,6 +3,28 @@ export default {
"chain": "ETH",
"chainId": 59141,
"explorers": [
{
"name": "Etherscan",
"url": "https://sepolia.lineascan.build",
"standard": "EIP3091",
"icon": {
"url": "ipfs://QmURjritnHL7a8TwZgsFwp3f272DJmG5paaPtWDZ98QZwH",
"width": 97,
"height": 102,
"format": "svg"
}
},
{
"name": "Blockscout",
"url": "https://explorer.sepolia.linea.build",
"standard": "EIP3091",
"icon": {
"url": "ipfs://QmURjritnHL7a8TwZgsFwp3f272DJmG5paaPtWDZ98QZwH",
"width": 97,
"height": 102,
"format": "svg"
}
},
{
"name": "blockscout",
"url": "https://explorer.sepolia.linea.build/",
@@ -39,10 +61,15 @@ export default {
"redFlags": [],
"rpc": [
"https://59141.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://rpc.sepolia.linea.build"
"https://linea-sepolia.infura.io/v3/${INFURA_API_KEY}",
"wss://linea-sepolia.infura.io/ws/v3/${INFURA_API_KEY}",
"https://rpc.sepolia.linea.build",
"wss://rpc.sepolia.linea.build"
],
"shortName": "linea-sepolia",
"slip44": 1,
"slug": "linea-sepolia",
"status": "active",
"testnet": true,
"title": "Linea Sepolia Testnet"
} as const satisfies Chain;
+28
View File
@@ -0,0 +1,28 @@
import type { Chain } from "../src/types";
export default {
"chain": "BounceBit",
"chainId": 6001,
"explorers": [
{
"name": "BBScan Mainnet Explorer",
"url": "https://bbscan.io",
"standard": "none"
}
],
"faucets": [],
"infoURL": "https://bouncebit.io",
"name": "BounceBit Mainnet",
"nativeCurrency": {
"name": "BounceBit",
"symbol": "BB",
"decimals": 18
},
"networkId": 6001,
"rpc": [
"https://6001.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://fullnode-mainnet.bouncebitapi.com/"
],
"shortName": "bouncebit-mainnet",
"slug": "bouncebit",
"testnet": false
} as const satisfies Chain;
+20
View File
@@ -0,0 +1,20 @@
import type { Chain } from "../src/types";
export default {
"chain": "ETH",
"chainId": 60808,
"explorers": [],
"faucets": [],
"infoURL": "https://gobob.xyz",
"name": "BOB",
"nativeCurrency": {
"name": "Ether",
"symbol": "ETH",
"decimals": 18
},
"networkId": 60808,
"rpc": [],
"shortName": "bob",
"slug": "bob",
"status": "incubating",
"testnet": false
} as const satisfies Chain;
+5
View File
@@ -3,6 +3,11 @@ export default {
"chain": "KEC",
"chainId": 61406,
"explorers": [
{
"name": "KaiChain Explorer",
"url": "https://explorer.kaichain.net",
"standard": "EIP3091"
},
{
"name": "KEC SCAN",
"url": "https://explorer.kaichain.net/",
+4 -4
View File
@@ -1,6 +1,6 @@
import type { Chain } from "../src/types";
export default {
"chain": "FlowEVM",
"chain": "Flow",
"chainId": 646,
"explorers": [
{
@@ -19,7 +19,7 @@ export default {
"format": "png"
},
"infoURL": "https://developers.flow.com/evm/about",
"name": "FlowEVM PreviewNet",
"name": "Previewnet",
"nativeCurrency": {
"name": "FLOW",
"symbol": "FLOW",
@@ -30,7 +30,7 @@ export default {
"https://646.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://previewnet.evm.nodes.onflow.org"
],
"shortName": "flowevm-preview",
"slug": "flowevm-previewnet",
"shortName": "flow-previewnet",
"slug": "previewnet",
"testnet": false
} as const satisfies Chain;
+4 -4
View File
@@ -1,6 +1,6 @@
import type { Chain } from "../src/types";
export default {
"chain": "FlowEVM",
"chain": "Flow",
"chainId": 747,
"explorers": [
{
@@ -17,7 +17,7 @@ export default {
"format": "png"
},
"infoURL": "https://developers.flow.com/evm/about",
"name": "FlowEVM Mainnet",
"name": "Mainnet",
"nativeCurrency": {
"name": "FLOW",
"symbol": "FLOW",
@@ -28,7 +28,7 @@ export default {
"https://747.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://mainnet.evm.nodes.onflow.org"
],
"shortName": "flowevm-mainnet",
"slug": "flowevm",
"shortName": "flow-mainnet",
"slug": "flow-mainnet",
"testnet": false
} as const satisfies Chain;
+4 -1
View File
@@ -30,7 +30,10 @@ export default {
"redFlags": [],
"rpc": [
"https://80002.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://rpc-amoy.polygon.technology"
"https://rpc-amoy.polygon.technology",
"https://https://rpc-amoy.polygon.technology",
"https://polygon-amoy-bor-rpc.publicnode.com",
"wss://polygon-amoy-bor-rpc.publicnode.com"
],
"shortName": "polygonamoy",
"slip44": 1,
+29
View File
@@ -0,0 +1,29 @@
import type { Chain } from "../src/types";
export default {
"chain": "REXX",
"chainId": 888882,
"explorers": [
{
"name": "REXX Mainnet Explorer",
"url": "https://rexxnetwork.com",
"standard": "EIP3091"
}
],
"faucets": [],
"infoURL": "https://rexxnetwork.com",
"name": "REXX Mainnet",
"nativeCurrency": {
"name": "REXX",
"symbol": "REXX",
"decimals": 18
},
"networkId": 888882,
"rpc": [
"https://888882.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://rpc.rexxnetwork.com"
],
"shortName": "REXX",
"slug": "rexx",
"testnet": false,
"title": "REXX Mainnet"
} as const satisfies Chain;
@@ -0,0 +1,29 @@
import type { Chain } from "../src/types";
export default {
"chain": "PTCE",
"chainId": 889910246,
"explorers": [
{
"name": "PTCESCAN Explorer",
"url": "https://ptcscan.io",
"standard": "EIP3091"
}
],
"faucets": [],
"infoURL": "https://ptcscan.io",
"name": "PTCESCAN Mainnet",
"nativeCurrency": {
"name": "PTCE",
"symbol": "PTCE",
"decimals": 18
},
"networkId": 889910246,
"rpc": [
"https://889910246.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://rpc.ptcscan.io"
],
"shortName": "POLYTECH",
"slug": "ptcescan",
"testnet": false,
"title": "PTCESCAN Mainnet"
} as const satisfies Chain;
+28
View File
@@ -0,0 +1,28 @@
import type { Chain } from "../src/types";
export default {
"chain": "Fxcore",
"chainId": 90001,
"explorers": [],
"faucets": [],
"icon": {
"url": "ipfs://QmauD5hzc5q1VgEU1nT4AGLCeV79CB8GgfnXXeeLmBZVHf",
"width": 36,
"height": 36,
"format": "png"
},
"infoURL": "https://functionx.io/",
"name": "F(x)Core Testnet Network",
"nativeCurrency": {
"name": "Function X",
"symbol": "FX",
"decimals": 18
},
"networkId": 90001,
"rpc": [
"https://90001.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://testnet-fx-json-web3.functionx.io:8545"
],
"shortName": "dhobyghaut",
"slug": "f-x-core-testnet-network",
"testnet": true
} as const satisfies Chain;
+1 -1
View File
@@ -5,7 +5,7 @@ export default {
"explorers": [
{
"name": "Boba BNB Testnet block explorer",
"url": "https://blockexplorer.testnet.bnb.boba.network",
"url": "https://testnet.bobascan.com",
"standard": "none"
}
],
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/chains",
"version": "0.1.91",
"version": "0.1.92",
"main": "dist/thirdweb-dev-chains.cjs.js",
"module": "dist/thirdweb-dev-chains.esm.js",
"exports": {
File diff suppressed because one or more lines are too long
+10
View File
@@ -1,5 +1,15 @@
# thirdweb
## 0.13.67
### Patch Changes
- Updated dependencies [[`d836889`](https://github.com/thirdweb-dev/js/commit/d836889f464a4fc9617839f30e2cc780b3bcca78), [`d65713a`](https://github.com/thirdweb-dev/js/commit/d65713a7af67bf20e5b88b72f5d43dfba9172b3b)]:
- @thirdweb-dev/sdk@4.0.61
- @thirdweb-dev/chains@0.1.92
- @thirdweb-dev/wallets@2.5.1
- @thirdweb-dev/auth@4.1.59
## 0.13.66
### Patch Changes
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@thirdweb-dev/cli",
"main": "dist/cli/index.js",
"version": "0.13.66",
"version": "0.13.67",
"repository": "https://github.com/thirdweb-dev/js/tree/main/legacy_packages/cli",
"author": "thirdweb eng <[email protected]>",
"license": "Apache-2.0",
+64
View File
@@ -1,5 +1,69 @@
# @thirdweb-dev/react-core
## 4.6.0
### Minor Changes
- [#2579](https://github.com/thirdweb-dev/js/pull/2579) [`d836889`](https://github.com/thirdweb-dev/js/commit/d836889f464a4fc9617839f30e2cc780b3bcca78) Thanks [@ElasticBottle](https://github.com/ElasticBottle)! - Adds "buy with crypto" Hooks, Here's an example of how you can use it to build a UI for Swapping tokens
```tsx
function Component() {
const signer = useSigner();
// 1. get a quote for swapping tokens
const quoteQuery = useBuyWithCryptoQuote(swapParams);
const [buyTxHash, setBuyTxHash] = useState<string | undefined>();
const statusQuery = useBuyWithCryptoStatus(
buyTxHash
? {
clientId: "YOUR_CLIENT_ID",
transactionHash: buyTxHash,
}
: undefined,
);
async function handleBuyWithCrypto() {
if (!quoteQuery.data || !signer) {
return;
}
// 2. if approval is required
if (quoteQuery.data.approval) {
const approveTx = await signer.sendTransaction(
quoteQuery.data.approval,
);
await approveTx.wait();
}
// 3. send the transaction to buy crypto
const buyTx = await signer.sendTransaction(
quoteQuery.data.transactionRequest,
);
await buyTx.wait();
// set buyTx.transactionHash to poll the status of the swap transaction
setBuyTxHash(buyTx.hash);
}
// 4. wait for the status of the transaction
if (statusQuery.data) {
console.log("swap status:", statusQuery.data);
}
return <button onClick={handleBuyWithCrypto}>Swap</button>;
}
```
For more information, check out the [pay documentation](https://portal.thirdweb.com/connect/pay/buy-with-crypto) for purchases with crypto
### Patch Changes
- Updated dependencies [[`d836889`](https://github.com/thirdweb-dev/js/commit/d836889f464a4fc9617839f30e2cc780b3bcca78), [`d65713a`](https://github.com/thirdweb-dev/js/commit/d65713a7af67bf20e5b88b72f5d43dfba9172b3b)]:
- @thirdweb-dev/[email protected]
- @thirdweb-dev/[email protected]
- @thirdweb-dev/[email protected]
- @thirdweb-dev/[email protected]
## 4.5.0
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/react-core",
"version": "4.5.0",
"version": "4.6.0",
"repository": "https://github.com/thirdweb-dev/js/tree/main/legacy_packages/react-core",
"author": "thirdweb eng <[email protected]>",
"license": "Apache-2.0",
@@ -0,0 +1,51 @@
import {
useQuery,
type UseQueryOptions,
type UseQueryResult,
} from "@tanstack/react-query";
import {
BuyWithCryptoHistoryData,
BuyWithCryptoHistoryParams,
getBuyWithCryptoHistory,
} from "@thirdweb-dev/sdk";
type BuyWithCryptoHistoryQueryOptions = Omit<
UseQueryOptions<BuyWithCryptoHistoryData>,
"queryFn" | "queryKey" | "enabled"
>;
/**
* Hook to get the history of purchases a given wallet has performed.
*
* This hook is a React Query wrapper of the [`getBuyWithCryptoHistory`](https://portal.thirdweb.com/references/typescript/v4/getBuyWithCryptoHistory) function.
* You can also use that function directly
* @param params - object of type [`BuyWithCryptoHistoryParams`](https://portal.thirdweb.com/references/typescript/v4/BuyWithCryptoHistoryParams)
* @param queryParams - options to configure the react query
* @returns A React Query object which contains the data of type [`BuyWithCryptoHistoryData`](https://portal.thirdweb.com/references/typescript/v4/BuyWithCryptoHistoryData)
* @example
* ```tsx
* import { useBuyWithCryptoHistory } from "@thirdweb-dev/react";
*
* function Component() {
* const buyWithCryptoHistory = useBuyWithCryptoHistory(params);
* console.log(buyWithCryptoHistory.data);
* return <div> ... </div>;
* }
* ```
*/
export function useBuyWithCryptoHistory(
params?: BuyWithCryptoHistoryParams,
queryParams?: BuyWithCryptoHistoryQueryOptions,
): UseQueryResult<BuyWithCryptoHistoryData> {
return useQuery<BuyWithCryptoHistoryData>({
...queryParams,
queryKey: ["buyWithCryptoHistory", params] as const,
queryFn: () => {
if (!params) {
throw new Error("Swap params are required");
}
return getBuyWithCryptoHistory(params);
},
enabled: !!params,
});
}
@@ -0,0 +1,86 @@
import {
useQuery,
type UseQueryResult,
type UseQueryOptions,
} from "@tanstack/react-query";
import {
BuyWithCryptoQuote,
getBuyWithCryptoQuote,
GetBuyWithCryptoQuoteParams,
} from "@thirdweb-dev/sdk";
type BuyWithCryptoQuoteQueryOptions = Omit<
UseQueryOptions<BuyWithCryptoQuote>,
"queryFn" | "queryKey" | "enabled"
>;
/**
* Hook to get a quote of type [`BuyWithCryptoQuote`](https://portal.thirdweb.com/references/typescript/v4/BuyWithCryptoQuote) for buying tokens with crypto.
* This quote contains the information about the purchase such as token amounts, processing fees, estimated time etc.
*
* This hook is a React Query wrapper of the [`getBuyWithCryptoQuote`](https://portal.thirdweb.com/references/typescript/v4/getBuyWithCryptoQuote) function.
* You can also use that function directly
*
* Once you have the quote, you can use the [`useBuyWithCryptoStatus`](https://portal.thirdweb.com/references/react/v4/useBuyWithCryptoStatus) function to get the status of the swap transaction.
* @param params - object of type [`GetBuyWithCryptoQuoteParams`](https://portal.thirdweb.com/references/react/v4/GetBuyWithCryptoQuoteParams)
* @param queryParams - options to configure the react query
* @returns A React Query object which contains the data of type [`BuyWithCryptoQuote`](https://portal.thirdweb.com/references/typescript/v4/BuyWithCryptoQuote)
* @example
* ```tsx
* import { useSigner, useBuyWithCryptoQuote, useBuyWithCryptoStatus } from "@thirdweb-dev/react";
*
* function Component() {
* const buyWithCryptoQuoteQuery = useBuyWithCryptoQuote* (swapParams);
* const signer = useSigner();
* const [buyTxHash, setBuyTxHash] = useState<string | undefined>();
* const buyWithCryptoStatusQuery = useBuyWithCryptoStatus* (buyTxHash ? {
* clientId: "YOUR_CLIENT_ID",
* transactionHash: buyTxHash,
* }: undefined);
*
* async function handleBuyWithCrypto() {
* if (!buyWithCryptoQuoteQuery.data || !signer) {
* return;
* }
*
* // if approval is required
* if (buyWithCryptoQuoteQuery.data.approval) {
* const approveTx = await signer.sendTransaction* (buyWithCryptoQuoteQuery.data.approval);
* await approveTx.wait();
* }
*
* // send the transaction to buy crypto
* // this promise is resolved when user confirms the transaction * in the wallet and the transaction is sent to the blockchain
* const buyTx = await signer.sendTransaction* (buyWithCryptoQuoteQuery.data.transactionRequest);
* await buyTx.wait();
*
* // set buyTx.hash to poll the status of the swap * transaction
* setBuyTxHash(buyTx.hash);
* }
*
* if (buyWithCryptoStatusQuery.data) {
* console.log('buyWithCryptoStatusQuery.data', * buyWithCryptoStatusQuery.data)
* }
* return <button onClick={handleBuyWithCrypto}>Swap</button>
* }
* ```
*/
export function useBuyWithCryptoQuote(
params?: GetBuyWithCryptoQuoteParams,
queryParams?: BuyWithCryptoQuoteQueryOptions,
): UseQueryResult<BuyWithCryptoQuote> {
return useQuery<BuyWithCryptoQuote>({
...queryParams,
queryKey: ["buyWithCryptoQuote", params],
queryFn: () => {
if (!params) {
throw new Error("Swap params are required");
}
if (!params?.clientId) {
throw new Error("Client ID is required in swap params");
}
return getBuyWithCryptoQuote(params);
},
enabled: !!params,
});
}
@@ -0,0 +1,91 @@
import { useQuery } from "@tanstack/react-query";
import {
BuyWithCryptoStatus,
BuyWithCryptoTransaction,
getBuyWithCryptoStatus,
} from "@thirdweb-dev/sdk";
import { useState } from "react";
// TODO: use the estimate to vary the polling interval
const DEFAULT_POLL_INTERVAL = 5000;
/**
* A hook to get a status of Buy with Crypto transaction.
*
* This hook is a React Query wrapper of the [`getBuyWithCryptoStatus`](https://portal.thirdweb.com/references/typescript/v4/getBuyWithCryptoStatus) function.
* You can also use that function directly.
* @param params - object of type [`BuyWithCryptoTransaction`](https://portal.thirdweb.com/references/react/v4/BuyWithCryptoTransaction)
* @returns A react query object which contains the data of type [`BuyWithCryptoStatus`](https://portal.thirdweb.com/references/typescript/v4/BuyWithCryptoStatus)
* @example
* ```tsx
* import { useSigner, useBuyWithCryptoQuote, useBuyWithCryptoStatus } from "@thirdweb-dev/react";
*
* function Component() {
* const buyWithCryptoQuoteQuery = useBuyWithCryptoQuote(swapParams);
* const signer = useSigner();
* const [buyTxHash, setBuyTxHash] = useState<string | undefined>();
* const buyWithCryptoStatusQuery = useBuyWithCryptoStatus* (buyTxHash ? {
* clientId: "YOUR_CLIENT_ID",
* transactionHash: buyTxHash,
* }: undefined);
*
* async function handleBuyWithCrypto() {
* if (!buyWithCryptoQuoteQuery.data || !signer) {
* return;
* }
*
* // if approval is required
* if (buyWithCryptoQuoteQuery.data.approval) {
* const approveTx = await signer.sendTransaction(buyWithCryptoQuoteQuery.data.approval);
* await approveTx.wait();
* }
*
* // send the transaction to buy crypto
* // this promise is resolved when user confirms the transaction * in the wallet and the transaction is sent to the blockchain
* const buyTx = await signer.sendTransaction(buyWithCryptoQuoteQuery.data.transactionRequest);
* await buyTx.wait();
*
* // set buyTx.transactionHash to poll the status of the swap * transaction
* setBuyTxHash(buyTx.hash);
* }
*
* if (buyWithCryptoStatusQuery.data) {
* console.log('buyWithCryptoStatusQuery.data', * buyWithCryptoStatusQuery.data)
* }
* return <button onClick={handleBuyWithCrypto}>Swap</button>
* }
* ```
*/
export function useBuyWithCryptoStatus(params?: BuyWithCryptoTransaction) {
const [refetchInterval, setRefetchInterval] = useState<number>(
DEFAULT_POLL_INTERVAL,
);
return useQuery<BuyWithCryptoStatus, Error>({
queryKey: ["swapStatus", params?.transactionHash] as const,
queryFn: async () => {
if (!params) {
throw new Error("Missing swap status params");
}
if (!params?.clientId) {
throw new Error("Missing clientId in swap status params");
}
const swapStatus_ = await getBuyWithCryptoStatus({
...params,
clientId: params.clientId,
});
if (
swapStatus_.status === "COMPLETED" ||
swapStatus_.status === "FAILED"
) {
setRefetchInterval(0);
}
return swapStatus_;
},
enabled: !!params,
refetchInterval: refetchInterval,
refetchIntervalInBackground: true,
retry: true,
});
}
@@ -302,3 +302,8 @@ export { getErcs, getErc1155, getErc721, getErc20 } from "./types";
// transaction hooks
export { useWatchTransactions } from "./hooks/useTransactions";
export type { UseWatchTransactionsParams } from "./hooks/useTransactions";
// pay hooks
export { useBuyWithCryptoHistory } from "./hooks/pay/useBuyWithCryptoHistory";
export { useBuyWithCryptoQuote } from "./hooks/pay/useBuyWithCryptoQuote";
export { useBuyWithCryptoStatus } from "./hooks/pay/useBuyWithCryptoStatus";
@@ -1,5 +1,7 @@
# @thirdweb-dev/react-native-compat
## 0.7.32
## 0.7.31
## 0.7.30
@@ -1,7 +1,7 @@
{
"name": "@thirdweb-dev/react-native-compat",
"description": "Shims for Thirdweb in React Native Projects",
"version": "0.7.31",
"version": "0.7.32",
"author": "thirdweb eng <[email protected]>",
"repository": "https://github.com/thirdweb-dev/js/tree/main/legacy_packages/react-native-compat",
"license": "Apache-2.0",
+10
View File
@@ -1,5 +1,15 @@
# @thirdweb-dev/react-native
## 0.7.32
### Patch Changes
- Updated dependencies [[`d836889`](https://github.com/thirdweb-dev/js/commit/d836889f464a4fc9617839f30e2cc780b3bcca78), [`d836889`](https://github.com/thirdweb-dev/js/commit/d836889f464a4fc9617839f30e2cc780b3bcca78), [`d65713a`](https://github.com/thirdweb-dev/js/commit/d65713a7af67bf20e5b88b72f5d43dfba9172b3b)]:
- @thirdweb-dev/react-core@4.6.0
- @thirdweb-dev/sdk@4.0.61
- @thirdweb-dev/chains@0.1.92
- @thirdweb-dev/wallets@2.5.1
## 0.7.31
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/react-native",
"version": "0.7.31",
"version": "0.7.32",
"repository": "https://github.com/thirdweb-dev/js/tree/main/legacy_packages/react-native",
"author": "thirdweb eng <[email protected]>",
"license": "Apache-2.0",
+16
View File
@@ -1,5 +1,21 @@
# @thirdweb-dev/react
## 4.6.0
### Minor Changes
- [#2579](https://github.com/thirdweb-dev/js/pull/2579) [`d836889`](https://github.com/thirdweb-dev/js/commit/d836889f464a4fc9617839f30e2cc780b3bcca78) Thanks [@ElasticBottle](https://github.com/ElasticBottle)! - Adds `Buy` button in `ConnectWallet` component's details Modal to allow users to Swap tokens. Setting `clientId` on `ThirdWebProvider` is required to enable this feature.
A new prop `hideBuyButton` is added to `ConnectWallet` component to hide the `Buy` button in the details Modal. By default, the `Buy` button is visible.
### Patch Changes
- Updated dependencies [[`d836889`](https://github.com/thirdweb-dev/js/commit/d836889f464a4fc9617839f30e2cc780b3bcca78), [`d836889`](https://github.com/thirdweb-dev/js/commit/d836889f464a4fc9617839f30e2cc780b3bcca78), [`d65713a`](https://github.com/thirdweb-dev/js/commit/d65713a7af67bf20e5b88b72f5d43dfba9172b3b)]:
- @thirdweb-dev/react-core@4.6.0
- @thirdweb-dev/sdk@4.0.61
- @thirdweb-dev/chains@0.1.92
- @thirdweb-dev/wallets@2.5.1
## 4.5.0
### Minor Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/react",
"version": "4.5.0",
"version": "4.6.0",
"repository": "https://github.com/thirdweb-dev/js/tree/main/legacy_packages/react",
"author": "thirdweb eng <[email protected]>",
"license": "Apache-2.0",
@@ -1,9 +1,10 @@
import { Chain } from "@thirdweb-dev/chains";
import { useStorage } from "@thirdweb-dev/react-core";
import { StyledDiv } from "../design-system/elements";
import { Img } from "./Img";
const defaultChainIcon =
"ipfs://QmcxZHpyJa8T4i63xqjPYrZ6tKrt55tZJpbXcjSDKuKaf9/ethereum/512.png";
export const fallbackChainIcon =
"data:image/svg+xml;charset=UTF-8,%3csvg width='15' height='14' viewBox='0 0 15 14' fill='none' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M7 8.04238e-07C5.1435 8.04238e-07 3.36301 0.737501 2.05025 2.05025C0.7375 3.36301 0 5.1435 0 7C0 7.225 -1.52737e-07 7.445 0.0349998 7.665C0.16385 9.0151 0.68213 10.2988 1.52686 11.3598C2.37158 12.4209 3.50637 13.2137 4.79326 13.642C6.0801 14.0702 7.4637 14.1153 8.7758 13.7719C10.0879 13.4285 11.2719 12.7113 12.184 11.7075C13.0961 10.7038 13.6969 9.4567 13.9135 8.1178C14.1301 6.7789 13.9531 5.406 13.4039 4.16587C12.8548 2.92574 11.9573 1.87184 10.8204 1.13228C9.6835 0.392721 8.3563 -0.000649196 7 8.04238e-07ZM7 1C8.581 1.00137 10.0975 1.62668 11.22 2.74V3.24C9.2438 2.55991 7.0956 2.56872 5.125 3.265C4.96758 3.1116 4.76997 3.00586 4.555 2.96H4.43C4.37 2.75 4.315 2.54 4.27 2.325C4.225 2.11 4.2 1.92 4.175 1.715C5.043 1.24658 6.0137 1.00091 7 1ZM5.5 3.935C7.3158 3.32693 9.2838 3.34984 11.085 4C10.8414 5.2703 10.3094 6.4677 9.53 7.5C9.312 7.4077 9.0707 7.3855 8.8395 7.4366C8.6083 7.4877 8.3988 7.6094 8.24 7.785C8.065 7.685 7.89 7.585 7.74 7.47C6.7307 6.7966 5.8877 5.9023 5.275 4.855C5.374 4.73221 5.4461 4.58996 5.4866 4.43749C5.5271 4.28502 5.5351 4.12575 5.51 3.97L5.5 3.935ZM3.5 2.135C3.5 2.24 3.53 2.35 3.55 2.455C3.595 2.675 3.655 2.89 3.715 3.105C3.52353 3.21838 3.36943 3.38531 3.2717 3.58522C3.17397 3.78513 3.13688 4.00927 3.165 4.23C2.37575 4.7454 1.67078 5.3795 1.075 6.11C1.19455 5.3189 1.47112 4.55966 1.88843 3.87701C2.30575 3.19437 2.85539 2.60208 3.505 2.135H3.5ZM3.5 9.99C3.30481 10.0555 3.13037 10.1714 2.9943 10.3259C2.85822 10.4804 2.76533 10.6681 2.725 10.87H2.405C1.59754 9.9069 1.1146 8.7136 1.025 7.46L1.08 7.365C1.70611 6.3942 2.52463 5.562 3.485 4.92C3.62899 5.0704 3.81094 5.179 4.01162 5.2345C4.2123 5.2899 4.42423 5.2901 4.625 5.235C5.2938 6.3652 6.208 7.3306 7.3 8.06C7.505 8.195 7.715 8.32 7.925 8.44C7.9082 8.6312 7.9391 8.8237 8.015 9C7.1 9.7266 6.0445 10.256 4.915 10.555C4.78401 10.3103 4.57028 10.1201 4.31199 10.0184C4.05369 9.9167 3.76766 9.9102 3.505 10L3.5 9.99ZM7 12.99C5.9831 12.9903 4.98307 12.7304 4.095 12.235L4.235 12.205C4.43397 12.1397 4.61176 12.0222 4.74984 11.8648C4.88792 11.7074 4.98122 11.5158 5.02 11.31C6.2985 10.984 7.4921 10.3872 8.52 9.56C8.7642 9.7027 9.0525 9.75 9.3295 9.6927C9.6064 9.6355 9.8524 9.4778 10.02 9.25C10.7254 9.4334 11.4511 9.5275 12.18 9.53H12.445C11.9626 10.5673 11.1938 11.4451 10.2291 12.0599C9.2643 12.6747 8.144 13.0009 7 13V12.99ZM10.255 8.54C10.2545 8.3304 10.1975 8.1249 10.09 7.945C10.9221 6.8581 11.5012 5.5991 11.785 4.26C12.035 4.37667 12.2817 4.50667 12.525 4.65C13.0749 5.9495 13.1493 7.4012 12.735 8.75C11.9049 8.8142 11.0698 8.7484 10.26 8.555L10.255 8.54Z' fill='%23646D7A'/%3e%3c/svg%3e";
export const ChainIcon: React.FC<{
chain?: Chain;
@@ -11,13 +12,25 @@ export const ChainIcon: React.FC<{
active?: boolean;
className?: string;
loading?: "lazy" | "eager";
fallbackImage?: string;
}> = (props) => {
const url = props.chain?.icon?.url || defaultChainIcon;
const storage = useStorage();
const src = storage
? storage.resolveScheme(url)
: url.replace("ipfs://", "https://ipfs.io/ipfs/");
const getSrc = () => {
const url = props.chain?.icon?.url;
if (!url) {
return fallbackChainIcon;
}
try {
return storage
? storage.resolveScheme(url)
: url.replace("ipfs://", "https://ipfs.io/ipfs/");
} catch {
return fallbackChainIcon;
}
};
return (
<div
@@ -28,23 +41,11 @@ export const ChainIcon: React.FC<{
alignItems: "center",
}}
>
<img
src={src || defaultChainIcon}
onError={(e) => {
if (defaultChainIcon && e.currentTarget.src !== defaultChainIcon) {
e.currentTarget.src = defaultChainIcon;
}
}}
alt=""
<Img
src={getSrc()}
width={props.size}
height={props.size}
className={props.className}
loading={props.loading}
style={{
objectFit: "contain",
width: props.size + "px",
height: props.size + "px",
}}
fallbackImage={fallbackChainIcon}
/>
{props.active && <ActiveDot />}
</div>
@@ -0,0 +1,116 @@
import { useCustomTheme } from "../design-system/CustomThemeProvider";
import { fadeInAnimation } from "../design-system/animations";
import { StyledDiv } from "../design-system/elements";
import { iconSize, radius, spacing } from "../design-system/index";
import { keyframes } from "@emotion/react";
import { IconButton } from "./buttons";
import { Cross2Icon } from "@radix-ui/react-icons";
import { CrossContainer } from "./Modal";
import { forwardRef, useRef } from "react";
type DrawerProps = {
children: React.ReactNode;
close: () => void;
};
/**
*
* @internal
*/
export const Drawer = /* @__PURE__ */ forwardRef<HTMLDivElement, DrawerProps>(
function Drawer_(props, ref) {
return (
<>
<DrawerContainer ref={ref}>
<CrossContainer>
<IconButton type="button" aria-label="Close" onClick={props.close}>
<Cross2Icon
width={iconSize.md}
height={iconSize.md}
style={{
color: "inherit",
}}
/>
</IconButton>
</CrossContainer>
{props.children}
</DrawerContainer>
</>
);
},
);
export const DrawerContainer = /* @__PURE__ */ StyledDiv(() => {
const theme = useCustomTheme();
return {
zIndex: 10000,
padding: spacing.lg,
borderTopLeftRadius: radius.xl,
borderTopRightRadius: radius.xl,
background: theme.colors.modalBg,
position: "fixed",
bottom: 0,
left: 0,
right: 0,
animation: `${drawerOpenAnimation} 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.1)`,
borderTop: `1px solid ${theme.colors.borderColor}`,
};
});
export const drawerOpenAnimation = keyframes`
from {
opacity: 0;
transform: translateY(100px);
}
to {
opacity: 1;
transform: translateY(0);
}
`;
export const DrawerOverlay = /* @__PURE__ */ StyledDiv(() => {
const theme = useCustomTheme();
return {
backgroundColor: theme.colors.modalOverlayBg,
zIndex: 9999,
position: "absolute",
inset: 0,
animation: `${fadeInAnimation} 400ms cubic-bezier(0.16, 1, 0.3, 1)`,
};
});
/**
*
* @internal
*/
export function useDrawer() {
const drawerRef = useRef<HTMLDivElement>(null);
const drawerOverlayRef = useRef<HTMLDivElement>(null);
const onClose = (closeDrawer: () => void) => {
if (drawerRef.current) {
const animOptions = {
easing: "cubic-bezier(0.175, 0.885, 0.32, 1.1)",
fill: "forwards",
duration: 300,
} as const;
const closeAnimation = drawerRef.current.animate(
[{ transform: "translateY(100%)", opacity: 0 }],
animOptions,
);
drawerOverlayRef.current?.animate([{ opacity: 0 }], animOptions);
closeAnimation.onfinish = closeDrawer;
} else {
closeDrawer();
}
};
return {
drawerRef,
drawerOverlayRef,
onClose,
};
}
+63 -15
View File
@@ -1,17 +1,33 @@
import { useStorage } from "@thirdweb-dev/react-core";
import { useState } from "react";
import { Skeleton } from "./Skeleton";
export const Img: React.FC<{
width?: string;
height?: string;
src: string;
src?: string;
alt?: string;
loading?: "eager" | "lazy";
className?: string;
style?: React.CSSProperties;
fallbackImage?: string;
}> = (props) => {
const storage = useStorage();
const [isLoaded, setIsLoaded] = useState(false);
const propSrc = props.src;
const widthPx = props.width + "px";
const heightPx = (props.height || props.width) + "px";
if (!propSrc) {
return <Skeleton width={widthPx} height={heightPx} />;
}
const getSrc = () => {
if (!props.src) {
return props.fallbackImage || "";
}
try {
return storage
? storage.resolveScheme(props.src)
@@ -21,22 +37,54 @@ export const Img: React.FC<{
}
};
const src = getSrc();
return (
<img
width={props.width}
height={props.height}
src={getSrc()}
alt={props.alt || ""}
loading={props.loading}
decoding="async"
<div
style={{
height: props.height ? props.height + "px" : undefined,
width: props.width ? props.width + "px" : undefined,
userSelect: "none",
...props.style,
position: "relative",
display: "inline-flex",
flexShrink: 0,
alignItems: "center",
}}
draggable={false}
className={props.className}
/>
>
{!isLoaded && <Skeleton width={widthPx} height={heightPx} />}
<img
onLoad={() => {
setIsLoaded(true);
}}
key={src}
width={props.width}
height={props.height}
src={src}
alt={props.alt || ""}
loading={props.loading}
decoding="async"
style={{
objectFit: "contain",
height: !isLoaded
? 0
: props.height
? props.height + "px"
: undefined,
width: !isLoaded ? 0 : props.width ? props.width + "px" : undefined,
userSelect: "none",
visibility: isLoaded ? "visible" : "hidden",
opacity: isLoaded ? 1 : 0,
transition: "opacity 0.4s ease",
...props.style,
}}
draggable={false}
className={props.className}
onError={(e) => {
if (
props.fallbackImage &&
e.currentTarget.src !== props.fallbackImage
) {
e.currentTarget.src = props.fallbackImage;
}
}}
/>
</div>
);
};
@@ -0,0 +1,20 @@
import { Container } from "./basic";
import { Spinner } from "./Spinner";
/**
* @internal
*/
export function LoadingScreen(props: { height?: string }) {
return (
<Container
style={{
minHeight: props.height || "350px",
}}
fullHeight
flex="row"
center="both"
>
<Spinner size="lg" color="secondaryText" />
</Container>
);
}
@@ -195,6 +195,9 @@ const DialogContent = /* @__PURE__ */ StyledDiv(() => {
backgroundColor: theme.colors.selectedTextBg,
color: theme.colors.selectedTextColor,
},
"& *": {
boxSizing: "border-box",
},
...noScrollBar,
};
});
@@ -0,0 +1,56 @@
import { iconSize } from "../design-system";
import { genericTokenIcon } from "../wallet/ConnectWallet/icons/dataUris";
import {
isNativeToken,
type ERC20OrNativeToken,
type NativeToken,
} from "../wallet/ConnectWallet/nativeToken";
import { useChainQuery } from "../wallet/hooks/useChainQuery";
import { Img } from "./Img";
/**
* @internal
*/
export function TokenIcon(props: {
token: ERC20OrNativeToken;
chainId: number;
size: keyof typeof iconSize;
}) {
const token = props.token;
if (isNativeToken(token)) {
return (
<NativeTokenIcon
chainId={props.chainId}
nativeToken={token}
size={props.size}
/>
);
}
return (
<Img
src={token.icon}
width={iconSize[props.size]}
height={iconSize[props.size]}
fallbackImage={genericTokenIcon}
/>
);
}
function NativeTokenIcon(props: {
chainId: number;
nativeToken: NativeToken;
size: keyof typeof iconSize;
}) {
const chainQuery = useChainQuery(props.chainId);
return (
<Img
src={chainQuery.data?.icon?.url}
width={iconSize[props.size]}
height={iconSize[props.size]}
fallbackImage={genericTokenIcon}
/>
);
}
@@ -0,0 +1,53 @@
import { type Theme, fontSize } from "../design-system";
import {
isNativeToken,
type ERC20OrNativeToken,
} from "../wallet/ConnectWallet/nativeToken";
import { useChainQuery } from "../wallet/hooks/useChainQuery";
import { Skeleton } from "./Skeleton";
import { Text } from "./text";
/**
* @internal
*/
export function TokenSymbol(props: {
token: ERC20OrNativeToken;
chainId: number;
size: "sm" | "md" | "lg";
color?: keyof Theme["colors"];
}) {
if (!isNativeToken(props.token)) {
return (
<Text size={props.size} color={props.color || "primaryText"}>
{props.token.symbol}
</Text>
);
}
return (
<NativeTokenSymbol
chainId={props.chainId}
size={props.size}
color={props.color}
/>
);
}
function NativeTokenSymbol(props: {
chainId: number;
size: "sm" | "md" | "lg";
color?: keyof Theme["colors"];
}) {
const chainQuery = useChainQuery(props.chainId);
const data = chainQuery.data;
if (!data) {
return <Skeleton width="70px" height={fontSize[props.size]} />;
}
return (
<Text size={props.size} color={props.color || "primaryText"}>
{data.nativeCurrency.symbol}
</Text>
);
}
@@ -88,6 +88,8 @@ export function Container(props: {
scrollY?: boolean;
color?: keyof Theme["colors"];
debug?: boolean;
bg?: keyof Theme["colors"];
borderColor?: keyof Theme["colors"];
}) {
const styles: React.CSSProperties = {};
@@ -160,7 +162,9 @@ export function Container(props: {
<Box
data-scrolly={props.scrollY}
data-animate={props.animate}
bg={props.bg}
color={props.color}
borderColor={props.borderColor}
style={{
...styles,
...props.style,
@@ -173,12 +177,18 @@ export function Container(props: {
type BoxProps = {
color?: keyof Theme["colors"];
bg?: keyof Theme["colors"] | undefined;
borderColor?: keyof Theme["colors"] | undefined;
};
const Box = /* @__PURE__ */ StyledDiv((props: BoxProps) => {
const theme = useCustomTheme();
return {
color: props.color ? theme.colors[props.color] : "inherit",
background: props.bg ? theme.colors[props.bg] : undefined,
borderColor: props.borderColor
? theme.colors[props.borderColor]
: undefined,
"&[data-animate='fadein']": {
opacity: 0,
animation: `${fadeInAnimation} 350ms ease forwards`,
@@ -1,4 +1,4 @@
import { fontSize, radius, spacing, Theme } from "../design-system";
import { fontSize, radius, spacing, type Theme } from "../design-system/index";
import { StyledButton } from "../design-system/elements";
import { useCustomTheme } from "../design-system/CustomThemeProvider";
@@ -28,6 +28,8 @@ export const Button = /* @__PURE__ */ StyledButton((props: ButtonProps) => {
transition: "border 200ms ease",
gap: (props.gap && spacing[props.gap]) || 0,
width: props.fullWidth ? "100%" : undefined,
textAlign: "center",
maxWidth: "100%",
background: (() => {
switch (props.variant) {
case "primary":
@@ -62,6 +64,15 @@ export const Button = /* @__PURE__ */ StyledButton((props: ButtonProps) => {
"&[disabled]": {
cursor: "not-allowed",
},
"&[data-disabled='true']": {
background: theme.colors.walletSelectorButtonHoverBg,
color: theme.colors.secondaryText,
borderColor: "transparent",
boxShadow: "none",
"&:hover": {
borderColor: "transparent",
},
},
...(() => {
if (props.variant === "outline") {
return {
@@ -88,6 +99,8 @@ export const Button = /* @__PURE__ */ StyledButton((props: ButtonProps) => {
},
};
}
return {};
})(),
};
});
@@ -1,6 +1,6 @@
import { fontSize, radius, Theme, spacing } from "../design-system";
import { StyledDiv, StyledInput, StyledLabel } from "../design-system/elements";
import { useCustomTheme } from "../design-system/CustomThemeProvider";
import { StyledLabel, StyledInput, StyledDiv } from "../design-system/elements";
import { fontSize, spacing, radius, type Theme } from "../design-system";
type LabelProps = {
color?: keyof Theme["colors"];
@@ -26,6 +26,7 @@ export const Input = /* @__PURE__ */ StyledInput((props: InputProps) => {
const theme = useCustomTheme();
return {
fontSize: fontSize.md,
fontFamily: "inherit",
display: "block",
padding: props.sm ? spacing.sm : fontSize.sm,
boxSizing: "border-box",
@@ -53,9 +54,15 @@ export const Input = /* @__PURE__ */ StyledInput((props: InputProps) => {
WebkitBoxShadow: `0 0 0px 1000px ${theme.colors.inputAutofillBg} inset, 0 0 0 2px ${theme.colors.accentText} !important`,
boxShadow: `0 0 0px 1000px ${theme.colors.inputAutofillBg} inset, 0 0 0 2px ${theme.colors.accentText} !important`,
},
"&[data-placeholder='true']": {
color: theme.colors.secondaryText,
},
"&:focus": {
boxShadow: `0 0 0 2px ${theme.colors.accentText}`,
},
"&[data-focus='false']:focus": {
boxShadow: "none",
},
"&:not([type='password'])": {
overflow: "hidden",
textOverflow: "ellipsis",
@@ -194,6 +194,8 @@ export const iconSize = {
lg: "32",
xl: "48",
xxl: "64",
"3xl": "96",
"4xl": "128",
};
// desktop first style media query
@@ -189,6 +189,18 @@ export type ConnectWalletProps = {
*/
hideReceiveButton?: boolean;
/**
* Hide the "Buy" button in the ConnectWallet Details Modal to allow users to Buy tokens using other tokens (Swap) or using their Credit/Debit card (Fiat onramp)
*
* By default it is `false` - Buy button is shown
*
* @example
* ```tsx
* <ConnectWallet hideBuyButton={true} />
* ```
*/
hideBuyButton?: boolean;
/**
* Whether to show "Switch Network" button if the wallet is connected,
* but it is not connected to the `activeChain` provided in [`ThirdwebProvider`](https://portal.thirdweb.com/react/v4/ThirdwebProvider)
@@ -406,249 +418,9 @@ const TW_CONNECT_WALLET = "tw-connect-wallet";
* ```
*
* @param props -
* Props for the ConnectWallet component
* Props for the ConnectWallet component.
*
* ### btnTitle (optional)
* set custom label for the button.
*
* The default is `"Connect"`
*
* ```tsx
* <ConnectWallet btnTitle="Sign in" />
* ```
*
* ### modalSize (optional)
* Set the size of the modal - `compact` or `wide` on desktop
*
* Modal size is always `compact` on mobile
*
* By default it is `"wide"` for desktop.
*
* ### modalTitle (optional)
* Change the title of ConnectWallet Modal
*
* The default is `"Connect"`
*
* ### modalTitleIconUrl (optional)
* Replace the thirdweb icon next to modalTitle and set your own iconUrl
*
* Set to empty string to hide the icon
*
* ### auth (optional)
* The object contains the following properties to customize the authentication
* - `loginOptional` - specify whether signing in is optional or not. By default it is `false` ( Sign in is required ) if `authConfig` is set on [`ThirdwebProvider`](https://portal.thirdweb.com/react/v4/ThirdwebProvider)
* - `onLogin` - Callback to be called after user signs in with their wallet
* - `onLogout` - Callback to be called after user signs out
*
* ### theme (optional)
* Set the theme for the button and modal.
*
* By default it is set to "dark" if `theme` is not set on [`ThirdwebProvider`](https://portal.thirdweb.com/react/v4/ThirdwebProvider)
* If a `theme` is set on [`ThirdwebProvider`](https://portal.thirdweb.com/react/v4/ThirdwebProvider) then that theme will be used by default which can be overridden by setting `theme` prop on [`ConnectWallet`](https://portal.thirdweb.com/react/v4/components/ConnectWallet) component
*
* theme can be set to either "dark" or "light" or a custom theme object. You can also import `lightTheme` or `darkTheme` functions from `@thirdweb-dev/react` to use the default themes as base and overrides parts of it.
*
* ```ts
* import { lightTheme } from "@thirdweb-dev/react";
* const customTheme = lightTheme({
* colors: {
* modalBg: 'red'
* }
* })
* ```
*
* ### className (optional)
* CSS class to apply to the button element
*
* ### detailsBtn
* Render a custom button to display connected wallet details instead of the default one
*
* ```tsx
* const address = useAddress();
*
* <ConnectWallet
* detailsBtn={() => {
* return (
* <button>
* connected to {address}
* </button>
* )
* }}
* />
* ```
*
*
* ### style (optional)
* CSS styles to apply to the button element
*
* ### networkSelector (optional)
* Customize the Network selector shown
*
* ### hideTestnetFaucet (optional)
* Hide the "Request Testnet funds" link in ConnectWallet Details Modal when user is connected to a testnet. By default it is `true`
*
* If you want to show the "Request Testnet funds" link when user is connected to a testnet, set this prop to `false`
*
* ```tsx
* <ConnectWallet hideTestnetFaucet={false} />
* ```
*
* ### switchToActiveChain (optional)
* Whether to show "Switch Network" button if the wallet is connected,
* but it is not connected to the `activeChain` provided in [`ThirdwebProvider`](https://portal.thirdweb.com/react/v4/ThirdwebProvider)
*
* Please, note that if you support multiple networks in your app this prop should
* be set to `false` to allow users to switch between networks.
*
* By default it is `false`
*
* For some CSS properties, you may need to use the !important to override the default styles
*
* ```tsx
* <ConnectWallet className="my-custom-class" />
* ```
*
* ### termsOfServiceUrl
* If provided, Modal will show a Terms of Service message at the bottom with below link
*
* ```tsx
* <ConnectWallet termsOfServiceUrl="https://your-terms-of-service-url.com" />
* ```
*
* ### privacyPolicyUrl
* If provided, Modal will show a Privacy Policy message at the bottom with below link
*
* ```tsx
* <ConnectWallet privacyPolicyUrl="https://your-privacy-policy-url.com" />
* ```
*
* ### welcomeScreen
* Customize the welcome screen. This prop is only applicable when modalSize prop is set to "wide". On "wide" Modal size, a welcome screen is shown on the right side of the modal.
*
* This screen can be customized in two ways
*
* #### 1. Customize Metadata and Image
*
* ```tsx
* <ConnectWallet welcomeScreen={{
* title: "your title",
* subtitle: "your subtitle",
* img: {
* src: "https://your-image-url.png",
* width: 300,
* height: 50,
* },
* }} />
* ```
*
* #### 2. Render Custom Component
*
* ```tsx
* <ConnectWallet
* welcomeScreen={() => {
* return <YourCustomComponent />
* }}
* />
* ```
*
*
* ### supportedTokens
* Customize the tokens shown in the "Send Funds" screen for various networks.
*
* By default, The "Send Funds" screen shows a few popular tokens for default chains and the native token. For other chains it only shows the native token.
*
* supportedTokens prop allows you to customize this list as shown below which shows "Dai Stablecoin" when users wallet is connected to the "Base" mainnet.
*
* ```tsx
* import { ConnectWallet } from '@thirdweb-dev/react';
* import { Base } from '@thirdweb-dev/chains';
*
* function Example() {
* return (
* <ConnectWallet
* supportedTokens={{
* [Base.chainId]: [
* {
* address: '0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb', // token contract address
* name: 'Dai Stablecoin',
* symbol: 'DAI',
* icon: 'https://assets.coingecko.com/coins/images/9956/small/Badge_Dai.png?1687143508',
* },
* ],
* }}
* />
* );
* }
* ```
*
* ### displayBalanceToken
* Display the balance of a token instead of the native token in ConnectWallet details button.
*
* ```tsx
* import { Base } from "@thirdweb-dev/chains";
*
* <ConnectWallet balanceToken={{
* 1: "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599" // show USDC balance when connected to Ethereum mainnet
* [Base.chainId]: "0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb", // show Dai stablecoin token balance when connected to Base mainnet
* }}
* />
* ```
*
* ### hideSwitchToPersonalWallet
* Hide the "Switch to Personal wallet" option in the wallet modal which is shown when wallet is connected to either Smart Account or Safe.
*
* By default it is `false`
*
* ```tsx
* <ConnectWallet hideSwitchToPersonalWallet={true} />
* ```
*
* ### hideDisconnect
* Hide the "Disconnect Wallet" button in the ConnectWallet Details Modal
*
* By default it is `false`
*
* ```tsx
* <ConnectWallet hideDisconnect={true} />
* ```
*
* ### detailsModalFooter
* Render custom UI at the bottom of the ConnectWallet Details Modal.
*
* The given function is passed an object with a `close` function which can be used to close the modal.
*
* ```tsx
* <ConnectWallet
* detailsModalFooter={(props) => {
* const { close } = props;
* return <div> ... </div>
* })
* />
* ```
*
*
* ### showThirdwebBranding
* By default ConnectWallet shows "Powered by Thirdweb" branding at the bottom of the ConnectWallet Modal.
*
* If you want to hide the branding, set this prop to `false`
*
* ```tsx
* <ConnectWallet showThirdwebBranding={false} />
* ```
*
* ### hideSendButton
* Hide the "Send" button in the ConnectWallet Details Modal. By default it is `false` - Send button is shown
*
* ```tsx
* <ConnectWallet hideSendButton={true} />
* ```
*
* ### hideReceiveButton
* Hide the "Receive" button in the ConnectWallet Details Modal. By default it is `false` - Receive button is shown
*
* ```tsx
* <ConnectWallet hideReceiveButton={true} />
* ```
* Refer to the [`ConnectWalletProps`](https://portal.thirdweb.com/references/react/v4/ConnectWalletProps) for more details
*/
export function ConnectWallet(props: ConnectWalletProps) {
const activeWallet = useWallet();
@@ -848,6 +620,7 @@ export function ConnectWallet(props: ConnectWalletProps) {
detailsModalFooter={props.detailsModalFooter}
hideSendButton={props.hideSendButton}
hideReceiveButton={props.hideReceiveButton}
hideBuyButton={props.hideBuyButton}
/>
);
})()}
@@ -7,6 +7,7 @@ import {
PinBottomIcon,
ShuffleIcon,
TextAlignJustifyIcon,
PlusIcon,
} from "@radix-ui/react-icons";
import { Localhost } from "@thirdweb-dev/chains";
import {
@@ -24,13 +25,13 @@ import {
useWalletConfig,
useWalletContext,
} from "@thirdweb-dev/react-core";
import { useEffect, useState, useSyncExternalStore } from "react";
import {
MetaMaskWallet,
walletIds,
type EmbeddedWallet,
type SmartWallet,
} from "@thirdweb-dev/wallets";
import { useEffect, useState } from "react";
import { ChainIcon } from "../../components/ChainIcon";
import { CopyIcon } from "../../components/CopyIcon";
import { Img } from "../../components/Img";
@@ -74,6 +75,10 @@ import {
facebookIconUri,
googleIconUri,
} from "./icons/socialLogins";
import { useChainQuery } from "../hooks/useChainQuery";
import { SwapScreen } from "./screens/Buy/swap/SwapScreen";
import { SwapTransactionsScreen } from "./screens/SwapTransactionsScreen";
import { swapTransactionsStore } from "./screens/Buy/swap/pendingSwapTx";
const TW_CONNECTED_WALLET = "tw-connected-wallet";
@@ -81,8 +86,10 @@ type WalletDetailsModalScreen =
| "main"
| "export"
| "send"
| "buy"
| "receive"
| "network-switcher";
| "network-switcher"
| "pending-tx";
export const ConnectedWalletDetails: React.FC<{
onDisconnect: () => void;
@@ -102,10 +109,18 @@ export const ConnectedWalletDetails: React.FC<{
detailsModalFooter?: (props: { close: () => void }) => JSX.Element;
hideSendButton?: boolean;
hideReceiveButton?: boolean;
hideBuyButton?: boolean;
}> = (props) => {
const locale = useTWLocale().connectWallet;
const chain = useChain();
const walletChainId = useChainId();
const chainQuery = useChainQuery(walletChainId);
const swapTxs = useSyncExternalStore(
swapTransactionsStore.subscribe,
swapTransactionsStore.getValue,
);
const pendingSwapTxs = swapTxs.filter((tx) => tx.status === "PENDING");
const disconnect = useDisconnect();
const chains = useSupportedChains();
@@ -272,10 +287,10 @@ export const ConnectedWalletDetails: React.FC<{
position: "relative",
}}
>
<ChainIcon chain={chain} size={iconSize.md} active />
<ChainIcon chain={chain || chainQuery.data} size={iconSize.md} active />
</div>
<Text color="primaryText" multiline>
{chain?.name || `Unknown chain #${walletChainId}`}
{chain?.name || chainQuery.data?.name || `chain #${walletChainId}`}
</Text>
<StyledChevronRightIcon
width={iconSize.sm}
@@ -292,8 +307,17 @@ export const ConnectedWalletDetails: React.FC<{
const showFaucet =
props.hideTestnetFaucet === undefined ? false : !props.hideTestnetFaucet;
const showBothButtons =
props.hideSendButton !== true && props.hideReceiveButton !== true;
let buttonsToShow = 3;
if (props.hideReceiveButton) {
buttonsToShow -= 1;
}
if (props.hideSendButton) {
buttonsToShow -= 1;
}
if (props.hideBuyButton) {
buttonsToShow -= 1;
}
let content = (
<div>
@@ -361,17 +385,11 @@ export const ConnectedWalletDetails: React.FC<{
{/* Send and Receive */}
<Container
style={
showBothButtons
? {
display: "grid",
gridTemplateColumns: "1fr 1fr",
gap: spacing.sm,
}
: {
display: "grid",
}
}
style={{
display: "grid",
gridTemplateColumns: `repeat(${buttonsToShow}, 1fr)`,
gap: spacing.sm,
}}
>
{props.hideSendButton !== true && (
<Button
@@ -416,12 +434,31 @@ export const ConnectedWalletDetails: React.FC<{
{locale.receive}{" "}
</Button>
)}
{props.hideBuyButton !== true && (
<Button
variant="outline"
style={{
fontSize: fontSize.sm,
display: "flex",
gap: spacing.xs,
alignItems: "center",
padding: spacing.sm,
}}
onClick={() => {
setScreen("buy");
}}
>
<Container color="secondaryText" flex="row" center="both">
<PlusIcon width={iconSize.sm} height={iconSize.sm} />
</Container>
{"Buy"}
</Button>
)}
</Container>
</Container>
{props.hideSendButton && props.hideReceiveButton ? null : (
<Spacer y="md" />
)}
{buttonsToShow === 0 ? null : <Spacer y="md" />}
<Container px="md">
{/* Network Switcher */}
@@ -433,6 +470,24 @@ export const ConnectedWalletDetails: React.FC<{
>
{networkSwitcherButton}
{/* Transactions */}
<MenuButton
onClick={() => {
setScreen("pending-tx");
}}
style={{
fontSize: fontSize.sm,
}}
>
<TextAlignJustifyIcon width={iconSize.md} height={iconSize.md} />
<Container flex="row" gap="xs" center="y">
<Text color="primaryText">{"Transactions"}</Text>
{pendingSwapTxs && pendingSwapTxs.length > 0 && (
<BadgeCount>{pendingSwapTxs.length}</BadgeCount>
)}
</Container>
</MenuButton>
{/* Switch to Personal Wallet for Safe */}
{personalWallet &&
personalWalletConfig &&
@@ -501,27 +556,6 @@ export const ConnectedWalletDetails: React.FC<{
</MenuLink>
)}
{/* Explorer link */}
{chain?.explorers && chain.explorers[0]?.url && (
<MenuLink
href={chain.explorers[0].url + "/address/" + address}
target="_blank"
as="a"
style={{
textDecoration: "none",
color: "inherit",
}}
>
<Container flex="row" center="y" color="secondaryText">
<TextAlignJustifyIcon
width={iconSize.md}
height={iconSize.md}
/>
</Container>
{locale.transactionHistory}
</MenuLink>
)}
{/* Export Wallet */}
{activeWallet?.walletId === walletIds.localWallet && (
<div>
@@ -611,13 +645,27 @@ export const ConnectedWalletDetails: React.FC<{
}}
/>
);
} else if (screen === "send") {
} else if (screen === "send" && walletChainId) {
content = (
<SendFunds
supportedTokens={props.supportedTokens}
onBack={() => {
setScreen("main");
}}
chainId={walletChainId}
/>
);
} else if (screen === "pending-tx") {
content = <SwapTransactionsScreen onBack={() => setScreen("main")} />;
}
// swap tokens
else if (screen === "buy") {
content = (
<SwapScreen
onBack={() => setScreen("main")}
supportedTokens={props.supportedTokens}
onViewPendingTx={() => setScreen("pending-tx")}
/>
);
} else if (screen === "receive") {
@@ -836,3 +884,19 @@ function EmbeddedWalletDetails() {
return undefined;
}
const BadgeCount = /* @__PURE__ */ StyledDiv(() => {
const theme = useCustomTheme();
return {
background: theme.colors.primaryButtonBg,
color: theme.colors.primaryButtonText,
fontSize: fontSize.sm,
fontWeight: 500,
borderRadius: "50%",
display: "flex",
alignItems: "center",
justifyContent: "center",
minWidth: "22px",
minHeight: "22px",
};
});
@@ -181,6 +181,7 @@ export function NetworkSelector(props: NetworkSelectorProps) {
export function NetworkSelectorContent(
props: NetworkSelectorProps & {
onBack?: () => void;
showTabs?: boolean;
},
) {
const [searchTerm, setSearchTerm] = useState("");
@@ -261,28 +262,33 @@ export function NetworkSelectorContent(
</Container>
<Tabs.Root className="TabsRoot" defaultValue="all">
<Container px="lg">
<Tabs.List
className="TabsList"
aria-label="Manage your account"
style={{
display: "flex",
gap: spacing.xxs,
}}
>
<TabButton className="TabsTrigger" value="all">
{locale.allNetworks}
</TabButton>
<TabButton className="TabsTrigger" value="mainnet">
{locale.mainnets}
</TabButton>
<TabButton className="TabsTrigger" value="testnet">
{locale.testnets}
</TabButton>
</Tabs.List>
</Container>
{props.showTabs !== false && (
<>
<Container px="lg">
<Tabs.List
className="TabsList"
aria-label="Manage your account"
style={{
display: "flex",
gap: spacing.xxs,
}}
>
<TabButton className="TabsTrigger" value="all">
{locale.allNetworks}
</TabButton>
<TabButton className="TabsTrigger" value="mainnet">
{locale.mainnets}
</TabButton>
<TabButton className="TabsTrigger" value="testnet">
{locale.testnets}
</TabButton>
</Tabs.List>
</Container>
<Spacer y="lg" />
</>
)}
<Spacer y="lg" />
{props.showTabs === false && <Spacer y="xxs" />}
{chains.length > 10 && (
<>
@@ -517,8 +523,6 @@ 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;
@@ -577,11 +581,9 @@ const NetworkList = /* @__PURE__ */ memo(function NetworkList(props: {
const confirming = switchingChainId === chain.chainId;
const switchingFailed = errorSwitchingChainId === chain.chainId;
const chainName = <span>{chain.name} </span>;
if (RenderChain) {
return (
<li key={chain.chainId}>
return (
<li key={chain.chainId}>
{RenderChain ? (
<RenderChain
switchChain={() => {
handleSwitch(chain);
@@ -591,57 +593,14 @@ const NetworkList = /* @__PURE__ */ memo(function NetworkList(props: {
switchFailed={errorSwitchingChainId === chain.chainId}
close={props.close}
/>
</li>
);
}
return (
<li key={chain.chainId}>
<NetworkButton
data-active={activeChainId === chain.chainId}
onClick={() => {
handleSwitch(chain);
}}
>
<ChainIcon
) : (
<ChainButton
chain={chain}
size={iconSize.lg}
active={activeChainId === chain.chainId}
loading="lazy"
confirming={confirming}
onClick={() => handleSwitch(chain)}
switchingFailed={switchingFailed}
/>
{confirming || switchingFailed ? (
<div
style={{
display: "flex",
flexDirection: "column",
gap: spacing.xs,
}}
>
{chainName}
<Container animate="fadein" flex="row" gap="xs">
{confirming && (
<>
<Text size="xs" color="accentText">
{twLocale.connectWallet.confirmInWallet}
</Text>
<Spinner size="xs" color="accentText" />
</>
)}
{switchingFailed && (
<Container animate="fadein">
<Text size="xs" color="danger">
{locale.failedToSwitch}
</Text>
</Container>
)}
</Container>
</div>
) : (
chainName
)}
</NetworkButton>
)}
</li>
);
})}
@@ -649,6 +608,65 @@ const NetworkList = /* @__PURE__ */ memo(function NetworkList(props: {
);
});
export const ChainButton = /* @__PURE__ */ memo(function ChainButton(props: {
confirming: boolean;
switchingFailed: boolean;
onClick: () => void;
chain: Chain;
}) {
const twLocale = useTWLocale();
const { confirming, onClick, switchingFailed, chain } = props;
const locale = twLocale.connectWallet.networkSelector;
const activeChainId = useChainId();
const chainName = <span>{chain.name} </span>;
return (
<NetworkButton
data-active={activeChainId === chain.chainId}
onClick={onClick}
>
<ChainIcon
chain={chain}
size={iconSize.lg}
active={activeChainId === chain.chainId}
loading="lazy"
/>
{confirming || switchingFailed ? (
<div
style={{
display: "flex",
flexDirection: "column",
gap: spacing.xs,
}}
>
{chainName}
<Container animate="fadein" flex="row" gap="xs">
{confirming && (
<>
<Text size="xs" color="accentText">
{twLocale.connectWallet.confirmInWallet}
</Text>
<Spinner size="xs" color="accentText" />
</>
)}
{switchingFailed && (
<Container animate="fadein">
<Text size="xs" color="danger">
{locale.failedToSwitch}
</Text>
</Container>
)}
</Container>
</div>
) : (
chainName
)}
</NetworkButton>
);
});
const TabButton = /* @__PURE__ */ (() =>
styled(Tabs.Trigger)(() => {
const theme = useCustomTheme();
@@ -3,41 +3,37 @@ import { Spacer } from "../../components/Spacer";
import { Container, ModalHeader } from "../../components/basic";
import { Button } from "../../components/buttons";
import { Input, Label } from "../../components/formElements";
import {
useBalance,
useChain,
useChainId,
useWallet,
} from "@thirdweb-dev/react-core";
import { ChainIcon } from "../../components/ChainIcon";
import { fontSize, iconSize, spacing } from "../../design-system";
import { Text } from "../../components/text";
import { Spinner } from "../../components/Spinner";
import { CrossCircledIcon, CheckCircledIcon } from "@radix-ui/react-icons";
import { Skeleton } from "../../components/Skeleton";
import { useMutation } from "@tanstack/react-query";
import { Spinner } from "../../components/Spinner";
import { TransactionResult } from "@thirdweb-dev/sdk";
import { CheckCircledIcon, CrossCircledIcon } from "@radix-ui/react-icons";
import { utils } from "ethers";
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";
import { StyledDiv } from "../../design-system/elements";
import { useCustomTheme } from "../../design-system/CustomThemeProvider";
// TODO - use a better way to fetch token Info instead of useBalance
import { iconSize, spacing, fontSize } from "../../design-system";
import { type SupportedTokens, defaultTokens } from "./defaultTokens";
import { Text } from "../../components/text";
import { type ERC20OrNativeToken, NATIVE_TOKEN } from "./nativeToken";
import { useTWLocale } from "../../evm/providers/locale-provider";
import { useWallet, type TransactionResult } from "@thirdweb-dev/react-core";
import { utils } from "ethers";
import { TokenIcon } from "../../components/TokenIcon";
import { formatTokenBalance } from "../utils/formatTokenBalance";
import { TokenSelector } from "./screens/TokenSelector";
import { useMultiChainBalance } from "../hooks/useMultiChainBalance";
type TXError = Error & { data?: { message?: string } };
/**
* @internal
*/
export function SendFunds(props: {
supportedTokens: SupportedTokens;
onBack: () => void;
chainId: number;
}) {
const [screen, setScreen] = useState<"base" | "tokenSelector">("base");
const chainId = props.chainId;
const chainId = useChainId();
let defaultToken: TokenInfo | undefined = undefined;
let defaultToken: ERC20OrNativeToken = NATIVE_TOKEN;
if (
// if we know chainId
chainId &&
@@ -54,14 +50,17 @@ export function SendFunds(props: {
}
}
const [token, setToken] = useState<TokenInfo | undefined>(defaultToken);
const [token, setToken] = useState<ERC20OrNativeToken>(defaultToken);
const [receiverAddress, setReceiverAddress] = useState("");
const [amount, setAmount] = useState("0");
const tokenList = props.supportedTokens[chainId] || [];
if (screen === "tokenSelector") {
return (
<TokenSelector
supportedTokens={props.supportedTokens}
tokenList={tokenList}
onBack={() => {
setScreen("base");
}}
@@ -69,6 +68,7 @@ export function SendFunds(props: {
setToken(_token);
setScreen("base");
}}
chainId={chainId}
/>
);
}
@@ -84,41 +84,43 @@ export function SendFunds(props: {
amount={amount}
setAmount={setAmount}
onBack={props.onBack}
chainId={chainId}
/>
);
}
export function SendFundsForm(props: {
/**
* @internal
*/
function SendFundsForm(props: {
onTokenSelect: () => void;
token?: TokenInfo;
token: ERC20OrNativeToken;
receiverAddress: string;
setReceiverAddress: (value: string) => void;
amount: string;
setAmount: (value: string) => void;
onBack: () => void;
chainId: number;
}) {
const locale = useTWLocale().connectWallet.sendFundsScreen;
const tokenAddress = props.token?.address;
const balanceQuery = useBalance(tokenAddress);
const wallet = useWallet();
const tokenAddress =
props.token && "address" in props.token ? props.token.address : undefined;
const balanceQuery = useMultiChainBalance({
tokenAddress,
chainId: props.chainId,
});
const { receiverAddress, setReceiverAddress, amount, setAmount } = props;
const chain = useChain();
const chainId = useChainId();
const wallet = useWallet();
// Ethereum or Rinkeby or Goerli
const isENSSupported = chainId === 1 || chainId === 5 || chainId === 4;
// TODO support ens
const isValidReceiverAddress = useMemo(() => {
const isENS = receiverAddress.endsWith(".eth");
if (!isENSSupported && isENS) {
return false;
}
return isENS || utils.isAddress(receiverAddress);
}, [receiverAddress, isENSSupported]);
return utils.isAddress(receiverAddress);
}, [receiverAddress]);
const showInvalidAddressError = receiverAddress && !isValidReceiverAddress;
@@ -203,8 +205,13 @@ export function SendFundsForm(props: {
);
}
const tokenName = props.token?.name || balanceQuery?.data?.name;
const tokenSymbol = props.token?.symbol || balanceQuery.data?.symbol;
const tokenName =
(props.token && "name" in props.token ? props.token.name : undefined) ||
balanceQuery?.data?.name;
const tokenSymbol =
(props.token && "symbol" in props.token ? props.token.symbol : undefined) ||
balanceQuery?.data?.symbol;
return (
<Container p="lg" animate="fadein">
@@ -232,15 +239,7 @@ export function SendFundsForm(props: {
}}
onClick={props.onTokenSelect}
>
{props.token ? (
<Img
src={props.token.icon}
width={iconSize.lg}
height={iconSize.lg}
/>
) : (
<ChainIcon chain={chain} size={iconSize.lg} />
)}
<TokenIcon token={props.token} chainId={props.chainId} size="lg" />
<Container flex="column" gap="xs">
{tokenName ? (
@@ -252,7 +251,7 @@ export function SendFundsForm(props: {
)}
{balanceQuery.data ? (
<Text size="xs">{formatBalance(balanceQuery.data)}</Text>
<Text size="xs">{formatTokenBalance(balanceQuery.data)}</Text>
) : (
<Skeleton height={fontSize.xs} width="100px" />
)}
@@ -262,15 +261,15 @@ export function SendFundsForm(props: {
<Spacer y="lg" />
{/* Send to */}
<Label htmlFor="receiever" color="secondaryText">
<Label htmlFor="receiver" color="secondaryText">
{locale.sendTo}
</Label>
<Spacer y="sm" />
<Input
data-error={showInvalidAddressError}
required
id="receiever"
placeholder={isENSSupported ? `0x... / ENS name` : "0x..."}
id="receiver"
placeholder={"0x..."}
variant="outline"
value={receiverAddress}
onChange={(e) => {
@@ -318,7 +317,7 @@ export function SendFundsForm(props: {
variant="accent"
type="submit"
onClick={async () => {
if (!receiverAddress || !wallet || !amount) {
if (!receiverAddress || !amount) {
return;
}
@@ -340,225 +339,6 @@ export function SendFundsForm(props: {
);
}
function useNativeToken(): TokenInfo | undefined {
const balanceQuery = useBalance();
const chain = useChain();
if (balanceQuery.data) {
return {
name: balanceQuery.data.name,
symbol: balanceQuery.data.symbol,
address: NATIVE_TOKEN_ADDRESS,
icon: chain?.icon?.url || "",
};
}
}
function useToken(tokenAddress: string): {
isLoading: boolean;
data: TokenInfo | undefined;
} {
const balanceQuery = useBalance(tokenAddress);
const chain = useChain();
if (!utils.isAddress(tokenAddress)) {
return {
isLoading: false,
data: undefined,
};
}
if (balanceQuery.isLoading) {
return {
isLoading: true,
data: undefined,
};
}
if (balanceQuery.data) {
return {
isLoading: false,
data: {
name: balanceQuery.data.name,
symbol: balanceQuery.data.symbol,
address: tokenAddress,
icon: chain?.icon?.url || "",
},
};
}
return {
isLoading: false,
data: undefined,
};
}
export function TokenSelector(props: {
onTokenSelect: (token?: TokenInfo) => void;
onBack: () => void;
supportedTokens: SupportedTokens;
}) {
const [input, setInput] = useState("");
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) || [];
if (nativeTokenInfo) {
tokenList = [nativeTokenInfo, ...tokenList];
}
if (foundToken) {
tokenList = [foundToken, ...tokenList];
}
const filteredList = input
? tokenList.filter((t) => {
const inputStr = input.toLowerCase();
return (
t.name.toLowerCase().includes(inputStr) ||
t.symbol.toLowerCase().includes(inputStr) ||
t.address.includes(input)
);
})
: tokenList;
return (
<Container animate="fadein">
<Container p="lg">
<ModalHeader onBack={props.onBack} title={locale.selectTokenTitle} />
<Spacer y="xl" />
<Input
placeholder={locale.searchToken}
variant="outline"
value={input}
onChange={(e) => {
setInput(e.target.value);
}}
/>
</Container>
{filteredList.length > 0 && (
<Container
flex="column"
gap="xs"
p="md"
scrollY
style={{
paddingTop: 0,
paddingBottom: spacing.lg,
maxHeight: "400px",
}}
>
{filteredList.map((token) => {
return (
<SelectTokenButton
onClick={() => props.onTokenSelect(token)}
token={token}
key={token.address}
/>
);
})}
</Container>
)}
{findingToken && (
<Container
animate="fadein"
p="lg"
flex="column"
gap="md"
center="both"
style={{
minHeight: "150px",
paddingTop: 0,
}}
color="secondaryText"
>
<Spinner size="lg" color="accentText" />
</Container>
)}
{filteredList.length === 0 && !findingToken && (
<Container
animate="fadein"
p="lg"
flex="column"
gap="md"
center="both"
style={{
minHeight: "150px",
paddingTop: 0,
}}
color="secondaryText"
>
<CrossCircledIcon width={iconSize.lg} height={iconSize.lg} />
{locale.noTokensFound}
</Container>
)}
</Container>
);
}
function SelectTokenButton(props: { token?: TokenInfo; onClick: () => void }) {
const balanceQuery = useBalance(props.token?.address);
const chain = useChain();
const tokenName = props.token?.name || balanceQuery.data?.name;
return (
<SelectTokenBtn fullWidth variant="secondary" onClick={props.onClick}>
{/* icon */}
{props.token?.icon ? (
<Img width={iconSize.lg} height={iconSize.lg} src={props.token.icon} />
) : (
<ChainIcon chain={chain} size={iconSize.lg} />
)}
<Container flex="column" gap="xs">
{tokenName ? (
<Text size="sm" color="primaryText">
{tokenName}
</Text>
) : (
<Skeleton height={fontSize.md} width="150px" />
)}
{balanceQuery.data ? (
<Text size="xs"> {formatBalance(balanceQuery.data)}</Text>
) : (
<Skeleton height={fontSize.xs} width="100px" />
)}
</Container>
</SelectTokenBtn>
);
}
const SelectTokenBtn = /* @__PURE__ */ styled(Button)(() => {
const theme = useCustomTheme();
return {
background: "transparent",
justifyContent: "flex-start",
gap: spacing.sm,
padding: spacing.sm,
"&:hover": {
background: theme.colors.secondaryButtonBg,
transform: "scale(1.01)",
},
transition: "background 200ms ease, transform 150ms ease",
};
});
function formatBalance(balanceData: {
symbol: string;
name: string;
decimals: number;
displayValue: string;
}) {
return Number(balanceData.displayValue).toFixed(3) + " " + balanceData.symbol;
}
const CurrencyBadge = /* @__PURE__ */ StyledDiv({
position: "absolute",
top: "50%",
@@ -110,6 +110,12 @@ export const defaultTokens: SupportedTokens = {
symbol: "WETH",
icon: wrappedEthIcon,
},
{
address: "0x0b2c639c533813f4aa9d7837caf62653d097ff85",
name: "USD Coin",
symbol: "USDC",
icon: usdcIcon,
},
],
"56": [
{
@@ -140,6 +146,12 @@ export const defaultTokens: SupportedTokens = {
},
],
"137": [
{
address: "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",
name: "USD Coin",
symbol: "USDC",
icon: usdcIcon,
},
{
address: "0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270",
name: "Wrapped Matic",
@@ -152,18 +164,6 @@ export const defaultTokens: SupportedTokens = {
symbol: "WETH",
icon: wrappedEthIcon,
},
{
address: "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174",
name: "USD Coin (Bridged)",
symbol: "USDC.e",
icon: usdcIcon,
},
{
address: "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",
name: "USD Coin",
symbol: "USDC",
icon: usdcIcon,
},
{
address: "0xc2132D05D31c914a87C6611C10748AEb04B58e8F",
name: "Tether USD",
@@ -319,4 +319,13 @@ export const defaultTokens: SupportedTokens = {
icon: usdcIcon,
},
],
// Base mainnet
"8453": [
{
address: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
name: "USD Coin",
symbol: "USDC",
icon: usdcIcon,
},
],
};
@@ -0,0 +1,47 @@
import type { IconFC } from "./types";
/**
* @internal
*/
export const AccentFailIcon: IconFC = (props) => {
return (
<svg
width={props.size}
height={props.size}
viewBox="0 0 109 109"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<g clipPath="url(#clip0_4036_4155)">
<path
d="M54.5 109C84.5995 109 109 84.5995 109 54.5C109 24.4005 84.5995 0 54.5 0C24.4005 0 0 24.4005 0 54.5C0 84.5995 24.4005 109 54.5 109Z"
fill="#4BABFF"
/>
<path
d="M55 96C77.6437 96 96 77.6437 96 55C96 32.3563 77.6437 14 55 14C32.3563 14 14 32.3563 14 55C14 77.6437 32.3563 96 55 96Z"
fill="#1C85FF"
/>
<path
d="M73.2352 69.389C73.7459 69.8997 74.0329 70.5924 74.0329 71.3147C74.0329 72.037 73.7459 72.7298 73.2352 73.2405C72.7244 73.7513 72.0317 74.0382 71.3094 74.0382C70.5871 74.0382 69.8944 73.7513 69.3836 73.2405L54.9992 58.8515L40.6102 73.236C40.0994 73.7467 39.4067 74.0337 38.6844 74.0337C37.9621 74.0337 37.2694 73.7467 36.7586 73.236C36.2479 72.7252 35.9609 72.0325 35.9609 71.3102C35.9609 70.5879 36.2479 69.8952 36.7586 69.3844L51.1476 55L36.7632 40.611C36.2524 40.1002 35.9655 39.4075 35.9655 38.6852C35.9655 37.9629 36.2524 37.2702 36.7632 36.7594C37.2739 36.2487 37.9666 35.9617 38.6889 35.9617C39.4112 35.9617 40.104 36.2487 40.6147 36.7594L54.9992 51.1484L69.3881 36.7572C69.8989 36.2464 70.5916 35.9595 71.3139 35.9595C72.0362 35.9595 72.729 36.2464 73.2397 36.7572C73.7505 37.2679 74.0374 37.9606 74.0374 38.6829C74.0374 39.4052 73.7505 40.098 73.2397 40.6087L58.8507 55L73.2352 69.389Z"
fill="url(#paint0_linear_4036_4155)"
/>
</g>
<defs>
<linearGradient
id="paint0_linear_4036_4155"
x1="54.9992"
y1="35.9595"
x2="54.9992"
y2="74.0382"
gradientUnits="userSpaceOnUse"
>
<stop stopColor="white" />
<stop offset="1" stopColor="#90CBFF" />
</linearGradient>
<clipPath id="clip0_4036_4155">
<rect width="109" height="109" fill="white" />
</clipPath>
</defs>
</svg>
);
};
@@ -0,0 +1,73 @@
import type { IconFC } from "./types";
/**
* @internal
*/
export const BuyIcon: IconFC = (props) => {
return (
<svg
width={props.size}
height={props.size}
viewBox="0 0 16 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<g clipPath="url(#clip0_3977_6218)">
<path
d="M8 1.33337C9.29343 1.33314 10.559 1.70916 11.6425 2.41561C12.7259 3.12206 13.5805 4.12843 14.102 5.31207C14.6235 6.4957 14.7894 7.80548 14.5795 9.08176C14.3697 10.3581 13.7931 11.5457 12.92 12.5"
stroke="#646D7A"
strokeWidth="1.33333"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M8 5.33337V10.6667"
stroke="#646D7A"
strokeWidth="1.33333"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M10.6663 8H5.33301"
stroke="#646D7A"
strokeWidth="1.33333"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M1.66634 5.91663C1.45391 6.5624 1.3415 7.23686 1.33301 7.91663"
stroke="#646D7A"
strokeWidth="1.33333"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M1.88672 10.6666C2.26105 11.5278 2.81311 12.3003 3.50672 12.9333"
stroke="#646D7A"
strokeWidth="1.33333"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M3.09082 3.49003C3.27685 3.28752 3.47523 3.09671 3.68482 2.9187"
stroke="#646D7A"
strokeWidth="1.33333"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M5.7627 14.2801C7.42516 14.8723 9.25463 14.7812 10.85 14.0267"
stroke="#646D7A"
strokeWidth="1.33333"
strokeLinecap="round"
strokeLinejoin="round"
/>
</g>
<defs>
<clipPath id="clip0_3977_6218">
<rect width={props.size} height={props.size} fill="white" />
</clipPath>
</defs>
</svg>
);
};
@@ -0,0 +1,49 @@
import type { IconFC } from "./types";
/**
* @internal
*/
export const CryptoIcon: IconFC = (props) => {
return (
<svg
width={props.size}
height={props.size}
viewBox="0 0 16 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M8 16C12.4183 16 16 12.4183 16 8C16 3.58172 12.4183 0 8 0C3.58172 0 0 3.58172 0 8C0 12.4183 3.58172 16 8 16Z"
fill="#627EEA"
/>
<path
d="M8.24902 1.99902V6.43423L11.9975 8.10947L8.24902 1.99902Z"
fill="white"
fillOpacity="0.602"
/>
<path
d="M8.24947 1.99902L4.50098 8.10947L8.24947 6.43423V1.99902Z"
fill="white"
/>
<path
d="M8.24902 10.9829V13.9966L11.9997 8.80688L8.24902 10.9829Z"
fill="white"
fillOpacity="0.602"
/>
<path
d="M8.24947 13.9966V10.9829L4.50098 8.80688L8.24947 13.9966Z"
fill="white"
/>
<path
d="M8.24902 10.2854L11.9975 8.10931L8.24902 6.43408V10.2854Z"
fill="white"
fillOpacity="0.2"
/>
<path
d="M4.50098 8.10931L8.24947 10.2854V6.43408L4.50098 8.10931Z"
fill="white"
fillOpacity="0.602"
/>
</svg>
);
};
@@ -0,0 +1,21 @@
import type { IconFC } from "./types";
/**
* @internal
*/
export const WalletIcon: IconFC = (props) => {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 18 18"
width={props.size}
height={props.size}
>
<path
fill="currentColor"
d="M15.6 4.6H1.85v-.55l12.1-.968v.968h1.65V2.4c0-1.21-.98-2.059-2.177-1.888L2.378 2.089C1.18 2.26.2 3.39.2 4.6v11a2.2 2.2 0 002.2 2.2h13.2a2.2 2.2 0 002.2-2.2V6.8a2.2 2.2 0 00-2.2-2.2zm-1.65 7.707a1.65 1.65 0 01-.63-3.176 1.65 1.65 0 11.63 3.176z"
></path>
</svg>
);
};
@@ -9,3 +9,6 @@ export const emailAndPhoneIcon =
export const smartWalletIcon =
"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iODAiIGhlaWdodD0iODAiIHZpZXdCb3g9IjAgMCA4MCA4MCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHJlY3Qgd2lkdGg9IjgwIiBoZWlnaHQ9IjgwIiByeD0iMTIiIGZpbGw9InVybCgjcGFpbnQwX2xpbmVhcl8xXzkxKSIvPgo8cGF0aCBkPSJNMzkuOTk2OSAxOEw0MC4yMzMzIDE4LjAxNTZMNDAuMzUxMyAxOC4wMzMzTDQwLjQ3MzYgMTguMDYyMkw0MC42OTU4IDE4LjEzNzhDNDAuODQ5NCAxOC4yMDA2IDQwLjk5NTQgMTguMjg0MiA0MS4xMzA1IDE4LjM4NjdMNDEuMzM4NyAxOC41Njg5TDQxLjg0OTMgMTkuMDUzM0M0NS44ODkxIDIyLjc3NjggNTAuOTk0NyAyNC43NzYyIDU2LjI0NTggMjQuNjkxMUw1Ni45MzA3IDI0LjY2ODlDNTcuMzc4MyAyNC42NDYyIDU3LjgyIDI0Ljc5MDkgNTguMTg0OSAyNS4wNzk4QzU4LjU0OTggMjUuMzY4NyA1OC44MTY3IDI1Ljc4NSA1OC45NDMxIDI2LjI2MjJDNTkuOTI3MSAyOS45NzY5IDYwLjIyODMgMzMuODczMSA1OS44Mjg3IDM3LjcxOTRDNTkuNDI4OSA0MS41NjU4IDU4LjMzNjcgNDUuMjgzNiA1Ni42MTY1IDQ4LjY1MjNDNTQuODk2NSA1Mi4wMjA3IDUyLjU4MzYgNTQuOTcxNCA0OS44MTU2IDU3LjMyODVDNDcuMDQ3NiA1OS42ODU2IDQzLjg4MDkgNjEuNDAxMiA0MC41MDM2IDYyLjM3MzRDNDAuMTc0IDYyLjQ2ODMgMzkuODI4IDYyLjQ2ODMgMzkuNDk4MiA2Mi4zNzM0QzM2LjEyMDkgNjEuNDAxNCAzMi45NTM4IDU5LjY4NiAzMC4xODU2IDU3LjMyODlDMjcuNDE3NiA1NC45NzE4IDI1LjEwNDUgNTIuMDIxNCAyMy4zODQyIDQ4LjY1MjlDMjEuNjYzOCA0NS4yODQzIDIwLjU3MTMgNDEuNTY2MiAyMC4xNzE1IDM3LjcxOThDMTkuNzcxNyAzMy44NzM0IDIwLjA3MjcgMjkuOTc2OSAyMS4wNTY3IDI2LjI2MjJDMjEuMTgzMSAyNS43ODUgMjEuNDUwMSAyNS4zNjg3IDIxLjgxNSAyNS4wNzk4QzIyLjE3OTkgMjQuNzkwOSAyMi42MjE1IDI0LjY0NjIgMjMuMDY5MyAyNC42Njg5QzI4LjU1MTMgMjQuOTQ3IDMzLjkyOTMgMjIuOTQ0NCAzOC4xNTA3IDE5LjA1MzNMMzguNjc3MyAxOC41NTMzTDM4Ljg2OTYgMTguMzg2N0MzOS4wMDQ3IDE4LjI4NDIgMzkuMTUwNSAxOC4yMDA2IDM5LjMwNCAxOC4xMzc4TDM5LjUyODIgMTguMDYyMkMzOS42MDY5IDE4LjA0MTIgMzkuNjg2NSAxOC4wMjU2IDM5Ljc2NjcgMTguMDE1NkwzOS45OTY5IDE4Wk00MC4wMDA5IDMzLjU1NTZDMzguOTkwNSAzMy41NTUxIDM4LjAxNzMgMzMuOTc4NyAzNy4yNzY1IDM0Ljc0MTFDMzYuNTM1NiAzNS41MDM2IDM2LjA4MTYgMzYuNTQ4NSAzNi4wMDU4IDM3LjY2NjdMMzUuOTk1OCAzOEwzNi4wMDU4IDM4LjMzMzRDMzYuMDU1MSAzOS4wNTQ3IDM2LjI2MjUgMzkuNzUxOCAzNi42MDk4IDQwLjM2NEMzNi45NTY5IDQwLjk3NjIgMzcuNDMzNiA0MS40ODUxIDM3Ljk5ODUgNDEuODQ2N1Y0NS43Nzc4TDM4LjAxMjUgNDYuMDM3OEMzOC4wNzI3IDQ2LjYwMDMgMzguMzI0MiA0Ny4xMTU4IDM4LjcxNTYgNDcuNDc5NEMzOS4xMDcxIDQ3Ljg0MjkgMzkuNjA4NyA0OC4wMjY5IDQwLjExODIgNDcuOTkzOEM0MC42Mjc4IDQ3Ljk2MDUgNDEuMTA2NyA0Ny43MTI3IDQxLjQ1NzEgNDcuMzAwOUM0MS44MDc2IDQ2Ljg4ODkgNDIuMDAyOSA0Ni4zNDQzIDQyLjAwMzYgNDUuNzc3OEw0Mi4wMDU2IDQxLjg0ODlDNDIuNzY5MSA0MS4zNTk2IDQzLjM2NiA0MC42MDQyIDQzLjcwMzQgMzkuNzAwMkM0NC4wNDA3IDM4Ljc5NiA0NC4wOTk2IDM3Ljc5MzggNDMuODcxMSAzNi44NDg3QzQzLjY0MjcgMzUuOTAzNiA0My4xMzk2IDM1LjA2ODUgNDIuNDM5OCAzNC40NzMxQzQxLjc0IDMzLjg3NzYgNDAuODgyNyAzMy41NTUxIDQwLjAwMDkgMzMuNTU1NloiIGZpbGw9InVybCgjcGFpbnQxX2xpbmVhcl8xXzkxKSIvPgo8ZGVmcz4KPGxpbmVhckdyYWRpZW50IGlkPSJwYWludDBfbGluZWFyXzFfOTEiIHgxPSI0MCIgeTE9IjAiIHgyPSI0MCIgeTI9IjgwIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+CjxzdG9wIHN0b3AtY29sb3I9IiM4MzU2QkQiLz4KPHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjN0MyMEY0Ii8+CjwvbGluZWFyR3JhZGllbnQ+CjxsaW5lYXJHcmFkaWVudCBpZD0icGFpbnQxX2xpbmVhcl8xXzkxIiB4MT0iNDAiIHkxPSIxOCIgeDI9IjQwIiB5Mj0iNjIuNDQ0NSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPgo8c3RvcCBzdG9wLWNvbG9yPSJ3aGl0ZSIvPgo8c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiNFMUQ4RkIiLz4KPC9saW5lYXJHcmFkaWVudD4KPC9kZWZzPgo8L3N2Zz4K";
export const genericTokenIcon =
"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPGcgY2xpcC1wYXRoPSJ1cmwoI2NsaXAwXzQwNDhfNDIzMSkiPgo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTcuNTg0IDQuODU1NjZDNy43MTA2OSA0Ljc4MjUyIDcuODU0MzkgNC43NDQwMiA4LjAwMDY3IDQuNzQ0MDJDOC4xNDY5NSA0Ljc0NDAyIDguMjkwNjUgNC43ODI1MiA4LjQxNzM0IDQuODU1NjZMMTAuNTE1MyA2LjA2NjY2QzEwLjY0MiA2LjEzOTggMTAuNzQ3MiA2LjI0NSAxMC44MjA0IDYuMzcxNjhDMTAuODkzNSA2LjQ5ODM1IDEwLjkzMiA2LjY0MjA1IDEwLjkzMiA2Ljc4ODMzVjkuMjExQzEwLjkzMiA5LjM1NzI3IDEwLjg5MzUgOS41MDA5NyAxMC44MjA0IDkuNjI3NjVDMTAuNzQ3MiA5Ljc1NDMzIDEwLjY0MiA5Ljg1OTUzIDEwLjUxNTMgOS45MzI2Nkw4LjQxNzM0IDExLjE0NEM4LjI5MDY1IDExLjIxNzEgOC4xNDY5NSAxMS4yNTU2IDguMDAwNjcgMTEuMjU1NkM3Ljg1NDM5IDExLjI1NTYgNy43MTA2OSAxMS4yMTcxIDcuNTg0IDExLjE0NEw1LjQ4NiA5LjkzMjY2QzUuMzU5MzIgOS44NTk1MyA1LjI1NDEzIDkuNzU0MzMgNS4xODA5OSA5LjYyNzY1QzUuMTA3ODUgOS41MDA5NyA1LjA2OTM0IDkuMzU3MjcgNS4wNjkzNCA5LjIxMVY2Ljc4ODY2QzUuMDY5MjggNi42NDIzMyA1LjEwNzc2IDYuNDk4NTYgNS4xODA5IDYuMzcxODJDNS4yNTQwNSA2LjI0NTA4IDUuMzU5MjcgNi4xMzk4MyA1LjQ4NiA2LjA2NjY2TDcuNTg0IDQuODU1NjZaTTguMDg0IDUuNDMzQzguMDU4NjcgNS40MTgzNyA4LjAyOTkzIDUuNDEwNjcgOC4wMDA2NyA1LjQxMDY3QzcuOTcxNDEgNS40MTA2NyA3Ljk0MjY3IDUuNDE4MzcgNy45MTczNCA1LjQzM0w1LjgxOTM0IDYuNjQ0MzNDNS43OTQgNi42NTg5NiA1Ljc3Mjk2IDYuNjggNS43NTgzMyA2LjcwNTMzQzUuNzQzNyA2LjczMDY3IDUuNzM2IDYuNzU5NDEgNS43MzYgNi43ODg2NlY5LjIxMTMzQzUuNzM2IDkuMjQwNTkgNS43NDM3IDkuMjY5MzMgNS43NTgzMyA5LjI5NDY2QzUuNzcyOTYgOS4zMiA1Ljc5NCA5LjM0MTA0IDUuODE5MzQgOS4zNTU2Nkw3LjkxNzM0IDEwLjU2NjdDNy45NDI2NyAxMC41ODEzIDcuOTcxNDEgMTAuNTg5IDguMDAwNjcgMTAuNTg5QzguMDI5OTMgMTAuNTg5IDguMDU4NjcgMTAuNTgxMyA4LjA4NCAxMC41NjY3TDEwLjE4MiA5LjM1NTMzQzEwLjIwNzMgOS4zNDA3IDEwLjIyODQgOS4zMTk2NiAxMC4yNDMgOS4yOTQzM0MxMC4yNTc2IDkuMjY4OTkgMTAuMjY1MyA5LjI0MDI1IDEwLjI2NTMgOS4yMTFWNi43ODg2NkMxMC4yNjUzIDYuNzU5NDEgMTAuMjU3NiA2LjczMDY3IDEwLjI0MyA2LjcwNTMzQzEwLjIyODQgNi42OCAxMC4yMDczIDYuNjU4OTYgMTAuMTgyIDYuNjQ0MzNMOC4wODQgNS40MzNaIiBmaWxsPSIjNUM1QzVDIi8+CjwvZz4KPGNpcmNsZSBjeD0iOCIgY3k9IjgiIHI9IjcuNjkyMzEiIHN0cm9rZT0iIzVDNUM1QyIgc3Ryb2tlLXdpZHRoPSIwLjYxNTM4NSIvPgo8Y2lyY2xlIGN4PSI3Ljk5OTU1IiBjeT0iOC4wMDAwNCIgcj0iNS44NDYxNSIgc3Ryb2tlPSIjNUM1QzVDIiBzdHJva2Utd2lkdGg9IjAuNjE1Mzg1Ii8+CjxkZWZzPgo8Y2xpcFBhdGggaWQ9ImNsaXAwXzQwNDhfNDIzMSI+CjxyZWN0IHdpZHRoPSI4IiBoZWlnaHQ9IjgiIGZpbGw9IndoaXRlIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSg0IDQpIi8+CjwvY2xpcFBhdGg+CjwvZGVmcz4KPC9zdmc+Cg==";
@@ -0,0 +1,16 @@
import type { TokenInfo } from "./defaultTokens";
export type NativeToken = { nativeToken: true };
export const NATIVE_TOKEN: NativeToken = { nativeToken: true };
/**
* @internal
*/
export function isNativeToken(
token: TokenInfo | NativeToken,
): token is NativeToken {
return "nativeToken" in token;
}
export type ERC20OrNativeToken = TokenInfo | NativeToken;
@@ -0,0 +1,83 @@
/* eslint-disable i18next/no-literal-string */
import styled from "@emotion/styled";
import { Spacer } from "../../../../components/Spacer";
import { Container } from "../../../../components/basic";
import { Button } from "../../../../components/buttons";
import { spacing, fontSize, radius } from "../../../../design-system";
import { useCustomTheme } from "../../../../design-system/CustomThemeProvider";
import { StyledDiv } from "../../../../design-system/elements";
import { Text } from "../../../../components/text";
/**
* @internal
*/
export function PaymentSelection() {
return (
<div>
<Text size="sm">Pay with </Text>
<Spacer y="xs" />
<Container
style={{
display: "grid",
gridTemplateColumns: "1fr 1fr",
gridGap: spacing.sm,
}}
>
<CheckButton isChecked={true} variant="outline">
<Container gap="xxs" flex="row" center="y">
Crypto
</Container>
</CheckButton>
<div
style={{
position: "relative",
}}
>
<CheckButton
variant="outline"
isChecked={false}
style={{
opacity: 0.5,
}}
disabled
>
Credit Card
</CheckButton>
<FloatingBadge> Coming Soon </FloatingBadge>
</div>
</Container>
</div>
);
}
const CheckButton = /* @__PURE__ */ styled(Button)((props: {
isChecked: boolean;
}) => {
const theme = useCustomTheme();
return {
fontSize: fontSize.sm,
borderColor: props.isChecked
? theme.colors.accentText
: theme.colors.borderColor,
gap: spacing.xs,
paddingInline: spacing.xxs,
paddingBlock: spacing.sm,
width: "100%",
};
});
const FloatingBadge = /* @__PURE__ */ StyledDiv(() => {
const theme = useCustomTheme();
return {
position: "absolute",
top: 0,
right: 0,
transform: "translate(10%, -60%)",
backgroundColor: theme.colors.secondaryButtonBg,
paddingBlock: "3px",
paddingInline: spacing.xs,
fontSize: fontSize.xs,
borderRadius: radius.sm,
color: theme.colors.accentText,
};
});
@@ -0,0 +1,177 @@
import styled from "@emotion/styled";
import { ChevronDownIcon } from "@radix-ui/react-icons";
import { Skeleton } from "../../../../../components/Skeleton";
import { Spacer } from "../../../../../components/Spacer";
import { TokenIcon } from "../../../../../components/TokenIcon";
import { Container } from "../../../../../components/basic";
import { Button } from "../../../../../components/buttons";
import { Input } from "../../../../../components/formElements";
import { fontSize, iconSize, spacing } from "../../../../../design-system";
import { useCustomTheme } from "../../../../../design-system/CustomThemeProvider";
import { useChainQuery } from "../../../../hooks/useChainQuery";
import type { ERC20OrNativeToken } from "../../nativeToken";
import { TokenSymbol } from "../../../../../components/TokenSymbol";
import { Text } from "../../../../../components/text";
/**
* @internal
*/
export function BuyTokenInput(props: {
token: ERC20OrNativeToken;
chainId: number;
value: string;
onChange: (value: string) => void;
onSelectToken: () => void;
}) {
const chainQuery = useChainQuery(props.chainId);
const getWidth = () => {
let chars = props.value.replace(".", "").length;
const hasDot = props.value.includes(".");
if (hasDot) {
chars += 0.3;
}
return `calc(${Math.max(1, chars) + "ch"} + 6px)`;
};
return (
<Container>
{/* Input */}
<div
onClick={(e) => {
e.currentTarget.querySelector("input")?.focus();
}}
>
<Container
flex="row"
center="both"
gap="xs"
style={{
flexWrap: "nowrap",
}}
>
<Input
variant="outline"
pattern="^[0-9]*[.,]?[0-9]*$"
inputMode="decimal"
placeholder="0"
type="text"
data-placeholder={props.value === ""}
value={props.value || "0"}
onClick={(e) => {
// put cursor at the end of the input
if (props.value === "") {
e.currentTarget.setSelectionRange(
e.currentTarget.value.length,
e.currentTarget.value.length,
);
}
}}
onChange={(e) => {
let value = e.target.value;
if (value.startsWith(".")) {
value = "0" + value;
}
const numValue = Number(value);
if (isNaN(numValue)) {
return;
}
if (value.startsWith("0") && !value.startsWith("0.")) {
props.onChange(value.slice(1));
} else {
props.onChange(value);
}
}}
style={{
border: "none",
fontSize:
props.value.length > 10
? "26px"
: props.value.length > 6
? "34px"
: "50px",
boxShadow: "none",
padding: "0",
paddingBlock: "2px",
fontWeight: 600,
textAlign: "right",
width: getWidth(),
maxWidth: "calc(100% - 100px)",
}}
/>
<TokenSymbol
token={props.token}
chainId={props.chainId}
size="lg"
color="secondaryText"
/>
</Container>
</div>
<Spacer y="md" />
{/* Token / Chain selector */}
<Container flex="row" center="x">
<TokenButton
variant="secondary"
fullWidth
style={{
fontSize: fontSize.sm,
}}
gap="xxs"
onClick={props.onSelectToken}
>
<Container flex="row" center="y" gap="sm">
<TokenIcon token={props.token} chainId={props.chainId} size="md" />
<Container
flex="column"
style={{
gap: "4px",
}}
>
{/* Token Symbol */}
<TokenSymbol
token={props.token}
chainId={props.chainId}
size="sm"
/>
{/* Network Name */}
{chainQuery.data?.name ? (
<Text size="xs" color="secondaryText">
{chainQuery.data.name}
</Text>
) : (
<Skeleton width="90px" height={fontSize.xs} />
)}
</Container>
</Container>
<ChevronDownIcon
width={iconSize.sm}
height={iconSize.sm}
style={{
marginLeft: "auto",
}}
/>
</TokenButton>
</Container>
</Container>
);
}
const TokenButton = /* @__PURE__ */ styled(Button)(() => {
const theme = useCustomTheme();
return {
background: theme.colors.walletSelectorButtonHoverBg,
border: `1px solid ${theme.colors.borderColor}`,
justifyContent: "flex-start",
transition: "background 0.3s",
padding: spacing.sm,
};
});

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