├── .gitignore ├── .node-version ├── LICENSE ├── README.md ├── netlify.toml ├── package.json ├── public ├── favicon.ico ├── index.html ├── manifest.json └── robots.txt ├── src ├── App.test.js ├── App.tsx ├── Onboard.ts ├── abi │ └── AuthereumAccount.json ├── config.ts ├── index.css ├── index.tsx ├── react-app-env.d.ts ├── serviceWorker.js └── setupTests.js └── tsconfig.json /.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 17 | .env.local 18 | .env.development.local 19 | .env.test.local 20 | .env.production.local 21 | 22 | npm-debug.log* 23 | yarn-debug.log* 24 | yarn-error.log* 25 | 26 | package-lock.json 27 | yarn.lock 28 | -------------------------------------------------------------------------------- /.node-version: -------------------------------------------------------------------------------- 1 | 11.15 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT license 2 | 3 | Copyright (C) 2019 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | of the Software, and to permit persons to whom the Software is furnished to do 10 | so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Authereum Direct 2 | 3 | > Interact with your Authereum contract-based account directly. 4 | 5 | ## Getting started 6 | 7 | ```bash 8 | git clone git@github.com:authereum/direct.git 9 | cd direct 10 | npm install 11 | npm run start 12 | ``` 13 | 14 | Demo 15 | 16 | - [https://direct.authereum.com/](https://direct.authereum.com/) 17 | - [https://authereum-direct.netlify.app/](https://authereum-direct.netlify.app/) 18 | - [https://ipfs.infura.io/ipfs/QmVpdpRFwuzvC9PBcQfHgYtz6SQANsJ1qA1MuJcwsZMQoR](https://ipfs.infura.io/ipfs/QmVpdpRFwuzvC9PBcQfHgYtz6SQANsJ1qA1MuJcwsZMQoR) 19 | 20 | ## License 21 | 22 | [MIT](LICENSE) 23 | -------------------------------------------------------------------------------- /netlify.toml: -------------------------------------------------------------------------------- 1 | [[redirects]] 2 | from = "/*" 3 | to = "/index.html" 4 | status = 200 5 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "app", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@authereum/abi": "0.0.2-beta.30", 7 | "@authereum/utils": "0.0.1-beta.234", 8 | "@material-ui/core": "4.11.0", 9 | "@material-ui/icons": "4.9.1", 10 | "@material-ui/lab": "4.0.0-alpha.56", 11 | "@testing-library/jest-dom": "4.2.4", 12 | "@testing-library/react": "9.5.0", 13 | "@testing-library/user-event": "7.2.1", 14 | "@types/jest": "26.0.14", 15 | "@types/node": "14.11.8", 16 | "@types/react": "16.9.52", 17 | "@types/react-dom": "16.9.8", 18 | "@walletconnect/browser": "1.0.0", 19 | "@walletconnect/client": "1.2.2", 20 | "bnc-onboard": "1.14.0", 21 | "ethers": "5.0.17", 22 | "events": "3.2.0", 23 | "react": "16.13.1", 24 | "react-dom": "16.13.1", 25 | "react-scripts": "3.4.3", 26 | "typescript": "4.0.3" 27 | }, 28 | "scripts": { 29 | "dev": "BROWSER=none npm run start", 30 | "start": "react-scripts start", 31 | "build": "react-scripts build", 32 | "test": "react-scripts test", 33 | "eject": "react-scripts eject", 34 | "lint": "prettier-standard --format" 35 | }, 36 | "eslintConfig": { 37 | "extends": "react-app" 38 | }, 39 | "browserslist": { 40 | "production": [ 41 | ">0.2%", 42 | "not dead", 43 | "not op_mini all" 44 | ], 45 | "development": [ 46 | "last 1 chrome version", 47 | "last 1 firefox version", 48 | "last 1 safari version" 49 | ] 50 | }, 51 | "devDependencies": { 52 | "prettier-standard": "16.4.1" 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/authereum/direct/f17e6548840f0d979040f039cf4453edd8359c38/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 14 | 15 | 24 | Authereum Direct 25 | 26 | 27 | 28 |
29 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /src/App.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { render } from '@testing-library/react' 3 | import App from './App' 4 | 5 | test('renders learn react link', () => { 6 | const { getByText } = render() 7 | const linkElement = getByText(/learn react/i) 8 | expect(linkElement).toBeInTheDocument() 9 | }) 10 | -------------------------------------------------------------------------------- /src/App.tsx: -------------------------------------------------------------------------------- 1 | import { EventEmitter } from 'events' 2 | import React, { useState, useEffect } from 'react' 3 | import { ethers } from 'ethers' 4 | import validPrivateKey from '@authereum/utils/core/validPrivateKey' 5 | import WalletConnect from '@walletconnect/client' 6 | import encodeParameters from '@authereum/utils/core/encodeParameters' 7 | import getNetworkId from '@authereum/utils/core/getNetworkId' 8 | import hexToUtf8 from '@authereum/utils/core/hexToUtf8' 9 | import Onboard from './Onboard' 10 | 11 | import Button from '@material-ui/core/Button' 12 | import TextField from '@material-ui/core/TextField' 13 | import Grid from '@material-ui/core/Grid' 14 | import Card from '@material-ui/core/Card' 15 | import CardContent from '@material-ui/core/CardContent' 16 | 17 | function App () { 18 | const [ee] = React.useState(() => new EventEmitter()) 19 | const [privateKey, setPrivateKey] = useState(() => { 20 | return localStorage.getItem('privateKey') || '' 21 | }) 22 | const [contractAddress, setContractAddress] = useState(() => { 23 | return localStorage.getItem('contractAddress') || '' 24 | }) 25 | const [wallet, setWallet] = useState() 26 | const [onboard] = useState(() => { 27 | const network = localStorage.getItem('networkName') || 'mainnet' 28 | const networkId = getNetworkId(network) 29 | const rpcUrl = `https://${network}.rpc.authereum.com` 30 | return new Onboard({ networkId, rpcUrl }) 31 | }) 32 | const [walletName, setWalletName] = useState( 33 | localStorage.getItem('walletName') 34 | ) 35 | const [info, setInfo] = useState({}) 36 | const [connectUri, setConnectUri] = useState('') 37 | const [walletConnectSession, setWalletConnectSession] = useState( 38 | localStorage.getItem('walletconnect') 39 | ? JSON.parse(localStorage.getItem('walletconnect') as string) 40 | : null 41 | ) 42 | const [walletConnector, setWalletConnector] = useState() 43 | const [networkName, setNetworkName] = useState(() => { 44 | return localStorage.getItem('networkName') || 'mainnet' 45 | }) 46 | const [, setRpcUrl] = useState(() => { 47 | const network = localStorage.getItem('networkName') || 'mainnet' 48 | return `https://${network}.rpc.authereum.com` 49 | }) 50 | const [provider, setProvider] = useState(() => { 51 | const network = localStorage.getItem('networkName') || 'mainnet' 52 | const rpcUrl = `https://${network}.rpc.authereum.com` 53 | return new ethers.providers.JsonRpcProvider(rpcUrl) 54 | }) 55 | const [callRequest, setCallRequest] = React.useState(null) 56 | const [callRequestEditable, setCallRequestEditable] = React.useState( 57 | false 58 | ) 59 | const [customTx, setCustomTx] = React.useState(() => { 60 | return localStorage.getItem('customTx') || '' 61 | }) 62 | const [customTxHash, setCustomTxHash] = React.useState('') 63 | const exampleTx = { 64 | to: '', 65 | value: '0x0', 66 | data: '0x0', 67 | gasLimit: '0x0', 68 | gasPrice: '0x0' 69 | } 70 | 71 | useEffect(() => { 72 | localStorage.setItem('networkName', networkName) 73 | const rpcUrl = `https://${networkName}.rpc.authereum.com` 74 | setRpcUrl(rpcUrl) 75 | const provider = new ethers.providers.JsonRpcProvider(rpcUrl) 76 | setProvider(provider) 77 | const networkId = getNetworkId(networkName) 78 | onboard.setConfig({ networkId }) 79 | }, [networkName, onboard]) 80 | 81 | useEffect(() => { 82 | if (!privateKey) { 83 | return 84 | } 85 | 86 | if (validPrivateKey(privateKey)) { 87 | const priv = privateKey.replace(/^(0x)?/, '0x') 88 | const wal = new ethers.Wallet(priv, provider) 89 | localStorage.setItem('privateKey', priv) 90 | setWallet(wal) 91 | localStorage.setItem('walletName', 'privateKey') 92 | } 93 | }, [privateKey, provider]) 94 | 95 | useEffect(() => { 96 | const setup = async () => { 97 | if (!walletName) return 98 | if (walletName === 'privateKey') return 99 | await onboard.walletCheck(walletName) 100 | const state = await onboard.getState() 101 | const { address } = state 102 | if (!address) { 103 | return 104 | } 105 | 106 | setInfo({ address }) 107 | const signer = onboard.getSigner() 108 | setWallet(signer) 109 | } 110 | 111 | setup() 112 | }, [onboard, walletName]) 113 | 114 | const handlePrivateKeyChange = (event: any) => { 115 | event.preventDefault() 116 | const value = event.target.value.trim() 117 | setPrivateKey(value) 118 | } 119 | 120 | const handleConnectUri = (event: any) => { 121 | event.preventDefault() 122 | 123 | const uri = event.target.value 124 | setConnectUri(uri) 125 | localStorage.setItem('uri', uri) 126 | } 127 | 128 | function getCachedSession () { 129 | const local = localStorage ? localStorage.getItem('walletconnect') : null 130 | 131 | let session = null 132 | if (local) { 133 | try { 134 | session = JSON.parse(local) 135 | } catch (error) { 136 | throw error 137 | } 138 | } 139 | return session 140 | } 141 | 142 | const handleConnect = (event: any) => { 143 | onboard.showWalletSector() 144 | } 145 | 146 | // walletconnect doesn't have a way to unsubscribe from event emitter, 147 | // so we use a custom event emitter as a workaround. 148 | useEffect(() => { 149 | const events = [ 150 | 'connect', 151 | 'disconnect', 152 | 'session_request', 153 | 'call_request', 154 | 'error' 155 | ] 156 | for (let name of events) { 157 | walletConnector?.on(name, (...args: any[]) => ee.emit(name, ...args)) 158 | } 159 | }, [walletConnector, ee]) 160 | 161 | const handleApprove = (event: any) => { 162 | event.preventDefault() 163 | 164 | approveCallRequest(callRequest) 165 | } 166 | 167 | const handleReject = (event: any) => { 168 | event.preventDefault() 169 | 170 | rejectCallRequest(callRequest) 171 | } 172 | 173 | const rejectCallRequest = (payload: any) => { 174 | if (payload.error) { 175 | walletConnector.rejectRequest(payload) 176 | } else { 177 | walletConnector.rejectRequest({ id: payload.id, error: 'Cancelled' }) 178 | } 179 | 180 | setCallRequest(null) 181 | } 182 | 183 | const signTx = async (tx: any) => { 184 | console.log('user tx', tx) 185 | 186 | tx = await ethers.utils.resolveProperties(tx) 187 | 188 | let version = 0 189 | try { 190 | const iface = new ethers.utils.Interface([ 191 | { 192 | constant: true, 193 | inputs: [], 194 | name: 'authereumVersion', 195 | outputs: [ 196 | { 197 | name: '', 198 | type: 'string' 199 | } 200 | ], 201 | payable: false, 202 | stateMutability: 'view', 203 | type: 'function' 204 | } 205 | ]) 206 | const data = iface.encodeFunctionData('authereumVersion', []) 207 | 208 | let txObj: any = { 209 | from: contractAddress, 210 | to: contractAddress, 211 | value: '0x0', 212 | data: data, 213 | chainId: getNetworkId(networkName) 214 | } 215 | 216 | const result = await provider.call(txObj) 217 | const decoded = iface.decodeFunctionResult('authereumVersion', result) 218 | const _version = Number(decoded[0]) 219 | if (!_version) { 220 | throw new Error('Authereum version not found') 221 | } 222 | version = _version 223 | } catch (err) { 224 | const iface = new ethers.utils.Interface([ 225 | { 226 | constant: true, 227 | inputs: [], 228 | name: 'version', 229 | outputs: [ 230 | { 231 | name: '', 232 | type: 'string' 233 | } 234 | ], 235 | payable: false, 236 | stateMutability: 'view', 237 | type: 'function' 238 | } 239 | ]) 240 | const data = iface.encodeFunctionData('version', []) 241 | 242 | let txObj: any = { 243 | from: contractAddress, 244 | to: contractAddress, 245 | value: '0x0', 246 | data: data, 247 | chainId: getNetworkId(networkName) 248 | } 249 | 250 | const result = await provider.call(txObj) 251 | const decoded = iface.decodeFunctionResult('version', result) 252 | const _version = Number(decoded[0]) 253 | if (!_version) { 254 | throw new Error('Authereum version not found') 255 | } 256 | version = _version 257 | } 258 | 259 | console.log('account version:', version) 260 | 261 | let data = '0x' 262 | const gasLimit = 300000 263 | if (version <= 2020021700) { 264 | const tx1 = encodeParameters( 265 | ['address', 'uint256', 'uint256', 'bytes'], 266 | [tx.to, tx.value || '0x', gasLimit, tx.data || '0x'] 267 | ) 268 | 269 | const txs = [tx1] 270 | console.log('user txs array', txs) 271 | 272 | const iface = new ethers.utils.Interface([ 273 | { 274 | constant: false, 275 | inputs: [ 276 | { 277 | name: '_transactions', 278 | type: 'bytes[]' 279 | } 280 | ], 281 | name: 'executeMultipleMetaTransactions', 282 | outputs: [ 283 | { 284 | name: '', 285 | type: 'bytes[]' 286 | } 287 | ], 288 | payable: false, 289 | stateMutability: 'nonpayable', 290 | type: 'function' 291 | } 292 | ]) 293 | data = iface.encodeFunctionData('executeMultipleMetaTransactions', [txs]) 294 | } else { 295 | const gasLimit = 300000 296 | const tx1 = encodeParameters( 297 | ['address', 'uint256', 'uint256', 'bytes'], 298 | [tx.to, tx.value || '0x', gasLimit, tx.data || '0x'] 299 | ) 300 | 301 | const txs = [tx1] 302 | console.log('user txs array', txs) 303 | 304 | const iface = new ethers.utils.Interface([ 305 | { 306 | constant: false, 307 | inputs: [ 308 | { 309 | name: '_transactions', 310 | type: 'bytes[]' 311 | } 312 | ], 313 | name: 'executeMultipleTransactions', 314 | outputs: [ 315 | { 316 | name: '', 317 | type: 'bytes[]' 318 | } 319 | ], 320 | payable: false, 321 | stateMutability: 'nonpayable', 322 | type: 'function' 323 | } 324 | ]) 325 | data = iface.encodeFunctionData('executeMultipleTransactions', [txs]) 326 | } 327 | 328 | const nonce = await provider.getTransactionCount(await wallet.getAddress()) 329 | 330 | let txObj: any = { 331 | to: contractAddress, 332 | value: '0x0', 333 | gasLimit, 334 | gasPrice: tx.gasPrice, 335 | data: data, 336 | nonce, 337 | chainId: getNetworkId(networkName) 338 | } 339 | 340 | console.log('contract exec tx', txObj) 341 | 342 | txObj = await ethers.utils.resolveProperties(txObj) 343 | txObj = await wallet.populateTransaction(txObj) 344 | 345 | let hash = '' 346 | if (walletName === 'privateKey') { 347 | const signed = await wallet.signTransaction(txObj) 348 | const { hash: h } = await provider.sendTransaction(signed) 349 | hash = h 350 | } else { 351 | hash = await wallet.sendUncheckedTransaction(txObj) 352 | } 353 | 354 | return hash 355 | } 356 | 357 | const approveCallRequest = async (payload: any) => { 358 | try { 359 | const isTx = ['eth_signTransaction', 'eth_sendTransaction'].includes( 360 | payload.method 361 | ) 362 | let result = null 363 | 364 | if (isTx) { 365 | const tx = payload.params[0] 366 | const hash = await signTx(tx) 367 | console.log('TXHASH', hash) 368 | result = { 369 | id: payload.id, 370 | result: hash 371 | } 372 | } else { 373 | console.log('PARAMS', payload.params) 374 | let data = payload.params[0] 375 | let sig = null 376 | 377 | data = hexToUtf8(data) 378 | console.log('DATA', data) 379 | sig = await wallet.signMessage(data) 380 | console.log('SIG', sig) 381 | 382 | result = { 383 | id: payload.id, 384 | result: sig 385 | } 386 | } 387 | 388 | walletConnector.approveRequest(result) 389 | setCallRequest(null) 390 | } catch (err) { 391 | rejectCallRequest({ id: payload.id, error: err.message }) 392 | } 393 | } 394 | 395 | const connect = async () => { 396 | const connector = new WalletConnect({ 397 | uri: connectUri.trim(), 398 | clientMeta: { 399 | description: 'WalletConnect Developer App', 400 | url: window.location.href, 401 | icons: ['https://walletconnect.org/walletconnect-logo.png'], 402 | name: 'WalletConnect' 403 | } 404 | }) 405 | 406 | console.log('CONNECTED', connector.connected) 407 | if (!connector.connected) { 408 | await connector.createSession() 409 | } 410 | 411 | setWalletConnector(connector) 412 | } 413 | 414 | const getSession = () => { 415 | try { 416 | // localStorage 'walletconnect' value is set by walletconnect library 417 | const session = localStorage.getItem('walletconnect') 418 | if (!session) { 419 | return null 420 | } 421 | 422 | return JSON.parse(session) 423 | } catch (err) { 424 | return null 425 | } 426 | } 427 | 428 | useEffect(() => { 429 | const session = getSession() 430 | if (session) { 431 | const walletConnector = new WalletConnect({ session }) 432 | setWalletConnector(walletConnector) 433 | } 434 | }, []) 435 | 436 | useEffect(() => { 437 | if (!walletConnector) return 438 | if (!contractAddress) return 439 | console.log('setup Ws') 440 | 441 | const handleConnectWs = async (err: Error, p: any) => { 442 | console.log('connect cb', err, p) 443 | const session = getCachedSession() 444 | setWalletConnectSession(session) 445 | } 446 | 447 | const handleSessionRequest = async (err: Error, payload: any) => { 448 | if (err) { 449 | console.error(err) 450 | return 451 | } 452 | 453 | console.log('session request', payload) 454 | walletConnector?.approveSession({ 455 | accounts: [contractAddress], 456 | chainId: getNetworkId(networkName) 457 | }) 458 | } 459 | 460 | const handleCallRequest = async (err: Error, payload: any) => { 461 | if (err) { 462 | console.error(err) 463 | return 464 | } 465 | 466 | console.log('call request', payload) 467 | setCallRequest(payload) 468 | } 469 | 470 | const handleDisconnectWs = (err: Error, payload: any) => { 471 | if (err) { 472 | console.error(err) 473 | return 474 | } 475 | 476 | console.log('disconnect', payload) 477 | 478 | setConnectUri('') 479 | setWalletConnectSession(null) 480 | setWalletConnector(null) 481 | } 482 | 483 | ee.on('connect', handleConnectWs) 484 | ee.on('session_request', handleSessionRequest) 485 | ee.on('call_request', handleCallRequest) 486 | ee.on('disconnect', handleDisconnectWs) 487 | 488 | return () => { 489 | ee.off('connect', handleConnectWs) 490 | ee.off('session_request', handleSessionRequest) 491 | ee.off('call_request', handleCallRequest) 492 | ee.off('disconnect', handleDisconnectWs) 493 | } 494 | }, [ee, walletConnector, contractAddress, networkName]) 495 | 496 | const handleWalletDisconnect = async (event: any) => { 497 | try { 498 | await onboard.reset() 499 | } catch (err) { 500 | console.error(err) 501 | } 502 | setWallet(null) 503 | setWalletName(null) 504 | localStorage.removeItem('walletName') 505 | setContractAddress('') 506 | handleWalletConnectDisconnect(event) 507 | } 508 | 509 | const handleWalletConnectDisconnect = async (event: any) => { 510 | setConnectUri('') 511 | 512 | try { 513 | if (walletConnector) { 514 | walletConnector.killSession() 515 | setWalletConnector(null) 516 | } 517 | } catch (err) { 518 | console.error(err) 519 | } 520 | } 521 | 522 | const handleContractAddressChange = (event: any) => { 523 | const contractAddress = event.target.value 524 | setContractAddress(contractAddress) 525 | localStorage.setItem('contractAddress', contractAddress) 526 | } 527 | 528 | onboard.on('walletChange', (wallet: any) => { 529 | let { name } = wallet 530 | if (!name) return 531 | 532 | localStorage.setItem('walletName', name) 533 | setWalletName(name) 534 | }) 535 | 536 | onboard.on('accountChange', (address: any) => {}) 537 | 538 | useEffect(() => { 539 | const setup = async () => { 540 | if (!wallet) return 541 | const address = await wallet.getAddress() 542 | setInfo({ 543 | address 544 | }) 545 | } 546 | 547 | setup() 548 | }, [wallet]) 549 | const handleEditClick = (event: any) => { 550 | setCallRequestEditable(true) 551 | } 552 | const updateCallRequest = (event: any) => { 553 | const value = event.target.value 554 | try { 555 | setCallRequest(JSON.parse(value)) 556 | } catch (err) { 557 | console.error(err) 558 | } 559 | } 560 | const handleEditBlur = (event: any) => { 561 | setCallRequestEditable(false) 562 | } 563 | 564 | const updateNetworkName = (event: any) => { 565 | setNetworkName(event.target.value) 566 | } 567 | const updateCustomTx = (event: any) => { 568 | setCustomTx(event.target.value) 569 | } 570 | useEffect(() => { 571 | localStorage.setItem('customTx', customTx) 572 | }, [customTx]) 573 | const handleCustomTxSubmit = async (event: any) => { 574 | event.preventDefault() 575 | try { 576 | setCustomTxHash('') 577 | const hash = await signTx(JSON.parse(customTx)) 578 | setCustomTxHash(hash) 579 | } catch (err) { 580 | alert(err.message) 581 | } 582 | } 583 | const renderNetworkSelect = () => { 584 | return ( 585 | 592 | ) 593 | } 594 | const renderCustomTxForm = () => { 595 | return ( 596 |
597 |
598 | 599 | 604 | 607 | {customTxHash} 608 |
609 |
610 | ) 611 | } 612 | const renderConnected = () => { 613 | if (!wallet) return null 614 | return ( 615 | <> 616 |
Wallet Name: {walletName}
617 |
618 | {info.address && ( 619 |
620 | Connected address (admin key): {info.address} 621 |
622 | )} 623 | {contractAddress && ( 624 |
625 | Authereum account address: {contractAddress} 626 |
627 | )} 628 | {!walletConnectSession && ( 629 | <> 630 | {contractAddress && ( 631 |
632 |
633 | 634 | 642 | 649 |
650 |
or
651 | {renderCustomTxForm()} 652 |
653 | )} 654 | 655 | )} 656 |
657 | {!contractAddress && ( 658 |
659 | 660 | 668 |
669 | )} 670 | {walletConnectSession && ( 671 |
672 | 687 |
688 | {callRequest ? ( 689 |
690 | {callRequestEditable ? ( 691 |