├── .gitignore ├── README.md ├── package-lock.json ├── package.json ├── src └── index.ts └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | dist/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # web3-react-trust-wallet 2 | 3 | Trust Wallet connector for [web3-react](https://www.npmjs.com/package/web3-react) 4 | 5 | # Usage 6 | 7 | ``` 8 | import { TrustWallet } from "@trustwallet/web3-react-trust-wallet"; 9 | 10 | const [trustWallet, hooks] = initializeConnector( 11 | (actions) => new TrustWallet({ actions }) 12 | ); 13 | 14 | trustWallet.activate(); 15 | ``` -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@web3-react/trust-wallet", 3 | "version": "8.0.20-beta.0", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "@web3-react/trust-wallet", 9 | "version": "8.0.20-beta.0", 10 | "license": "GPL-3.0-or-later", 11 | "dependencies": { 12 | "@web3-react/types": "^8.0.20-beta.0" 13 | }, 14 | "devDependencies": { 15 | "@tsconfig/recommended": "^1.0.1" 16 | } 17 | }, 18 | "node_modules/@tsconfig/recommended": { 19 | "version": "1.0.2", 20 | "resolved": "https://registry.npmjs.org/@tsconfig/recommended/-/recommended-1.0.2.tgz", 21 | "integrity": "sha512-dbHBtbWBOjq0/otpopAE02NT2Cm05Qe2JsEKeCf/wjSYbI2hz8nCqnpnOJWHATgjDz4fd3dchs3Wy1gQGjfN6w==", 22 | "dev": true 23 | }, 24 | "node_modules/@web3-react/types": { 25 | "version": "8.0.20-beta.0", 26 | "resolved": "https://registry.npmjs.org/@web3-react/types/-/types-8.0.20-beta.0.tgz", 27 | "integrity": "sha512-qOZYMyUmsm3Um6t6Pg3OgnE86ufhWZpB5/VxsooB8cdpXc/C/f8KMyYSeM63GoKSMScOKwfqV6yODFL7g/Qc8g==", 28 | "dependencies": { 29 | "zustand": "^4.0.0-rc.0" 30 | } 31 | }, 32 | "node_modules/js-tokens": { 33 | "version": "4.0.0", 34 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", 35 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", 36 | "peer": true 37 | }, 38 | "node_modules/loose-envify": { 39 | "version": "1.4.0", 40 | "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", 41 | "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", 42 | "peer": true, 43 | "dependencies": { 44 | "js-tokens": "^3.0.0 || ^4.0.0" 45 | }, 46 | "bin": { 47 | "loose-envify": "cli.js" 48 | } 49 | }, 50 | "node_modules/react": { 51 | "version": "18.2.0", 52 | "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", 53 | "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", 54 | "peer": true, 55 | "dependencies": { 56 | "loose-envify": "^1.1.0" 57 | }, 58 | "engines": { 59 | "node": ">=0.10.0" 60 | } 61 | }, 62 | "node_modules/use-sync-external-store": { 63 | "version": "1.2.0", 64 | "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", 65 | "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", 66 | "peerDependencies": { 67 | "react": "^16.8.0 || ^17.0.0 || ^18.0.0" 68 | } 69 | }, 70 | "node_modules/zustand": { 71 | "version": "4.3.3", 72 | "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.3.3.tgz", 73 | "integrity": "sha512-x2jXq8S0kfLGNwGh87nhRfEc2eZy37tSatpSoSIN+O6HIaBhgQHSONV/F9VNrNcBcKQu/E80K1DeHDYQC/zCrQ==", 74 | "dependencies": { 75 | "use-sync-external-store": "1.2.0" 76 | }, 77 | "engines": { 78 | "node": ">=12.7.0" 79 | }, 80 | "peerDependencies": { 81 | "immer": ">=9.0", 82 | "react": ">=16.8" 83 | }, 84 | "peerDependenciesMeta": { 85 | "immer": { 86 | "optional": true 87 | }, 88 | "react": { 89 | "optional": true 90 | } 91 | } 92 | } 93 | }, 94 | "dependencies": { 95 | "@tsconfig/recommended": { 96 | "version": "1.0.2", 97 | "resolved": "https://registry.npmjs.org/@tsconfig/recommended/-/recommended-1.0.2.tgz", 98 | "integrity": "sha512-dbHBtbWBOjq0/otpopAE02NT2Cm05Qe2JsEKeCf/wjSYbI2hz8nCqnpnOJWHATgjDz4fd3dchs3Wy1gQGjfN6w==", 99 | "dev": true 100 | }, 101 | "@web3-react/types": { 102 | "version": "8.0.20-beta.0", 103 | "resolved": "https://registry.npmjs.org/@web3-react/types/-/types-8.0.20-beta.0.tgz", 104 | "integrity": "sha512-qOZYMyUmsm3Um6t6Pg3OgnE86ufhWZpB5/VxsooB8cdpXc/C/f8KMyYSeM63GoKSMScOKwfqV6yODFL7g/Qc8g==", 105 | "requires": { 106 | "zustand": "^4.0.0-rc.0" 107 | } 108 | }, 109 | "js-tokens": { 110 | "version": "4.0.0", 111 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", 112 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", 113 | "peer": true 114 | }, 115 | "loose-envify": { 116 | "version": "1.4.0", 117 | "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", 118 | "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", 119 | "peer": true, 120 | "requires": { 121 | "js-tokens": "^3.0.0 || ^4.0.0" 122 | } 123 | }, 124 | "react": { 125 | "version": "18.2.0", 126 | "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", 127 | "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", 128 | "peer": true, 129 | "requires": { 130 | "loose-envify": "^1.1.0" 131 | } 132 | }, 133 | "use-sync-external-store": { 134 | "version": "1.2.0", 135 | "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", 136 | "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", 137 | "requires": {} 138 | }, 139 | "zustand": { 140 | "version": "4.3.3", 141 | "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.3.3.tgz", 142 | "integrity": "sha512-x2jXq8S0kfLGNwGh87nhRfEc2eZy37tSatpSoSIN+O6HIaBhgQHSONV/F9VNrNcBcKQu/E80K1DeHDYQC/zCrQ==", 143 | "requires": { 144 | "use-sync-external-store": "1.2.0" 145 | } 146 | } 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@trustwallet/web3-react-trust-wallet", 3 | "keywords": [ 4 | "web3-react", 5 | "trust-wallet", 6 | "TrustWallet" 7 | ], 8 | "author": "Albert Cieplinski", 9 | "license": "GPL-3.0-or-later", 10 | "repository": "github:trustwallet/web3-react-trust-wallet", 11 | "publishConfig": { 12 | "access": "public" 13 | }, 14 | "version": "0.0.5", 15 | "files": [ 16 | "dist/*" 17 | ], 18 | "type": "commonjs", 19 | "types": "./dist/index.d.ts", 20 | "main": "./dist/index.js", 21 | "exports": "./dist/index.js", 22 | "scripts": { 23 | "prebuild": "rm -rf dist", 24 | "build": "tsc", 25 | "start": "tsc --watch" 26 | }, 27 | "dependencies": { 28 | "@web3-react/types": "^8.0.20-beta.0" 29 | }, 30 | "devDependencies": { 31 | "@tsconfig/recommended": "^1.0.1" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import { Actions, AddEthereumChainParameter, Connector, Provider, ProviderConnectInfo, ProviderRpcError, RequestArguments } from '@web3-react/types' 2 | 3 | type TrustWalletProvider = Provider & { 4 | isTrust?: boolean; 5 | isTrustWallet?: boolean; 6 | providers?: Omit[]; 7 | isConnected: () => boolean; 8 | request(args: RequestArguments): Promise; 9 | chainId: string; 10 | selectedAddress: string; 11 | on: (event: string, args: any) => any; 12 | }; 13 | 14 | interface TrustWalletConstructorArgs { 15 | actions: Actions; 16 | onError?: () => void; 17 | } 18 | 19 | type Window = typeof Window & { 20 | ethereum?: TrustWalletProvider; 21 | trustwallet?: TrustWalletProvider; 22 | } 23 | 24 | export class TrustWallet extends Connector { 25 | provider?: TrustWalletProvider 26 | 27 | private get connected() { 28 | return !!this.provider?.isConnected?.() 29 | } 30 | 31 | constructor({ actions, onError }: TrustWalletConstructorArgs) { 32 | super(actions, onError) 33 | } 34 | 35 | public activate(desiredChainIdOrChainParameters?: number | AddEthereumChainParameter): Promise | undefined { 36 | this.detectProvider(); 37 | 38 | if (!this.provider) { 39 | window.open('https://trustwallet.com/browser-extension/', '_blank') 40 | return 41 | } 42 | 43 | const desiredChainId = 44 | typeof desiredChainIdOrChainParameters === 'number' 45 | ? desiredChainIdOrChainParameters 46 | : desiredChainIdOrChainParameters?.chainId 47 | 48 | if (this.connected && desiredChainId && desiredChainId === this.parseChainId(this.provider.chainId)) { 49 | 50 | const desiredChainIdHex = `0x${desiredChainId.toString(16)}` 51 | 52 | // eslint-disable-next-line @typescript-eslint/no-non-null-assertion 53 | return this.provider.request({ 54 | method: 'wallet_switchEthereumChain', 55 | params: [{ chainId: desiredChainIdHex }], 56 | }).catch(async (error: ProviderRpcError) => { 57 | console.log('err!', error); 58 | 59 | if (error.code === 4902 && typeof desiredChainIdOrChainParameters !== 'number') { 60 | // if we're here, we can try to add a new network 61 | // eslint-disable-next-line @typescript-eslint/no-non-null-assertion 62 | return this.provider!.request({ 63 | method: 'wallet_addEthereumChain', 64 | params: [{ ...desiredChainIdOrChainParameters, chainId: desiredChainIdHex }], 65 | }) 66 | } 67 | 68 | throw error 69 | }) 70 | } 71 | 72 | return Promise.all([ 73 | this.provider.request({ method: 'eth_chainId' }) as Promise, 74 | this.provider.request({ method: 'eth_requestAccounts' }) as Promise, 75 | ]).then(([chainId, accounts]) => { 76 | 77 | const receivedChainId = this.parseChainId(chainId) 78 | 79 | // eslint-disable-next-line @typescript-eslint/no-non-null-assertion 80 | if (!desiredChainId || desiredChainId === receivedChainId) { 81 | return this.actions.update({ chainId: receivedChainId, accounts }) 82 | } 83 | 84 | const desiredChainIdHex = `0x${desiredChainId.toString(16)}` 85 | 86 | // eslint-disable-next-line @typescript-eslint/no-non-null-assertion 87 | return this.provider!.request({ 88 | method: 'wallet_switchEthereumChain', 89 | params: [{ chainId: desiredChainIdHex }], 90 | }).catch(async (error: ProviderRpcError) => { 91 | 92 | console.log('err!', error); 93 | 94 | if (error.code === 4902 && typeof desiredChainIdOrChainParameters !== 'number') { 95 | // if we're here, we can try to add a new network 96 | // eslint-disable-next-line @typescript-eslint/no-non-null-assertion 97 | return this.provider!.request({ 98 | method: 'wallet_addEthereumChain', 99 | params: [{ ...desiredChainIdOrChainParameters, chainId: desiredChainIdHex }], 100 | }) 101 | } 102 | 103 | throw error 104 | }) 105 | }); 106 | 107 | } 108 | 109 | /** {@inheritdoc Connector.connectEagerly} */ 110 | public async connectEagerly(): Promise { 111 | 112 | this.isomorphicInitialize() 113 | 114 | if (!this.provider) return 115 | 116 | return Promise.all([ 117 | this.provider.request({ method: 'eth_chainId' }) as Promise, 118 | this.provider.request({ method: 'eth_accounts' }) as Promise, 119 | ]) 120 | .then(([chainId, accounts]) => { 121 | if (accounts.length) { 122 | this.actions.update({ chainId: this.parseChainId(chainId), accounts }) 123 | } else { 124 | throw new Error('No accounts returned') 125 | } 126 | }) 127 | .catch((error) => { 128 | console.debug('Could not connect eagerly', error) 129 | this.actions.resetState() 130 | }) 131 | } 132 | 133 | private detectProvider(): TrustWalletProvider | void { 134 | this.provider = this.isTrust((window as unknown as Window).ethereum) || 135 | (window as unknown as Window).trustwallet || 136 | (window as unknown as Window).ethereum?.providers?.find( 137 | (provider: Omit) => provider.isTrust || provider.isTrustWallet 138 | ) 139 | 140 | if (this.provider) { 141 | return this.provider 142 | } 143 | } 144 | 145 | private isTrust(ethereum?: TrustWalletProvider) { 146 | const isTrustWallet = !!ethereum?.isTrust || !!ethereum?.isTrustWallet 147 | if (!isTrustWallet) return 148 | return ethereum 149 | } 150 | 151 | private isomorphicInitialize(): void { 152 | const provider = this.detectProvider() 153 | 154 | if (provider) { 155 | provider.on('connect', ({ chainId }: ProviderConnectInfo): void => { 156 | this.actions.update({ chainId: this.parseChainId(chainId) }) 157 | }) 158 | 159 | provider.on('disconnect', (error: ProviderRpcError): void => { 160 | // eslint-disable-next-line @typescript-eslint/no-floating-promises 161 | this.provider?.request({method: 'PUBLIC_disconnectSite'}) 162 | 163 | this.actions.resetState() 164 | this.onError?.(error) 165 | }) 166 | 167 | provider.on('chainChanged', (chainId: string): void => { 168 | 169 | this.actions.update({ chainId: Number(chainId) }) 170 | }) 171 | 172 | provider.on('accountsChanged', (accounts: string[]): void => { 173 | if (accounts.length === 0) { 174 | // handle this edge case by disconnecting 175 | this.actions.resetState() 176 | } else { 177 | this.actions.update({ accounts }) 178 | } 179 | }) 180 | } 181 | } 182 | 183 | private parseChainId(chainId: string) { 184 | return Number.parseInt(chainId, 16) 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tsconfig/recommended/tsconfig.json", 3 | "include": ["./src"], 4 | "compilerOptions": { 5 | "outDir": "./dist", 6 | "module": "CommonJS", 7 | "declaration": true, 8 | "moduleResolution": "Node" 9 | }, 10 | "exclude": ["**/*.spec.ts"] 11 | } 12 | --------------------------------------------------------------------------------