Barkswap Docs

Barkswap Contracts & Integration Documentation

Table of Contents

  1. Architecture Overview
  2. Deployed Contract Addresses
  3. Core DEX — Algebra Integral
  4. Plugin System
  5. Farming System
  6. ve(3,3) Governance Layer
  7. Complete User Flow Diagrams
  8. Programmatic Integration Guide
  9. Key Constants & Parameters
  10. Error Reference
  11. Events Reference
  12. Security Considerations

1. Architecture Overview

Barkswap is a full-stack ve(3,3) DEX built on top of Algebra Integral concentrated liquidity AMM, deployed on DogeOS Testnet. The system has three major layers:

┌──────────────────────────────────────────────────────────────────────────┐
│                        USER INTERFACES                                   │
│              (Frontend / Scripts / External Integrators)                  │
├────────────┬──────────────┬────────────────┬────────────────────────────┤
│            │              │                │                            │
│   SWAP     │  LIQUIDITY   │    FARMING     │    GOVERNANCE (ve3,3)      │
│            │              │                │                            │
│ SwapRouter │ NFTPosition  │ FarmingCenter  │  VotingEscrow  VoterV4    │
│ QuoterV2   │   Manager    │ EternalFarming │  Minter        Gauges     │
│            │              │                │  RewardsDist   Bribes     │
├────────────┴──────────────┴────────────────┴────────────────────────────┤
│                        ALGEBRA CORE                                      │
│                                                                          │
│  AlgebraFactory ──► AlgebraPool (concentrated liquidity, dynamic fees)   │
│  AlgebraPoolDeployer    CommunityVault    BasePluginV1Factory            │
├──────────────────────────────────────────────────────────────────────────┤
│                      PLUGIN LAYER                                        │
│  DynamicFeePlugin │ VolatilityOracle │ FarmingProxy │ SlidingFee         │
└──────────────────────────────────────────────────────────────────────────┘

Key Design Principles

  • Concentrated Liquidity: Liquidity positions are bounded by tick ranges (like Uniswap V3), allowing capital-efficient market making.
  • No Fixed Fee Tiers: Unlike Uniswap V3, Algebra uses a single pool per token pair with dynamic fees adjusted by plugins based on volatility.
  • Plugin Architecture: Each pool can have a plugin that hooks into swap/mint/burn lifecycle events for custom logic (oracles, dynamic fees, farming integration).
  • ve(3,3) Tokenomics: Token holders lock BARK into veNFTs, vote on pool emissions, earn bribes, and receive anti-dilution rebases.
  • Persistent Voting (VoterV4): Votes persist across epochs — no need to re-vote weekly.

2. Deployed Contract Addresses

Algebra DEX Core

ContractAddressDescription
AlgebraPoolDeployer0xeb4E9b84990C7c07D5205D35647A29de1B33dE7eDeploys new pool instances
AlgebraFactory0x099F459D81ce99aD3eCE1Ca2c77d9869883d2457Pool registry & creation
CommunityVault0x49645d2050f8924379C9c39E1a3DeF5FF820866CCollects community fees
VaultFactory0x70B0De92e5d40d48E0D1f1b645Ab7AFC1f50e90eCreates vaults for pools
BasePluginV1Factory0xa60884E876eb272fC1FFcb40E89836D89bc37219Creates plugins for pools
WrappedNative (WDOGE)0xF6BDB158A5ddF77F1B83bC9074F6a472c58D78aEWrapped native token

Algebra Periphery

ContractAddressDescription
SwapRouter0x77147f436cE9739D2A54Ffe428DBe02b90c0205eExecute swaps
NonfungiblePositionManager0x4Bb4A5CF44028519908D6B4A90C570fEaA8c9a07Manage LP NFT positions
Quoter0xFA0c644c1Ea307538cA7d8Fb0582e4b52ecE3ABEOff-chain price quotes (V1)
QuoterV20xcEF56157baaB2Fe9D16ccF0eB4a9Df354380257DOff-chain price quotes (V2, recommended)
TickLens0x0b990916aE4e30367F7949020C71491787154BBBRead tick data
AlgebraInterfaceMulticall0xF59eA3173B5FBA06DE0854D754Bb6142Aed2CE65Batch read calls
CustomPoolEntryPoint0xa7D1592c3c0EE11da11f5856181a8D4c8E595586Custom pool deployer entry

Farming

ContractAddressDescription
AlgebraEternalFarming0xccdF60E4EfE4fB84183595Ad897E30a9ED4042EAEternal farming incentives
FarmingCenter0xc3BfA70A15556A01D35Fca03D8905A2afB7A5DbCUser-facing farming manager

ve(3,3) Governance

ContractAddressDescription
VeToken (BARK)0xB4e0150c0CF27Aa0005BA494B17322a3e2554E4dBase governance token
VeArtProxy0x336BC58ced0921edb572348bDb8535faf4bb444EDynamic veNFT SVG art
VotingEscrow0xC7457Ba36B06C891cf094EDc9d3e2eeB1c624bb5Lock tokens → veNFT
PermissionsRegistry0xfc6C03aFA7E184853B1ff258C7EDd4aF87e7Ea42Role management
RewardsDistributor0x7f06c051934930257f2F4312fB74b4027d4E62B1ve rebase distribution
GaugeFactoryNFT0x84ee580c8a9ba1c77A4A7e916f58Ea76f254E7edCreates Algebra NFT gauges
BribeFactory0xa2C8de87E00806945Bc46EA529Ce7FcA5b6785C6Creates bribe contracts
VoterV4 (Proxy)0xDb036a6C675b775e260F685d6634b70A4C9136b9Persistent voting + gauge management
Minter0x4270CE6Cae2f730fceD7c21aD6BE135977E18aA2Weekly emission control

3. Core DEX — Algebra Integral

3.1 Factory & Pool Creation

The AlgebraFactory manages pool creation and global defaults. Unlike Uniswap V3, there is one pool per token pair (no fee tiers).

Creating a Pool

hljs solidity
// IAlgebraFactory
function createPool(
    address tokenA,
    address tokenB,
    bytes calldata data    // Plugin initialization data (can be empty: "0x")
) external returns (address pool);
  • Tokens can be in any order; factory sorts them internally.
  • Reverts if pool already exists.
  • On creation, the factory calls defaultPluginFactory to auto-attach a plugin (oracle, dynamic fee).
  • The pool must be initialized separately with a starting price before any swaps or mints.

Initializing a Pool

hljs solidity
// IAlgebraPool
function initialize(uint160 initialPrice) external;
  • initialPrice is a Q64.96 sqrt price = $\sqrt{\frac{\text{token1}}{\text{token0}}} \times 2^{96}$
  • Should be done atomically with pool creation to prevent front-running.
  • The periphery provides a helper:
hljs solidity
// IPoolInitializer (on NonfungiblePositionManager)
function createAndInitializePoolIfNecessary(
    address token0,       // Must be sorted: token0 < token1
    address token1,
    address deployer,     // address(0) for standard pools
    uint160 sqrtPriceX96,
    bytes calldata data   // Plugin init data
) external payable returns (address pool);

Reading Pool State

hljs solidity
// Safe method (checks reentrancy lock)
function safelyGetStateOfAMM() external view returns (
    uint160 sqrtPrice,       // Current sqrt price Q64.96
    int24   tick,            // Current tick
    uint16  lastFee,         // Current fee in 1e-6 (e.g., 500 = 0.05%)
    uint8   pluginConfig,    // Active plugin hooks bitmap
    uint128 activeLiquidity, // In-range liquidity
    int24   nextTick,        // Next initialized tick
    int24   previousTick     // Previous initialized tick
);

// Raw global state (caller must verify reentrancy lock)
function globalState() external view returns (
    uint160 price,
    int24   tick,
    uint16  lastFee,
    uint8   pluginConfig,
    uint16  communityFee,    // Community fee in 1e-3 (e.g., 100 = 10%)
    bool    unlocked         // Must be true for safe reads
);

Pool Lookup

hljs solidity
// Get existing pool by token pair
function poolByPair(address tokenA, address tokenB) external view returns (address pool);

// Compute pool address deterministically (no on-chain call needed)
function computePoolAddress(address token0, address token1) external view returns (address pool);

3.2 Pool Actions

These are low-level pool functions. Most users should use the periphery contracts (SwapRouter, NonfungiblePositionManager).

Swap

hljs solidity
function swap(
    address recipient,       // Address to receive output tokens
    bool    zeroToOne,       // true = token0 → token1, false = token1 → token0
    int256  amountRequired,  // Positive = exact input, Negative = exact output
    uint160 limitSqrtPrice,  // Price limit (MIN_SQRT_RATIO+1 or MAX_SQRT_RATIO-1)
    bytes   calldata data    // Passed to algebraSwapCallback
) external returns (int256 amount0, int256 amount1);

The caller must implement IAlgebraSwapCallback:

hljs solidity
function algebraSwapCallback(
    int256 amount0Delta,  // Positive = you owe the pool this much token0
    int256 amount1Delta,  // Positive = you owe the pool this much token1
    bytes calldata data
) external;

Mint (Add Liquidity)

hljs solidity
function mint(
    address leftoversRecipient,  // Receives surplus tokens
    address recipient,           // Position owner
    int24   bottomTick,          // Lower tick boundary
    int24   topTick,             // Upper tick boundary
    uint128 liquidityDesired,    // Amount of liquidity to add
    bytes   calldata data        // Passed to algebraMintCallback
) external returns (uint256 amount0, uint256 amount1, uint128 liquidityActual);

Burn (Remove Liquidity)

hljs solidity
function burn(
    int24   bottomTick,
    int24   topTick,
    uint128 amount,       // Liquidity to remove
    bytes   calldata data // Passed to plugin
) external returns (uint256 amount0, uint256 amount1);

Note: burn does NOT transfer tokens. You must call collect afterward.

Collect (Withdraw Tokens)

hljs solidity
function collect(
    address recipient,
    int24   bottomTick,
    int24   topTick,
    uint128 amount0Requested,
    uint128 amount1Requested
) external returns (uint128 amount0, uint128 amount1);

Flash Loan

hljs solidity
function flash(
    address recipient,
    uint256 amount0,
    uint256 amount1,
    bytes   calldata data  // Passed to algebraFlashCallback
) external;

Callback:

hljs solidity
function algebraFlashCallback(
    uint256 fee0,   // Fee owed in token0
    uint256 fee1,   // Fee owed in token1
    bytes calldata data
) external;

3.3 SwapRouter — User-Facing Swaps

The SwapRouter (0x77147f436cE9739D2A54Ffe428DBe02b90c0205e) is the recommended entry point for swaps. It handles callbacks, deadline checks, and slippage protection.

Exact Input (Single Pool)

hljs solidity
struct ExactInputSingleParams {
    address tokenIn;
    address tokenOut;
    address deployer;          // address(0) for standard pools
    address recipient;
    uint256 deadline;          // block.timestamp deadline
    uint256 amountIn;
    uint256 amountOutMinimum;  // Slippage protection
    uint160 limitSqrtPrice;    // 0 for no limit
}

function exactInputSingle(ExactInputSingleParams calldata params)
    external payable returns (uint256 amountOut);

Exact Input (Multi-Hop)

hljs solidity
struct ExactInputParams {
    bytes   path;              // Encoded path: tokenIn + deployer + tokenOut [+ deployer + tokenN ...]
    address recipient;
    uint256 deadline;
    uint256 amountIn;
    uint256 amountOutMinimum;
}

function exactInput(ExactInputParams calldata params)
    external payable returns (uint256 amountOut);

Exact Output (Single Pool)

hljs solidity
struct ExactOutputSingleParams {
    address tokenIn;
    address tokenOut;
    address deployer;          // address(0) for standard pools
    address recipient;
    uint256 deadline;
    uint256 amountOut;         // Desired output amount
    uint256 amountInMaximum;   // Max input (slippage protection)
    uint160 limitSqrtPrice;
}

function exactOutputSingle(ExactOutputSingleParams calldata params)
    external payable returns (uint256 amountIn);

Exact Output (Multi-Hop)

hljs solidity
struct ExactOutputParams {
    bytes   path;              // Encoded path (REVERSED order)
    address recipient;
    uint256 deadline;
    uint256 amountOut;
    uint256 amountInMaximum;
}

function exactOutput(ExactOutputParams calldata params)
    external payable returns (uint256 amountIn);

Fee-on-Transfer Token Support

hljs solidity
function exactInputSingleSupportingFeeOnTransferTokens(
    ExactInputSingleParams calldata params
) external payable returns (uint256 amountOut);

3.4 Quoter — Price Estimation

Use QuoterV2 (0xcEF56157baaB2Fe9D16ccF0eB4a9Df354380257D) for off-chain price quotes. These functions are not view — call them via eth_call (static call).

Quote Exact Input Single

hljs solidity
struct QuoteExactInputSingleParams {
    address tokenIn;
    address tokenOut;
    address deployer;       // address(0) for standard pools
    uint256 amountIn;
    uint160 limitSqrtPrice; // 0 for no limit
}

function quoteExactInputSingle(QuoteExactInputSingleParams memory params)
    external returns (
        uint256 amountOut,
        uint256 amountIn,
        uint160 sqrtPriceX96After,
        uint32  initializedTicksCrossed,
        uint256 gasEstimate,
        uint16  fee
    );

Quote Exact Input (Multi-Hop)

hljs solidity
function quoteExactInput(bytes memory path, uint256 amountInRequired)
    external returns (
        uint256[] memory amountOutList,
        uint256[] memory amountInList,
        uint160[] memory sqrtPriceX96AfterList,
        uint32[]  memory initializedTicksCrossedList,
        uint256 gasEstimate,
        uint16[]  memory feeList
    );

Quote Exact Output Single

hljs solidity
struct QuoteExactOutputSingleParams {
    address tokenIn;
    address tokenOut;
    address deployer;
    uint256 amount;          // Desired output
    uint160 limitSqrtPrice;
}

function quoteExactOutputSingle(QuoteExactOutputSingleParams memory params)
    external returns (
        uint256 amountOut,
        uint256 amountIn,
        uint160 sqrtPriceX96After,
        uint32  initializedTicksCrossed,
        uint256 gasEstimate,
        uint16  fee
    );

3.5 NonfungiblePositionManager — Liquidity Positions

The NonfungiblePositionManager (0x4Bb4A5CF44028519908D6B4A90C570fEaA8c9a07) wraps liquidity positions as ERC-721 NFTs.

Mint New Position

hljs solidity
struct MintParams {
    address token0;          // Must be sorted: token0 < token1
    address token1;
    address deployer;        // address(0) for standard pools
    int24   tickLower;       // Must be multiple of tickSpacing
    int24   tickUpper;       // Must be multiple of tickSpacing
    uint256 amount0Desired;
    uint256 amount1Desired;
    uint256 amount0Min;      // Slippage protection
    uint256 amount1Min;      // Slippage protection
    address recipient;
    uint256 deadline;
}

function mint(MintParams calldata params) external payable
    returns (uint256 tokenId, uint128 liquidity, uint256 amount0, uint256 amount1);

Increase Liquidity

hljs solidity
struct IncreaseLiquidityParams {
    uint256 tokenId;
    uint256 amount0Desired;
    uint256 amount1Desired;
    uint256 amount0Min;
    uint256 amount1Min;
    uint256 deadline;
}

function increaseLiquidity(IncreaseLiquidityParams calldata params) external payable
    returns (uint128 liquidity, uint256 amount0, uint256 amount1);

Decrease Liquidity

hljs solidity
struct DecreaseLiquidityParams {
    uint256 tokenId;
    uint128 liquidity;       // Amount of liquidity to remove
    uint256 amount0Min;
    uint256 amount1Min;
    uint256 deadline;
}

function decreaseLiquidity(DecreaseLiquidityParams calldata params) external payable
    returns (uint256 amount0, uint256 amount1);

Collect Fees

hljs solidity
struct CollectParams {
    uint256 tokenId;
    address recipient;
    uint128 amount0Max;      // Use type(uint128).max to collect all
    uint128 amount1Max;
}

function collect(CollectParams calldata params) external payable
    returns (uint256 amount0, uint256 amount1);

Read Position

hljs solidity
function positions(uint256 tokenId) external view returns (
    uint88  nonce,
    address operator,
    address token0,
    address token1,
    address deployer,
    int24   tickLower,
    int24   tickUpper,
    uint128 liquidity,
    uint256 feeGrowthInside0LastX128,
    uint256 feeGrowthInside1LastX128,
    uint128 tokensOwed0,
    uint128 tokensOwed1
);

Burn Position NFT

hljs solidity
function burn(uint256 tokenId) external payable;
// Requires: liquidity == 0 AND tokensOwed0 == 0 AND tokensOwed1 == 0

Farming Approval

hljs solidity
function approveForFarming(
    uint256 tokenId,
    bool    approve,
    address farmingAddress  // Must match the connected farming center
) external payable;

3.6 Multi-Hop Path Encoding

Algebra uses a custom path encoding for multi-hop swaps. Each hop is encoded as:

[tokenAddress (20 bytes)][deployerAddress (20 bytes)][tokenAddress (20 bytes)]...
  • For standard pools, the deployer is address(0) (20 zero bytes).
  • For exact output multi-hop, the path is encoded in reverse order.

Example: Token A → Token B → Token C (two hops)

0x + addressA (20 bytes) + deployer1 (20 bytes) + addressB (20 bytes) + deployer2 (20 bytes) + addressC (20 bytes)

With standard pools (deployer = 0x0):

hljs javascript
const path = ethers.solidityPacked(
    ['address', 'address', 'address', 'address', 'address'],
    [tokenA, ethers.ZeroAddress, tokenB, ethers.ZeroAddress, tokenC]
);

3.7 Pool Address Computation

Pool addresses are deterministic. You can compute them off-chain without querying the factory:

pool = CREATE2(
    0xFF,
    poolDeployer,                                    // 0xeb4E9b84990C7c07D5205D35647A29de1B33dE7e
    keccak256(abi.encode(token0, token1)),           // salt (for standard pools)
    POOL_INIT_CODE_HASH                              // from PoolAddress.sol
)

POOL_INIT_CODE_HASH: 0xa18736c3ee97fe3c96c9428c0cc2a9116facec18e84f95f9da30543f8238a782

hljs javascript
// JavaScript computation
const { ethers } = require('ethers');

function computePoolAddress(poolDeployer, token0, token1) {
    // Ensure token0 < token1
    if (token0.toLowerCase() > token1.toLowerCase()) {
        [token0, token1] = [token1, token0];
    }
    const salt = ethers.keccak256(
        ethers.AbiCoder.defaultAbiCoder().encode(['address', 'address'], [token0, token1])
    );
    return ethers.getCreate2Address(
        poolDeployer,
        salt,
        '0xa18736c3ee97fe3c96c9428c0cc2a9116facec18e84f95f9da30543f8238a782'
    );
}

4. Plugin System

Each Algebra pool can have a plugin that hooks into lifecycle events. The default plugin (BasePluginV1) combines:

Plugin ComponentFunctionality
VolatilityOraclePluginTracks TWAP observations for price oracle
DynamicFeePluginAdjusts fees based on volatility (adaptive fee)
FarmingProxyPluginRoutes virtual pool ticks for farming incentives
SlidingFeePluginAdjusts fees based on price direction

Plugin Config Bitmap

The pluginConfig field (uint8) controls which hooks are active:

BitHookDescription
0BEFORE_SWAPCalled before swap execution
1AFTER_SWAPCalled after swap execution
2BEFORE_POSITION_MODIFYBefore mint/burn
3AFTER_POSITION_MODIFYAfter mint/burn
4BEFORE_FLASHBefore flash loan
5AFTER_FLASHAfter flash loan
6AFTER_INITAfter pool initialization
7DYNAMIC_FEEEnable dynamic fee calculation

TWAP Oracle

The VolatilityOracle plugin stores historical price observations. Query via:

hljs solidity
// AlgebraOracleV1TWAP lens contract
function getAverageTick(address pool, uint32 period) external view returns (int24 averageTick);

5. Farming System

5.1 Eternal Farming

AlgebraEternalFarming (0xccdF60E4EfE4fB84183595Ad897E30a9ED4042EA) provides continuous reward distribution for liquidity providers.

IncentiveKey Structure

Every farming incentive is identified by a key:

hljs solidity
struct IncentiveKey {
    IERC20Minimal rewardToken;        // Main reward token
    IERC20Minimal bonusRewardToken;   // Bonus reward token
    IAlgebraPool  pool;               // Target Algebra pool
    uint256       nonce;              // Incentive nonce (increments per pool)
}

The incentiveId is computed as: keccak256(abi.encode(key))

Creating an Incentive (Admin)

hljs solidity
struct IncentiveParams {
    uint128 reward;                // Total main reward tokens
    uint128 bonusReward;           // Total bonus reward tokens
    uint128 rewardRate;            // Main reward per second
    uint128 bonusRewardRate;       // Bonus reward per second
    uint24  minimalPositionWidth;  // Min tick range (tickUpper - tickLower)
}

function createEternalFarming(
    IncentiveKey memory key,
    IncentiveParams memory params,
    address plugin               // Pool's plugin address
) external returns (address virtualPool);

5.2 FarmingCenter

FarmingCenter (0xc3BfA70A15556A01D35Fca03D8905A2afB7A5DbC) is the user-facing farming manager. Users interact with this contract, not EternalFarming directly.

Enter Farming

hljs solidity
function enterFarming(IncentiveKey memory key, uint256 tokenId) external;
  • tokenId: Your NFT position from NonfungiblePositionManager
  • The NFT must be approved for farming first

Exit Farming

hljs solidity
function exitFarming(IncentiveKey memory key, uint256 tokenId) external;

Collect Farming Rewards

hljs solidity
function collectRewards(IncentiveKey memory key, uint256 tokenId)
    external returns (uint256 reward, uint256 bonusReward);

Claim Rewards

hljs solidity
function claimReward(
    IERC20Minimal rewardToken,
    address to,
    uint256 amountRequested    // 0 = claim all
) external returns (uint256 rewardBalanceBefore);

5.3 Full Farming Flow

Step 1: Mint LP position via NonfungiblePositionManager.mint()
           ↓
Step 2: Approve NFT for farming
        NonfungiblePositionManager.approveForFarming(tokenId, true, farmingCenterAddress)
           ↓
Step 3: Enter farming
        FarmingCenter.enterFarming(incentiveKey, tokenId)
           ↓
Step 4: Accrue rewards over time...
           ↓
Step 5: Collect accrued rewards
        FarmingCenter.collectRewards(incentiveKey, tokenId)
           ↓
Step 6: Claim reward tokens to wallet
        FarmingCenter.claimReward(rewardToken, recipientAddress, 0)
           ↓
Step 7: (Optional) Exit farming
        FarmingCenter.exitFarming(incentiveKey, tokenId)
           ↓
Step 8: (Optional) Decrease liquidity / burn NFT

6. ve(3,3) Governance Layer

6.1 VeToken (BARK)

Address: 0xB4e0150c0CF27Aa0005BA494B17322a3e2554E4d

Standard ERC-20 token used as the base governance token. Users lock BARK to receive veNFTs with voting power.

6.2 VotingEscrow — Lock & veNFT

Address: 0xC7457Ba36B06C891cf094EDc9d3e2eeB1c624bb5

The VotingEscrow contract lets users lock BARK tokens for a duration (up to 2 years) and receive an ERC-721 veNFT representing their lock position.

Key Concepts

  • Max Lock Duration: 2 years (730 days)
  • Voting Power Decay: Linear decay — power = lockedAmount × remainingTime / maxTime
  • veNFT: ERC-721 token, transferable, with dynamic SVG art
  • Checkpointing: Historical voting power tracked for governance snapshots

Create Lock

hljs solidity
function create_lock_for(
    uint256 _value,           // Amount of BARK to lock
    uint256 _lock_duration,   // Duration in seconds (max 2 years)
    address _to               // Recipient of the veNFT
) external returns (uint256 tokenId);

Caller must first approve() the VotingEscrow for _value BARK tokens.

Increase Lock Amount

hljs solidity
function increase_amount(uint256 _tokenId, uint256 _value) external;

Extend Lock Duration

hljs solidity
function increase_unlock_time(uint256 _tokenId, uint256 _lock_duration) external;

Withdraw (After Lock Expires)

hljs solidity
function withdraw(uint256 _tokenId) external;
// Requires: block.timestamp >= locked[_tokenId].end

Merge Two veNFTs

hljs solidity
function merge(uint256 _from, uint256 _to) external;
// Combines lock amounts; uses the later expiry

Read Voting Power

hljs solidity
function balanceOfNFT(uint256 _tokenId) external view returns (uint256);
// Returns current voting power of the veNFT

function locked(uint256 _tokenId) external view returns (int128 amount, uint256 end);
// Returns the locked amount and unlock timestamp

6.3 VoterV4 — Persistent Voting

Address: 0xDb036a6C675b775e260F685d6634b70A4C9136b9 (Upgradeable Proxy)

VoterV4 implements persistent voting — votes automatically carry over across epochs. Users vote once and their allocation stays active permanently (until changed).

Core State

hljs solidity
mapping(address => uint256) public weights;              // pool → total vote weight
mapping(uint256 => uint256) public usedWeights;          // veNFT → total weight used
mapping(uint256 => mapping(address => uint256)) public votes;  // veNFT → pool → weight
uint256 public totalWeight;                              // Global total
mapping(uint256 => uint256) public lastVoted;            // veNFT → last vote timestamp

Vote

hljs solidity
function vote(
    uint256   _tokenId,   // veNFT token ID
    address[] _poolVote,  // Array of pool addresses
    uint256[] _weights    // Array of weights (sum ≤ 10000, where 10000 = 100%)
) external;
  • Caller must be the veNFT owner or approved operator.
  • Resets any previous votes automatically.
  • Minimum 1 day delay between votes per veNFT.
  • Only whitelisted pools can receive votes.

Reset Votes

hljs solidity
function reset(uint256 _tokenId) external;

Poke (Refresh Vote Power)

hljs solidity
function poke(uint256 _tokenId) external;
// Recasts existing votes with current veNFT voting power
// (e.g., after power decay or after locking more tokens)

Create Gauge for Pool

hljs solidity
function createGauge(address _pool) external
    returns (address _gauge, address _internalBribe, address _externalBribe);
  • Pool must be whitelisted.
  • Creates a GaugeV2_NFT (for Algebra positions), internal bribe, and external bribe.

Claim Emissions

hljs solidity
function claimRewards(address[] memory _gauges) external;
// Claims earned emissions from gauges

Claim Bribes

hljs solidity
function claimBribes(
    address[] memory _bribes,
    address[][] memory _tokens,
    uint256 _tokenId
) external;

View Functions

hljs solidity
function getPoolVote(uint256 _tokenId) external view returns (address[] memory);
function getVotes(uint256 _tokenId, address _pool) external view returns (uint256);
function getPoolWeight(address _pool) external view returns (uint256);
function getTotalWeight() external view returns (uint256);
function poolsLength() external view returns (uint256);
function pools(uint256 index) external view returns (address);
function gauges(address pool) external view returns (address gauge);
function internalBribes(address gauge) external view returns (address);
function externalBribes(address gauge) external view returns (address);
function isAlive(address gauge) external view returns (bool);
function isWhitelisted(address pool) external view returns (bool);

6.4 Gauges — Staking & Rewards

Gauges (GaugeV2_NFT) work with Algebra NFT positions — users stake their LP NFTs to earn BARK emissions.

Deposit LP NFT

hljs solidity
function deposit(uint256 tokenId) external;
// Transfers NFT to gauge; starts earning rewards

Withdraw LP NFT

hljs solidity
function withdraw(uint256 tokenId) external;

Claim Gauge Rewards

hljs solidity
function getReward(address account, address[] memory tokens) external;

Claim Trading Fees

hljs solidity
function claimFees() external returns (uint256 claimed0, uint256 claimed1);

Read Earned Rewards

hljs solidity
function earned(address token, address account) external view returns (uint256);

6.5 Bribes

Bribes incentivize veNFT holders to vote for specific pools. There are two types:

TypeSourceRecipient
Internal BribesTrading fees from the poolveNFT voters of that pool
External BribesThird-party incentivesveNFT voters of that pool

Claim Bribe Rewards

hljs solidity
// On Bribe contract
function getRewardForOwner(uint256 tokenId, address[] memory tokens) external;

6.6 Minter — Emissions

Address: 0x4270CE6Cae2f730fceD7c21aD6BE135977E18aA2

Controls weekly BARK token emissions to gauges through the Voter.

Update Period (Trigger New Epoch)

hljs solidity
function update_period() external returns (uint256 period);
// Mints new BARK and distributes to Voter for gauge allocation

Key Parameters

ParameterValueDescription
Epoch Duration7 daysWeekly emission epochs
Emission Rate99%Weekly emission = previous × 0.99
Tail Emission2 bpsMinimum emission floor
Max Rebase3%Maximum anti-dilution rebase
Max Team Rate5%Maximum team allocation

Read Functions

hljs solidity
function active_period() external view returns (uint256);    // Current epoch start
function weekly_emission() external view returns (uint256);  // Current weekly emission
function circulating_supply() external view returns (uint256);

6.7 RewardsDistributor — ve Rebases

Address: 0x7f06c051934930257f2F4312fB74b4027d4E62B1

Distributes anti-dilution rebases to veNFT holders proportional to their voting power.

hljs solidity
function claim(uint256 _tokenId) external returns (uint256);
// Claims accumulated rebase for a veNFT

function claimable(uint256 _tokenId) external view returns (uint256);
// View pending rebase amount

7. Complete User Flow Diagrams

Flow 1: Simple Token Swap

User                         SwapRouter                    AlgebraPool
  │                              │                              │
  │ approve(router, amount)      │                              │
  │──────────────────────────►   │                              │
  │                              │                              │
  │ exactInputSingle({           │                              │
  │   tokenIn, tokenOut,         │                              │
  │   deployer: 0x0,             │                              │
  │   recipient: user,           │                              │
  │   deadline,                  │                              │
  │   amountIn,                  │                              │
  │   amountOutMinimum,          │                              │
  │   limitSqrtPrice: 0          │                              │
  │ })                           │                              │
  │─────────────────────────────►│                              │
  │                              │  swap(recipient, ...)        │
  │                              │─────────────────────────────►│
  │                              │  algebraSwapCallback(...)    │
  │                              │◄─────────────────────────────│
  │                              │  transferFrom(user, pool)    │
  │                              │─────────────────────────────►│
  │                              │                              │
  │◄─── amountOut tokens ────────│◄─────────────────────────────│

Flow 2: Add Liquidity → Farm → Vote → Earn

1. ADD LIQUIDITY
   User → approve token0 & token1 for NonfungiblePositionManager
   User → NonfungiblePositionManager.mint({token0, token1, tickLower, tickUpper, amounts...})
   User ← receives LP NFT (tokenId)

2. FARM LP POSITION
   User → NonfungiblePositionManager.approveForFarming(tokenId, true, farmingCenter)
   User → FarmingCenter.enterFarming(incentiveKey, tokenId)
   ...time passes, farming rewards accrue...
   User → FarmingCenter.collectRewards(incentiveKey, tokenId)
   User → FarmingCenter.claimReward(rewardToken, userAddress, 0)

3. LOCK BARK FOR veNFT
   User → BARK.approve(votingEscrow, amount)
   User → VotingEscrow.create_lock_for(amount, 2_years, userAddress)
   User ← receives veNFT (veTokenId)

4. VOTE ON POOLS
   User → VoterV4.vote(veTokenId, [pool1, pool2], [6000, 4000])
   (Votes persist across epochs — no need to re-vote!)

5. EARN BRIBES + REBASES
   ...next epoch...
   User → VoterV4.claimBribes([bribeContracts], [[tokens]], veTokenId)
   User → RewardsDistributor.claim(veTokenId)

Flow 3: Epoch Cycle (Automated)

Every 7 days:
  ┌──────────────────────────────────────────────────────────────┐
  │ 1. Minter.update_period()                                    │
  │    → Mints weekly BARK emission                              │
  │    → Sends to Voter.updateClaimable(amount)                  │
  │                                                              │
  │ 2. Voter calculates gauge shares based on vote weights       │
  │    → claimable[gauge] += amount × poolWeight / totalWeight   │
  │                                                              │
  │ 3. Voter.distribute([gauges])                                │
  │    → Sends BARK to each gauge via notifyRewardAmount()       │
  │                                                              │
  │ 4. RewardsDistributor receives rebase allocation             │
  │    → veNFT holders can claim()                               │
  └──────────────────────────────────────────────────────────────┘

8. Programmatic Integration Guide

8.1 ethers.js / viem Setup

hljs javascript
import { ethers } from 'ethers';

// Network Configuration
const DOGEOS_TESTNET = {
    chainId: 6281971,
    name: 'DogeOS Testnet',
    rpcUrl: 'https://rpc.testnet.dogeos.com',
};

const provider = new ethers.JsonRpcProvider(DOGEOS_TESTNET.rpcUrl);
const signer = new ethers.Wallet(PRIVATE_KEY, provider);

// Contract Addresses
const ADDRESSES = {
    factory:            '0x099F459D81ce99aD3eCE1Ca2c77d9869883d2457',
    poolDeployer:       '0xeb4E9b84990C7c07D5205D35647A29de1B33dE7e',
    swapRouter:         '0x77147f436cE9739D2A54Ffe428DBe02b90c0205e',
    positionManager:    '0x4Bb4A5CF44028519908D6B4A90C570fEaA8c9a07',
    quoterV2:           '0xcEF56157baaB2Fe9D16ccF0eB4a9Df354380257D',
    eternalFarming:     '0xccdF60E4EfE4fB84183595Ad897E30a9ED4042EA',
    farmingCenter:      '0xc3BfA70A15556A01D35Fca03D8905A2afB7A5DbC',
    veToken:            '0xB4e0150c0CF27Aa0005BA494B17322a3e2554E4d',
    votingEscrow:       '0xC7457Ba36B06C891cf094EDc9d3e2eeB1c624bb5',
    voter:              '0xDb036a6C675b775e260F685d6634b70A4C9136b9',
    minter:             '0x4270CE6Cae2f730fceD7c21aD6BE135977E18aA2',
    rewardsDistributor: '0x7f06c051934930257f2F4312fB74b4027d4E62B1',
    wrappedNative:      '0xF6BDB158A5ddF77F1B83bC9074F6a472c58D78aE',
};

8.2 Executing a Swap

hljs javascript
const SWAP_ROUTER_ABI = [
    'function exactInputSingle((address tokenIn, address tokenOut, address deployer, address recipient, uint256 deadline, uint256 amountIn, uint256 amountOutMinimum, uint160 limitSqrtPrice)) external payable returns (uint256 amountOut)',
    'function exactInput((bytes path, address recipient, uint256 deadline, uint256 amountIn, uint256 amountOutMinimum)) external payable returns (uint256 amountOut)',
];

const ERC20_ABI = [
    'function approve(address spender, uint256 amount) external returns (bool)',
    'function balanceOf(address account) external view returns (uint256)',
    'function decimals() external view returns (uint8)',
];

async function swapExactInput(tokenIn, tokenOut, amountIn) {
    const router = new ethers.Contract(ADDRESSES.swapRouter, SWAP_ROUTER_ABI, signer);
    const token = new ethers.Contract(tokenIn, ERC20_ABI, signer);

    // Step 1: Approve router
    const approveTx = await token.approve(ADDRESSES.swapRouter, amountIn);
    await approveTx.wait();

    // Step 2: Get quote (optional but recommended)
    const quotedOut = await getQuote(tokenIn, tokenOut, amountIn);
    const minOut = quotedOut * 99n / 100n; // 1% slippage

    // Step 3: Execute swap
    const deadline = Math.floor(Date.now() / 1000) + 600; // 10 min
    const tx = await router.exactInputSingle({
        tokenIn,
        tokenOut,
        deployer: ethers.ZeroAddress,  // Standard pool
        recipient: signer.address,
        deadline,
        amountIn,
        amountOutMinimum: minOut,
        limitSqrtPrice: 0n,            // No price limit
    });

    const receipt = await tx.wait();
    console.log('Swap executed:', receipt.hash);
    return receipt;
}

// Multi-hop swap: tokenA → tokenB → tokenC
async function swapMultiHop(tokenA, tokenB, tokenC, amountIn) {
    const router = new ethers.Contract(ADDRESSES.swapRouter, SWAP_ROUTER_ABI, signer);

    const path = ethers.solidityPacked(
        ['address', 'address', 'address', 'address', 'address'],
        [tokenA, ethers.ZeroAddress, tokenB, ethers.ZeroAddress, tokenC]
    );

    const tx = await router.exactInput({
        path,
        recipient: signer.address,
        deadline: Math.floor(Date.now() / 1000) + 600,
        amountIn,
        amountOutMinimum: 0n, // Replace with actual slippage check!
    });

    return await tx.wait();
}

8.3 Adding Liquidity

hljs javascript
const POSITION_MANAGER_ABI = [
    'function mint((address token0, address token1, address deployer, int24 tickLower, int24 tickUpper, uint256 amount0Desired, uint256 amount1Desired, uint256 amount0Min, uint256 amount1Min, address recipient, uint256 deadline)) external payable returns (uint256 tokenId, uint128 liquidity, uint256 amount0, uint256 amount1)',
    'function createAndInitializePoolIfNecessary(address token0, address token1, address deployer, uint160 sqrtPriceX96, bytes data) external payable returns (address pool)',
    'function positions(uint256 tokenId) external view returns (uint88 nonce, address operator, address token0, address token1, address deployer, int24 tickLower, int24 tickUpper, uint128 liquidity, uint256 feeGrowthInside0LastX128, uint256 feeGrowthInside1LastX128, uint128 tokensOwed0, uint128 tokensOwed1)',
];

async function addLiquidity(token0, token1, amount0, amount1, tickLower, tickUpper) {
    // Ensure token0 < token1
    if (token0.toLowerCase() > token1.toLowerCase()) {
        [token0, token1] = [token1, token0];
        [amount0, amount1] = [amount1, amount0];
    }

    const pm = new ethers.Contract(ADDRESSES.positionManager, POSITION_MANAGER_ABI, signer);
    const t0 = new ethers.Contract(token0, ERC20_ABI, signer);
    const t1 = new ethers.Contract(token1, ERC20_ABI, signer);

    // Approve
    await (await t0.approve(ADDRESSES.positionManager, amount0)).wait();
    await (await t1.approve(ADDRESSES.positionManager, amount1)).wait();

    // Mint position
    const tx = await pm.mint({
        token0,
        token1,
        deployer: ethers.ZeroAddress,
        tickLower,
        tickUpper,
        amount0Desired: amount0,
        amount1Desired: amount1,
        amount0Min: 0n,  // Replace with slippage-adjusted value
        amount1Min: 0n,  // Replace with slippage-adjusted value
        recipient: signer.address,
        deadline: Math.floor(Date.now() / 1000) + 600,
    });

    const receipt = await tx.wait();

    // Parse tokenId from IncreaseLiquidity event
    const iface = new ethers.Interface([
        'event IncreaseLiquidity(uint256 indexed tokenId, uint128 liquidityDesired, uint128 actualLiquidity, uint256 amount0, uint256 amount1, address pool)',
    ]);
    const log = receipt.logs.find(l => {
        try { return iface.parseLog(l) !== null; } catch { return false; }
    });
    const parsed = iface.parseLog(log);
    console.log('NFT Token ID:', parsed.args.tokenId.toString());
    return parsed.args.tokenId;
}

8.4 Removing Liquidity

hljs javascript
const PM_ABI_EXTENDED = [
    ...POSITION_MANAGER_ABI,
    'function decreaseLiquidity((uint256 tokenId, uint128 liquidity, uint256 amount0Min, uint256 amount1Min, uint256 deadline)) external payable returns (uint256 amount0, uint256 amount1)',
    'function collect((uint256 tokenId, address recipient, uint128 amount0Max, uint128 amount1Max)) external payable returns (uint256 amount0, uint256 amount1)',
    'function burn(uint256 tokenId) external payable',
];

async function removeLiquidity(tokenId, liquidityToRemove) {
    const pm = new ethers.Contract(ADDRESSES.positionManager, PM_ABI_EXTENDED, signer);

    // Step 1: Decrease liquidity
    const decreaseTx = await pm.decreaseLiquidity({
        tokenId,
        liquidity: liquidityToRemove,
        amount0Min: 0n,
        amount1Min: 0n,
        deadline: Math.floor(Date.now() / 1000) + 600,
    });
    await decreaseTx.wait();

    // Step 2: Collect tokens + fees
    const collectTx = await pm.collect({
        tokenId,
        recipient: signer.address,
        amount0Max: ethers.MaxUint256 >> 128n,  // type(uint128).max
        amount1Max: ethers.MaxUint256 >> 128n,
    });
    await collectTx.wait();

    // Step 3: (Optional) Burn empty NFT
    // Only if liquidity == 0 and no tokens owed
    // await pm.burn(tokenId);
}

8.5 Farming (Stake/Unstake/Claim)

hljs javascript
const FARMING_CENTER_ABI = [
    'function enterFarming((address rewardToken, address bonusRewardToken, address pool, uint256 nonce) key, uint256 tokenId) external',
    'function exitFarming((address rewardToken, address bonusRewardToken, address pool, uint256 nonce) key, uint256 tokenId) external',
    'function collectRewards((address rewardToken, address bonusRewardToken, address pool, uint256 nonce) key, uint256 tokenId) external returns (uint256 reward, uint256 bonusReward)',
    'function claimReward(address rewardToken, address to, uint256 amountRequested) external returns (uint256 rewardBalanceBefore)',
];

const NFT_PM_FARMING_ABI = [
    'function approveForFarming(uint256 tokenId, bool approve, address farmingAddress) external payable',
];

async function stakeFarm(tokenId, incentiveKey) {
    const pm = new ethers.Contract(ADDRESSES.positionManager, NFT_PM_FARMING_ABI, signer);
    const fc = new ethers.Contract(ADDRESSES.farmingCenter, FARMING_CENTER_ABI, signer);

    // Step 1: Approve for farming
    await (await pm.approveForFarming(tokenId, true, ADDRESSES.farmingCenter)).wait();

    // Step 2: Enter farming
    await (await fc.enterFarming(incentiveKey, tokenId)).wait();
    console.log('Staked in farm');
}

async function collectAndClaimFarmRewards(tokenId, incentiveKey) {
    const fc = new ethers.Contract(ADDRESSES.farmingCenter, FARMING_CENTER_ABI, signer);

    // Step 1: Collect (accrue rewards internally)
    const collectTx = await fc.collectRewards(incentiveKey, tokenId);
    const receipt = await collectTx.wait();
    console.log('Rewards collected');

    // Step 2: Claim reward tokens to wallet
    await (await fc.claimReward(
        incentiveKey.rewardToken,
        signer.address,
        0n  // 0 = claim all
    )).wait();

    // Step 3: Claim bonus reward tokens (if any)
    if (incentiveKey.bonusRewardToken !== ethers.ZeroAddress) {
        await (await fc.claimReward(
            incentiveKey.bonusRewardToken,
            signer.address,
            0n
        )).wait();
    }
}

async function unstakeFarm(tokenId, incentiveKey) {
    const fc = new ethers.Contract(ADDRESSES.farmingCenter, FARMING_CENTER_ABI, signer);

    // Collect final rewards then exit
    await collectAndClaimFarmRewards(tokenId, incentiveKey);
    await (await fc.exitFarming(incentiveKey, tokenId)).wait();
    console.log('Exited farm');
}

8.6 ve(3,3) Locking & Voting

hljs javascript
const VOTING_ESCROW_ABI = [
    'function create_lock_for(uint256 _value, uint256 _lock_duration, address _to) external returns (uint256)',
    'function increase_amount(uint256 _tokenId, uint256 _value) external',
    'function increase_unlock_time(uint256 _tokenId, uint256 _lock_duration) external',
    'function withdraw(uint256 _tokenId) external',
    'function balanceOfNFT(uint256 _tokenId) external view returns (uint256)',
    'function locked(uint256 _tokenId) external view returns (int128 amount, uint256 end)',
    'function merge(uint256 _from, uint256 _to) external',
];

const VOTER_ABI = [
    'function vote(uint256 _tokenId, address[] _poolVote, uint256[] _weights) external',
    'function reset(uint256 _tokenId) external',
    'function poke(uint256 _tokenId) external',
    'function claimBribes(address[] _bribes, address[][] _tokens, uint256 _tokenId) external',
    'function getPoolVote(uint256 _tokenId) external view returns (address[])',
    'function getPoolWeight(address _pool) external view returns (uint256)',
    'function getTotalWeight() external view returns (uint256)',
    'function gauges(address pool) external view returns (address)',
    'function internalBribes(address gauge) external view returns (address)',
    'function externalBribes(address gauge) external view returns (address)',
];

// Lock BARK tokens for 2 years and get veNFT
async function lockBark(amount) {
    const bark = new ethers.Contract(ADDRESSES.veToken, ERC20_ABI, signer);
    const ve = new ethers.Contract(ADDRESSES.votingEscrow, VOTING_ESCROW_ABI, signer);

    // Approve
    await (await bark.approve(ADDRESSES.votingEscrow, amount)).wait();

    // Lock for 2 years (max)
    const TWO_YEARS = 2 * 365 * 24 * 60 * 60;
    const tx = await ve.create_lock_for(amount, TWO_YEARS, signer.address);
    const receipt = await tx.wait();

    // Parse veNFT token ID from Transfer event
    const iface = new ethers.Interface([
        'event Transfer(address indexed from, address indexed to, uint256 indexed tokenId)',
    ]);
    const log = receipt.logs.find(l => {
        try {
            const p = iface.parseLog(l);
            return p && p.args.from === ethers.ZeroAddress;
        } catch { return false; }
    });
    const veTokenId = iface.parseLog(log).args.tokenId;
    console.log('veNFT minted:', veTokenId.toString());
    return veTokenId;
}

// Vote on pools with veNFT
async function voteOnPools(veTokenId, pools, weights) {
    const voter = new ethers.Contract(ADDRESSES.voter, VOTER_ABI, signer);

    // weights are in basis points out of 10000
    // e.g., [6000, 4000] = 60% pool1, 40% pool2
    const tx = await voter.vote(veTokenId, pools, weights);
    await tx.wait();
    console.log('Voted successfully');
}

// Example: Vote 60% on pool1, 40% on pool2
// await voteOnPools(veTokenId, [pool1Address, pool2Address], [6000, 4000]);

8.7 Claiming Rewards & Bribes

hljs javascript
const REWARDS_DISTRIBUTOR_ABI = [
    'function claim(uint256 _tokenId) external returns (uint256)',
    'function claimable(uint256 _tokenId) external view returns (uint256)',
];

const GAUGE_ABI = [
    'function getReward(address account, address[] tokens) external',
    'function earned(address token, address account) external view returns (uint256)',
    'function deposit(uint256 tokenId) external',
    'function withdraw(uint256 tokenId) external',
];

// Claim ve rebase
async function claimRebase(veTokenId) {
    const rd = new ethers.Contract(ADDRESSES.rewardsDistributor, REWARDS_DISTRIBUTOR_ABI, signer);

    const pending = await rd.claimable(veTokenId);
    console.log('Pending rebase:', ethers.formatEther(pending));

    if (pending > 0n) {
        const tx = await rd.claim(veTokenId);
        await tx.wait();
        console.log('Rebase claimed');
    }
}

// Claim bribe rewards for a veNFT
async function claimAllBribes(veTokenId, poolAddresses) {
    const voter = new ethers.Contract(ADDRESSES.voter, VOTER_ABI, signer);

    // Build arrays of bribe addresses and reward tokens
    const bribes = [];
    const tokens = [];

    for (const pool of poolAddresses) {
        const gauge = await voter.gauges(pool);
        if (gauge === ethers.ZeroAddress) continue;

        const intBribe = await voter.internalBribes(gauge);
        const extBribe = await voter.externalBribes(gauge);

        // Internal bribe (trading fees)
        bribes.push(intBribe);
        tokens.push([/* list of reward token addresses for this bribe */]);

        // External bribe (third-party incentives)
        bribes.push(extBribe);
        tokens.push([/* list of reward token addresses for this bribe */]);
    }

    if (bribes.length > 0) {
        const tx = await voter.claimBribes(bribes, tokens, veTokenId);
        await tx.wait();
        console.log('Bribes claimed');
    }
}

// Claim gauge emissions (for staked LP positions)
async function claimGaugeRewards(gaugeAddress, rewardTokens) {
    const gauge = new ethers.Contract(gaugeAddress, GAUGE_ABI, signer);

    // Check earned
    for (const token of rewardTokens) {
        const earned = await gauge.earned(token, signer.address);
        console.log(`Earned ${token}: ${ethers.formatEther(earned)}`);
    }

    // Claim
    const tx = await gauge.getReward(signer.address, rewardTokens);
    await tx.wait();
    console.log('Gauge rewards claimed');
}

9. Key Constants & Parameters

Algebra Core Constants

ConstantValueDescription
FEE_DENOMINATOR1,000,000 (1e6)Fee precision. Fee of 500 = 0.05%
INIT_DEFAULT_FEE500 (0.05%)Default pool fee
MAX_DEFAULT_FEE50,000 (5%)Maximum allowed fee
FLASH_FEE100 (0.01%)Flash loan fee
INIT_DEFAULT_TICK_SPACING60Default tick spacing
MAX_TICK_SPACING500Maximum tick spacing
MIN_TICK_SPACING1Minimum tick spacing
MAX_COMMUNITY_FEE1000 (100%)Max community fee share
FEE_TRANSFER_FREQUENCY8 hoursCommunity fee transfer interval
MAX_LIQUIDITY_PER_TICK~1.9 × 10^32Overflow protection

Tick/Price Math

ConstantValue
MIN_TICK-887272
MAX_TICK887272
MIN_SQRT_RATIO4295128739
MAX_SQRT_RATIO1461446703485210103287273052203988822378723970342
Q962^96 = 79228162514264337593543950336

SqrtPrice to Price Conversion

$$\text{price} = \left(\frac{\text{sqrtPriceX96}}{2^{96}}\right)^2$$

$$\text{sqrtPriceX96} = \sqrt{\text{price}} \times 2^{96}$$

ve(3,3) Parameters

ParameterValue
Max Lock Duration2 years (730 days)
Epoch Duration7 days
Vote Delay1 day
Vote Weight Precision10,000 (basis points)
Emission Decay1% per epoch (99% retention)
Tail Emission0.02% of supply
Max Rebase3%
Max Team Rate5%

10. Error Reference

Pool Errors

ErrorCause
locked()Reentrancy detected
alreadyInitialized()Pool already has a price
notInitialized()Pool needs initialize() first
zeroAmountRequired()Swap amount cannot be 0
invalidAmountRequired()Bad swap amount value
insufficientInputAmount()Pool didn't receive enough tokens
zeroLiquidityDesired()Cannot mint 0 liquidity
invalidLimitSqrtPrice()Price limit out of bounds
tickIsNotSpaced()Tick not a multiple of tickSpacing
flashInsufficientPaid0/1()Didn't repay flash loan

Farming Errors

ErrorCause
farmDoesNotExist()No farm for this token ID
tokenAlreadyFarmed()NFT already in a farm
incentiveNotExist()Bad incentive key
incentiveStopped()Incentive deactivated
positionIsTooNarrow()Position width < minimalPositionWidth
zeroLiquidity()Cannot farm with 0 liquidity

11. Events Reference

Core Pool Events

hljs solidity
event Initialize(uint160 price, int24 tick);
event Mint(address sender, address indexed owner, int24 indexed bottomTick, int24 indexed topTick, uint128 liquidityAmount, uint256 amount0, uint256 amount1);
event Burn(address indexed owner, int24 indexed bottomTick, int24 indexed topTick, uint128 liquidityAmount, uint256 amount0, uint256 amount1, uint24 pluginFee);
event Swap(address indexed sender, address indexed recipient, int256 amount0, int256 amount1, uint160 price, uint128 liquidity, int24 tick, uint24 overrideFee, uint24 pluginFee);
event Collect(address indexed owner, address recipient, int24 indexed bottomTick, int24 indexed topTick, uint128 amount0, uint128 amount1);
event Flash(address indexed sender, address indexed recipient, uint256 amount0, uint256 amount1, uint256 paid0, uint256 paid1);
event Fee(uint16 fee);
event CommunityFee(uint16 communityFeeNew);
event TickSpacing(int24 newTickSpacing);
event Plugin(address newPluginAddress);

Position Manager Events

hljs solidity
event IncreaseLiquidity(uint256 indexed tokenId, uint128 liquidityDesired, uint128 actualLiquidity, uint256 amount0, uint256 amount1, address pool);
event DecreaseLiquidity(uint256 indexed tokenId, uint128 liquidity, uint256 amount0, uint256 amount1);
event Collect(uint256 indexed tokenId, address recipient, uint256 amount0, uint256 amount1);

Farming Events

hljs solidity
event FarmEntered(uint256 indexed tokenId, bytes32 indexed incentiveId, uint128 liquidity);
event FarmEnded(uint256 indexed tokenId, bytes32 indexed incentiveId, address indexed rewardAddress, address bonusRewardToken, address owner, uint256 reward, uint256 bonusReward);
event RewardsCollected(uint256 tokenId, bytes32 incentiveId, uint256 rewardAmount, uint256 bonusRewardAmount);
event RewardClaimed(address indexed to, uint256 reward, address indexed rewardAddress, address indexed owner);

Voter Events

hljs solidity
event GaugeCreated(address indexed gauge, address creator, address internalBribe, address externalBribe, address indexed pool);
event Voted(address indexed voter, uint256 indexed tokenId, address[] pools, uint256[] weights);
event DistributeReward(address indexed sender, address indexed gauge, uint256 amount);
event Whitelisted(address indexed pool);
event Blacklisted(address indexed pool);

12. Security Considerations

For Integrators

  1. Read-Only Reentrancy: Always use safelyGetStateOfAMM() instead of globalState() when reading pool state from external contracts. The safe variant checks the reentrancy lock.

  2. Slippage Protection: Always set amountOutMinimum / amountInMaximum / amount0Min / amount1Min to non-zero values based on quotes. Never use 0 in production.

  3. Deadline: Always set reasonable deadlines. Stale transactions can be exploited by MEV bots.

  4. Token Sorting: token0 < token1 is mandatory when calling pool-level functions or the position manager. The SwapRouter handles sorting internally.

  5. Approval Management: Use exact approval amounts or implement approval reset patterns. Avoid unlimited approvals to periphery contracts in production.

  6. Price Manipulation: Pool prices can be manipulated within a single block. Use TWAP oracles for price-sensitive operations.

  7. Flash Loan Attacks: If building on top of Barkswap, ensure your contract cannot be exploited via flash-loan-funded price manipulation.

  8. veNFT Voting Lock: After voting, there is a 1-day cooldown before the veNFT can be voted again. veNFTs cannot be transferred while actively voting (must reset first).


Next →Indexer API