Get Started with Wallet Kit
Wallet Kit makes it easy to build Station functionality into your React application. It contains custom hooks that drastically simplify common tasks like connecting a wallet and triggering transactions for both Station mobile and the Station extension.
This guide covers how to set up a React app, integrate Wallet Kit, check the balance of the connected account, and call a token swap. If you want to integrate Station into an existing React app, jump to the Wrap your app in WalletProvider
section.
Prerequisites
- The latest version of the Station Chrome extension (Station Wallet 7.4.2 and above)
- 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-kit
package:_1npm install @terra-money/wallet-kit
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 getInitialConfig
.
Navigate to your Index.js
in a code editor and replace the code with the following:
_13import ReactDOM from 'react-dom';_13import './index.css';_13import App from './App';_13import { getInitialConfig, WalletProvider } from '@terra-money/wallet-kit';_13_13getInitialConfig().then((defaultNetworks) => {_13 ReactDOM.render(_13 <WalletProvider defaultNetworks={defaultNetworks}>_13 <App />_13 </WalletProvider>,_13 document.getElementById('root'),_13 );_13});
3. Add support for station Mobile
To support Station Mobile:
-
Install the
@terra-money/terra-station-mobile
package:_1npm install @terra-money/terra-station-mobile -
Add
TerraStationMobileWallet
to theextraWallets
array prop in theWalletProvider
component._17import ReactDOM from 'react-dom';_17import './index.css';_17import App from './App';_17import TerraStationMobileWallet from '@terra-money/terra-station-mobile';_17import { getInitialConfig, WalletProvider } from '@terra-money/wallet-kit';_17_17getInitialConfig().then((defaultNetworks) => {_17ReactDOM.render(_17<WalletProvider_17extraWallets={[new TerraStationMobileWallet()]}_17defaultNetworks={defaultNetworks}_17>_17<App />_17</WalletProvider>,_17document.getElementById('root'),_17);_17});
4. Start the application
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 // its important to specify the version -
Reinstall your dependencies:
_1npm install -
Restart your app:
_1npm start
- Create a new directory called
components
in thesource
directory. This directory will house components to trigger different actions from our connected wallet.
5. 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
and useConnectedWallet
hooks to connect your Station extension to your web browser.
-
Create a new component file called
Connect.js
. -
Populate the
Connect.js
file with the following:
_29import React from 'react';_29import { useConnectedWallet, useWallet } from '@terra-money/wallet-kit';_29_29function Connect() {_29 const connectedWallet = useConnectedWallet();_29 const { connect, disconnect, availableWallets } = useWallet();_29_29 return (_29 <section>_29 <h4>Connect info:</h4>_29 {connectedWallet ? (_29 <>_29 <button onClick={() => disconnect()}>Disconnect</button>_29 <code>_29 <pre>{JSON.stringify(connectedWallet, null, 2)}</pre>_29 </code>_29 </>_29 ) : (_29 availableWallets.map(({ id, name, isInstalled }) => (_29 <button onClick={() => connect(id)} disabled={!isInstalled} key={id}>_29 Connect {name}_29 </button>_29 ))_29 )}_29 </section>_29 );_29}_29_29export default Connect;
- 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() {_14 return (_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.
6. Query 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:_26import { useConnectedWallet, useLcdClient } from '@terra-money/wallet-kit';_26import React, { useEffect, useState } from 'react';_26_26export default function Query() {_26const lcd = useLcdClient(); // LCD stands for Light Client Daemon_26const connected = useConnectedWallet();_26const [balance, setBalance] = useState('');_26const chainID = 'phoenix-1'; // or any other mainnet or testnet chainID supported by station (e.g. osmosis-1)_26_26useEffect(() => {_26if (connected) {_26lcd.bank.balance(connected.addresses[chainID]).then(([coins]) => {_26setBalance(coins.toString());_26});_26} else {_26setBalance(null);_26}_26}, [connected, lcd]); // useEffect is called when these variables change_26_26return (_26<div>_26{balance && <p>{balance}</p>}_26{!connected && <p>Wallet not connected!</p>}_26</div>_26);_26} -
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.
7. Send a transaction
You can also create and send transactions to the Terra network while using Wallet Kit. 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 your Station extension, click on Networks, then select Testnets.
You can request testnet 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 retreive thebaseAsset
from the network object._55import { MsgSend } from '@terra-money/feather.js';_55import { useConnectedWallet, useWallet } from '@terra-money/wallet-kit';_55import React, { useCallback, useState } from 'react';_55_55const TEST_TO_ADDRESS = 'terra12hnhh5vtyg5juqnzm43970nh4fw42pt27nw9g9';_55_55function Tx() {_55const [txResult, setTxResult] = useState(null);_55const [txError, setTxError] = useState('');_55_55const connectedWallet = useConnectedWallet();_55const wallet = useWallet();_55const chainID = 'phoenix-1';_55_55const testTx = useCallback(async () => {_55if (!connectedWallet) {_55return;_55}_55_55if (connectedWallet.network === 'mainnet') {_55alert(`Please only execute this example on Testnet`);_55return;_55}_55_55try {_55const transactionMsg = {_55chainID,_55msgs: [_55new MsgSend(connectedWallet.addresses[chainID], TEST_TO_ADDRESS, {_55uluna: 1, // parse baseAsset from network object and use here (e.g.`[baseAsset]`)_55}),_55],_55};_55_55const tx = await wallet.post(transactionMsg);_55setTxResult(tx);_55} catch (error) {_55setTxError(_55'Error: ' + (error instanceof Error ? error.message : String(error)),_55);_55}_55}, [connectedWallet, wallet]);_55_55return (_55<>_55{connectedWallet && !txResult && !txError && (_55<button onClick={testTx}>Send 1USD to {TEST_TO_ADDRESS}</button>_55)}_55_55{txResult && <>{JSON.stringify(txResult, null, 2)}</>}_55{txError && <pre>{txError}</pre>}_55</>_55);_55}_55export default Tx;📝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; -
After refreshing 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.