https://github.com/terra-money/wallet-provider
Library to make React dApps easier using Terra Station Extension or Terra Station Mobile.
https://github.com/terra-money/wallet-provider
blockchain dapp react terrajs
Last synced: about 1 year ago
JSON representation
Library to make React dApps easier using Terra Station Extension or Terra Station Mobile.
- Host: GitHub
- URL: https://github.com/terra-money/wallet-provider
- Owner: terra-money
- License: apache-2.0
- Created: 2021-06-17T06:30:44.000Z (about 5 years ago)
- Default Branch: main
- Last Pushed: 2023-08-29T15:19:03.000Z (almost 3 years ago)
- Last Synced: 2025-04-14T01:18:38.841Z (over 1 year ago)
- Topics: blockchain, dapp, react, terrajs
- Language: TypeScript
- Homepage: https://www.npmjs.com/package/@terra-money/wallet-provider
- Size: 6.65 MB
- Stars: 89
- Watchers: 22
- Forks: 69
- Open Issues: 31
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# Terra Wallet Provider
Library to make React dApps easier using Station Extension or Station Mobile.
# Quick Start
Use templates to get your projects started quickly
### Code Sandbox
If you want to test features quickly, you can simply run them on CodeSandbox without having to download Templates.
- [Wallet Provider + Create-React-App](https://githubbox.com/terra-money/wallet-provider/tree/main/templates/create-react-app)
- [Wallet Provider + Create-React-App-17](https://githubbox.com/terra-money/wallet-provider/tree/main/templates/create-react-app-17)
The following templates do not support Wallet-Provider 4 at the moment.
- [Wallet Provider + Next.js](https://githubbox.com/terra-money/wallet-provider/tree/main/templates/next)
- [Wallet Provider + Vite.js](https://githubbox.com/terra-money/wallet-provider/tree/main/templates/vite)
- [Wallet Controller + Lit](https://githubbox.com/terra-money/wallet-provider/tree/main/templates/lit)
- [Wallet Controller + Vue.js](https://githubbox.com/terra-money/wallet-provider/tree/main/templates/vue)
- [Wallet Controller + Svelte](https://githubbox.com/terra-money/wallet-provider/tree/main/templates/svelte)
And if you need to start your project from local computer, use the templates below. 👇
### Create React App
```sh
npx terra-templates get wallet-provider:create-react-app your-app-name
cd your-app-name
yarn install
yarn start
```
### Create React App (React 17)
```sh
npx terra-templates get wallet-provider:create-react-app-17 your-app-name
cd your-app-name
yarn install
yarn start
```
### Next.js
```sh
npx terra-templates get wallet-provider:next your-app-name
cd your-app-name
yarn install
yarn run dev
```
### Other templates
- [Wallet Provider + Vite.js](https://github.com/terra-money/wallet-provider/tree/main/templates/vite)
- [Wallet Controller + Lit](https://github.com/terra-money/wallet-provider/tree/main/templates/lit)
- [Wallet Controller + Vue.js](https://github.com/terra-money/wallet-provider/tree/main/templates/vue)
- [Wallet Controller + Svelte](https://github.com/terra-money/wallet-provider/tree/main/templates/svelte)
### Community templates (experimental)
You can find more templates in . (This is the beginning stage, so it may not be enough yet)
If you make a different type of template, you can register [here](https://github.com/terra-money/templates).
# Basic Usage
First, please add `` on your html page.
Since then, browser extensions (e.g. Station chrome extension) will not attempt to connect in a Web app where this `` tag is not found.
```html
```
If you have used `react-router-dom`'s ``, `useLocation()`, you can easily understand it.
```jsx
import {
NetworkInfo,
WalletProvider,
WalletStatus,
getChainOptions,
} from '@terra-money/wallet-provider';
import React from 'react';
import ReactDOM from 'react-dom';
// getChainOptions(): Promise<{ defaultNetwork, walletConnectChainIds }>
getChainOptions().then((chainOptions) => {
ReactDOM.render(
,
document.getElementById('root'),
);
});
```
First, you need to wrap your React App with the `` component.
```jsx
import { useWallet } from '@terra-money/wallet-provider';
import React from 'react';
function Component() {
const { status, network, wallets } = useWallet();
return (
{JSON.stringify(
{
status,
network,
wallets,
},
null,
2,
)}
);
}
```
Afterwards, you can use React Hooks such as `useWallet()`, `useConnectedWallet()` and `useLCDClient()` anywhere in your app.
# API
<WalletProvider>
```jsx
import {
WalletProvider,
NetworkInfo,
ReadonlyWalletSession,
} from '@terra-money/wallet-provider';
// network information
const mainnet: NetworkInfo = {
'phoenix-1': {
baseAsset: 'uluna',
chainID: 'phoenix-1',
coinType: '330',
explorer: {
address: 'https://terrasco.pe/mainnet/address/{}',
block: 'https://terrasco.pe/mainnet/block/{}',
tx: 'https://terrasco.pe/mainnet/tx/{}',
validator: 'https://terrasco.pe/mainnet/validator/{}',
},
gasAdjustment: 1.75,
gasPrices: {
uluna: 0.015,
},
icon: 'https://station-assets.terra.money/img/chains/Terra.svg',
lcd: 'https://phoenix-lcd.terra.dev',
name: 'Terra',
prefix: 'terra',
},
};
const testnet: NetworkInfo = {
'pisco-1': {
baseAsset: 'uluna',
chainID: 'pisco-1',
coinType: '330',
explorer: {
address: 'https://terrasco.pe/testnet/address/{}',
block: 'https://terrasco.pe/testnet/block/{}',
tx: 'https://terrasco.pe/testnet/tx/{}',
validator: 'https://terrasco.pe/testnet/validator/{}',
},
gasAdjustment: 3.5,
gasPrices: {
uluna: 0.015,
},
icon: 'https://station-assets.terra.money/img/chains/Terra.svg',
lcd: 'https://pisco-lcd.terra.dev',
name: 'Terra',
prefix: 'terra',
},
};
// ⚠️ If there is no special reason, use `getChainOptions()` instead of `walletConnectChainIds` above.
// Optional
// If you need to modify the modal, such as changing the design, you can put it in,
// and if you don't put the value in, there is a default modal.
async function createReadonlyWalletSession(): Promise {
const terraAddress = prompt('YOUR TERRA ADDRESS');
return {
network: mainnet,
terraAddress,
};
}
// Optional
// WalletConnect Client option.
const connectorOpts: IWalletConnectOptions | undefined = undefined;
const pushServerOpts: IPushServerOptions | undefined = undefined;
// Optional
// Time to wait for the Chrome Extension window.isTerraExtensionAvailable.
// If not entered, wait for default 1000 * 3 miliseconds.
// If you reduce excessively, Session recovery of Chrome Extension may fail.
const waitingChromeExtensionInstallCheck: number | undefined = undefined;
ReactDOM.render(
,
document.getElementById('root'),
);
```
useWallet()
This is a React Hook that can receive all the information. (Other hooks are functions for the convenience of Wrapping
this `useWallet()`)
[packages/src/@terra-money/use-wallet/useWallet.ts](packages/src/@terra-money/use-wallet/useWallet.ts)
````ts
export interface Wallet {
/**
* current client status
*
* this will be one of WalletStatus.INITIALIZING | WalletStatus.WALLET_NOT_CONNECTED | WalletStatus.WALLET_CONNECTED
*
* INITIALIZING = checking that the session and the chrome extension installation. (show the loading to users)
* WALLET_NOT_CONNECTED = there is no connected wallet (show the connect and install options to users)
* WALLET_CONNECTED = there is aconnected wallet (show the wallet info and disconnect button to users)
*
* @see Wallet#refetchStates
* @see WalletController#status
*/
status: WalletStatus;
/**
* current selected network
*
* - if status is INITIALIZING or WALLET_NOT_CONNECTED = this will be the defaultNetwork
* - if status is WALLET_CONNECTED = this depends on the connected environment
*
* @see WalletProviderProps#defaultNetwork
* @see WalletController#network
*/
network: NetworkInfo;
/**
* available connect types on the browser
*
* @see Wallet#connect
* @see WalletController#availableConnectTypes
*/
availableConnectTypes: ConnectType[];
/**
* available connections includes identifier, name, icon
*
* @example
* ```
* const { availableConnections, connect } = useWallet()
*
* return (
*
* {
* availableConnections.map(({type, identifier, name, icon}) => (
* connect(type, identifier)}>
*
{name}
*
* ))
* }
*
* )
* ```
*/
availableConnections: Connection[];
/**
* current connected connection
*/
connection: Connection | undefined;
/**
* connect to wallet
*
* @example
* ```
* const { status, availableConnectTypes, connect } = useWallet()
*
* return status === WalletStatus.WALLET_NOT_CONNECTED &&
* availableConnectTypes.includs(ConnectType.EXTENSION) &&
* connect(ConnectType.EXTENSION)}>
* Connct Chrome Extension
*
* ```
*
* @see Wallet#availableConnectTypes
* @see WalletController#connect
*/
connect: (type?: ConnectType, identifier?: string) => void;
/**
* manual connect to read only session
*
* @see Wallet#connectReadonly
*/
connectReadonly: (terraAddress: string, network: NetworkInfo) => void;
/**
* available install types on the browser
*
* in this time, this only contains [ConnectType.EXTENSION]
*
* @see Wallet#install
* @see WalletController#availableInstallTypes
*/
availableInstallTypes: ConnectType[];
/**
* available installations includes identifier, name, icon, url
*
* @example
* ```
* const { availableInstallations } = useWallet()
*
* return (
*
* )
* ```
*
* @see Wallet#install
* @see WalletController#availableInstallations
*/
availableInstallations: Installation[];
/**
* @deprecated Please use availableInstallations
*
* install for the connect type
*
* @example
* ```
* const { status, availableInstallTypes } = useWallet()
*
* return status === WalletStatus.WALLET_NOT_CONNECTED &&
* availableInstallTypes.includes(ConnectType.EXTENSION) &&
* install(ConnectType.EXTENSION)}>
* Install Extension
*
* ```
*
* @see Wallet#availableInstallTypes
* @see WalletController#install
*/
install: (type: ConnectType) => void;
/**
* connected wallets
*
* this will be like
* `[{ connectType: ConnectType.WALLETCONNECT, terraAddress: 'XXXXXXXXX' }]`
*
* in this time, you can get only one wallet. `wallets[0]`
*
* @see WalletController#wallets
*/
wallets: WalletInfo[];
/**
* disconnect
*
* @example
* ```
* const { status, disconnect } = useWallet()
*
* return status === WalletStatus.WALLET_CONNECTED &&
* disconnect()}>
* Disconnect
*
* ```
*/
disconnect: () => void;
/**
* reload the connected wallet states
*
* in this time, this only work on the ConnectType.EXTENSION
*
* @see WalletController#refetchStates
*/
refetchStates: () => void;
/**
* @deprecated please use refetchStates(). this function will remove on next major update
*/
recheckStatus: () => void;
/**
* support features of this connection
*
* @example
* ```
* const { supportFeatures } = useWallet()
*
* return (
*
* {
* supportFeatures.has('post') &&
* post
* }
* {
* supportFeatures.has('cw20-token') &&
* add cw20 token
* }
*
* )
* ```
*
* This type is same as `import type { TerraWebExtensionFeatures } from '@terra-money/web-extension-interface'`
*/
supportFeatures: Set<
'post' | 'sign' | 'sign-bytes' | 'cw20-token' | 'network'
>;
/**
* post transaction
*
* @example
* ```
* const { post } = useWallet()
*
* const callback = useCallback(async () => {
* try {
* const result: TxResult = await post({...ExtensionOptions})
* // DO SOMETHING...
* } catch (error) {
* if (error instanceof UserDenied) {
* // DO SOMETHING...
* } else {
* // DO SOMETHING...
* }
* }
* }, [])
* ```
*
* @param { ExtensionOptions } tx transaction data
* @param terraAddress - does not work at this time. for the future extension
*
* @return { Promise }
*
* @throws { UserDenied } user denied the tx
* @throws { CreateTxFailed } did not create txhash (error dose not broadcasted)
* @throws { TxFailed } created txhash (error broadcated)
* @throws { Timeout } user does not act anything in specific time
* @throws { TxUnspecifiedError } unknown error
*
* @see WalletController#post
*/
post: (tx: ExtensionOptions, terraAddress?: string) => Promise;
/**
* sign transaction
*
* @example
* ```
* const { sign } = useWallet()
*
* const callback = useCallback(async () => {
* try {
* const result: SignResult = await sign({...ExtensionOptions})
*
* // Broadcast SignResult
* const tx = result.result
*
* const lcd = new LCDClient({
* chainID: connectedWallet.network.chainID,
* URL: connectedWallet.network.lcd,
* })
*
* const txResult = await lcd.tx.broadcastSync(tx)
*
* // DO SOMETHING...
* } catch (error) {
* if (error instanceof UserDenied) {
* // DO SOMETHING...
* } else {
* // DO SOMETHING...
* }
* }
* }, [])
* ```
*
* @param { ExtensionOptions } tx transaction data
* @param terraAddress - does not work at this time. for the future extension
*
* @return { Promise }
*
* @throws { UserDenied } user denied the tx
* @throws { CreateTxFailed } did not create txhash (error dose not broadcasted)
* @throws { TxFailed } created txhash (error broadcated)
* @throws { Timeout } user does not act anything in specific time
* @throws { TxUnspecifiedError } unknown error
*
* @see WalletController#sign
*/
sign: (tx: ExtensionOptions, terraAddress?: string) => Promise;
/**
* sign any bytes
*
* @example
* ```
* const { signBytes } = useWallet()
*
* const BYTES = Buffer.from('hello world')
*
* const callback = useCallback(async () => {
* try {
* const { result }: SignBytesResult = await signBytes(BYTES)
*
* console.log(result.recid)
* console.log(result.signature)
* console.log(result.public_key)
*
* const verified: boolean = verifyBytes(BYTES, result)
* } catch (error) {
* if (error instanceof UserDenied) {
* // DO SOMETHING...
* } else {
* // DO SOMETHING...
* }
* }
* }, [])
* ```
*
* @param bytes
*/
signBytes: (bytes: Buffer, terraAddress?: string) => Promise;
/**
* check if tokens are added on the extension
*
* @param chainID
* @param tokenAddrs cw20 token addresses
*
* @return token exists
*
* @see WalletController#hasCW20Tokens
*/
hasCW20Tokens: (
chainID: string,
...tokenAddrs: string[]
) => Promise<{
[tokenAddr: string]: boolean;
}>;
/**
* request add token addresses to browser extension
*
* @param chainID
* @param tokenAddrs cw20 token addresses
*
* @return token exists
*
* @see WalletController#addCW20Tokens
*/
addCW20Tokens: (
chainID: string,
...tokenAddrs: string[]
) => Promise<{
[tokenAddr: string]: boolean;
}>;
/**
* check if network is added on the extension
*
* @param network
*
* @return network exists
*
* @see WalletController#hasNetwork
*/
hasNetwork: (network: Omit) => Promise;
/**
* request add network to browser extension
*
* @param network
*
* @return network exists
*
* @see WalletController#addNetwork
*/
addNetwork: (network: NetworkInfo) => Promise;
/**
* Some mobile wallet emulates the behavior of chrome extension.
* It confirms that the current connection environment is such a wallet.
* (If you are running connect() by checking availableConnectType, you do not need to use this API.)
*
* @see WalletController#isChromeExtensionCompatibleBrowser
*/
isChromeExtensionCompatibleBrowser: () => boolean;
}
````
useConnectedWallet()
```jsx
import { useConnectedWallet } from '@terra-money/wallet-provider'
function Component() {
const connectedWallet = useConnectedWallet()
const postTx = useCallback(async () => {
if (!connectedWallet) return
console.log('wallet addresses are', Object.values(connectedWallet.addresses))
console.log('network is', connectedWallet.network)
console.log('connectType is', connectedWallet.connectType)
const result = await connectedWallet.post({...})
}, [])
return (
postTx}>
Post Tx
)
}
```
useLCDClient()
```jsx
import { useLCDClient } from '@terra-money/wallet-provider';
function Component() {
const lcd = useLCDClient();
const [result, setResult] = useState('');
useEffect(() => {
lcd.treasury.taxRate().then((taxRate) => {
setResult(taxRate.toString());
});
}, []);
return
Result: {result};
}
```
# Links
- [Releases (Changelog)](https://github.com/terra-money/wallet-provider/releases)
# Trouble-shooting guide
wallet-provider contains the original source codes in sourcemaps.

You can check `src/@terra-money/wallet-provider/` in the Chrome Devtools / Sources Tab, and you can also use breakpoints
here for debug.
(It may not be visible depending on your development settings such as Webpack.)
# For Chrome Extension compatible wallet developers
Chrome Extension compatible wallet development guide
### 1. Create dApp for test
There is the `dangerously__chromeExtensionCompatibleBrowserCheck` option to allow you to create a test environment for
wallet development.
By declaring the `dangerously__chromeExtensionCompatibleBrowserCheck`, you can make your wallet recognized as the chrome
extension.
```jsx
/YourWallet/.test(userAgent)
}
>
...
```
### 2. Register your wallet as default allow
If your wallet has been developed,
Please send me your wallet App link (Testlight version is OK)
And send me Pull Request by modifying `DEFAULT_CHROME_EXTENSION_COMPATIBLE_BROWSER_CHECK` in
the `packages/src/@terra-money/wallet-provider/env.ts` file. (or just make an issue is OK)
```diff
export const DEFAULT_CHROME_EXTENSION_COMPATIBLE_BROWSER_CHECK = (userAgent: string) => {
- return /MathWallet\//.test(userAgent);
+ return /MathWallet\//.test(userAgent) || /YourWallet/.test(userAgent);
}
```