How to integrate the G$ token
Last updated
Was this helpful?
Last updated
Was this helpful?
The G$ token is an compliant token used to power Universal Basic Income (UBI) within the GoodDollar ecosystem. This guide helps you integrate G$ into your dApp, with practical examples using Viem/Wagmi (React) and Ethers v6 (JavaScript). You'll learn how to use transferAndCall
for streamlined contract interactions, and when to fall back on the standard approve
+ transferFrom
method.
Before integrating, ensure you are familiar with:
ERC-20 and ERC-677/ERC-777 token standards
React & Wagmi hooks (for frontend apps)
Ethers v6 (for browser or Node environments)
The G$ token contract on supported chains (e.g. Celo, Fuse, Ethereum)
For making G$ transfers for your dapps users, below are some options to consider
transferAndCall
(Recommended)Use transferAndCall
to send G$ and call a contract function in a single transaction. This improves gas efficiency and simplifies the user experience when the receiving contract implements:
In a marketplace dApp, a user can pay 10 G$ and specify an item ID, completing a purchase in one click.
Example: Viem/Wagmi (React)
Overview
The GoodDollar token (G$) also embraces the ERC-777 standard, offering another advanced way to handle token interactions, particularly when sending tokens to smart contracts. Similar to ERC-677's transferAndCall
, ERC-777 allows for a token transfer and a subsequent action on the recipient contract within a single transaction. This is achieved using the send
function and a "tokens received hook."
Key Benefits of using ERC-777 send
:
Single Transaction Efficiency: Just like transferAndCall
, the send
function in ERC-777 transfers G$ tokens and notifies the recipient contract in one go, saving on gas and simplifying the user experience.
Standardized Reception: Recipient contracts can implement the tokensReceived
hook. This function is automatically called by the G$ token contract when it receives tokens via the send
function.
How it Works:
When you use the send
function from an ERC-777 compliant G$ token contract:
send(address to, uint256 amount, bytes calldata data)
The G$ tokens are transferred to the to
address.
If the to
address is a contract that implements the IERC777Recipient
interface, its tokensReceived
hook will be called with details of the transfer:
operator
: The address that initiated the token movement (could be the from
address or an authorized operator).
from
: The address that sent the tokens.
to
: The recipient address (your contract).
amount
: The amount of G$ tokens received.
userData
: Arbitrary data passed by the sender, similar to the data
in transferAndCall
.
operatorData
: Arbitrary data passed by the operator, if different from the sender.
When to Consider ERC-777:
If you're building contracts that need to react to incoming G$ tokens in a standardized way.
When you appreciate the broader features of ERC-777, such as operator approvals (which offer more flexibility than ERC-20 allowances) and sender/recipient hooks for various use cases.
If you aim for compatibility with other protocols or tools that specifically leverage ERC-777 hooks.
Example:
While we showcased transferAndCall
with our FuseFaucetV2 for ERC-677, a contract designed to work with ERC-777 G$ tokens would implement the tokensReceived
Hook like this:
approve
+ transferFrom
Use this method when the receiving contract does not implement onTokenTransfer
, or when the dApp needs explicit allowance control. This requires two transactions:
Approve the contract to spend G$
Call the function that uses transferFrom
internally
Step 1: Approve G$ spending
Step 2: Call buy function
G$ transfers may include a protocol fee via the _processFees
function. This can result in the recipient receiving slightly less than the sent amount. Always account for this in your logic or inform users ahead of time.
G$ uses 18 decimals on Celo, and 2 decimals on Fuse . Use utilities like parseUnits
or formatUnits
for correct calculations.
Check the user's balance with balanceOf()
before calling transfer methods to reduce the risk of failed transactions.
Ensure the recipient contract supports this function for transferAndCall
to succeed:
If not, fall back to approve + transferFrom
or add a wrapper contract that can handle the callback.
Provider updates: Use BrowserProvider
instead of Web3Provider
Signers: getSigner()
is now async
Utility changes: Use ethers.parseUnits
, ethers.AbiCoder.defaultAbiCoder().encode
BigInt usage: Native BigInt replaces BigNumber throughout the library
transferAndCall
Send G$ + buy item in one tx
✅ Gas-efficient ✅ One-click UX
⚠️ Requires onTokenTransfer
approve + transferFrom
Approve first, then buy
✅ Compatible with most ERC20 apps
❌ Two steps ❌ Higher gas
You can see an example implementation of this in our faucet contract: