Skip to content

trilloc/react-web3-provider

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

react-web3-provider

Simple higher-order component (HOC) providing a web3 context to React app.

Detects whether the user is using MetaMask or Ethereum wallet-enabled browser. If not, it will access the Ethereum network through a given Web3 fallback provider (e.g. INFURA node).

Ready for the upcoming changes in MetaMask.

Installation

$ yarn add react-web3-provider

Basic usage

Add the Web3Provider to your root React component:

import Web3 from 'web3';
import Web3Provider from 'react-web3-provider';

ReactDOM.render(
	<Web3Provider
		defaultProvider={(cb) => cb(new Web3(new Web3.providers.HttpProvider("https://mainnet.infura.io/YOUR_API_KEY")))}
		loading="Loading..."
		error={(err) => `Connection error: ${err.message}`}
	>
		<App />
	</Web3Provider>
)

Then in component where you want to use Web3:

import { withWeb3 } from 'react-web3-provider';

class MyComponent {
	render() {
		const { web3 } = this.props;

		web3.eth.getAccounts(console.log);

		// Version 1.0.0-beta.35
		return "Web3 version: {web3.version}";
	}
}

export default withWeb3(MyComponent);

Custom web3 state handling

You can render the web3 state somewhere else in the page instead of the global loading and error components:

import Web3 from 'web3';
import Web3Provider from 'react-web3-provider';

ReactDOM.render(
	<Web3Provider
		defaultProvider={(cb) => cb(new Web3(new Web3.providers.HttpProvider("https://mainnet.infura.io/YOUR_API_KEY")))}
	>
		<App />
	</Web3Provider>
)

You can use the injected web3State property in your components:

import { withWeb3 } from 'react-web3-provider';

class MyComponent {
	render() {
		const { web3, web3State } = this.props;

		return (
			<pre>
				{web3State.isConnected && "Connected!\n"}
				{web3State.isLoading && "Loading...\n"}
				{web3State.error && `Connection error: ${web3State.error.message}\n`}
				Web3 version: {web3.version}
			</pre>
		);
	}
}

export default withWeb3(MyComponent);

Web3 Provider filtering

It may be useful to skip the MetaMask Provider if the user has the MetaMask extension installed but is currently not signed-in. We can use acceptProvider parameter to filter out Web3 Provider. The given defaultProvider is always accepted.

ReactDOM.render(
	<Web3Provider
		defaultProvider={...}
		acceptProvider={(web3, accept, reject) => {
			web3.eth.getAccounts().then((accounts) => {
				if (accounts.length >= 1) accept();
				else reject();
			});
		}}
	>
		<App />
	</Web3Provider>
);

Hooked wallet

More complex example demonstrating transaction sending with a zero-client wallet.

import Web3 from 'web3';
import Lightwallet from 'eth-lightwallet';
import Web3ProviderEngine from 'web3-provider-engine';
import HookedWalletSubprovider from 'web3-provider-engine/subproviders/hooked-wallet';
import SubscriptionsSubprovider from 'web3-provider-engine/subproviders/subscriptions';
import RpcSubprovider from 'web3-provider-engine/subproviders/rpc';
import waterfall from 'async-waterfall';
import Web3Provider from 'react-web3-provider';

const defaultWeb3Provider = (cb) => {
	// Light-wallet options
	const vaultOpts = {
		seedPhrase: '...',
		password: '...',
		hdPathString: "m/44'/60'/0'/0",
	}
	const lightWalletEnabled = true;

	waterfall([
		// 1. Initialize Web3 Provider engine
		(wcb) => wcb(null, new Web3ProviderEngine()),
		// 2. Add Hooked wallet sub-provider
		(engine, wcb) => {
			if (lightWalletEnabled) {
				try {
					Lightwallet.keystore.createVault(vaultOpts, (err1, ks) => {
						if (err1) throw err1;

						ks.keyFromPassword(vaultOpts.password, (err2, pwDerivedKey) => {
							if (err2) throw err2;
			
							ks.generateNewAddress(pwDerivedKey, 1);
							engine.addProvider(new HookedWalletSubprovider({
								getAccounts: (ecb) => cb(null, ks.getAddresses()),
								signTransaction: (tx, ecb) => ks.signTransaction(tx, ecb),
							}));
							wcb(null, engine);
						});
					});
				} catch((err) => wcb(err, engine));
			} else wcb(null, engine);
		},
		// 3. Add RPC subprovider
		(engine, wcb) => {
			const web3 = new Web3(engine);
			engine.addProvider(new SubscriptionsSubprovider());
			engine.addProvider(new RpcSubprovider({
				rpcUrl: 'https://mainnet.infura.io/YOUR_API_KEY',
			}));
			engine.start();
			wcb(null, web3);
		},
		// 4. Pass the selected Web3 to the Web3Provider callback
	], (_, web3) => cb(web3));
}

ReactDOM.render(
	<Web3Provider
		defaultProvider={defaultWeb3Provider}
	>
		<App />
	</Web3Provider>
);

Sending transaction:

import waterfall from 'async-waterfall';
import { withWeb3 } from 'react-web3-provider';

class MyComponent {
	sendEther(amount, to) {
		const { web3 } = this.props;

		waterfall([
			(wcb) => {
				web3.eth.getAccounts().then((accounts) => {
					if (accounts && accounts.length >= 1) {
						wcb(null, accounts[0]);
					} else {
						wcb('Unknown account', null);
					}
				});
			},
			(account, wcb) => {
				web3.eth.sendTransaction({
					from: account,
					to,
					value: amount * 1000000000000000000,
				}, wcb);
			},
 		], console.log);
  }

	render() {
		return <button onClick={() => this.sendEther(0.1, '0x12345...')}>SEND TRANSACTION</button>;
	}
}

Contributors

  • Peter

About

changed window.web to window.ethereum

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • JavaScript 100.0%