Get started with Wallet Provider
Wallet-Provider is deprecated. Use Wallet Kit instead.
Wallet Provider makes it easy to build Station (browser extension and mobile) functionality into your React application. It contains custom hooks that drastically simplify common tasks like connecting a wallet and triggering transactions.
This guide will cover how to set up a React app, integrate Wallet Provider, check the balance of the connected account, and call a token swap. If you want to integrate Station into an existing React app you can skip past the Project Setup
section.
Check out the getting started section for the premade templates on GitHub.
If you're using a frontend framework other than React you'll need to use Wallet Controller instead. Controller provides the sub-structure of Provider. You can see an example of how Wallet Controller works in the Vue.js template example.
Prerequisites
- Station Chrome extension
- NPM
- NVM
- Node.js version 16
Most users will need to specify Node version 16 before continuing. You can manage node versions with NVM.
_1nvm install 16 nvm use 16
1. Project Setup
-
To get started, you'll need some basic React scaffolding. To generate this, run the following in your terminal:
_2npx create-react-app my-terra-app_2cd my-terra-app -
Then, install the
@terra-money/wallet-provider
package:_1npm install @terra-money/wallet-provider
2. Wrap your app in WalletProvider
Next, you'll wrap your App
with <WalletProvider>
to give all your components access to useful data, hooks, and utilities. You'll also need to pass in information about Terra networks, such as the mainnet or chainId, into the provider via getChainOptions
.
-
Navigate to your
Index.js
in a code editor and replace the code with the following:_17import ReactDOM from 'react-dom';_17import './index.css';_17import App from './App';_17import reportWebVitals from './reportWebVitals';_17import {_17getChainOptions,_17WalletProvider,_17} from '@terra-money/wallet-provider';_17_17getChainOptions().then((chainOptions) => {_17ReactDOM.render(_17<WalletProvider {...chainOptions}>_17<App />_17</WalletProvider>,_17document.getElementById('root'),_17);_17}); -
Start the application to make sure it works:
_1npm start
Your browser should open to http://localhost:3000/
and you should see the react logo with a black background and some text.
Getting polyfill
errors?
To solve these errors, can downgrade react-scripts
: 4.0.3
in your package.json
and reinstall your dependencies as a quick fix:
-
Navigate to
my-terra-app
in your terminal and run the following:_1npm install react-scripts@4.0.3 -
Reinstall your dependencies:
_1npm install -
Restart your app:
_1npm start
Alternatively, you can configure your webpack to include the necessary fallbacks. Here's an example that uses react-app-rewired.
- Create a new directory called
components
in thesource
directory. This directory will house components to trigger different actions from our connected wallet.
3. Put useWallet
to work
Now that App.js
has inherited the context of WalletProvider
, you can start putting your imports to work. You'll use the multi-purpose useWallet
hook to connect your Station extension to your web browser.
-
Create a new file in the
components
directory calledConnect.js
. -
Populate the
Connect.js
file with the following:_38import { useWallet, WalletStatus } from '@terra-money/wallet-provider';_38import React from 'react';_38export default function Connect() {_38const {_38status,_38network,_38wallets,_38availableConnectTypes,_38connect,_38disconnect,_38} = useWallet();_38_38const chainID = 'phoenix-1'; // or any other mainnet or testnet chainID supported by station (e.g. osmosis-1)_38return (_38<>_38{JSON.stringify(_38{ status, network: network[chainID], wallets },_38null,_382,_38)}_38{status === WalletStatus.WALLET_NOT_CONNECTED && (_38<>_38{availableConnectTypes.map((connectType) => (_38<button_38key={'connect-' + connectType}_38onClick={() => connect(connectType)}_38>_38Connect {connectType}_38</button>_38))}_38</>_38)}_38{status === WalletStatus.WALLET_CONNECTED && (_38<button onClick={() => disconnect()}>Disconnect</button>_38)}_38</>_38);_38} -
Open
App.js
in your code editor and replace the code with the following:_14import './App.css';_14import Connect from './components/Connect';_14_14function App() {_14return (_14<div className="App">_14<header className="App-header">_14<Connect />_14</header>_14</div>_14);_14}_14_14export default App; -
Refresh your browser. There should be some new text and buttons in your browser.
-
Make sure your Station extension is connected to a wallet. Click Connect EXTENSION and the app will connect to your wallet.
The status
, network
, and wallets
properties in your browser provide useful information about the state of the Terra wallet. Before connecting, the status
variable will be WALLET_NOT_CONNECTED
and upon connection the status becomes WALLET_CONNECTED
. In addition, the wallets
array now has one entry with the connectType
and terraAddress
you used to connect.
You should be able to see these changes in real-time.
4. Querying a wallet balance
It's common for an app to show the connected user's LUNA balance. To achieve this you'll need two hooks. The first is useLCDClient
. An LCDClient
is essentially a REST-based adapter for the Terra blockchain. You can use it to query an account balance. The second is useConnectedWallet
, which tells you if a wallet is connected and, if so, basic information about that wallet such as its address.
Be aware that you will not see any tokens if your wallet is empty.
-
Create a file in your
Components
folder namedQuery.js
. -
Populate
Query.js
with the following:_31import {_31useConnectedWallet,_31useLCDClient,_31} from '@terra-money/wallet-provider';_31import React, { useEffect, useState } from 'react';_31_31export default function Query() {_31const lcd = useLCDClient(); // LCD stands for Light Client Daemon_31const connectedWallet = useConnectedWallet();_31const [balance, setBalance] = useState<null | string>(null);_31const chainID = 'phoenix-1'; // or any other mainnet or testnet chainID supported by station (e.g. osmosis-1)_31_31useEffect(() => {_31if (connectedWallet) {_31lcd.bank_31.balance(connectedWallet.addresses[chainID])_31.then(([coins]) => {_31setBalance(coins.toString());_31});_31} else {_31setBalance(null);_31}_31}, [connectedWallet, lcd]); // useEffect is called when these variables change_31_31return (_31<div>_31{balance && <p>{balance}</p>}_31{!connectedWallet && <p>Wallet not connected!</p>}_31</div>_31);_31} -
Open
App.js
in your code editor and addimport Query from './components/Query'
to line 3, and<Query />
to line 10. The whole file should look like the following:_16import './App.css';_16import Connect from './components/Connect';_16import Query from './components/Query';_16_16function App() {_16return (_16<div className="App">_16<header className="App-header">_16<Connect />_16<Query />_16</header>_16</div>_16);_16}_16_16export default App; -
Refresh your browser. Your wallet balance will appear in micro-denominations. Multiply by for an accurate balance.
5. Sending a transaction
You can also create and send transactions to the Terra network while utilizing Wallet Provider. You can use feather.js
to generate a sample transaction:
_1npm install @terra-money/feather.js
Before broadcasting this example transaction, ensure you're on the Terra testnet. To change networks click the gear icon in Station and select testnet
.
You can request tesnet funds from the Terra Testnet Faucet.
To transfer LUNA, you will need to supply a message containing the sender address, recipient address, and send amount (in this case 1 LUNA), as well as fee parameters. Once the message is constructed, the post
method on connectedWallet
broadcasts it to the network.
Wallet Provider supplies useful error types. This example will handle the UserDenied
error case, but you can find other cases for error handling on GitHub.
-
Create a file in your
Components
folder namedTx.js
. -
Populate
Tx.js
with the following. To make this example interchain retrieve thebaseAsset
from the network object._70import { Fee, MsgSend } from '@terra-money/feather.js';_70import {_70useConnectedWallet,_70UserDenied,_70} from '@terra-money/wallet-provider';_70import React, { useCallback, useState } from 'react';_70_70const TEST_TO_ADDRESS = 'terra12hnhh5vtyg5juqnzm43970nh4fw42pt27nw9g9';_70_70export default function Tx() {_70const [txResult, setTxResult] = useState<TxResult | null>(null);_70const [txError, setTxError] = useState<string | null>(null);_70_70const connectedWallet = useConnectedWallet();_70const chainID = 'phoenix-1';_70_70const testTx = useCallback(async () => {_70if (!connectedWallet) {_70return;_70}_70_70const isMainnet = Object.keys(connectedWallet.network).some((key) =>_70key.startsWith('phoenix-'),_70);_70_70if (isMainnet) {_70alert(`Please only execute this example on Testnet`);_70return;_70}_70_70try {_70const transactionMsg = {_70fee: new Fee(1000000, '20000uluna'),_70chainID,_70msgs: [_70new MsgSend(connectedWallet.addresses[chainID], TEST_TO_ADDRESS, {_70uluna: 1000000, // parse baseAsset from network object and use here (e.g.`[baseAsset]`)_70}),_70],_70};_70_70const tx = await connectedWallet.post(transactionMsg);_70setTxResult(tx);_70} catch (error) {_70if (error instanceof UserDenied) {_70setTxError('User Denied');_70} else {_70setTxError(_70'Unknown Error: ' +_70(error instanceof Error ? error.message : String(error)),_70);_70}_70}_70}, [connectedWallet]);_70_70return (_70<>_70{connectedWallet?.availablePost && !txResult && !txError && (_70<button onClick={testTx}>Send 1USD to {TEST_TO_ADDRESS}</button>_70)}_70_70{txResult && <>{JSON.stringify(txResult, null, 2)}</>}_70{txError && <pre>{txError}</pre>}_70_70{connectedWallet && !connectedWallet.availablePost && (_70<p>This connection does not support post()</p>_70)}_70</>_70);_70}📝noteBecause all coins are denominated in micro-units, you will need to multiply any coins by . For example, 1000000 uluna = 1 LUNA.
-
Open
App.js
in your code editor and addimport Tx from './components/Tx'
to line 4, and<Tx />
to line 12. The whole file should look like the following:_18import './App.css';_18import Connect from './components/Connect';_18import Query from './components/Query';_18import Tx from './components/Tx';_18_18function App() {_18return (_18<div className="App">_18<header className="App-header">_18<Connect />_18<Query />_18<Tx />_18</header>_18</div>_18);_18}_18_18export default App; -
Refresh your browser. You'll see a new Send button. Click the button to send your transaction. Your Station extension will ask you to confirm the transaction.
That's all! You can find more examples of WalletProvider
capabilities in the following example templates: