SDK with web3-react
If your DApp has restricted the loading policy of iframes through CSP, please make sure to add the domain ruby.one
to the whitelist. For example:
Content-Security-Policy: frame-src 'self' https://rube.one;
web3-react is a widely recognized DApp development library developed and maintained by uniswap
. It's the most widely used Ethereum wallet management solution in the React community. In the following tutorial, we will guide you step by step in building a fully functional DApp application, implementing the following essential functions:
- Request wallet connection
- Retrieve connected wallet's account address
- Query the ETH balance of an account
- Query the balance of an ERC20 token
- Transfer ERC20 tokens
Unlike traditional DApp development, we will use the Ruby One SDK
to replace browser plugin wallets like MetaMask
. This allows you to experience all of the above functionalities even with standard mobile browsers. Before getting started, we assume you already have some DApp development experience and are familiar with the usage of react
and web3-react
.
Environment Setup
If you haven't set up a Node.js development environment, please download and install it first.
We use pnpm
as a package manager for the project, replacing npm
. So, after installing Node.js, you need to install pnpm
.
npm i pnpm -g
Create a React Project
We'll use Vite
to create a very lightweight pure frontend React project and enable TypeScript.
pnpm create vite rubyone-web3-react --template react-ts
Next, install the necessary dependencies. The Ruby One SDK is provided through the @rubyone/jssdk
package.
cd rubyone-web3-react
pnpm install @rubyone/jssdk @web3-react/core @web3-react/eip1193 react-toastify ethers@5.7.2
In this example, we use the latest version of web3-react
, which is the 8.x
major version. However, since web3-react
hasn't yet provided support for ethers@6
, we'll temporarily use the older version ethers@5.7.2
as a substitute.
Create a Provider
@rubyone/jssdk
offers a core class named RubyOneProvider
, which facilitates communication with Ruby One. Our first step is to create a new instance of it. Write the following code in the sdk.ts
file:
import { RubyOneProvider } from '@rubyone/jssdk'
const rubyOneProvider = new RubyOneProvider()
export default rubyOneProvider
It's straightforward to use; we only need to instantiate a new RubyOneProvider
, and that's it.
Configure Connector
web3-react
offers various Connector types. When you aim to connect to different wallet types, you need to select the appropriate Connector. As we provide an eip1193
interface, we'll utilize the Connector provided by @web3-react/eip1193
. Create a new code file connector.ts
and write the following:
import { initializeConnector } from '@web3-react/core'
import { EIP1193 } from '@web3-react/eip1193'
import rubyOneProvider from './sdk'
export const [eip1193, hooks] = initializeConnector<EIP1193>(
actions => new EIP1193({ actions, provider: rubyOneProvider })
)
The code above sets up an eip1193
connector and its associated react hooks, built upon the rubyOneProvider
we established in the prior step. It bridges web3-react
and Ruby One
, making it very straightforward.
Write the Hook
As mentioned earlier, we want to implement a series of wallet functionalities based on the SDK. Now, we'll craft a react hook to execute these functionalities. For demonstration purposes, we deployed an ERC20 test token on the Mumbai
chain. You can replace it with your own EVM chain and token.
https://mumbai.polygonscan.com/address/0xfffdfc767016f7a3baa9895d70f895302f82cfe9
Create a code file named useWallet.ts
and write:
import { useCallback, useEffect, useState } from 'react'
import { utils, Contract } from 'ethers'
import ERC20 from './ERC20.json'
import { eip1193, hooks } from './connector'
const RBT = '0xffFDFC767016f7a3Baa9895D70f895302f82Cfe9'
const DEAD = '0x000000000000000000000000000000000000dEaD'
export default function useWallet() {
const account = hooks.useAccount()
const chainId = hooks.useChainId()
const provider = hooks.useProvider()
const [ethBalance, setEthBalance] = useState(0)
const [rbtBalance, setRbtBalance] = useState(0)
const getData = useCallback(async () => {
if (!account || !provider) return
const contract = new Contract(RBT, ERC20, provider)
const ethBalance = await provider.getBalance(account)
const rbtBalance = await contract.balanceOf(account)
const ethValue = Number(utils.formatUnits(ethBalance))
const rbtValue = Number(utils.formatUnits(rbtBalance))
setEthBalance(ethValue)
setRbtBalance(rbtValue)
}, [provider, account])
const transferRBT = useCallback(async () => {
if (!account) return
const signer = provider?.getSigner()
const contract = new Contract(RBT, ERC20, signer)
const value = utils.parseUnits('10')
const trans = await contract.transfer(DEAD, value)
await trans.wait(1)
getData()
return trans.hash
}, [provider, account, getData])
const init = useCallback(async () =>{
eip1193.connectEagerly()
}, [])
const connect = useCallback(async () => {
eip1193.activate()
}, [])
useEffect(() => {
getData()
}, [getData])
useEffect(() => {
init()
}, [init])
return {
chainId,
account,
ethBalance,
rbtBalance,
provider,
connect,
getData,
transferRBT
}
}
If you've used web3-react
with MetaMask
, you'll find the code above quite familiar. The usage is almost identical, with virtually no differences. This illustrates our emphasis on a zero learning curve.
Write the Component
Finally, we'll create a React component interface to interact with users. Modify the default code file index.tsx
and write:
import { useState } from 'react'
import { toast } from 'react-toastify'
import useWallet from './useWallet'
import '@rubyone/jssdk/dist/style.css'
export default function Web3React() {
const [loading, setLoading] = useState(false)
const { ethBalance, rbtBalance, account, chainId, connect, transferRBT } =
useWallet()
const onTransfer = async () => {
try {
setLoading(true)
await toast.promise(transferRBT, {
pending: 'Sending...',
success: 'Transaction successful',
error: 'Transaction failed'
})
} finally {
setLoading(false)
}
}
return (
<div className="p-5">
{!account ? (
<button className="my-btn" onClick={connect}>
Connect Wallet
</button>
) : (
<div className="grid gap-3">
<div>Chain ID: {chainId}</div>
<div>Account: {account}</div>
<div>ETH Balance: {ethBalance}</div>
<div>RBT Balance: {rbtBalance}</div>
<div>
<button
className="my-btn"
disabled={rbtBalance <= 0 || loading}
onClick={onTransfer}
>
Transfer 10 RBT
</button>
</div>
</div>
)}
</div>
)
}
Be sure not to forget to include the CSS style files required by the SDK into the page.
import '@rubyone/jssdk/dist/style.css'
Now, we've finished the entire example development. Execute the command below to start the preview:
pnpm dev
Conclusion
Now, your DApp has successfully substituted MetaMask with Ruby One. Users can more easily manage and operate their assets, while developers can enjoy a more concise, efficient, and secure solution.