Direct contract integration
This page documents direct smart contract patterns that complement the SDK quick-starts. For most JavaScript / TypeScript integrations, use the SDK — it handles approve, multicall, harvest checks, and stake calculator inputs automatically.
Stake GNO on Gnosis Chain
GNO is an ERC-20 token (not the native asset of Gnosis Chain). The Vault must be approved to spend GNO before deposit, so the flow takes three transactions: approve → deposit → mint osGNO.
import { BrowserProvider, Contract, ZeroAddress, parseEther } from 'ethers'
const GNO_ADDRESS = '0x9C58BAcC331c9aa871AFD802DB6379a98e80CEdb'
const eip1193Provider = window.ethereum
const browserProvider = new BrowserProvider(eip1193Provider, {
chainId: 100, // Gnosis Chain
name: 'gnosis',
})
type Input = {
amount: string
userAddress: string
vaultAddress: string
osTokenShares: bigint
}
const stakeAndMintOsGno = async (values: Input) => {
const { amount, userAddress, vaultAddress, osTokenShares } = values
try {
const assets = parseEther(amount)
const signer = await browserProvider.getSigner(userAddress)
const gno = new Contract(GNO_ADDRESS, [
'function approve(address spender, uint256 amount) returns (bool)',
], signer)
const vault = new Contract(vaultAddress, [
'function deposit(uint256 assets, address receiver, address referrer) returns (uint256 shares)',
'function mintOsToken(address receiver, uint256 osTokenShares, address referrer) returns (uint256 assets)',
], signer)
// 1. Approve the Vault to spend GNO
const approveTx = await gno.approve(vaultAddress, assets)
await browserProvider.waitForTransaction(approveTx.hash)
// 2. Deposit GNO into the Vault
const depositTx = await vault.deposit(assets, userAddress, ZeroAddress)
await browserProvider.waitForTransaction(depositTx.hash)
// 3. Mint osGNO against the staked position
const mintTx = await vault.mintOsToken(userAddress, osTokenShares, ZeroAddress)
await browserProvider.waitForTransaction(mintTx.hash)
}
catch (error) {
console.error(error)
}
}
stakeAndMintOsGno({
amount: '0.5', // GNO
osTokenShares: parseEther('0.45'), // osGNO to mint
userAddress: 'USER_ADDRESS',
vaultAddress: 'VAULT_ADDRESS',
})
Find Vault addresses on the vault marketplace or
via the VaultsRegistry contract. Per-network protocol contracts are listed in
Networks → Gnosis.
Transfer osETH or osGNO (ERC-20 operations)
osETH and osGNO are standard ERC-20 tokens with EIP-2612 permit support.
Use them directly via the OsToken contract address — supports transfer,
transferFrom, approve, balanceOf, allowance, permit.
OsToken contract addresses (from contract networks):
- osETH on Ethereum Mainnet:
0xf1C9acDc66974dFB6dEcB12aA385b9cD01190E38 - osETH on Hoodi testnet:
0x7345fC8268459413beE9e9dd327f31283C65Ee7e - osGNO on Gnosis Chain:
0xF490c80aAE5f2616d3e3BDa2483E30C4CB21d1A0
import { BrowserProvider, Contract, parseEther } from 'ethers'
const OSGNO_ADDRESS = '0xF490c80aAE5f2616d3e3BDa2483E30C4CB21d1A0'
const eip1193Provider = window.ethereum
const browserProvider = new BrowserProvider(eip1193Provider, {
chainId: 100, // Gnosis Chain
name: 'gnosis',
})
type Input = {
amount: string
fromAddress: string
toAddress: string
}
const transferOsGno = async (values: Input) => {
const { amount, fromAddress, toAddress } = values
try {
const value = parseEther(amount)
const signer = await browserProvider.getSigner(fromAddress)
const osToken = new Contract(OSGNO_ADDRESS, [
'function transfer(address to, uint256 amount) returns (bool)',
'function transferFrom(address from, address to, uint256 amount) returns (bool)',
'function approve(address spender, uint256 amount) returns (bool)',
'function balanceOf(address account) view returns (uint256)',
'function allowance(address owner, address spender) view returns (uint256)',
], signer)
const balance = await osToken.balanceOf(fromAddress)
if (balance < value) {
throw new Error('Insufficient osGNO balance.')
}
const tx = await osToken.transfer(toAddress, value)
await browserProvider.waitForTransaction(tx.hash)
}
catch (error) {
console.error(error)
}
}
transferOsGno({
amount: '0.5', // osGNO
fromAddress: 'USER_ADDRESS',
toAddress: 'RECIPIENT_ADDRESS',
})
Substitute the Mainnet osETH address (0xf1C9acDc66974dFB6dEcB12aA385b9cD01190E38)
and chainId: 1 for Ethereum Mainnet to operate on osETH.
Use the SDK for everything else
For deposits on Ethereum, redemptions (unstake), boost / unboost, balance and
APY queries, exchange-rate reads, vault data, staker action history — use the
SDK. It handles state freshness checks via Keeper.canHarvest, the
StakeCalculator helper for share computations, and exit-queue polling
automatically.