Unibit
A TypeScript / React project that integrates with the UniBit API to provide financial functionality like wallet connectivity, real-time data fetching, and smart contract interaction.
Install / Use
/learn @smartcodemotive/UnibitREADME
Features
- Wallet connectivity using Onboard.
- Supported networks and UI indicators when the network is not supported.
- TokenInput with validations src/components/token/TokenInput.tsx
- Spend token src/components/token/TokenSpend.tsx
- Configurable Token list modal src/components/token/TokenModal.tsx
- Auto-generated types for smart contracts.
- Transactions Life cycle. With homemade toasts.
- Support for connecting with subgraphs.
- Typed hooks to read data from smart contracts.
- Typed hooks to call methods of smart contracts.
- Web3ConnectionProvider: it has many helpers that let you know about the status of a wallet connection.
- React suspense.
- Fetching data through useSWR.
- Basic layout with support for mobile (menu, sidebar, content).
- Cookie policy banner.
- Many UI components have been created with styled-components.
- Light and dark mode.
Getting Started
You can fork this repo using Github templates. Here is an explanation of how to do it creating-a-repository-from-a-template.
Installation
- Install dependencies
yarn install
- Make a copy of
.env.exampleto.env.localand assign values to the variables
cp .env.example .env.local
- Run the app. It will be available at
http://localhost:3000.
yarn dev
File structure
This starter is based on nextjs.
Inside the src folder you will find:
components: Stateless components. Mostly focused on UI.constantscontracts: All the contracts your dApp will interact with have to be configured here.hooks: React hooks.queries: Hooks that retrieve data from the blockchain (or api). shouldn't be used directly from inside a Componentpresentation: Hooks that transform and combine data to show in the UI. Should be used in componentsmutations: Hooks that provides a mutation to write data on the blockchain (or api). Should be used in components
pagePartials: This folder was thought to be used as a complement to the files inpagesfolder. Sometimes a page can become big and this is the place where helper components related to a particular page will reside. We suggest having one folder per page.providers: React providers.subgraph: Queries and configuration for Subgraphs.theme: general UI styles.utils: Some utility functions.config: Global configurations.
How it works
The main entry point of this starter is the src/config/web3.ts file, where you can set up the supported chains.
export const Chains = {
mainnet: 1,
goerli: 5,
} as const
Then, you can start adding the smart-contract you want to interact with. To do so, you need to edit this file src/contracts/contracts.ts. Note that you will need to get the ABI. If a new contract is added, you will need to run this script yarn types-gen, it will generate the Typescript types for the ABI.
Once your contracts are configured, you can make use of the following react hooks.
useContractCall: will allow you to call a smart-contract method or methods, it is typed, so it will let you know the arguments of the methods as well as the response it returns.
// first, you need to import the types from the auto-generated types.
import { ERC20, ERC20__factory } from '@/types/typechain'
// create a contract instance. Note that the second parameter is the name we have assigned when we defined the smart-contract in `src/constants/config/contracts.ts`
const erc20 = useContractInstance(ERC20__factory, 'USDC')
// create an array with the methods selectors you want to call.
const calls = [erc20.balanceOf, erc20.decimals] as const
// make use of useContractCall to call them.
const [{ data }, refetch] = useContractCall<ERC20, typeof calls>(calls, [
[address || ZERO_ADDRESS],
[],
])
// data will be typed.
const [usdcBalance, usdcDecimals] = data || [ZERO_BN, 18]
const sendTx = useTransaction()
const tx = erc20.transfer(addressToSend, valueToSend)
try {
const transaction = await sendTx(tx)
const receipt = await transaction.wait()
console.log(receipt)
} catch (error) {
console.error(error)
}
we also provide a Button helper that works like so:
<TxButton
disabled={disableSubmit}
onMined={(r) => {
// do something
}}
onSend={(tx) => tx && clearForm()}
tx={() => erc20.transfer(addressToSend, valueToSend)}
>
Send
</TxButton>
How to add a new smart contract
- Put your contract ABI file in
src/contracts/abisas a.jsonfile. - Go to
src/contracts/contracts.ts. - Provide a key name for your contract in
const contracts = {...}. - Provide a contract address for all the chains you have configured.
- Import your contract ABI file and add it to the contract key created in step 3.
Example for an ERC20 contract (USDC):
import ERC_20_abi from '@/src/abis/ERC20.json'
export const contracts = {
// Other contracts
USDC: {
address: {
[Chains.mainnet]: '0x123...',
[Chains.goerli]: '0x456...',
},
abi: ERC_20_abi,
},
} as const
Subgraph consumption
To interact with a subgraph you should follow these steps:
- Go to
src/subgraph/subgraph.tsand a new entry to theenum subgraphName. - Go to
src/subgraph/subgraph-endpoints.jsonand complete the object following the structure{ chain: [name: endpoint] }.
{
"5": {
"subgraphName": "subgraph endpoint"
}
}
- Create queries for the subgraph in the folder
src/queries. Remember that we already have asrc/subgraph/queries/examples.tsthat you can use as a guide. - run
yarn subgraph-codegen. This step has to be done every time you add or modify a query. - Consume now you will have all the queries typed and available to be called.
import { SubgraphName, getSubgraphSdkByNetwork } from '@/src/constants/config/subgraph'
const { appChainId } = useWeb3Connection()
const gql = getSubgraphSdkByNetwork(appChainId, SubgraphName.Rentals)
const res = gql.useSubgraphErrors()
console.log({ res: res.data?._meta }) // res is typed 😎
export const Paragraph = styled.p`
color: ${({ theme: { colors } }) => colors.textColor}; // from light.ts or dark.ts
font-family: ${({ theme: { fonts } }) => fonts.family}; // from common.ts
font-size: 1.5rem;
font-weight: 400;
`
Tokens
We use Token Lists to provide a list of tokens to the app. You can find the map of tokens sources in src/config/web3.ts, at TokensLists. You can add a new token source to the map by following the same structure as the other lists.
export const TokensLists = {
'1INCH': 'https://gateway.ipfs.io/ipns/tokens.1inch.eth',
COINGECKO: 'https://tokens.coingecko.com/uniswap/all.json',
OPTIMISM: 'https://static.optimism.io/optimism.tokenlist.json',
} as const
To properly support the token images source, you need to whitelist the domains in next.config.js, at images.domains:
images: {
domains: ['tokens.1inch.io', 'ethereum-optimism.github.io', 'assets.coingecko.com']
}
To consume them, we implemented the hook useTokensLists. You can find a usage example in src/components/token/TokenDropdown.tsx. And see it in action in our example page.
Protocol Data
useMarketsData
filepath: src/hooks/presentation/useAgaveMarketsData.tsx
React hook that can be used to get Agave markets information from an array of tokens addresses (reserve tokens addresses). The hook accepts an array of tokens addresses and returns data for each token as marketData, such as priceData, reserveData, assetData, and incentiveData. Additionally, the hook provides a number of functions that can be used to get data about a single market from the result, such as getMarketSize, getTotalBorrowed, getDepositAPY, getBorrowRate, and getIncentiveRate.
