├── .DS_Store ├── .eslintignore ├── .eslintrc ├── .gitignore ├── .prettierignore ├── .prettierrc ├── .vscode └── settings.json ├── LICENSE ├── README.md ├── babel.config.json ├── examples ├── create-react-app │ ├── .gitignore │ ├── README.md │ ├── package.json │ ├── public │ │ ├── favicon.ico │ │ ├── index.html │ │ ├── logo192.png │ │ ├── logo512.png │ │ ├── manifest.json │ │ └── robots.txt │ ├── src │ │ ├── App.css │ │ ├── App.js │ │ ├── App.test.js │ │ ├── index.css │ │ ├── index.js │ │ ├── logo.svg │ │ ├── reportWebVitals.js │ │ └── setupTests.js │ └── yarn.lock ├── next │ ├── .gitignore │ ├── next-env.d.ts │ ├── next.config.js │ ├── package.json │ ├── pages │ │ ├── _app.tsx │ │ ├── connect.tsx │ │ ├── hooks.tsx │ │ └── index.tsx │ └── tsconfig.json └── parcel │ ├── index.html │ ├── package.json │ └── src │ ├── App.tsx │ ├── connect.tsx │ ├── hooks.tsx │ └── index.tsx ├── package.json ├── packages ├── .DS_Store ├── hooks │ ├── README.md │ ├── package.json │ └── src │ │ ├── Web3Provider.tsx │ │ ├── index.ts │ │ ├── useConnectWallet.ts │ │ ├── useSwitchNetwork.ts │ │ └── useWeb3.ts └── react │ ├── .DS_Store │ ├── README.md │ ├── package.json │ └── src │ ├── .DS_Store │ ├── components │ ├── providers │ │ ├── ThemeProvider.tsx │ │ └── ThirdwebProvider.tsx │ ├── shared │ │ ├── Card.tsx │ │ └── NetworkIcon.tsx │ └── wallet │ │ ├── AddressCopyButton.tsx │ │ ├── ConnectButton.tsx │ │ ├── ConnectWallet.tsx │ │ ├── ModalConnected.tsx │ │ ├── ModalDisconnected.tsx │ │ └── index.ts │ ├── index.ts │ └── utils │ └── shortenAddress.ts ├── tsconfig.json └── yarn.lock /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thirdweb-dev/ui/348a33e70b9842a0a08245373b26ec3cbbf85fa9/.DS_Store -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | dist 2 | node_modules 3 | coverage 4 | .next 5 | build -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "extends": [ 4 | "plugin:import/recommended", 5 | "plugin:import/typescript", 6 | "plugin:react-hooks/recommended", 7 | "prettier" 8 | ], 9 | "plugins": [ 10 | "react-hooks", 11 | "@typescript-eslint/eslint-plugin", 12 | "inclusive-language", 13 | "eslint-plugin-tsdoc" 14 | ], 15 | "rules": { 16 | // tsdoc 17 | "tsdoc/syntax": "warn", 18 | // typescript 19 | "@typescript-eslint/ban-ts-comment": [ 20 | "error", 21 | { 22 | // future defaults 23 | "ts-expect-error": "allow-with-description", 24 | "minimumDescriptionLength": 10 25 | } 26 | ], 27 | "@typescript-eslint/ban-types": [ 28 | "error", 29 | { 30 | "types": { 31 | "{}": false 32 | } 33 | } 34 | ], 35 | "@typescript-eslint/explicit-module-boundary-types": "off", 36 | "@typescript-eslint/no-empty-interface": "off", 37 | "@typescript-eslint/no-explicit-any": "off", 38 | "@typescript-eslint/no-non-null-assertion": "error", 39 | "@typescript-eslint/no-parameter-properties": "error", 40 | "@typescript-eslint/no-unused-vars": "off", 41 | // import 42 | "import/first": "error", 43 | "import/newline-after-import": "error", 44 | "import/no-cycle": "warn", 45 | "import/no-default-export": "off", 46 | "import/no-useless-path-segments": "error", 47 | // eslint 48 | "curly": "error", 49 | "eqeqeq": "error", 50 | "getter-return": "off", 51 | "key-spacing": [ 52 | "error", 53 | { "beforeColon": false, "afterColon": true, "mode": "strict" } 54 | ], 55 | "keyword-spacing": ["error", { "before": true, "after": true }], 56 | "line-comment-position": "error", 57 | "new-cap": "error", 58 | "no-alert": "error", 59 | "no-case-declarations": "off", 60 | 61 | "no-duplicate-imports": "error", 62 | "no-eval": "error", 63 | "no-floating-decimal": "error", 64 | "no-implicit-coercion": ["error", { "boolean": false }], 65 | "no-implied-eval": "error", 66 | "no-irregular-whitespace": "error", 67 | "no-label-var": "error", 68 | "no-multiple-empty-lines": "error", 69 | "no-octal-escape": "error", 70 | "no-restricted-globals": ["error", "xdescribe", "fit", "fdescribe"], 71 | // has false positives 72 | "no-shadow": "off", 73 | // replaced with this 74 | "@typescript-eslint/no-shadow": "error", 75 | "no-tabs": "error", 76 | "no-template-curly-in-string": "error", 77 | "no-throw-literal": "error", 78 | "no-trailing-spaces": "error", 79 | "no-undef": "off", 80 | "no-unused-expressions": "error", 81 | "no-useless-computed-key": "error", 82 | "no-whitespace-before-property": "error", 83 | "object-curly-spacing": ["error", "always"], 84 | "object-shorthand": ["error", "always"], 85 | "prefer-const": "error", 86 | "prefer-object-spread": "error", 87 | "prefer-template": "error", 88 | "quote-props": ["error", "as-needed"], 89 | // 'sort-imports': ['warn', { ignoreDeclarationSort: true }], 90 | // 'sort-keys': ['warn', 'asc', { natural: true }], 91 | "spaced-comment": ["error", "always", { "markers": ["/ This is an **legacy** version of the Thirdweb UI Components. 4 | 5 | > [Looking for version 2? Click here!](https://github.com/thirdweb-dev/react) 6 | 7 | ## Resources 8 | 9 | - Documentation for the Thirdweb Component Library is available on the [Thirdweb Web3 Portal](https://thirdweb.com/portal/). 10 | - You can visit the [Thirdweb](https://thirdweb.com) website for information on the platform. 11 | 12 | ## Packages 13 | 14 | ### [Thirdweb React](./packages/react/README.md) 15 | -------------------------------------------------------------------------------- /babel.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "@babel/preset-typescript", 4 | "@babel/preset-react", 5 | [ 6 | "@parcel/babel-preset-env", 7 | { 8 | "bugfixes": true, 9 | "loose": true 10 | } 11 | ] 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /examples/create-react-app/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /examples/create-react-app/README.md: -------------------------------------------------------------------------------- 1 | # Getting Started with Create React App 2 | 3 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). 4 | 5 | ## Available Scripts 6 | 7 | In the project directory, you can run: 8 | 9 | ### `yarn start` 10 | 11 | Runs the app in the development mode.\ 12 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser. 13 | 14 | The page will reload if you make edits.\ 15 | You will also see any lint errors in the console. 16 | 17 | ### `yarn test` 18 | 19 | Launches the test runner in the interactive watch mode.\ 20 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. 21 | 22 | ### `yarn build` 23 | 24 | Builds the app for production to the `build` folder.\ 25 | It correctly bundles React in production mode and optimizes the build for the best performance. 26 | 27 | The build is minified and the filenames include the hashes.\ 28 | Your app is ready to be deployed! 29 | 30 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. 31 | 32 | ### `yarn eject` 33 | 34 | **Note: this is a one-way operation. Once you `eject`, you can’t go back!** 35 | 36 | If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. 37 | 38 | Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own. 39 | 40 | You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it. 41 | 42 | ## Learn More 43 | 44 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). 45 | 46 | To learn React, check out the [React documentation](https://reactjs.org/). 47 | 48 | ### Code Splitting 49 | 50 | This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting) 51 | 52 | ### Analyzing the Bundle Size 53 | 54 | This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size) 55 | 56 | ### Making a Progressive Web App 57 | 58 | This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app) 59 | 60 | ### Advanced Configuration 61 | 62 | This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration) 63 | 64 | ### Deployment 65 | 66 | This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment) 67 | 68 | ### `yarn build` fails to minify 69 | 70 | This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify) 71 | -------------------------------------------------------------------------------- /examples/create-react-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "create-react-app", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@3rdweb/react": "^1.3.1", 7 | "@testing-library/jest-dom": "^5.11.4", 8 | "@testing-library/react": "^11.1.0", 9 | "@testing-library/user-event": "^12.1.10", 10 | "react": "^17.0.2", 11 | "react-dom": "^17.0.2", 12 | "react-scripts": "4.0.3", 13 | "web-vitals": "^1.0.1" 14 | }, 15 | "scripts": { 16 | "start": "react-scripts start", 17 | "build": "react-scripts build", 18 | "test": "react-scripts test", 19 | "eject": "react-scripts eject" 20 | }, 21 | "eslintConfig": { 22 | "extends": [ 23 | "react-app", 24 | "react-app/jest" 25 | ] 26 | }, 27 | "browserslist": { 28 | "production": [ 29 | ">0.2%", 30 | "not dead", 31 | "not op_mini all" 32 | ], 33 | "development": [ 34 | "last 1 chrome version", 35 | "last 1 firefox version", 36 | "last 1 safari version" 37 | ] 38 | }, 39 | "devDependencies": { 40 | "eslint-config-react-app": "^6.0.0" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /examples/create-react-app/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thirdweb-dev/ui/348a33e70b9842a0a08245373b26ec3cbbf85fa9/examples/create-react-app/public/favicon.ico -------------------------------------------------------------------------------- /examples/create-react-app/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /examples/create-react-app/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thirdweb-dev/ui/348a33e70b9842a0a08245373b26ec3cbbf85fa9/examples/create-react-app/public/logo192.png -------------------------------------------------------------------------------- /examples/create-react-app/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thirdweb-dev/ui/348a33e70b9842a0a08245373b26ec3cbbf85fa9/examples/create-react-app/public/logo512.png -------------------------------------------------------------------------------- /examples/create-react-app/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /examples/create-react-app/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /examples/create-react-app/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | height: 40vmin; 7 | pointer-events: none; 8 | } 9 | 10 | @media (prefers-reduced-motion: no-preference) { 11 | .App-logo { 12 | animation: App-logo-spin infinite 20s linear; 13 | } 14 | } 15 | 16 | .App-header { 17 | background-color: #282c34; 18 | min-height: 100vh; 19 | display: flex; 20 | flex-direction: column; 21 | align-items: center; 22 | justify-content: center; 23 | font-size: calc(10px + 2vmin); 24 | color: white; 25 | } 26 | 27 | .App-link { 28 | color: #61dafb; 29 | } 30 | 31 | @keyframes App-logo-spin { 32 | from { 33 | transform: rotate(0deg); 34 | } 35 | to { 36 | transform: rotate(360deg); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /examples/create-react-app/src/App.js: -------------------------------------------------------------------------------- 1 | import { ThirdwebProvider, ConnectWallet } from '@3rdweb/react'; 2 | import './App.css'; 3 | 4 | const supportedChainIds = [1, 4, 137, 250, 43114, 80001]; 5 | const connectors = { 6 | injected: {}, 7 | magic: { 8 | apiKey: "pk_live_712C1E6230EA31BC", 9 | chainId: 1, 10 | }, 11 | walletconnect: {}, 12 | walletlink: { 13 | appName: "thirdweb - demo", 14 | url: "https://thirdweb.com", 15 | darkMode: false, 16 | }, 17 | }; 18 | 19 | function ExampleApp() { 20 | return ( 21 | 25 | 26 | 27 | ); 28 | } 29 | 30 | export default ExampleApp; 31 | -------------------------------------------------------------------------------- /examples/create-react-app/src/App.test.js: -------------------------------------------------------------------------------- 1 | import { render, screen } from '@testing-library/react'; 2 | import App from './App'; 3 | 4 | test('renders learn react link', () => { 5 | render(); 6 | const linkElement = screen.getByText(/learn react/i); 7 | expect(linkElement).toBeInTheDocument(); 8 | }); 9 | -------------------------------------------------------------------------------- /examples/create-react-app/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /examples/create-react-app/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App'; 5 | import reportWebVitals from './reportWebVitals'; 6 | 7 | ReactDOM.render( 8 | 9 | 10 | , 11 | document.getElementById('root') 12 | ); 13 | 14 | // If you want to start measuring performance in your app, pass a function 15 | // to log results (for example: reportWebVitals(console.log)) 16 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 17 | reportWebVitals(); 18 | -------------------------------------------------------------------------------- /examples/create-react-app/src/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/create-react-app/src/reportWebVitals.js: -------------------------------------------------------------------------------- 1 | const reportWebVitals = onPerfEntry => { 2 | if (onPerfEntry && onPerfEntry instanceof Function) { 3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 4 | getCLS(onPerfEntry); 5 | getFID(onPerfEntry); 6 | getFCP(onPerfEntry); 7 | getLCP(onPerfEntry); 8 | getTTFB(onPerfEntry); 9 | }); 10 | } 11 | }; 12 | 13 | export default reportWebVitals; 14 | -------------------------------------------------------------------------------- /examples/create-react-app/src/setupTests.js: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom'; 6 | -------------------------------------------------------------------------------- /examples/next/.gitignore: -------------------------------------------------------------------------------- 1 | .env.local -------------------------------------------------------------------------------- /examples/next/next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | // NOTE: This file should not be edited 5 | // see https://nextjs.org/docs/basic-features/typescript for more information. 6 | -------------------------------------------------------------------------------- /examples/next/next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | 3 | const withPreconstruct = require("@preconstruct/next"); 4 | 5 | module.exports = withPreconstruct(); 6 | 7 | // content security headers things 8 | const securityHeaders = [ 9 | { 10 | key: "X-DNS-Prefetch-Control", 11 | value: "on", 12 | }, 13 | { 14 | key: "X-XSS-Protection", 15 | value: "1; mode=block", 16 | }, 17 | { 18 | key: "X-Frame-Options", 19 | value: "SAMEORIGIN", 20 | }, 21 | { 22 | key: "Referrer-Policy", 23 | value: "origin-when-cross-origin", 24 | }, 25 | // { 26 | // key: "Content-Security-Policy", 27 | // value: `default-src * 'self' 'unsafe-eval' localhost:* thirdweb.com *.thirdweb.com *.vercel.app *.nftlabs.co *.ingest.sentry.io vitals.vercel-insights.com *.g.alchemy.com rpc.ftm.tools api.avax.network nftlabs.mypinata.cloud https:; style-src 'self' 'unsafe-eval' 'unsafe-inline' rsms.me fonts.googleapis.com; object-src 'none'; font-src rsms.me *.gstatic.com; base-uri 'none'; connect-src *; img-src * blob: data:;`, 28 | // }, 29 | ]; 30 | 31 | const moduleExports = { 32 | reactStrictMode: true, 33 | outputFileTracing: false, 34 | 35 | async redirects() { 36 | return []; 37 | }, 38 | async rewrites() { 39 | return []; 40 | }, 41 | webpack: (config) => { 42 | config.resolve.fallback = { 43 | fs: false, 44 | path: false, 45 | child_process: false, 46 | crypto: false, 47 | os: false, 48 | tty: false, 49 | worker_threads: false, 50 | process: false, 51 | }; 52 | return config; 53 | }, 54 | }; 55 | 56 | module.exports = withPreconstruct(moduleExports); 57 | -------------------------------------------------------------------------------- /examples/next/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "next-example", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "license": "MIT", 6 | "private": true, 7 | "dependencies": { 8 | "@3rdweb/react": "*", 9 | "@3rdweb/hooks": "*", 10 | "@chakra-ui/react": "^1.7.0", 11 | "@preconstruct/next": "^3.0.1", 12 | "next": "^12.0.3", 13 | "react": "^17.0.2", 14 | "react-dom": "^17.0.2" 15 | }, 16 | "devDependencies": { 17 | "@types/react": "^17.0.34", 18 | "typescript": "^4.4.4" 19 | }, 20 | "scripts": { 21 | "dev": "next dev", 22 | "build": "next build" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /examples/next/pages/_app.tsx: -------------------------------------------------------------------------------- 1 | import { ThirdwebProvider } from "@3rdweb/react"; 2 | import React from "react"; 3 | 4 | const supportedChainIds = [1, 4, 137, 250, 43114, 80001]; 5 | const connectors = { 6 | injected: {}, 7 | magic: { 8 | apiKey: "pk_live_712C1E6230EA31BC", 9 | chainId: 1, 10 | }, 11 | walletconnect: {}, 12 | walletlink: { 13 | appName: "thirdweb - demo", 14 | url: "https://thirdweb.com", 15 | darkMode: false, 16 | }, 17 | }; 18 | 19 | function ExampleApp({ Component, pageProps }) { 20 | return ( 21 | 25 | 26 | 27 | ); 28 | } 29 | 30 | export default ExampleApp; 31 | -------------------------------------------------------------------------------- /examples/next/pages/connect.tsx: -------------------------------------------------------------------------------- 1 | import { ConnectWallet } from "@3rdweb/react"; 2 | import { Flex, Text } from "@chakra-ui/react"; 3 | import React from "react"; 4 | 5 | export default function Connect() { 6 | return ( 7 | 14 | 15 | Web3 Connector 16 | 17 | 25 | Our web3 connector component completely handles wallet connection and 26 | network switching and comes with a context wrapper for your app. 27 |

28 | You can also customize which connector types you support for your app, 29 | including Metamask, Coinbase, WalletConnect, and Magic Link. 30 |
31 |
32 | You can interact with the component below. 33 |
34 | 35 | 36 | Network Switching Enabled: 37 | 38 | 39 | 40 | 41 | Network Switching Disabled: 42 | 43 | 44 |
45 | ); 46 | } 47 | -------------------------------------------------------------------------------- /examples/next/pages/hooks.tsx: -------------------------------------------------------------------------------- 1 | import { useSwitchNetwork, useWeb3 } from "@3rdweb/hooks"; 2 | import { AspectRatio, Button, Flex, Image, Text } from "@chakra-ui/react"; 3 | import React from "react"; 4 | 5 | export default function Hooks() { 6 | const { address, chainId, connectWallet, disconnectWallet } = useWeb3(); 7 | const { canAttemptSwitch } = useSwitchNetwork(); 8 | 9 | return ( 10 | 17 | 18 | Web3 Connection Hooks 19 | 20 | 28 | Our web3 connection hooks let you use our wallet conenction and network 29 | switching setup with your own custom components. 30 |
31 |
32 | The component below is built entirely with our connection hooks. 33 |
34 | 35 | 42 | 43 | Current Status 44 | 45 | 46 | ChainID: {chainId || "N/A"} 47 | 48 | 49 | Can Switch: {`${!!canAttemptSwitch}`} 50 | 51 | 52 | Connected: {`${!!address}`} 53 | 54 | 55 | Wallet Address:{" "} 56 | {address ? `${address.slice(0, 16)}...` : "N/A"} 57 | 58 | 59 | {address && ( 60 | 68 | )} 69 | 70 | 77 | Connect Wallet 78 | 79 | 80 | 96 | 97 | 112 | 113 | 127 | 128 |
129 | ); 130 | } 131 | -------------------------------------------------------------------------------- /examples/next/pages/index.tsx: -------------------------------------------------------------------------------- 1 | import { Flex, Heading, Link, Text } from "@chakra-ui/react"; 2 | import NextLink from "next/link"; 3 | 4 | export default function () { 5 | return ( 6 | 7 | 8 | 15 | 23 | Thirdweb 24 | 25 |
26 | Component Library 27 |
28 | 29 | 36 | Welcome to the Thirdweb Component Library. This is a toolbox of all 37 | the components you'll need to start easily integrating web3 into your 38 | apps. 39 |
40 |
41 | You can learn more about our components by clicking on the links 42 | below. 43 |
44 | 45 | 46 | 59 | 60 | Web3 Connector 61 | 62 | 63 | This component completely handles wallet connection and network 64 | switching. 65 | 66 | 67 | 68 | 69 | 70 | 83 | 84 | Web3 Connection Hooks 85 | 86 | 87 | These hooks let you make your own custom web3 connection setup. 88 | 89 | 90 | 91 |
92 |
93 | ); 94 | } 95 | -------------------------------------------------------------------------------- /examples/next/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": [ 5 | "dom", 6 | "dom.iterable", 7 | "esnext" 8 | ], 9 | "allowJs": true, 10 | "skipLibCheck": true, 11 | "strict": false, 12 | "forceConsistentCasingInFileNames": true, 13 | "noEmit": true, 14 | "incremental": true, 15 | "esModuleInterop": true, 16 | "module": "esnext", 17 | "moduleResolution": "node", 18 | "resolveJsonModule": true, 19 | "isolatedModules": true, 20 | "jsx": "preserve" 21 | }, 22 | "include": [ 23 | "next-env.d.ts", 24 | "**/*.ts", 25 | "**/*.tsx" 26 | ], 27 | "exclude": [ 28 | "node_modules" 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /examples/parcel/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | My Parcel App 6 | 7 | 8 |
9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /examples/parcel/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "parcel", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "license": "MIT", 6 | "dependencies": { 7 | "@3rdweb/react": "*", 8 | "react": "^17.0.2", 9 | "react-dom": "^17.0.2" 10 | }, 11 | "devDependencies": { 12 | "@types/react": "^17.0.35", 13 | "@types/react-dom": "^17.0.11", 14 | "parcel": "^2.0.1" 15 | }, 16 | "scripts": { 17 | "start": "parcel index.html", 18 | "build": "parcel build index.html" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /examples/parcel/src/App.tsx: -------------------------------------------------------------------------------- 1 | import { Flex, Heading, Link, Text } from "@chakra-ui/react"; 2 | import React from "react"; 3 | import { Connect } from "./connect"; 4 | import { Hooks } from "./hooks"; 5 | 6 | export function ExampleApp() { 7 | const [page, setPage] = React.useState(""); 8 | if (page === "connect") { 9 | return ; 10 | } else if (page === "hooks") { 11 | return ; 12 | } 13 | return ( 14 | 15 | 16 | 23 | 31 | Thirdweb 32 | 33 |
34 | Component Library 35 |
36 | 37 | 44 | Welcome to the Thirdweb Component Library. This is a toolbox of all 45 | the components you'll need to start easily integrating web3 into your 46 | apps. 47 |
48 |
49 | You can learn more about our components by clicking on the links 50 | below. 51 |
52 | 53 | setPage("connect")}> 54 | 67 | 68 | Web3 Connector 69 | 70 | 71 | This component completely handles wallet connection and network 72 | switching. 73 | 74 | 75 | 76 | 77 | setPage("hooks")}> 78 | 91 | 92 | Web3 Connection Hooks 93 | 94 | 95 | These hooks let you make your own custom web3 connection setup. 96 | 97 | 98 | 99 |
100 |
101 | ); 102 | } 103 | -------------------------------------------------------------------------------- /examples/parcel/src/connect.tsx: -------------------------------------------------------------------------------- 1 | import { ConnectWallet } from "@3rdweb/react"; 2 | import { Flex, Text } from "@chakra-ui/react"; 3 | import React from "react"; 4 | 5 | export function Connect() { 6 | return ( 7 | 14 | 15 | Web3 Connector 16 | 17 | 25 | Our web3 connector component completely handles wallet connection and 26 | network switching and comes with a context wrapper for your app. 27 |

28 | You can also customize which connector types you support for your app, 29 | including Metamask, Coinbase, WalletConnect, and Magic Link. 30 |
31 |
32 | You can interact with the component below. 33 |
34 | 35 |
36 | ); 37 | } 38 | -------------------------------------------------------------------------------- /examples/parcel/src/hooks.tsx: -------------------------------------------------------------------------------- 1 | import { useSwitchNetwork, useWeb3 } from "@3rdweb/hooks"; 2 | import { AspectRatio, Button, Flex, Image, Text } from "@chakra-ui/react"; 3 | import React from "react"; 4 | 5 | export function Hooks() { 6 | const { address, chainId, connectWallet, disconnectWallet } = useWeb3(); 7 | const { canAttemptSwitch } = useSwitchNetwork(); 8 | 9 | return ( 10 | 17 | 18 | Web3 Connection Hooks 19 | 20 | 28 | Our web3 connection hooks let you use our wallet conenction and network 29 | switching setup with your own custom components. 30 |
31 |
32 | The component below is built entirely with our connection hooks. 33 |
34 | 35 | 42 | 43 | Current Status 44 | 45 | 46 | ChainID: {chainId || "N/A"} 47 | 48 | 49 | Can Switch: {`${!!canAttemptSwitch}`} 50 | 51 | 52 | Connected: {`${!!address}`} 53 | 54 | 55 | Wallet Address:{" "} 56 | {address ? `${address.slice(0, 16)}...` : "N/A"} 57 | 58 | 59 | {address && ( 60 | 68 | )} 69 | 70 | 77 | Connect Wallet 78 | 79 | 80 | 96 | 97 | 112 | 113 | 127 | 128 |
129 | ); 130 | } 131 | -------------------------------------------------------------------------------- /examples/parcel/src/index.tsx: -------------------------------------------------------------------------------- 1 | import { ThirdwebProvider } from "@3rdweb/react"; 2 | import React from "react"; 3 | import ReactDOM from "react-dom"; 4 | import { ExampleApp } from "./App"; 5 | 6 | const supportedChainIds = [1, 4, 137, 250, 43114, 80001]; 7 | const connectors = { 8 | injected: {}, 9 | magic: { 10 | apiKey: "pk_live_712C1E6230EA31BC", 11 | chainId: 1, 12 | }, 13 | walletconnect: {}, 14 | walletlink: { 15 | appName: "thirdweb - demo", 16 | url: "https://thirdweb.com", 17 | darkMode: false, 18 | }, 19 | }; 20 | 21 | const app = document.getElementById("app"); 22 | ReactDOM.render( 23 | 27 | 28 | , 29 | app, 30 | ); 31 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "thirdweb-ui", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "author": "Jonas Daniels ", 6 | "license": "Apache-2.0", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/thirdweb-dev/ui" 10 | }, 11 | "private": true, 12 | "homepage": "https://thirdweb.com/", 13 | "workspaces": [ 14 | "packages/*", 15 | "examples/*" 16 | ], 17 | "preconstruct": { 18 | "packages": [ 19 | "packages/*" 20 | ] 21 | }, 22 | "scripts": { 23 | "postinstall": "preconstruct dev", 24 | "fix": "preconstruct fix", 25 | "build": "preconstruct build", 26 | "dev": "preconstruct dev", 27 | "lint": "eslint packages/**/src/ --ext .ts,.tsx --config .eslintrc", 28 | "fix:pkgs": "manypkg fix" 29 | }, 30 | "dependencies": { 31 | "@babel/core": "^7.16.0", 32 | "@babel/preset-env": "^7.16.0", 33 | "@babel/preset-react": "^7.16.0", 34 | "@babel/preset-typescript": "^7.16.0", 35 | "@manypkg/cli": "^0.19.1", 36 | "@parcel/babel-preset-env": "^2.0.1", 37 | "@preconstruct/cli": "^2.1.5", 38 | "@typescript-eslint/eslint-plugin": "^5.3.1", 39 | "@typescript-eslint/parser": "^5.3.1", 40 | "eslint": "^7", 41 | "eslint-config-prettier": "^8.3.0", 42 | "eslint-plugin-import": "^2.24.2", 43 | "eslint-plugin-inclusive-language": "^2.1.1", 44 | "eslint-plugin-prettier": "^4.0.0", 45 | "eslint-plugin-react-hooks": "^4.3.0", 46 | "eslint-plugin-tsdoc": "^0.2.14", 47 | "prettier": "^2.4.1", 48 | "typescript": "^4.4.4" 49 | }, 50 | "resolutions": { 51 | "glob-parent": "^6.0.2", 52 | "ansi-regex": "^5.0.1", 53 | "json-schema": "^0.4.0" 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /packages/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thirdweb-dev/ui/348a33e70b9842a0a08245373b26ec3cbbf85fa9/packages/.DS_Store -------------------------------------------------------------------------------- /packages/hooks/README.md: -------------------------------------------------------------------------------- 1 | # Thirdweb Hooks 2 | 3 |
4 | 5 | ## Introduction 6 | 7 | Welcome to the Thirdweb ReactnHooks Library. This package provides you with extensible react hooks to handle the web3 side of your app. 8 | 9 | We simplify the process of integrating web3 into your apps while making sure that you still have all the control you would using other lower level web3 frontend libraries. 10 | 11 | Our main features are: 12 | 13 | - Support for most commonly used web3 providers including: [MetaMask](https://metamask.io/), [WalletConnect](https://walletconnect.com/), [Coinbase Wallet](https://wallet.coinbase.com/), and [Magic Link](https://magic.link/). 14 | - An app wide context containing an [ethers.js](https://github.com/ethers-io/ethers.js/) or [web3.js](https://web3js.readthedocs.io/en/v1.5.2/) instance with everything you need to integrate with the blockchain. 15 | 16 |
17 | 18 | ## Getting Started 19 | 20 | To get started with using our hooks, you just need to setup the `ThirdwebWeb3Provider` that provides all the context consumed by your app. 21 | 22 | Setting up this context is as easy as wrapping your app with the following setup: 23 | 24 | ```javascript 25 | import { ThirdwebWeb3Provider } from "@3rdweb/hooks"; 26 | 27 | const App = ({ children }) => { 28 | // Put the ethereum chain ids of the chains you want to support 29 | const supportedChainIds = [1, 4, 137]; 30 | 31 | /** 32 | * Include the connectors you want to support 33 | * injected - MetaMask 34 | * magic - Magic Link 35 | * walletconnect - Wallet Connect 36 | * walletlink - Coinbase Wallet 37 | */ 38 | const connectors = { 39 | injected: {}, 40 | magic: { 41 | apiKey: "pk_...", // Your magic api key 42 | chainId: 1, // The chain ID you want to allow on magic 43 | }, 44 | walletconnect: {}, 45 | walletlink: { 46 | appName: "thirdweb - demo", 47 | url: "https://thirdweb.com", 48 | darkMode: false, 49 | }, 50 | }; 51 | 52 | /** 53 | * Make sure that your app is wrapped with these contexts. 54 | * If you're using Next JS, you'll have to replace children with the Component setup 55 | */ 56 | return ( 57 | 61 | {children} 62 | 63 | ); 64 | }; 65 | ``` 66 | 67 | ### **Use Custom Hooks** 68 | 69 | You can build your own connect wallet button with our `useWeb3` and `useSwitchNetwork` hooks. 70 | 71 | You can see how these hooks are used in the following example. 72 | 73 | ```javascript 74 | import React, { useState } from "react" 75 | import { useWeb3, useSwitchNetwork } from "@3rdweb/hooks" 76 | 77 | const supportedChainIds = [1, 4, 137]; 78 | 79 | const CustomConnect = () => { 80 | const { address, chainId, connectWallet, disconnectWallet, getNetworkMetadata } = useWeb3(); 81 | const { switchNetwork } = useSwitchNetwork(); 82 | const [email, setEmail] = useState(""); 83 | 84 | return ( 85 | <> 86 | Address: {address} 87 |
88 | Chain ID: {chainId} 89 |
90 | 91 | {address && ( 92 | 95 | )} 96 | 97 |

Switch Network

98 | {supportChainIds.map((cId) => ( 99 | 102 | ))} 103 | 104 | setEmail(e.target.value)} /> 105 | 112 | 113 | 116 | 119 | 122 | <> 123 | ) 124 | } 125 | ``` 126 | 127 | For a fully functional setup using our custom hooks, you can checkout our [NextJS example hooks page](https://github.com/nftlabs/ui/blob/main/examples/next/pages/hooks.tsx). 128 | 129 |
130 | 131 | ### **Access Web3 Setup** 132 | 133 | After you setup wallet connection with the above method, accessing your connected web3 provider and its related info is as easy as the following: 134 | 135 | ```javascript 136 | import React from "react"; 137 | import { useWeb3 } from "@3rdweb/react"; 138 | 139 | const Component = () => { 140 | // You can do whatever you want with this data 141 | const { address, chainId, provider } = useWeb3(); 142 | 143 | return ( 144 |
145 | Address: {address} 146 |
147 | Chain ID: {chainId} 148 |
149 | ); 150 | }; 151 | ``` 152 | -------------------------------------------------------------------------------- /packages/hooks/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@3rdweb/hooks", 3 | "version": "1.9.2", 4 | "main": "dist/3rdweb-hooks.cjs.js", 5 | "module": "dist/3rdweb-hooks.esm.js", 6 | "private": false, 7 | "browser": { 8 | "./dist/3rdweb-hooks.cjs.js": "./dist/3rdweb-hooks.browser.cjs.js", 9 | "./dist/3rdweb-hooks.esm.js": "./dist/3rdweb-hooks.browser.esm.js" 10 | }, 11 | "types": "dist/3rdweb-hooks.cjs.d.ts", 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/thirdweb-dev/ui", 15 | "directory": "/packages/hooks" 16 | }, 17 | "author": "Adam Majmudar", 18 | "license": "Apache-2.0", 19 | "sideEffects": false, 20 | "files": [ 21 | "dist" 22 | ], 23 | "peerDependencies": { 24 | "react": ">=16.8 || ^17 || ^18" 25 | }, 26 | "devDependencies": { 27 | "@types/react": "^17.0.34", 28 | "react": "^17.0.2" 29 | }, 30 | "dependencies": { 31 | "@3rdweb/chain-icons": "^1.0.1", 32 | "@ethersproject/providers": "^5.5.0", 33 | "@ethersproject/units": "^5.5.0", 34 | "@web3-react/core": "^6.1.9", 35 | "@web3-react/injected-connector": "^6.0.7", 36 | "@web3-react/magic-connector": "^6.1.9", 37 | "@web3-react/walletconnect-connector": "6.2.8", 38 | "@web3-react/walletlink-connector": "^6.2.8", 39 | "regenerator-runtime": "^0.13.9", 40 | "tiny-invariant": "^1.2.0" 41 | }, 42 | "resolutions": { 43 | "walletlink": "^2.4.5", 44 | "@magic-sdk/provider": "^7.0.0", 45 | "@walletconnect/ethereum-provider": "1.6.0" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /packages/hooks/src/Web3Provider.tsx: -------------------------------------------------------------------------------- 1 | import { Web3Provider } from "@ethersproject/providers"; 2 | import { Web3ReactProvider } from "@web3-react/core"; 3 | import { AbstractConnectorArguments } from "@web3-react/types"; 4 | import { WalletConnectConnectorArguments } from "@web3-react/walletconnect-connector"; 5 | import React, { createContext, useContext } from "react"; 6 | import invariant from "tiny-invariant"; 7 | 8 | interface MagicConnectorArguments { 9 | apiKey: string; 10 | chainId: number; 11 | } 12 | 13 | interface WalletLinkConnectorArguments { 14 | url: string; 15 | appName: string; 16 | appLogoUrl?: string; 17 | darkMode?: boolean; 18 | supportedChainIds?: number[]; 19 | } 20 | 21 | export type ConnectorOptions = { 22 | // MetaMask 23 | injected: AbstractConnectorArguments; 24 | // Magic Link 25 | magic: MagicConnectorArguments; 26 | // Mobile Wallets 27 | walletconnect: WalletConnectConnectorArguments; 28 | // Coinbase Wallet 29 | walletlink: WalletLinkConnectorArguments; 30 | }; 31 | 32 | export type ConnectorType = keyof ConnectorOptions; 33 | 34 | export interface AddEthereumChainParameter { 35 | // A 0x-prefixed hexadecimal string 36 | chainId: string; 37 | chainName: string; 38 | nativeCurrency: { 39 | name: string; 40 | // 2-6 characters long 41 | symbol: string; 42 | decimals: 18; 43 | }; 44 | rpcUrls: string[]; 45 | blockExplorerUrls?: string[]; 46 | // Currently ignored 47 | iconUrls?: string[]; 48 | } 49 | 50 | export interface NetworkMetadata { 51 | chainName: string; 52 | icon: string | React.ComponentType; 53 | symbol: string; 54 | isTestnet: boolean; 55 | } 56 | 57 | export interface IThirdwebContext { 58 | _inProvider: boolean; 59 | readonly connectors: Partial; 60 | readonly supportedChainIds: number[]; 61 | readonly networkMetadata?: Record; 62 | readonly chainAddConfig?: Record; 63 | } 64 | 65 | function getLibrary(provider: any): Web3Provider { 66 | return new Web3Provider(provider, "any"); 67 | } 68 | 69 | const ThirdwebContext = createContext({ 70 | _inProvider: false, 71 | connectors: {}, 72 | supportedChainIds: [], 73 | }); 74 | 75 | export function useThirdwebContext(): IThirdwebContext { 76 | const context = useContext(ThirdwebContext); 77 | invariant( 78 | context._inProvider, 79 | ` 80 | Attempting to call useThirdwebContext from outside , 81 | did you forget to wrap your application in a ? 82 | `, 83 | ); 84 | return context; 85 | } 86 | 87 | export interface ThirdwebWeb3ProviderProps { 88 | connectors: IThirdwebContext["connectors"]; 89 | supportedChainIds: IThirdwebContext["supportedChainIds"]; 90 | networkMetadata?: IThirdwebContext["networkMetadata"]; 91 | chainAddConfig?: IThirdwebContext["chainAddConfig"]; 92 | } 93 | 94 | export const ThirdwebWeb3Provider: React.FC = ({ 95 | connectors, 96 | supportedChainIds, 97 | networkMetadata, 98 | chainAddConfig, 99 | children, 100 | }) => { 101 | return ( 102 | 111 | {children} 112 | 113 | ); 114 | }; 115 | -------------------------------------------------------------------------------- /packages/hooks/src/index.ts: -------------------------------------------------------------------------------- 1 | import "regenerator-runtime/runtime.js"; 2 | 3 | export * from "./useSwitchNetwork"; 4 | export * from "./useWeb3"; 5 | export * from "./Web3Provider"; 6 | -------------------------------------------------------------------------------- /packages/hooks/src/useConnectWallet.ts: -------------------------------------------------------------------------------- 1 | import { useWeb3React } from "@web3-react/core"; 2 | import { useCallback } from "react"; 3 | import invariant from "tiny-invariant"; 4 | import { 5 | ConnectorOptions, 6 | ConnectorType, 7 | useThirdwebContext, 8 | } from "./Web3Provider"; 9 | 10 | export type ConnectorActivateOptions = { 11 | // MetaMask 12 | injected: undefined; 13 | // Magic Link 14 | magic: { email: string }; 15 | // Mobile Wallets 16 | walletconnect: undefined; 17 | // Coinbase Wallet 18 | walletlink: undefined; 19 | }; 20 | 21 | export function useConnectWallet() { 22 | const { activate } = useWeb3React(); 23 | const { connectors, supportedChainIds } = useThirdwebContext(); 24 | 25 | return useCallback( 26 | async ( 27 | connectorType: TConnectorType, 28 | connectOptions?: ConnectorActivateOptions[TConnectorType], 29 | ) => { 30 | invariant( 31 | connectors[connectorType], 32 | ` 33 | Invalid connect() call for connector: ${connectorType}. 34 | This connector is not defined on the . 35 | `, 36 | ); 37 | 38 | const connectorOptions = connectors[connectorType] 39 | ? { ...connectors[connectorType], supportedChainIds } 40 | : { supportedChainIds }; 41 | 42 | switch (connectorType) { 43 | case "injected": { 44 | const { InjectedConnector } = await import( 45 | "@web3-react/injected-connector" 46 | ); 47 | return await activate( 48 | new InjectedConnector( 49 | connectorOptions as ConnectorOptions["injected"], 50 | ), 51 | ); 52 | } 53 | case "magic": { 54 | const { MagicConnector } = await import( 55 | "@web3-react/magic-connector" 56 | ); 57 | const { email } = connectOptions as ConnectorActivateOptions["magic"]; 58 | const _connectorOptions = connectors[ 59 | connectorType 60 | ] as ConnectorOptions["magic"]; 61 | return await activate( 62 | new MagicConnector({ ..._connectorOptions, email }), 63 | ); 64 | } 65 | case "walletlink": { 66 | const { WalletLinkConnector } = await import( 67 | "@web3-react/walletlink-connector" 68 | ); 69 | return await activate( 70 | new WalletLinkConnector( 71 | connectorOptions as ConnectorOptions["walletlink"], 72 | ), 73 | ); 74 | } 75 | case "walletconnect": { 76 | const { WalletConnectConnector } = await import( 77 | "@web3-react/walletconnect-connector" 78 | ); 79 | return await activate( 80 | new WalletConnectConnector( 81 | connectorOptions as ConnectorOptions["walletconnect"], 82 | ), 83 | ); 84 | } 85 | default: 86 | throw new Error(`Unsupported connector: ${connectorType}`); 87 | } 88 | }, 89 | [connectors, supportedChainIds, activate], 90 | ); 91 | } 92 | -------------------------------------------------------------------------------- /packages/hooks/src/useSwitchNetwork.ts: -------------------------------------------------------------------------------- 1 | import { Web3Provider } from "@ethersproject/providers"; 2 | import { useWeb3React } from "@web3-react/core"; 3 | import { useCallback, useEffect, useMemo, useState } from "react"; 4 | import { useThirdwebContext } from "./Web3Provider"; 5 | 6 | const defaultChainAddConfig = { 7 | 1: { 8 | chainId: `0x${Number(1).toString(16)}`, 9 | chainName: "Mainnet", 10 | nativeCurrency: { 11 | name: "Ethereum", 12 | symbol: "ETH", 13 | decimals: 18, 14 | }, 15 | rpcUrls: ["https://main-light.eth.linkpool.io/"], 16 | }, 17 | 4: { 18 | chainId: `0x${Number(4).toString(16)}`, 19 | chainName: "Rinkeby (ETH Testnet)", 20 | nativeCurrency: { 21 | name: "Ethereum", 22 | symbol: "ETH", 23 | decimals: 18, 24 | }, 25 | rpcUrls: ["https://rinkeby-light.eth.linkpool.io/"], 26 | }, 27 | 137: { 28 | chainId: `0x${Number(137).toString(16)}`, 29 | chainName: "Polygon Mainnet (Matic)", 30 | nativeCurrency: { 31 | name: "Matic", 32 | symbol: "MATIC", 33 | decimals: 18, 34 | }, 35 | rpcUrls: ["https://polygon-rpc.com"], 36 | blockExplorerUrls: ["https://polygonscan.com"], 37 | }, 38 | 250: { 39 | chainId: `0x${Number(250).toString(16)}`, 40 | chainName: "Fantom Opera", 41 | nativeCurrency: { 42 | name: "Fantom", 43 | symbol: "FTM", 44 | decimals: 18, 45 | }, 46 | rpcUrls: ["https://rpc.ftm.tools"], 47 | blockExplorerUrls: ["https://ftmscan.com"], 48 | }, 49 | 43114: { 50 | chainId: `0x${Number(43114).toString(16)}`, 51 | chainName: "Avalanche Mainnet C-Chain", 52 | nativeCurrency: { 53 | name: "Avalanche", 54 | symbol: "AVAX", 55 | decimals: 18, 56 | }, 57 | rpcUrls: ["https://api.avax.network/ext/bc/C/rpc"], 58 | blockExplorerUrls: ["https://cchain.explorer.avax.network"], 59 | }, 60 | 80001: { 61 | chainId: `0x${Number(80001).toString(16)}`, 62 | chainName: "Polygon Mumbai Testnet", 63 | nativeCurrency: { 64 | name: "Matic", 65 | symbol: "MATIC", 66 | decimals: 18, 67 | }, 68 | rpcUrls: [ 69 | "https://rpc-mumbai.maticvigil.com", 70 | "https://rpc-mumbai.matic.today", 71 | ], 72 | blockExplorerUrls: ["https://mumbai.polygonscan.com"], 73 | }, 74 | }; 75 | 76 | export function useSwitchNetwork() { 77 | const { chainAddConfig } = useThirdwebContext(); 78 | const { account, library, connector, chainId } = useWeb3React(); 79 | const [isSwitching, setIsSwitching] = useState(false); 80 | const [switchError, setSwitchError] = useState(); 81 | const [connectorProvider, setConnectorProvider] = useState(); 82 | 83 | useEffect(() => { 84 | const getProvider = async () => { 85 | setConnectorProvider(await connector?.getProvider()); 86 | }; 87 | 88 | if (connector) { 89 | getProvider(); 90 | } 91 | }, [connector]); 92 | 93 | useEffect(() => { 94 | setSwitchError(null); 95 | }, [chainId, account]); 96 | 97 | const canAttemptSwitch = useMemo(() => { 98 | return !!connectorProvider?.request; 99 | }, [connectorProvider?.request]); 100 | 101 | const switchNetwork = useCallback( 102 | async (newChainId: number) => { 103 | if (!connectorProvider?.request) { 104 | setSwitchError(new Error("No provider available to switch")); 105 | return; 106 | } 107 | 108 | setSwitchError(null); 109 | if (newChainId === chainId) { 110 | return; 111 | } 112 | 113 | setIsSwitching(true); 114 | const chainHex = `0x${newChainId.toString(16)}`; 115 | try { 116 | await connectorProvider.request({ 117 | method: "wallet_switchEthereumChain", 118 | params: [{ chainId: chainHex }], 119 | }); 120 | } catch (_switchError) { 121 | if ( 122 | (_switchError as any).code === 4902 && 123 | chainAddConfig && 124 | chainAddConfig[newChainId] 125 | ) { 126 | try { 127 | await connectorProvider.request({ 128 | method: "wallet_addEthereumChain", 129 | params: [chainAddConfig[newChainId]], 130 | }); 131 | } catch (addError) { 132 | setSwitchError(addError as Error); 133 | } 134 | } else if ( 135 | (_switchError as any).code === 4902 && 136 | defaultChainAddConfig[newChainId] 137 | ) { 138 | try { 139 | await connectorProvider.request({ 140 | method: "wallet_addEthereumChain", 141 | params: [defaultChainAddConfig[newChainId]], 142 | }); 143 | } catch (addError) { 144 | setSwitchError(addError as Error); 145 | } 146 | } else { 147 | setSwitchError(_switchError as Error); 148 | } 149 | } finally { 150 | setIsSwitching(false); 151 | } 152 | }, 153 | [chainAddConfig, connectorProvider, chainId], 154 | ); 155 | 156 | return { 157 | switchNetwork, 158 | canAttemptSwitch, 159 | isSwitching, 160 | switchError, 161 | }; 162 | } 163 | -------------------------------------------------------------------------------- /packages/hooks/src/useWeb3.ts: -------------------------------------------------------------------------------- 1 | import { BigNumber } from "@ethersproject/bignumber"; 2 | import { ExternalProvider, Web3Provider } from "@ethersproject/providers"; 3 | import { formatEther } from "@ethersproject/units"; 4 | import { AbstractConnector } from "@web3-react/abstract-connector"; 5 | import { useWeb3React } from "@web3-react/core"; 6 | import { InjectedConnector } from "@web3-react/injected-connector"; 7 | import { useCallback, useEffect, useMemo, useState } from "react"; 8 | import { Avalanche, Ethereum, Fantom, Polygon } from "@3rdweb/chain-icons"; 9 | import { useConnectWallet } from "./useConnectWallet"; 10 | import { 11 | ConnectorType, 12 | NetworkMetadata, 13 | useThirdwebContext, 14 | } from "./Web3Provider"; 15 | 16 | interface Balance { 17 | value?: BigNumber; 18 | formatted: string; 19 | } 20 | 21 | export interface Web3ContextInterface { 22 | error?: Error; 23 | chainId?: number; 24 | balance?: Balance; 25 | provider?: Web3Provider; 26 | connector?: AbstractConnector & { 27 | [key: string]: any; 28 | }; 29 | activeProvider?: ExternalProvider; 30 | address?: string; 31 | connectors: ConnectorType[]; 32 | connectWallet: ReturnType; 33 | disconnectWallet: () => void; 34 | getNetworkMetadata: (chainId: number) => NetworkMetadata; 35 | } 36 | 37 | const defaultNetworkMetadata: Record = { 38 | 1: { 39 | chainName: "Ethereum", 40 | icon: Ethereum, 41 | symbol: "ETH", 42 | isTestnet: false, 43 | }, 44 | 4: { 45 | chainName: "Rinkeby", 46 | icon: Ethereum, 47 | symbol: "ETH", 48 | isTestnet: true, 49 | }, 50 | 137: { 51 | chainName: "Polygon", 52 | icon: Polygon, 53 | symbol: "MATIC", 54 | isTestnet: false, 55 | }, 56 | 250: { 57 | chainName: "Fantom", 58 | icon: Fantom, 59 | symbol: "FTM", 60 | isTestnet: false, 61 | }, 62 | 43114: { 63 | chainName: "Avalanche", 64 | icon: Avalanche, 65 | symbol: "AVAX", 66 | isTestnet: false, 67 | }, 68 | 80001: { 69 | chainName: "Mumbai", 70 | icon: Polygon, 71 | symbol: "MATIC", 72 | isTestnet: true, 73 | }, 74 | }; 75 | 76 | export function useWeb3(): Web3ContextInterface { 77 | const connect = useConnectWallet(); 78 | const { connectors, networkMetadata } = useThirdwebContext(); 79 | const web3Context = useWeb3React(); 80 | const { library, connector, account, error, chainId, deactivate } = 81 | web3Context; 82 | 83 | const [balance, setBalance] = useState(); 84 | 85 | useEffect(() => { 86 | if (error?.message.includes("The user rejected the request.")) { 87 | deactivate(); 88 | } 89 | }, [error, deactivate]); 90 | 91 | useEffect(() => { 92 | const checkInjected = async () => { 93 | const injected = new InjectedConnector({}); 94 | if (await injected.isAuthorized()) { 95 | connect("injected"); 96 | } 97 | }; 98 | 99 | setTimeout(() => { 100 | checkInjected(); 101 | }, 500); 102 | }, [connect]); 103 | 104 | useEffect(() => { 105 | const getBalance = async () => { 106 | if (account) { 107 | const accountBalance = await library?.getBalance(account); 108 | setBalance({ 109 | value: accountBalance, 110 | formatted: formatEther(accountBalance || 0).slice(0, 6), 111 | }); 112 | } else { 113 | setBalance({ 114 | formatted: "0.0", 115 | }); 116 | } 117 | }; 118 | 119 | getBalance(); 120 | }, [library, account]); 121 | 122 | const activeProvider = useMemo(() => { 123 | return library?.provider; 124 | }, [library?.provider]); 125 | 126 | const disconnectWallet = useCallback(async () => { 127 | const provider = activeProvider; 128 | if (!provider) { 129 | return; 130 | } 131 | 132 | if (provider.isMetaMask && provider.request) { 133 | const request = await provider.request({ 134 | method: "wallet_requestPermissions", 135 | params: [{ eth_accounts: {} }], 136 | }); 137 | return request; 138 | } else { 139 | if (connector && (connector as any).close) { 140 | (connector as any).close(); 141 | return; 142 | } 143 | return deactivate(); 144 | } 145 | }, [activeProvider, connector, deactivate]); 146 | 147 | const getNetworkMetadata = useCallback( 148 | (_chainId: number): NetworkMetadata => { 149 | return ( 150 | (networkMetadata && networkMetadata[_chainId]) || 151 | defaultNetworkMetadata[_chainId] || { 152 | chainName: "", 153 | iconUrl: "", 154 | symbol: "", 155 | } 156 | ); 157 | }, 158 | [networkMetadata], 159 | ); 160 | 161 | return useMemo( 162 | () => ({ 163 | error, 164 | chainId, 165 | connector, 166 | balance, 167 | provider: library, 168 | activeProvider, 169 | // Force no null account 170 | address: account || undefined, 171 | connectors: Object.keys(connectors) as ConnectorType[], 172 | connectWallet: connect, 173 | disconnectWallet, 174 | getNetworkMetadata, 175 | }), 176 | [ 177 | account, 178 | chainId, 179 | balance, 180 | connector, 181 | activeProvider, 182 | connect, 183 | connectors, 184 | disconnectWallet, 185 | getNetworkMetadata, 186 | error, 187 | library, 188 | ], 189 | ); 190 | } 191 | -------------------------------------------------------------------------------- /packages/react/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thirdweb-dev/ui/348a33e70b9842a0a08245373b26ec3cbbf85fa9/packages/react/.DS_Store -------------------------------------------------------------------------------- /packages/react/README.md: -------------------------------------------------------------------------------- 1 | # Thirdweb React 2 | 3 |
4 | 5 | ## Introduction 6 | 7 | Welcome to the Thirdweb Component Library. This package provides you with extensible components to handle the web3 side of your app. 8 | 9 | We simplify the process of integrating web3 into your apps while making sure that you still have all the control you would using other lower level web3 frontend libraries. 10 | 11 | Our main features are: 12 | 13 | - Support for most commonly used web3 providers including: [MetaMask](https://metamask.io/), [WalletConnect](https://walletconnect.com/), [Coinbase Wallet](https://wallet.coinbase.com/), and [Magic Link](https://magic.link/). 14 | - An app wide context containing an [ethers.js](https://github.com/ethers-io/ethers.js/) or [web3.js](https://web3js.readthedocs.io/en/v1.5.2/) instance with everything you need to integrate with the blockchain. 15 | - Easy-to-use plug-and-play components that let you implement complex and fully-featured web3 app setups with only a few lines of code. 16 | 17 |
18 | 19 | ## Getting Started 20 | 21 | To get started with the Thirdweb Component Library, you just need to setup the `ThirdwebProvider` that provides all the context consumed by your app and lets you use our custom components. 22 | 23 | Setting up this context is as easy as wrapping your app with the following setup: 24 | 25 | ```javascript 26 | import { ThirdwebProvider } from "@3rdweb/react"; 27 | 28 | const App = ({ children }) => { 29 | // Put the ethereum chain ids of the chains you want to support 30 | const supportedChainIds = [1, 4, 137]; 31 | 32 | /** 33 | * Include the connectors you want to support 34 | * injected - MetaMask 35 | * magic - Magic Link 36 | * walletconnect - Wallet Connect 37 | * walletlink - Coinbase Wallet 38 | */ 39 | const connectors = { 40 | injected: {}, 41 | magic: { 42 | apiKey: "pk_...", // Your magic api key 43 | chainId: 1, // The chain ID you want to allow on magic 44 | }, 45 | walletconnect: {}, 46 | walletlink: { 47 | appName: "thirdweb - demo", 48 | url: "https://thirdweb.com", 49 | darkMode: false, 50 | }, 51 | }; 52 | 53 | /** 54 | * Make sure that your app is wrapped with these contexts. 55 | * If you're using Next JS, you'll have to replace children with the Component setup 56 | */ 57 | return ( 58 | 62 | {children} 63 | 64 | ); 65 | }; 66 | ``` 67 | 68 |
69 | 70 | ## Connect Wallet & Web3 Setup 71 | 72 | Currently, we provide you with components to easily integrate web3 into your app and setup an app wide context without having to deal with the complexity of lower level web3 configuration. 73 | 74 | You can use our fully configured `ConnectWallet` component to handle all web3 connection and integration, including wallet connection and network switching. This is the easiest way to use the Thirdweb Component Library. 75 | 76 | ### **Use Connect Wallet** 77 | 78 | Using our `ConnectWallet` component is the easiest way to integrate web3 into your app, complete with network switching, wallet connection, and everything else you need. Adding our connect wallet button is as easy as the following: 79 | 80 | ```javascript 81 | import React from "react"; 82 | import { ConnectWallet } from "@3rdweb/react"; 83 | 84 | const Connect = () => { 85 | return ; 86 | }; 87 | ``` 88 | 89 | You can place this button anywhere in your app and it will display a wallet connection that displays connected chain, wallet address, and balance information as well as a fully-featured connection manager modal. 90 | 91 | For a fully functional setup using our `ConnectWallet` button, you can checkout our [NextJS example connect page](https://github.com/nftlabs/ui/blob/main/examples/next/pages/connect.tsx). 92 | 93 |
94 | 95 | ### **Access Web3 Setup** 96 | 97 | After you setup wallet connection with the above method, accessing your connected web3 provider and its related info is as easy as the following: 98 | 99 | ```javascript 100 | import React from "react"; 101 | import { useWeb3 } from "@3rdweb/react"; 102 | 103 | const Component = () => { 104 | // You can do whatever you want with this data 105 | const { address, chainId, provider } = useWeb3(); 106 | 107 | return ( 108 |
109 | Address: {address} 110 |
111 | Chain ID: {chainId} 112 |
113 | ); 114 | }; 115 | ``` -------------------------------------------------------------------------------- /packages/react/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@3rdweb/react", 3 | "version": "1.9.2", 4 | "main": "dist/3rdweb-react.cjs.js", 5 | "module": "dist/3rdweb-react.esm.js", 6 | "browser": { 7 | "./dist/3rdweb-react.cjs.js": "./dist/3rdweb-react.browser.cjs.js", 8 | "./dist/3rdweb-react.esm.js": "./dist/3rdweb-react.browser.esm.js" 9 | }, 10 | "types": "dist/3rdweb-react.cjs.d.ts", 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/thirdweb-dev/ui", 14 | "directory": "packages/react" 15 | }, 16 | "homepage": "https://thirdweb.com/", 17 | "license": "Apache-2.0", 18 | "files": [ 19 | "dist" 20 | ], 21 | "sideEffects": false, 22 | "peerDependencies": { 23 | "react": ">=16.8 || ^17 || ^18" 24 | }, 25 | "devDependencies": { 26 | "@types/react": "^17.0.34", 27 | "react": "^17.0.2" 28 | }, 29 | "dependencies": { 30 | "@3rdweb/hooks": "^1", 31 | "@chakra-ui/icons": "^1.1.0", 32 | "@chakra-ui/react": "1.7.5", 33 | "@emotion/react": "^11.6.0", 34 | "@emotion/styled": "^11.6.0", 35 | "@types/styled-components": "^5.1.15", 36 | "framer-motion": "^5.3.0", 37 | "react-icons": "^4.3.1" 38 | }, 39 | "resolutions": { 40 | "@web3-react/walletconnect-connector": "6.2.8", 41 | "walletlink": "^2.4.5", 42 | "@magic-sdk/provider": "^7.0.0" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /packages/react/src/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thirdweb-dev/ui/348a33e70b9842a0a08245373b26ec3cbbf85fa9/packages/react/src/.DS_Store -------------------------------------------------------------------------------- /packages/react/src/components/providers/ThemeProvider.tsx: -------------------------------------------------------------------------------- 1 | import { ChakraProvider, Theme } from "@chakra-ui/react"; 2 | import React from "react"; 3 | 4 | export const ThirdwebThemeProvider: React.FC<{ 5 | theme?: Theme; 6 | }> = ({ theme, children }) => { 7 | return {children}; 8 | }; 9 | -------------------------------------------------------------------------------- /packages/react/src/components/providers/ThirdwebProvider.tsx: -------------------------------------------------------------------------------- 1 | import { ThirdwebWeb3Provider, ThirdwebWeb3ProviderProps } from "@3rdweb/hooks"; 2 | import type { Theme } from "@chakra-ui/react"; 3 | import React from "react"; 4 | import { ThirdwebThemeProvider } from "./ThemeProvider"; 5 | 6 | interface ThirdwebProviderProps extends ThirdwebWeb3ProviderProps { 7 | theme?: Theme; 8 | } 9 | 10 | export const ThirdwebProvider: React.FC = ({ 11 | theme, 12 | children, 13 | ...restProps 14 | }) => { 15 | return ( 16 | 17 | {children} 18 | 19 | ); 20 | }; 21 | -------------------------------------------------------------------------------- /packages/react/src/components/shared/Card.tsx: -------------------------------------------------------------------------------- 1 | import { Box, BoxProps } from "@chakra-ui/react"; 2 | import React from "react"; 3 | 4 | type DefaultedBoxProps = Pick< 5 | BoxProps, 6 | "shadow" | "backgroundColor" | "py" | "px" | "borderRadius" | "border" 7 | >; 8 | 9 | const defaultBoxProps: Required = { 10 | shadow: "md", 11 | backgroundColor: "white", 12 | px: 4, 13 | py: 4, 14 | borderRadius: "md", 15 | border: "1px solid var(--chakra-colors-gray-200)", 16 | }; 17 | 18 | export interface CardProps extends BoxProps {} 19 | export const Card: React.FC = ({ 20 | children, 21 | ...requiredBoxProps 22 | }) => { 23 | return {children}; 24 | }; 25 | -------------------------------------------------------------------------------- /packages/react/src/components/shared/NetworkIcon.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { useWeb3 } from "@3rdweb/hooks"; 3 | import { Icon, Image, IconProps } from "@chakra-ui/react"; 4 | 5 | interface INetworkIcon 6 | extends Pick { 7 | chainId: number; 8 | } 9 | 10 | export const NetworkIcon: React.FC = ({ 11 | chainId, 12 | height = "36px", 13 | width = "36px", 14 | borderRadius = 0, 15 | }) => { 16 | const { getNetworkMetadata } = useWeb3(); 17 | const icon = getNetworkMetadata(chainId).icon; 18 | 19 | if (typeof icon === "string") { 20 | return ( 21 | 27 | ); 28 | } 29 | 30 | return ( 31 | 32 | ); 33 | }; 34 | -------------------------------------------------------------------------------- /packages/react/src/components/wallet/AddressCopyButton.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | Button, 3 | ButtonGroup, 4 | ButtonGroupProps, 5 | Icon, 6 | IconButton, 7 | Tooltip, 8 | useClipboard, 9 | useToast, 10 | } from "@chakra-ui/react"; 11 | import React from "react"; 12 | import { IoCopy } from "react-icons/io5"; 13 | import { shortenAddress } from "../../utils/shortenAddress"; 14 | 15 | interface IAddressCopyButton extends Omit { 16 | address?: string; 17 | noIcon?: boolean; 18 | } 19 | 20 | export const AddressCopyButton: React.FC = ({ 21 | address, 22 | noIcon, 23 | ...restButtonProps 24 | }) => { 25 | const { onCopy } = useClipboard(address || ""); 26 | const toast = useToast(); 27 | 28 | const defaultProps: ButtonGroupProps = { 29 | flexGrow: 0, 30 | variant: "solid", 31 | size: "sm", 32 | fontSize: "md", 33 | fontWeight: "normal", 34 | }; 35 | return ( 36 | 37 | { 41 | e.stopPropagation(); 42 | e.preventDefault(); 43 | onCopy(); 44 | toast({ 45 | title: "Address copied.", 46 | status: "success", 47 | duration: 5000, 48 | isClosable: true, 49 | }); 50 | }} 51 | > 52 | {noIcon ? null : ( 53 | } 58 | /> 59 | )} 60 | 61 | 62 | 63 | ); 64 | }; 65 | -------------------------------------------------------------------------------- /packages/react/src/components/wallet/ConnectButton.tsx: -------------------------------------------------------------------------------- 1 | import { useSwitchNetwork, useWeb3 } from "@3rdweb/hooks"; 2 | import { Icon } from "@chakra-ui/icons"; 3 | import { Button, Divider, Flex, Stack, Text, Tooltip } from "@chakra-ui/react"; 4 | import React, { useMemo } from "react"; 5 | import { FiAlertTriangle } from "react-icons/fi"; 6 | import { IoWalletOutline } from "react-icons/io5"; 7 | import { NetworkIcon } from "../shared/NetworkIcon"; 8 | import { shortenAddress } from "../../utils/shortenAddress"; 9 | 10 | export const ConnectButton: React.FC<{ 11 | onOpen: () => void; 12 | isOpen: boolean; 13 | }> = ({ onOpen, isOpen, ...props }) => { 14 | const { address, balance, chainId, error, getNetworkMetadata } = useWeb3(); 15 | const { switchError } = useSwitchNetwork(); 16 | 17 | const networkMetadata = useMemo(() => { 18 | if (chainId) { 19 | return getNetworkMetadata(chainId); 20 | } 21 | }, [chainId, getNetworkMetadata]); 22 | 23 | return ( 24 | 38 | {address ? ( 39 | 53 | 54 | 55 | 56 | 57 | {shortenAddress(address)} 58 | 59 | 60 | {networkMetadata?.chainName} 61 | 62 | 63 | 64 | 69 | 76 | {balance?.formatted} 77 | {networkMetadata && networkMetadata.symbol.length > 2 &&
} 78 | {networkMetadata?.symbol} 79 |
80 |
81 | ) : ( 82 | 99 | )} 100 |
101 | ); 102 | }; 103 | -------------------------------------------------------------------------------- /packages/react/src/components/wallet/ConnectWallet.tsx: -------------------------------------------------------------------------------- 1 | import { useWeb3 } from "@3rdweb/hooks"; 2 | import { ButtonProps } from "@chakra-ui/button"; 3 | import { 4 | Flex, 5 | Modal, 6 | ModalBody, 7 | ModalCloseButton, 8 | ModalContent, 9 | ModalOverlay, 10 | useDisclosure, 11 | usePrevious, 12 | } from "@chakra-ui/react"; 13 | import React, { useEffect } from "react"; 14 | import { ConnectButton } from "./ConnectButton"; 15 | import { ModalConnected } from "./ModalConnected"; 16 | import { ModalDisconnected } from "./ModalDisconnected"; 17 | 18 | export const ConnectWallet: React.FC< 19 | ButtonProps & { 20 | disableNetworkSwitching?: boolean; 21 | } 22 | > = ({ disableNetworkSwitching, ...props }) => { 23 | const { isOpen, onOpen, onClose } = useDisclosure(); 24 | const { chainId, address, connector, error } = useWeb3(); 25 | 26 | const previousConnector = usePrevious(connector); 27 | const previousChainId = usePrevious(chainId); 28 | const previousAddress = usePrevious(address); 29 | 30 | // if chain id changes, then close modal 31 | useEffect(() => { 32 | if (previousChainId !== chainId) { 33 | onClose(); 34 | } 35 | }, [chainId, previousChainId, onClose]); 36 | 37 | // if chain id changes, then close modal 38 | useEffect(() => { 39 | if (previousAddress !== address) { 40 | onClose(); 41 | } 42 | }, [onClose, previousAddress, address]); 43 | 44 | // if connector changes, then close modal 45 | useEffect(() => { 46 | if ( 47 | (connector && !previousConnector) || 48 | (!connector && previousConnector) 49 | ) { 50 | onClose(); 51 | } 52 | }, [connector, onClose, previousConnector]); 53 | 54 | return ( 55 | <> 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | {connector && !error ? ( 65 | 68 | ) : ( 69 | 70 | )} 71 | 72 | 73 | 74 | 75 | 76 | ); 77 | }; 78 | -------------------------------------------------------------------------------- /packages/react/src/components/wallet/ModalConnected.tsx: -------------------------------------------------------------------------------- 1 | import { useSwitchNetwork, useThirdwebContext, useWeb3 } from "@3rdweb/hooks"; 2 | import { 3 | Alert, 4 | AlertIcon, 5 | Button, 6 | Divider, 7 | Flex, 8 | Heading, 9 | Image, 10 | Stack, 11 | Text, 12 | } from "@chakra-ui/react"; 13 | import React from "react"; 14 | import { NetworkIcon } from "../shared/NetworkIcon"; 15 | import { AddressCopyButton } from "./AddressCopyButton"; 16 | 17 | export const ModalConnected: React.FC<{ 18 | disableNetworkSwitching?: boolean; 19 | }> = ({ disableNetworkSwitching }) => { 20 | const { supportedChainIds } = useThirdwebContext(); 21 | const { switchError } = useSwitchNetwork(); 22 | const { 23 | chainId, 24 | connector, 25 | error, 26 | address, 27 | activeProvider, 28 | disconnectWallet, 29 | getNetworkMetadata, 30 | } = useWeb3(); 31 | 32 | return ( 33 | 34 | {!disableNetworkSwitching && 35 | !connector?.magic && 36 | !connector?.walletConnectProvider && ( 37 | <> 38 | 39 | 40 | Switch network 41 | 42 | {supportedChainIds 43 | .filter((cId) => !getNetworkMetadata(cId).isTestnet) 44 | .map((cId, index) => ( 45 | 46 | ))} 47 | {supportedChainIds 48 | .filter((cId) => getNetworkMetadata(cId).isTestnet) 49 | .map((cId, index) => ( 50 | 51 | ))} 52 | 53 | 54 | 55 | 56 | )} 57 | 58 | {disableNetworkSwitching && ( 59 | <> 60 | 61 | 62 | Connected network 63 | 64 | 65 | 66 | 67 | 68 | 69 | )} 70 | 71 | 72 | 73 | Connected wallet 74 | 75 | 76 | {error || switchError ? ( 77 | 83 | 84 | {switchError?.message || error?.message} 85 | 86 | ) : ( 87 | 88 | 89 | 90 | 91 | 92 | 100 | 101 | )} 102 | 103 | 104 | ); 105 | }; 106 | 107 | const Network: React.FC<{ 108 | index: number; 109 | cId: number; 110 | }> = ({ index, cId }) => { 111 | const { chainId, getNetworkMetadata } = useWeb3(); 112 | const { switchNetwork } = useSwitchNetwork(); 113 | 114 | return ( 115 | switchNetwork(cId)} 119 | align="center" 120 | width="md" 121 | px="20px" 122 | py="2px" 123 | cursor="pointer" 124 | > 125 | 136 | 137 | 138 | 139 | {getNetworkMetadata(cId).chainName} 140 | 141 | {getNetworkMetadata(cId).isTestnet && ( 142 | 143 |  (testnet) 144 | 145 | )} 146 | 147 | {cId === chainId && ( 148 | 149 | Connected 150 | 151 | )} 152 | 153 | 154 | ); 155 | }; 156 | -------------------------------------------------------------------------------- /packages/react/src/components/wallet/ModalDisconnected.tsx: -------------------------------------------------------------------------------- 1 | import { useWeb3 } from "@3rdweb/hooks"; 2 | import { 3 | AspectRatio, 4 | Button, 5 | Divider, 6 | Flex, 7 | Heading, 8 | Image, 9 | Input, 10 | Spinner, 11 | Stack, 12 | Text, 13 | } from "@chakra-ui/react"; 14 | import React, { useState } from "react"; 15 | 16 | export const ModalDisconnected: React.FC = () => { 17 | const [email, setEmail] = useState(""); 18 | const [loading, setLoading] = useState(false); 19 | const [error, setError] = useState(false); 20 | const { address, connectWallet, connectors } = useWeb3(); 21 | 22 | function isEmailValid() { 23 | const re = 24 | /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; 25 | return re.test(email); 26 | } 27 | 28 | async function connectMagic() { 29 | if (isEmailValid()) { 30 | setEmail(""); 31 | setLoading(true); 32 | await connectWallet("magic", { email }); 33 | setLoading(false); 34 | } else { 35 | setError(true); 36 | } 37 | } 38 | 39 | return ( 40 | 41 | {connectors.includes("magic") && ( 42 | 43 | 44 | Connect with email 45 | 46 | 47 | 48 | { 51 | setEmail(e.target.value); 52 | setError(false); 53 | }} 54 | placeholder="name@example.com" 55 | borderRadius="4px 0px 0px 4px" 56 | /> 57 | 70 | 71 | {error && ( 72 | 73 | Please enter a valid email. 74 | 75 | )} 76 | 77 | 78 | )} 79 | 80 | {connectors.includes("magic") && 81 | connectors.some((connector) => connector !== "magic") && ( 82 | 83 | )} 84 | 85 | {connectors.some((connector) => connector !== "magic") && ( 86 | 87 | 88 | Connect a{address ? " different" : ""} wallet 89 | 90 | 91 | {connectors.includes("injected") && 92 | typeof window !== "undefined" && 93 | "ethereum" in window && ( 94 | 108 | )} 109 | 110 | {connectors.includes("walletconnect") && ( 111 | 125 | )} 126 | 127 | {connectors.includes("walletlink") && ( 128 | 142 | )} 143 | 144 | )} 145 | 146 | ); 147 | }; 148 | -------------------------------------------------------------------------------- /packages/react/src/components/wallet/index.ts: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line import/no-cycle 2 | export { ConnectWallet } from "./ConnectWallet"; 3 | -------------------------------------------------------------------------------- /packages/react/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./components/providers/ThirdwebProvider"; 2 | export * from "./components/wallet"; 3 | -------------------------------------------------------------------------------- /packages/react/src/utils/shortenAddress.ts: -------------------------------------------------------------------------------- 1 | export function shortenAddress(str: string) { 2 | return `${str.substring(0, 6)}...${str.substring(str.length - 4)}`; 3 | } 4 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "esnext", 5 | "lib": ["dom", "esnext"], 6 | "declaration": true, 7 | "sourceMap": true, 8 | "moduleResolution": "node", 9 | "skipLibCheck": true, 10 | "strict": true, 11 | "isolatedModules": true, 12 | "noFallthroughCasesInSwitch": true, 13 | "suppressImplicitAnyIndexErrors": true, 14 | "noImplicitAny": true, 15 | "strictFunctionTypes": true, 16 | "strictNullChecks": true, 17 | "strictPropertyInitialization": true, 18 | "jsx": "react", 19 | "esModuleInterop": true, 20 | "resolveJsonModule": true, 21 | "allowSyntheticDefaultImports": true, 22 | "downlevelIteration": true, 23 | "incremental": true 24 | }, 25 | "include": ["packages"], 26 | "exclude": ["node_modules"], 27 | "ts-node": { 28 | "compilerOptions": { 29 | "module": "CommonJS", 30 | "resolveJsonModule": true 31 | } 32 | } 33 | } 34 | --------------------------------------------------------------------------------