Skip to main content

ve(3,3): lock, vote, earn

The ve(3,3) system aligns liquidity incentives with token holders. The cycle is:

  1. Lock BARK into a veNFT to get voting power.
  2. Vote each epoch for the pools you want emissions directed to.
  3. Earn the trading fees and bribes from the pools you voted for, plus a rebase.
  4. Liquidity providers stake their position NFTs into gauges to receive the BARK emissions.

Epochs are weekly (Thursday 00:00 UTC boundaries, THENA-style).

1. Lock BARK → veNFT

import { Contract, MaxUint256, parseUnits } from 'ethers';

const BARK = '0x776347AF16E01BE5CdC028b556bE029C156024a2';
const VOTING_ESCROW = '0x19cDA4B6EEb4AB71033Ef926c9ee096Af25E8bdd';

const ERC20_ABI = ['function approve(address,uint256) returns (bool)'];
const VE_ABI = [
'function create_lock(uint256 _value, uint256 _lock_duration) returns (uint256 tokenId)',
'function increase_amount(uint256 _tokenId, uint256 _value)',
'function increase_unlock_time(uint256 _tokenId, uint256 _lock_duration)',
'function withdraw(uint256 _tokenId)',
];

await (await new Contract(BARK, ERC20_ABI, signer).approve(VOTING_ESCROW, MaxUint256)).wait();

const ve = new Contract(VOTING_ESCROW, VE_ABI, signer);
const amount = parseUnits('1000', 18);
const twoYears = 2 * 365 * 24 * 60 * 60; // max lock
const tx = await ve.create_lock(amount, twoYears);
await tx.wait();
// The veNFT tokenId is emitted in the Deposit event; read it from the receipt logs.

Longer locks give more voting power. Extend with increase_unlock_time, add BARK with increase_amount, and withdraw once the lock has fully expired.

2. Vote for pools

Each epoch, allocate your veNFT's voting power across pools. Weights are relative — they're normalized to your total power.

const VOTER = '0x32d5633feB481A9a0A5C483C728CA75c3b29d1d8';
const VOTER_ABI = [
'function vote(uint256 _tokenId, address[] _poolVote, uint256[] _weights)',
'function reset(uint256 _tokenId)',
'function poke(uint256 _tokenId)',
];

const voter = new Contract(VOTER, VOTER_ABI, signer);
const tx = await voter.vote(
veTokenId,
['0xPoolA', '0xPoolB'],
[70, 30], // 70% / 30% of voting power
);
await tx.wait();

A veNFT can vote once per epoch. Use reset to clear votes, or poke to re-apply your last weights with refreshed voting power.

3. Claim rewards

After voting you accrue fees and bribes from the pools you backed, plus a rebase from the RewardsDistributor.

const VOTER_ABI = [
'function claimRewards(address[] _gauges)',
'function claimBribes(address[] _bribes, address[][] _tokens, uint256 _tokenId)',
'function claimFees(address[] _fees, address[][] _tokens, uint256 _tokenId)',
];
const REWARDS_DISTRIBUTOR = '0xA5c526a74b2262503200Bd4359212014cfE31f95';
const RD_ABI = ['function claim(uint256 _tokenId) returns (uint256)'];

const voter = new Contract(VOTER, VOTER_ABI, signer);
await (await voter.claimBribes(bribeAddrs, bribeTokens, veTokenId)).wait();
await (await voter.claimFees(feeAddrs, feeTokens, veTokenId)).wait();

// Claim the weekly rebase into your veNFT.
await (await new Contract(REWARDS_DISTRIBUTOR, RD_ABI, signer).claim(veTokenId)).wait();

Resolve a pool's bribe/fee contracts from its gauge — see the VoterV4 reference.

Stake a position for emissions

Liquidity providers earn BARK emissions by staking their position NFT into the pool's GaugeV2_NFT. The gauge takes custody of the NFT while staked.

const GAUGE_ABI = [
'function deposit(uint256 tokenId)',
'function withdraw(uint256 tokenId)',
'function getReward(address account)',
];
const NPM_ABI = ['function approve(address to, uint256 tokenId)'];

// 1. Resolve the gauge for the pool.
const gaugeAddr = await voter.gauges(poolAddress); // VoterV4.gauges(pool)

// 2. Approve the gauge to take the position NFT, then deposit.
await (await new Contract(NPM, NPM_ABI, signer).approve(gaugeAddr, positionTokenId)).wait();
const gauge = new Contract(gaugeAddr, GAUGE_ABI, signer);
await (await gauge.deposit(positionTokenId)).wait();

// 3. Claim accrued BARK emissions any time.
await (await gauge.getReward(await signer.getAddress())).wait();

// 4. Unstake — returns the NFT to you.
await (await gauge.withdraw(positionTokenId)).wait();

Gotchas

  • Approve the right spender. Locking approves BARK to VotingEscrow; staking approves the position NFT to the specific gauge.
  • One vote per epoch. Re-voting in the same epoch reverts (onlyNewEpoch). Use poke.
  • Custody. While staked, the gauge holds your position NFT — withdraw to get it back.
  • Claim explicitly. Emissions, fees, bribes, and the rebase are all pull-based; nothing is auto-sent to your wallet.