├── .gitignore ├── README.md ├── backend ├── .gitignore ├── package-lock.json ├── package.json └── src │ └── index.js ├── demo.gif └── mobile ├── .env.example ├── .gitignore ├── app.json ├── app ├── (app) │ ├── (auth) │ │ ├── _layout.tsx │ │ └── index.tsx │ ├── _layout.tsx │ └── sign-in.tsx ├── +html.tsx └── _layout.tsx ├── assets └── images │ ├── adaptive-icon.png │ ├── favicon.png │ ├── icon.png │ └── splash.png ├── babel.config.js ├── components ├── Header.tsx ├── SIWEAppKit.tsx ├── SIWEThirdweb.tsx └── SIWEViem.tsx ├── index.js ├── package-lock.json ├── package.json ├── tsconfig.json └── utils ├── SessionContext.tsx ├── useStorageState.ts └── walletConnect.ts /.gitignore: -------------------------------------------------------------------------------- 1 | # macOS 2 | .DS_Store -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # web3-react-native-siwe 2 | 3 | This repository showcases how to configure Sign-In With Ethereum (SIWE) on a React Native app, while validating the SIWE message in a Node.js backend. 4 | 5 | The React Native project is built with [Expo](https://expo.dev/), [Wagmi](https://wagmi.sh/), [Viem](https://viem.sh/) and [WalletConnect AppKit](https://docs.walletconnect.com/appkit/overview). The app asks the backend to validate the SIWE message and navigates to an authenticated route using Expo Router. 6 | 7 | Backend nonce generation and SIWE validation is also done using Viem. 8 | 9 | Two implementations are available to test: 10 | 11 | - Automatically through WalletConnect AppKit: after connecting the wallet, a WalletConnect modal will appear asking to sign in. This uses the configuration in `/mobile/utils/siweConfig.ts` 12 | - Manually through Viem: if the WalletConnect modal is dismissed, a green button appears to Sign In With Ethereum manually. This uses Viem manually, in `/mobile/app/index.tsx` 13 | 14 | ![demo.gif](demo.gif) 15 | 16 | ## Requirements 17 | 18 | - [Expo environment setup](https://docs.expo.dev/get-started/installation/#requirements) (Node.js, Git, Watchman) 19 | - A [Wallet Connect Cloud](https://cloud.walletconnect.com/sign-in) project ID 20 | - Expo Go app installed in your smartphone 21 | - One or more web3 wallets installed in your smartphone (e.g. MetaMask, Rainbow Wallet, Trust Wallet, etc) 22 | 23 | ## Running the application 24 | 25 | Run `npm start` on both the `/backend` and `/mobile` directories. 26 | 27 | ### Backend 28 | 29 | - `cd backend` 30 | - `npm install` 31 | - `npm start` 32 | 33 | ### Mobile 34 | 35 | - `cd mobile` 36 | - Rename `.env.example` to `.env` and fill in your Wallet Connect Cloud project ID, and your computer's local IP 37 | - `npm install` 38 | - `npm start` 39 | - Open Expo Go app in your smartphone 40 | - If your smartphone is in the same network as your computer, the local dev server should appear as the first option. If it doesn't, use the app to scan the QR Code presented in the terminal 41 | -------------------------------------------------------------------------------- /backend/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | -------------------------------------------------------------------------------- /backend/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "backend", 3 | "version": "1.0.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "backend", 9 | "version": "1.0.0", 10 | "license": "MIT", 11 | "dependencies": { 12 | "cors": "^2.8.5", 13 | "express": "^4.18.2", 14 | "express-session": "^1.18.0", 15 | "viem": "^2.16.3" 16 | } 17 | }, 18 | "node_modules/@noble/curves": { 19 | "version": "1.2.0", 20 | "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz", 21 | "integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==", 22 | "dependencies": { 23 | "@noble/hashes": "1.3.2" 24 | }, 25 | "funding": { 26 | "url": "https://paulmillr.com/funding/" 27 | } 28 | }, 29 | "node_modules/@noble/hashes": { 30 | "version": "1.3.2", 31 | "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz", 32 | "integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==", 33 | "engines": { 34 | "node": ">= 16" 35 | }, 36 | "funding": { 37 | "url": "https://paulmillr.com/funding/" 38 | } 39 | }, 40 | "node_modules/@scure/base": { 41 | "version": "1.1.7", 42 | "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.7.tgz", 43 | "integrity": "sha512-PPNYBslrLNNUQ/Yad37MHYsNQtK67EhWb6WtSvNLLPo7SdVZgkUjD6Dg+5On7zNwmskf8OX7I7Nx5oN+MIWE0g==", 44 | "funding": { 45 | "url": "https://paulmillr.com/funding/" 46 | } 47 | }, 48 | "node_modules/@scure/bip32": { 49 | "version": "1.3.2", 50 | "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.3.2.tgz", 51 | "integrity": "sha512-N1ZhksgwD3OBlwTv3R6KFEcPojl/W4ElJOeCZdi+vuI5QmTFwLq3OFf2zd2ROpKvxFdgZ6hUpb0dx9bVNEwYCA==", 52 | "dependencies": { 53 | "@noble/curves": "~1.2.0", 54 | "@noble/hashes": "~1.3.2", 55 | "@scure/base": "~1.1.2" 56 | }, 57 | "funding": { 58 | "url": "https://paulmillr.com/funding/" 59 | } 60 | }, 61 | "node_modules/@scure/bip39": { 62 | "version": "1.2.1", 63 | "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.2.1.tgz", 64 | "integrity": "sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg==", 65 | "dependencies": { 66 | "@noble/hashes": "~1.3.0", 67 | "@scure/base": "~1.1.0" 68 | }, 69 | "funding": { 70 | "url": "https://paulmillr.com/funding/" 71 | } 72 | }, 73 | "node_modules/abitype": { 74 | "version": "1.0.4", 75 | "resolved": "https://registry.npmjs.org/abitype/-/abitype-1.0.4.tgz", 76 | "integrity": "sha512-UivtYZOGJGE8rsrM/N5vdRkUpqEZVmuTumfTuolm7m/6O09wprd958rx8kUBwVAAAhQDveGAgD0GJdBuR8s6tw==", 77 | "funding": { 78 | "url": "https://github.com/sponsors/wevm" 79 | }, 80 | "peerDependencies": { 81 | "typescript": ">=5.0.4", 82 | "zod": "^3 >=3.22.0" 83 | }, 84 | "peerDependenciesMeta": { 85 | "typescript": { 86 | "optional": true 87 | }, 88 | "zod": { 89 | "optional": true 90 | } 91 | } 92 | }, 93 | "node_modules/accepts": { 94 | "version": "1.3.8", 95 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", 96 | "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", 97 | "dependencies": { 98 | "mime-types": "~2.1.34", 99 | "negotiator": "0.6.3" 100 | }, 101 | "engines": { 102 | "node": ">= 0.6" 103 | } 104 | }, 105 | "node_modules/array-flatten": { 106 | "version": "1.1.1", 107 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", 108 | "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" 109 | }, 110 | "node_modules/body-parser": { 111 | "version": "1.20.2", 112 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", 113 | "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", 114 | "dependencies": { 115 | "bytes": "3.1.2", 116 | "content-type": "~1.0.5", 117 | "debug": "2.6.9", 118 | "depd": "2.0.0", 119 | "destroy": "1.2.0", 120 | "http-errors": "2.0.0", 121 | "iconv-lite": "0.4.24", 122 | "on-finished": "2.4.1", 123 | "qs": "6.11.0", 124 | "raw-body": "2.5.2", 125 | "type-is": "~1.6.18", 126 | "unpipe": "1.0.0" 127 | }, 128 | "engines": { 129 | "node": ">= 0.8", 130 | "npm": "1.2.8000 || >= 1.4.16" 131 | } 132 | }, 133 | "node_modules/bytes": { 134 | "version": "3.1.2", 135 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", 136 | "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", 137 | "engines": { 138 | "node": ">= 0.8" 139 | } 140 | }, 141 | "node_modules/call-bind": { 142 | "version": "1.0.7", 143 | "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", 144 | "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", 145 | "dependencies": { 146 | "es-define-property": "^1.0.0", 147 | "es-errors": "^1.3.0", 148 | "function-bind": "^1.1.2", 149 | "get-intrinsic": "^1.2.4", 150 | "set-function-length": "^1.2.1" 151 | }, 152 | "engines": { 153 | "node": ">= 0.4" 154 | }, 155 | "funding": { 156 | "url": "https://github.com/sponsors/ljharb" 157 | } 158 | }, 159 | "node_modules/content-disposition": { 160 | "version": "0.5.4", 161 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", 162 | "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", 163 | "dependencies": { 164 | "safe-buffer": "5.2.1" 165 | }, 166 | "engines": { 167 | "node": ">= 0.6" 168 | } 169 | }, 170 | "node_modules/content-type": { 171 | "version": "1.0.5", 172 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", 173 | "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", 174 | "engines": { 175 | "node": ">= 0.6" 176 | } 177 | }, 178 | "node_modules/cookie": { 179 | "version": "0.6.0", 180 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", 181 | "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", 182 | "engines": { 183 | "node": ">= 0.6" 184 | } 185 | }, 186 | "node_modules/cookie-signature": { 187 | "version": "1.0.6", 188 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", 189 | "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" 190 | }, 191 | "node_modules/cors": { 192 | "version": "2.8.5", 193 | "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", 194 | "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", 195 | "dependencies": { 196 | "object-assign": "^4", 197 | "vary": "^1" 198 | }, 199 | "engines": { 200 | "node": ">= 0.10" 201 | } 202 | }, 203 | "node_modules/debug": { 204 | "version": "2.6.9", 205 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 206 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 207 | "dependencies": { 208 | "ms": "2.0.0" 209 | } 210 | }, 211 | "node_modules/define-data-property": { 212 | "version": "1.1.4", 213 | "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", 214 | "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", 215 | "dependencies": { 216 | "es-define-property": "^1.0.0", 217 | "es-errors": "^1.3.0", 218 | "gopd": "^1.0.1" 219 | }, 220 | "engines": { 221 | "node": ">= 0.4" 222 | }, 223 | "funding": { 224 | "url": "https://github.com/sponsors/ljharb" 225 | } 226 | }, 227 | "node_modules/depd": { 228 | "version": "2.0.0", 229 | "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", 230 | "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", 231 | "engines": { 232 | "node": ">= 0.8" 233 | } 234 | }, 235 | "node_modules/destroy": { 236 | "version": "1.2.0", 237 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", 238 | "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", 239 | "engines": { 240 | "node": ">= 0.8", 241 | "npm": "1.2.8000 || >= 1.4.16" 242 | } 243 | }, 244 | "node_modules/ee-first": { 245 | "version": "1.1.1", 246 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", 247 | "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" 248 | }, 249 | "node_modules/encodeurl": { 250 | "version": "1.0.2", 251 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", 252 | "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", 253 | "engines": { 254 | "node": ">= 0.8" 255 | } 256 | }, 257 | "node_modules/es-define-property": { 258 | "version": "1.0.0", 259 | "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", 260 | "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", 261 | "dependencies": { 262 | "get-intrinsic": "^1.2.4" 263 | }, 264 | "engines": { 265 | "node": ">= 0.4" 266 | } 267 | }, 268 | "node_modules/es-errors": { 269 | "version": "1.3.0", 270 | "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", 271 | "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", 272 | "engines": { 273 | "node": ">= 0.4" 274 | } 275 | }, 276 | "node_modules/escape-html": { 277 | "version": "1.0.3", 278 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", 279 | "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" 280 | }, 281 | "node_modules/etag": { 282 | "version": "1.8.1", 283 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", 284 | "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", 285 | "engines": { 286 | "node": ">= 0.6" 287 | } 288 | }, 289 | "node_modules/express": { 290 | "version": "4.19.2", 291 | "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", 292 | "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", 293 | "dependencies": { 294 | "accepts": "~1.3.8", 295 | "array-flatten": "1.1.1", 296 | "body-parser": "1.20.2", 297 | "content-disposition": "0.5.4", 298 | "content-type": "~1.0.4", 299 | "cookie": "0.6.0", 300 | "cookie-signature": "1.0.6", 301 | "debug": "2.6.9", 302 | "depd": "2.0.0", 303 | "encodeurl": "~1.0.2", 304 | "escape-html": "~1.0.3", 305 | "etag": "~1.8.1", 306 | "finalhandler": "1.2.0", 307 | "fresh": "0.5.2", 308 | "http-errors": "2.0.0", 309 | "merge-descriptors": "1.0.1", 310 | "methods": "~1.1.2", 311 | "on-finished": "2.4.1", 312 | "parseurl": "~1.3.3", 313 | "path-to-regexp": "0.1.7", 314 | "proxy-addr": "~2.0.7", 315 | "qs": "6.11.0", 316 | "range-parser": "~1.2.1", 317 | "safe-buffer": "5.2.1", 318 | "send": "0.18.0", 319 | "serve-static": "1.15.0", 320 | "setprototypeof": "1.2.0", 321 | "statuses": "2.0.1", 322 | "type-is": "~1.6.18", 323 | "utils-merge": "1.0.1", 324 | "vary": "~1.1.2" 325 | }, 326 | "engines": { 327 | "node": ">= 0.10.0" 328 | } 329 | }, 330 | "node_modules/express-session": { 331 | "version": "1.18.0", 332 | "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.18.0.tgz", 333 | "integrity": "sha512-m93QLWr0ju+rOwApSsyso838LQwgfs44QtOP/WBiwtAgPIo/SAh1a5c6nn2BR6mFNZehTpqKDESzP+fRHVbxwQ==", 334 | "dependencies": { 335 | "cookie": "0.6.0", 336 | "cookie-signature": "1.0.7", 337 | "debug": "2.6.9", 338 | "depd": "~2.0.0", 339 | "on-headers": "~1.0.2", 340 | "parseurl": "~1.3.3", 341 | "safe-buffer": "5.2.1", 342 | "uid-safe": "~2.1.5" 343 | }, 344 | "engines": { 345 | "node": ">= 0.8.0" 346 | } 347 | }, 348 | "node_modules/express-session/node_modules/cookie-signature": { 349 | "version": "1.0.7", 350 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", 351 | "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==" 352 | }, 353 | "node_modules/finalhandler": { 354 | "version": "1.2.0", 355 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", 356 | "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", 357 | "dependencies": { 358 | "debug": "2.6.9", 359 | "encodeurl": "~1.0.2", 360 | "escape-html": "~1.0.3", 361 | "on-finished": "2.4.1", 362 | "parseurl": "~1.3.3", 363 | "statuses": "2.0.1", 364 | "unpipe": "~1.0.0" 365 | }, 366 | "engines": { 367 | "node": ">= 0.8" 368 | } 369 | }, 370 | "node_modules/forwarded": { 371 | "version": "0.2.0", 372 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", 373 | "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", 374 | "engines": { 375 | "node": ">= 0.6" 376 | } 377 | }, 378 | "node_modules/fresh": { 379 | "version": "0.5.2", 380 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", 381 | "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", 382 | "engines": { 383 | "node": ">= 0.6" 384 | } 385 | }, 386 | "node_modules/function-bind": { 387 | "version": "1.1.2", 388 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", 389 | "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", 390 | "funding": { 391 | "url": "https://github.com/sponsors/ljharb" 392 | } 393 | }, 394 | "node_modules/get-intrinsic": { 395 | "version": "1.2.4", 396 | "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", 397 | "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", 398 | "dependencies": { 399 | "es-errors": "^1.3.0", 400 | "function-bind": "^1.1.2", 401 | "has-proto": "^1.0.1", 402 | "has-symbols": "^1.0.3", 403 | "hasown": "^2.0.0" 404 | }, 405 | "engines": { 406 | "node": ">= 0.4" 407 | }, 408 | "funding": { 409 | "url": "https://github.com/sponsors/ljharb" 410 | } 411 | }, 412 | "node_modules/gopd": { 413 | "version": "1.0.1", 414 | "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", 415 | "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", 416 | "dependencies": { 417 | "get-intrinsic": "^1.1.3" 418 | }, 419 | "funding": { 420 | "url": "https://github.com/sponsors/ljharb" 421 | } 422 | }, 423 | "node_modules/has-property-descriptors": { 424 | "version": "1.0.2", 425 | "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", 426 | "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", 427 | "dependencies": { 428 | "es-define-property": "^1.0.0" 429 | }, 430 | "funding": { 431 | "url": "https://github.com/sponsors/ljharb" 432 | } 433 | }, 434 | "node_modules/has-proto": { 435 | "version": "1.0.3", 436 | "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", 437 | "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", 438 | "engines": { 439 | "node": ">= 0.4" 440 | }, 441 | "funding": { 442 | "url": "https://github.com/sponsors/ljharb" 443 | } 444 | }, 445 | "node_modules/has-symbols": { 446 | "version": "1.0.3", 447 | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", 448 | "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", 449 | "engines": { 450 | "node": ">= 0.4" 451 | }, 452 | "funding": { 453 | "url": "https://github.com/sponsors/ljharb" 454 | } 455 | }, 456 | "node_modules/hasown": { 457 | "version": "2.0.2", 458 | "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", 459 | "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", 460 | "dependencies": { 461 | "function-bind": "^1.1.2" 462 | }, 463 | "engines": { 464 | "node": ">= 0.4" 465 | } 466 | }, 467 | "node_modules/http-errors": { 468 | "version": "2.0.0", 469 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", 470 | "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", 471 | "dependencies": { 472 | "depd": "2.0.0", 473 | "inherits": "2.0.4", 474 | "setprototypeof": "1.2.0", 475 | "statuses": "2.0.1", 476 | "toidentifier": "1.0.1" 477 | }, 478 | "engines": { 479 | "node": ">= 0.8" 480 | } 481 | }, 482 | "node_modules/iconv-lite": { 483 | "version": "0.4.24", 484 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", 485 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", 486 | "dependencies": { 487 | "safer-buffer": ">= 2.1.2 < 3" 488 | }, 489 | "engines": { 490 | "node": ">=0.10.0" 491 | } 492 | }, 493 | "node_modules/inherits": { 494 | "version": "2.0.4", 495 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 496 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 497 | }, 498 | "node_modules/ipaddr.js": { 499 | "version": "1.9.1", 500 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", 501 | "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", 502 | "engines": { 503 | "node": ">= 0.10" 504 | } 505 | }, 506 | "node_modules/isows": { 507 | "version": "1.0.4", 508 | "resolved": "https://registry.npmjs.org/isows/-/isows-1.0.4.tgz", 509 | "integrity": "sha512-hEzjY+x9u9hPmBom9IIAqdJCwNLax+xrPb51vEPpERoFlIxgmZcHzsT5jKG06nvInKOBGvReAVz80Umed5CczQ==", 510 | "funding": [ 511 | { 512 | "type": "github", 513 | "url": "https://github.com/sponsors/wagmi-dev" 514 | } 515 | ], 516 | "peerDependencies": { 517 | "ws": "*" 518 | } 519 | }, 520 | "node_modules/media-typer": { 521 | "version": "0.3.0", 522 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", 523 | "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", 524 | "engines": { 525 | "node": ">= 0.6" 526 | } 527 | }, 528 | "node_modules/merge-descriptors": { 529 | "version": "1.0.1", 530 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", 531 | "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" 532 | }, 533 | "node_modules/methods": { 534 | "version": "1.1.2", 535 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", 536 | "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", 537 | "engines": { 538 | "node": ">= 0.6" 539 | } 540 | }, 541 | "node_modules/mime": { 542 | "version": "1.6.0", 543 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", 544 | "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", 545 | "bin": { 546 | "mime": "cli.js" 547 | }, 548 | "engines": { 549 | "node": ">=4" 550 | } 551 | }, 552 | "node_modules/mime-db": { 553 | "version": "1.52.0", 554 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", 555 | "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", 556 | "engines": { 557 | "node": ">= 0.6" 558 | } 559 | }, 560 | "node_modules/mime-types": { 561 | "version": "2.1.35", 562 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", 563 | "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", 564 | "dependencies": { 565 | "mime-db": "1.52.0" 566 | }, 567 | "engines": { 568 | "node": ">= 0.6" 569 | } 570 | }, 571 | "node_modules/ms": { 572 | "version": "2.0.0", 573 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 574 | "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" 575 | }, 576 | "node_modules/negotiator": { 577 | "version": "0.6.3", 578 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", 579 | "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", 580 | "engines": { 581 | "node": ">= 0.6" 582 | } 583 | }, 584 | "node_modules/object-assign": { 585 | "version": "4.1.1", 586 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 587 | "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", 588 | "engines": { 589 | "node": ">=0.10.0" 590 | } 591 | }, 592 | "node_modules/object-inspect": { 593 | "version": "1.13.2", 594 | "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", 595 | "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", 596 | "engines": { 597 | "node": ">= 0.4" 598 | }, 599 | "funding": { 600 | "url": "https://github.com/sponsors/ljharb" 601 | } 602 | }, 603 | "node_modules/on-finished": { 604 | "version": "2.4.1", 605 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", 606 | "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", 607 | "dependencies": { 608 | "ee-first": "1.1.1" 609 | }, 610 | "engines": { 611 | "node": ">= 0.8" 612 | } 613 | }, 614 | "node_modules/on-headers": { 615 | "version": "1.0.2", 616 | "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", 617 | "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", 618 | "engines": { 619 | "node": ">= 0.8" 620 | } 621 | }, 622 | "node_modules/parseurl": { 623 | "version": "1.3.3", 624 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", 625 | "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", 626 | "engines": { 627 | "node": ">= 0.8" 628 | } 629 | }, 630 | "node_modules/path-to-regexp": { 631 | "version": "0.1.7", 632 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", 633 | "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" 634 | }, 635 | "node_modules/proxy-addr": { 636 | "version": "2.0.7", 637 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", 638 | "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", 639 | "dependencies": { 640 | "forwarded": "0.2.0", 641 | "ipaddr.js": "1.9.1" 642 | }, 643 | "engines": { 644 | "node": ">= 0.10" 645 | } 646 | }, 647 | "node_modules/qs": { 648 | "version": "6.11.0", 649 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", 650 | "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", 651 | "dependencies": { 652 | "side-channel": "^1.0.4" 653 | }, 654 | "engines": { 655 | "node": ">=0.6" 656 | }, 657 | "funding": { 658 | "url": "https://github.com/sponsors/ljharb" 659 | } 660 | }, 661 | "node_modules/random-bytes": { 662 | "version": "1.0.0", 663 | "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", 664 | "integrity": "sha512-iv7LhNVO047HzYR3InF6pUcUsPQiHTM1Qal51DcGSuZFBil1aBBWG5eHPNek7bvILMaYJ/8RU1e8w1AMdHmLQQ==", 665 | "engines": { 666 | "node": ">= 0.8" 667 | } 668 | }, 669 | "node_modules/range-parser": { 670 | "version": "1.2.1", 671 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", 672 | "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", 673 | "engines": { 674 | "node": ">= 0.6" 675 | } 676 | }, 677 | "node_modules/raw-body": { 678 | "version": "2.5.2", 679 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", 680 | "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", 681 | "dependencies": { 682 | "bytes": "3.1.2", 683 | "http-errors": "2.0.0", 684 | "iconv-lite": "0.4.24", 685 | "unpipe": "1.0.0" 686 | }, 687 | "engines": { 688 | "node": ">= 0.8" 689 | } 690 | }, 691 | "node_modules/safe-buffer": { 692 | "version": "5.2.1", 693 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 694 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", 695 | "funding": [ 696 | { 697 | "type": "github", 698 | "url": "https://github.com/sponsors/feross" 699 | }, 700 | { 701 | "type": "patreon", 702 | "url": "https://www.patreon.com/feross" 703 | }, 704 | { 705 | "type": "consulting", 706 | "url": "https://feross.org/support" 707 | } 708 | ] 709 | }, 710 | "node_modules/safer-buffer": { 711 | "version": "2.1.2", 712 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 713 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 714 | }, 715 | "node_modules/send": { 716 | "version": "0.18.0", 717 | "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", 718 | "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", 719 | "dependencies": { 720 | "debug": "2.6.9", 721 | "depd": "2.0.0", 722 | "destroy": "1.2.0", 723 | "encodeurl": "~1.0.2", 724 | "escape-html": "~1.0.3", 725 | "etag": "~1.8.1", 726 | "fresh": "0.5.2", 727 | "http-errors": "2.0.0", 728 | "mime": "1.6.0", 729 | "ms": "2.1.3", 730 | "on-finished": "2.4.1", 731 | "range-parser": "~1.2.1", 732 | "statuses": "2.0.1" 733 | }, 734 | "engines": { 735 | "node": ">= 0.8.0" 736 | } 737 | }, 738 | "node_modules/send/node_modules/ms": { 739 | "version": "2.1.3", 740 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 741 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" 742 | }, 743 | "node_modules/serve-static": { 744 | "version": "1.15.0", 745 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", 746 | "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", 747 | "dependencies": { 748 | "encodeurl": "~1.0.2", 749 | "escape-html": "~1.0.3", 750 | "parseurl": "~1.3.3", 751 | "send": "0.18.0" 752 | }, 753 | "engines": { 754 | "node": ">= 0.8.0" 755 | } 756 | }, 757 | "node_modules/set-function-length": { 758 | "version": "1.2.2", 759 | "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", 760 | "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", 761 | "dependencies": { 762 | "define-data-property": "^1.1.4", 763 | "es-errors": "^1.3.0", 764 | "function-bind": "^1.1.2", 765 | "get-intrinsic": "^1.2.4", 766 | "gopd": "^1.0.1", 767 | "has-property-descriptors": "^1.0.2" 768 | }, 769 | "engines": { 770 | "node": ">= 0.4" 771 | } 772 | }, 773 | "node_modules/setprototypeof": { 774 | "version": "1.2.0", 775 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", 776 | "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" 777 | }, 778 | "node_modules/side-channel": { 779 | "version": "1.0.6", 780 | "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", 781 | "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", 782 | "dependencies": { 783 | "call-bind": "^1.0.7", 784 | "es-errors": "^1.3.0", 785 | "get-intrinsic": "^1.2.4", 786 | "object-inspect": "^1.13.1" 787 | }, 788 | "engines": { 789 | "node": ">= 0.4" 790 | }, 791 | "funding": { 792 | "url": "https://github.com/sponsors/ljharb" 793 | } 794 | }, 795 | "node_modules/statuses": { 796 | "version": "2.0.1", 797 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", 798 | "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", 799 | "engines": { 800 | "node": ">= 0.8" 801 | } 802 | }, 803 | "node_modules/toidentifier": { 804 | "version": "1.0.1", 805 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", 806 | "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", 807 | "engines": { 808 | "node": ">=0.6" 809 | } 810 | }, 811 | "node_modules/type-is": { 812 | "version": "1.6.18", 813 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", 814 | "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", 815 | "dependencies": { 816 | "media-typer": "0.3.0", 817 | "mime-types": "~2.1.24" 818 | }, 819 | "engines": { 820 | "node": ">= 0.6" 821 | } 822 | }, 823 | "node_modules/uid-safe": { 824 | "version": "2.1.5", 825 | "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", 826 | "integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==", 827 | "dependencies": { 828 | "random-bytes": "~1.0.0" 829 | }, 830 | "engines": { 831 | "node": ">= 0.8" 832 | } 833 | }, 834 | "node_modules/unpipe": { 835 | "version": "1.0.0", 836 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 837 | "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", 838 | "engines": { 839 | "node": ">= 0.8" 840 | } 841 | }, 842 | "node_modules/utils-merge": { 843 | "version": "1.0.1", 844 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", 845 | "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", 846 | "engines": { 847 | "node": ">= 0.4.0" 848 | } 849 | }, 850 | "node_modules/vary": { 851 | "version": "1.1.2", 852 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", 853 | "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", 854 | "engines": { 855 | "node": ">= 0.8" 856 | } 857 | }, 858 | "node_modules/viem": { 859 | "version": "2.16.3", 860 | "resolved": "https://registry.npmjs.org/viem/-/viem-2.16.3.tgz", 861 | "integrity": "sha512-6ExbIpi77C1HzGSjx6W+fn39Cz1ULlVV74XHxi1kYpOAqY7/iAoynK5X2aQp2LvxstTqS1GIKEdpCAZ8dKi2Ag==", 862 | "funding": [ 863 | { 864 | "type": "github", 865 | "url": "https://github.com/sponsors/wevm" 866 | } 867 | ], 868 | "dependencies": { 869 | "@adraffy/ens-normalize": "1.10.0", 870 | "@noble/curves": "1.2.0", 871 | "@noble/hashes": "1.3.2", 872 | "@scure/bip32": "1.3.2", 873 | "@scure/bip39": "1.2.1", 874 | "abitype": "1.0.4", 875 | "isows": "1.0.4", 876 | "ws": "8.17.1" 877 | }, 878 | "peerDependencies": { 879 | "typescript": ">=5.0.4" 880 | }, 881 | "peerDependenciesMeta": { 882 | "typescript": { 883 | "optional": true 884 | } 885 | } 886 | }, 887 | "node_modules/viem/node_modules/@adraffy/ens-normalize": { 888 | "version": "1.10.0", 889 | "resolved": "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.10.0.tgz", 890 | "integrity": "sha512-nA9XHtlAkYfJxY7bce8DcN7eKxWWCWkU+1GR9d+U6MbNpfwQp8TI7vqOsBsMcHoT4mBu2kypKoSKnghEzOOq5Q==" 891 | }, 892 | "node_modules/ws": { 893 | "version": "8.17.1", 894 | "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", 895 | "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", 896 | "engines": { 897 | "node": ">=10.0.0" 898 | }, 899 | "peerDependencies": { 900 | "bufferutil": "^4.0.1", 901 | "utf-8-validate": ">=5.0.2" 902 | }, 903 | "peerDependenciesMeta": { 904 | "bufferutil": { 905 | "optional": true 906 | }, 907 | "utf-8-validate": { 908 | "optional": true 909 | } 910 | } 911 | } 912 | } 913 | } 914 | -------------------------------------------------------------------------------- /backend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "backend", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "type": "module", 6 | "license": "MIT", 7 | "scripts": { 8 | "start": "node src/index.js" 9 | }, 10 | "dependencies": { 11 | "cors": "^2.8.5", 12 | "express": "^4.18.2", 13 | "express-session": "^1.18.0", 14 | "viem": "^2.16.3" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /backend/src/index.js: -------------------------------------------------------------------------------- 1 | import cors from "cors"; 2 | import express from "express"; 3 | import Session from "express-session"; 4 | import { createPublicClient, http } from "viem"; 5 | import { mainnet } from "viem/chains"; 6 | import { generateSiweNonce, parseSiweMessage } from "viem/siwe"; 7 | 8 | const app = express(); 9 | app.use(express.json()); 10 | app.use(cors({ credentials: true })); 11 | app.use( 12 | Session({ 13 | name: "siwe-quickstart", 14 | secret: "siwe-quickstart-secret", 15 | resave: true, 16 | saveUninitialized: true, 17 | cookie: { secure: false, sameSite: true }, 18 | }) 19 | ); 20 | 21 | app.get("/nonce", function (req, res) { 22 | res.setHeader("Content-Type", "text/plain"); 23 | 24 | // Generate nonce 25 | const nonce = generateSiweNonce(); 26 | console.log("[BACKEND] Generated nonce:", nonce); 27 | 28 | // Store nonce in session 29 | req.session.nonce = nonce; 30 | 31 | // Send back response 32 | res.status(200).send(nonce); 33 | }); 34 | 35 | app.post("/verify", async function (req, res) { 36 | const { message, signature } = req.body; 37 | 38 | if (!message) { 39 | res.status(422).json({ message: "Expected SIWE message in body" }); 40 | return; 41 | } 42 | 43 | // Create publicClient 44 | const publicClient = createPublicClient({ 45 | chain: mainnet, 46 | transport: http(), 47 | }); 48 | 49 | // Parse message 50 | const messageFields = parseSiweMessage(message); 51 | 52 | // Verify whether the signed message coming from the frontend is valid for the given address 53 | const valid = await publicClient.verifySiweMessage({ 54 | message, 55 | signature, 56 | }); 57 | 58 | console.log(`[BACKEND] SIWE Message valid: ${valid}`); 59 | 60 | if (valid) { 61 | // If the message is valid, store the message object in the session 62 | req.session.siwe = messageFields; 63 | req.session.cookie.expires = new Date(messageFields.expirationTime); 64 | } else { 65 | // If the message is not valid, clear the session 66 | req.session.siwe = null; 67 | req.session.nonce = null; 68 | req.session.save(() => 69 | res.status(422).json({ message: "Invalid signature" }) 70 | ); 71 | return; 72 | } 73 | 74 | // Send back response 75 | res.status(200).send(valid); 76 | }); 77 | 78 | app.get("/get_session", function (req, res) { 79 | if (!req.session.siwe) { 80 | res.status(401).json({ message: "You have to first sign_in" }); 81 | return; 82 | } 83 | 84 | console.log("[BACKEND] User is authenticated!"); 85 | 86 | res.setHeader("Content-Type", "text/plain"); 87 | 88 | // Send back session data 89 | res.status(200).send({ 90 | address: req.session.siwe.address, 91 | chainId: req.session.siwe.chainId, 92 | }); 93 | }); 94 | 95 | app.get("/sign_out", function (req, res) { 96 | // Clear session 97 | req.session.siwe = null; 98 | req.session.nonce = null; 99 | req.session.save(() => res.status(200).send(true)); 100 | }); 101 | 102 | app.listen(3000); 103 | -------------------------------------------------------------------------------- /demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/callstack/web3-react-native-siwe/414155201c0af811a18ca38e907dc4a442ae1506/demo.gif -------------------------------------------------------------------------------- /mobile/.env.example: -------------------------------------------------------------------------------- 1 | EXPO_PUBLIC_WALLETCONNECT_CLOUD_PROJECT_ID="YOUR WALLET CONNECT CLOUD ID HERE" 2 | EXPO_PUBLIC_BACKEND_URL=http://your.ip.address.here:3000 3 | -------------------------------------------------------------------------------- /mobile/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .expo/ 3 | dist/ 4 | npm-debug.* 5 | *.jks 6 | *.p8 7 | *.p12 8 | *.key 9 | *.mobileprovision 10 | *.orig.* 11 | web-build/ 12 | 13 | # macOS 14 | .DS_Store 15 | 16 | # env 17 | .env 18 | 19 | # @generated expo-cli sync-2b81b286409207a5da26e14c78851eb30d8ccbdb 20 | # The following patterns were generated by expo-cli 21 | 22 | expo-env.d.ts 23 | # @end expo-cli -------------------------------------------------------------------------------- /mobile/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "expo": { 3 | "name": "web3-react-native-siwe", 4 | "slug": "web3-react-native-siwe", 5 | "version": "1.0.0", 6 | "orientation": "portrait", 7 | "icon": "./assets/images/icon.png", 8 | "scheme": "myapp", 9 | "userInterfaceStyle": "automatic", 10 | "splash": { 11 | "image": "./assets/images/splash.png", 12 | "resizeMode": "contain", 13 | "backgroundColor": "#ffffff" 14 | }, 15 | "ios": { 16 | "supportsTablet": true, 17 | "infoPlist": { 18 | "LSApplicationQueriesSchemes": [ 19 | "metamask", 20 | "trust", 21 | "safe", 22 | "rainbow", 23 | "uniswap" 24 | ] 25 | } 26 | }, 27 | "android": { 28 | "adaptiveIcon": { 29 | "foregroundImage": "./assets/images/adaptive-icon.png", 30 | "backgroundColor": "#ffffff" 31 | } 32 | }, 33 | "web": { 34 | "bundler": "metro", 35 | "output": "static", 36 | "favicon": "./assets/images/favicon.png" 37 | }, 38 | "plugins": [ 39 | "expo-router", 40 | "expo-secure-store" 41 | ], 42 | "experiments": { 43 | "typedRoutes": true 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /mobile/app/(app)/(auth)/_layout.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { useSession } from "@/utils/SessionContext"; 3 | import { Redirect, Slot } from "expo-router"; 4 | import { Text } from "react-native"; 5 | 6 | export default function AuthLayout() { 7 | const { session, isLoading } = useSession(); 8 | 9 | if (isLoading) { 10 | return Loading...; 11 | } 12 | 13 | if (!session) { 14 | return ; 15 | } 16 | 17 | return ; 18 | } 19 | -------------------------------------------------------------------------------- /mobile/app/(app)/(auth)/index.tsx: -------------------------------------------------------------------------------- 1 | import { useSession } from "@/utils/SessionContext"; 2 | import { StyleSheet, View, Text, Pressable } from "react-native"; 3 | 4 | export default function AuthenticatedScreen() { 5 | const { signOut } = useSession(); 6 | 7 | return ( 8 | 9 | Authenticated 10 | 11 | [{ opacity: pressed ? 0.8 : 1 }, styles.button]} 14 | > 15 | Sign out 16 | 17 | 18 | ); 19 | } 20 | 21 | const styles = StyleSheet.create({ 22 | container: { 23 | flex: 1, 24 | justifyContent: "center", 25 | alignItems: "center", 26 | backgroundColor: "#fff", 27 | gap: 10, 28 | }, 29 | button: { 30 | padding: 10, 31 | height: 50, 32 | minWidth: 200, 33 | justifyContent: "center", 34 | alignItems: "center", 35 | borderRadius: 20, 36 | borderCurve: "continuous", 37 | backgroundColor: "red", 38 | }, 39 | buttonText: { 40 | color: "#fff", 41 | fontWeight: "bold", 42 | }, 43 | }); 44 | -------------------------------------------------------------------------------- /mobile/app/(app)/_layout.tsx: -------------------------------------------------------------------------------- 1 | import React, { useMemo } from "react"; 2 | import { Stack } from "expo-router"; 3 | import { Web3Modal, createWeb3Modal } from "@web3modal/wagmi-react-native"; 4 | import { mainnet } from "viem/chains"; 5 | import { projectId, useSIWEConfig, wagmiConfig } from "@/utils/walletConnect"; 6 | import { WagmiProvider } from "wagmi"; 7 | import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; 8 | import Header from "@/components/Header"; 9 | 10 | // Setup the QueryClient 11 | const queryClient = new QueryClient(); 12 | 13 | // In the App Layout, we initialize Web3Modal and Wagmi 14 | export default function AppLayout() { 15 | const { siweConfig } = useSIWEConfig(); 16 | 17 | // Init Web3Modal instance 18 | // We create it inside the component to be able the siweConfig from the hook 19 | createWeb3Modal({ 20 | projectId, 21 | wagmiConfig, 22 | defaultChain: mainnet, 23 | siweConfig, 24 | }); 25 | 26 | return ( 27 | 28 | 29 |
, 32 | presentation: "modal", 33 | }} 34 | /> 35 | 36 | 37 | 38 | ); 39 | } 40 | -------------------------------------------------------------------------------- /mobile/app/(app)/sign-in.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { StyleSheet, View } from "react-native"; 3 | import SIWEViem from "@/components/SIWEViem"; 4 | import SIWEAppKit from "@/components/SIWEAppKit"; 5 | import SIWEThirdweb from "@/components/SIWEThirdweb"; 6 | 7 | export default function SignIn() { 8 | return ( 9 | 10 | 11 | 12 | {/* TODO: Implement Thirdweb SDK */} 13 | {/* */} 14 | 15 | ); 16 | } 17 | 18 | const styles = StyleSheet.create({ 19 | container: { 20 | flex: 1, 21 | justifyContent: "center", 22 | backgroundColor: "#fff", 23 | gap: 40, 24 | padding: 20, 25 | }, 26 | }); 27 | -------------------------------------------------------------------------------- /mobile/app/+html.tsx: -------------------------------------------------------------------------------- 1 | import { ScrollViewStyleReset } from 'expo-router/html'; 2 | import { type PropsWithChildren } from 'react'; 3 | 4 | /** 5 | * This file is web-only and used to configure the root HTML for every web page during static rendering. 6 | * The contents of this function only run in Node.js environments and do not have access to the DOM or browser APIs. 7 | */ 8 | export default function Root({ children }: PropsWithChildren) { 9 | return ( 10 | 11 | 12 | 13 | 14 | 15 | 16 | {/* 17 | Disable body scrolling on web. This makes ScrollView components work closer to how they do on native. 18 | However, body scrolling is often nice to have for mobile web. If you want to enable it, remove this line. 19 | */} 20 | 21 | 22 | {/* Using raw CSS styles as an escape-hatch to ensure the background color never flickers in dark-mode. */} 23 |