Skip to main content

SDK with web3-react

caution

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.

https://nodejs.org/

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
tip

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>
)
}
caution

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.