Barkswap Contracts & Integration Documentation
Table of Contents
- Architecture Overview
- Deployed Contract Addresses
- Core DEX — Algebra Integral
- Plugin System
- Farming System
- 5.1 Eternal Farming
- 5.2 FarmingCenter
- 5.3 Full Farming Flow
- ve(3,3) Governance Layer
- Complete User Flow Diagrams
- Programmatic Integration Guide
- Key Constants & Parameters
- Error Reference
- Events Reference
- 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
| Contract | Address | Description |
|---|---|---|
| AlgebraPoolDeployer | 0xeb4E9b84990C7c07D5205D35647A29de1B33dE7e | Deploys new pool instances |
| AlgebraFactory | 0x099F459D81ce99aD3eCE1Ca2c77d9869883d2457 | Pool registry & creation |
| CommunityVault | 0x49645d2050f8924379C9c39E1a3DeF5FF820866C | Collects community fees |
| VaultFactory | 0x70B0De92e5d40d48E0D1f1b645Ab7AFC1f50e90e | Creates vaults for pools |
| BasePluginV1Factory | 0xa60884E876eb272fC1FFcb40E89836D89bc37219 | Creates plugins for pools |
| WrappedNative (WDOGE) | 0xF6BDB158A5ddF77F1B83bC9074F6a472c58D78aE | Wrapped native token |
Algebra Periphery
| Contract | Address | Description |
|---|---|---|
| SwapRouter | 0x77147f436cE9739D2A54Ffe428DBe02b90c0205e | Execute swaps |
| NonfungiblePositionManager | 0x4Bb4A5CF44028519908D6B4A90C570fEaA8c9a07 | Manage LP NFT positions |
| Quoter | 0xFA0c644c1Ea307538cA7d8Fb0582e4b52ecE3ABE | Off-chain price quotes (V1) |
| QuoterV2 | 0xcEF56157baaB2Fe9D16ccF0eB4a9Df354380257D | Off-chain price quotes (V2, recommended) |
| TickLens | 0x0b990916aE4e30367F7949020C71491787154BBB | Read tick data |
| AlgebraInterfaceMulticall | 0xF59eA3173B5FBA06DE0854D754Bb6142Aed2CE65 | Batch read calls |
| CustomPoolEntryPoint | 0xa7D1592c3c0EE11da11f5856181a8D4c8E595586 | Custom pool deployer entry |
Farming
| Contract | Address | Description |
|---|---|---|
| AlgebraEternalFarming | 0xccdF60E4EfE4fB84183595Ad897E30a9ED4042EA | Eternal farming incentives |
| FarmingCenter | 0xc3BfA70A15556A01D35Fca03D8905A2afB7A5DbC | User-facing farming manager |
ve(3,3) Governance
| Contract | Address | Description |
|---|---|---|
| VeToken (BARK) | 0xB4e0150c0CF27Aa0005BA494B17322a3e2554E4d | Base governance token |
| VeArtProxy | 0x336BC58ced0921edb572348bDb8535faf4bb444E | Dynamic veNFT SVG art |
| VotingEscrow | 0xC7457Ba36B06C891cf094EDc9d3e2eeB1c624bb5 | Lock tokens → veNFT |
| PermissionsRegistry | 0xfc6C03aFA7E184853B1ff258C7EDd4aF87e7Ea42 | Role management |
| RewardsDistributor | 0x7f06c051934930257f2F4312fB74b4027d4E62B1 | ve rebase distribution |
| GaugeFactoryNFT | 0x84ee580c8a9ba1c77A4A7e916f58Ea76f254E7ed | Creates Algebra NFT gauges |
| BribeFactory | 0xa2C8de87E00806945Bc46EA529Ce7FcA5b6785C6 | Creates bribe contracts |
| VoterV4 (Proxy) | 0xDb036a6C675b775e260F685d6634b70A4C9136b9 | Persistent voting + gauge management |
| Minter | 0x4270CE6Cae2f730fceD7c21aD6BE135977E18aA2 | Weekly 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
// 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
defaultPluginFactoryto 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
// IAlgebraPool
function initialize(uint160 initialPrice) external;
initialPriceis 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:
// 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
// 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
// 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
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:
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)
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)
function burn(
int24 bottomTick,
int24 topTick,
uint128 amount, // Liquidity to remove
bytes calldata data // Passed to plugin
) external returns (uint256 amount0, uint256 amount1);
Note:
burndoes NOT transfer tokens. You must callcollectafterward.
Collect (Withdraw Tokens)
function collect(
address recipient,
int24 bottomTick,
int24 topTick,
uint128 amount0Requested,
uint128 amount1Requested
) external returns (uint128 amount0, uint128 amount1);
Flash Loan
function flash(
address recipient,
uint256 amount0,
uint256 amount1,
bytes calldata data // Passed to algebraFlashCallback
) external;
Callback:
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)
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)
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)
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)
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
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
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)
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
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
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
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
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
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
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
function burn(uint256 tokenId) external payable;
// Requires: liquidity == 0 AND tokensOwed0 == 0 AND tokensOwed1 == 0
Farming Approval
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):
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
// 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 Component | Functionality |
|---|---|
| VolatilityOraclePlugin | Tracks TWAP observations for price oracle |
| DynamicFeePlugin | Adjusts fees based on volatility (adaptive fee) |
| FarmingProxyPlugin | Routes virtual pool ticks for farming incentives |
| SlidingFeePlugin | Adjusts fees based on price direction |
Plugin Config Bitmap
The pluginConfig field (uint8) controls which hooks are active:
| Bit | Hook | Description |
|---|---|---|
| 0 | BEFORE_SWAP | Called before swap execution |
| 1 | AFTER_SWAP | Called after swap execution |
| 2 | BEFORE_POSITION_MODIFY | Before mint/burn |
| 3 | AFTER_POSITION_MODIFY | After mint/burn |
| 4 | BEFORE_FLASH | Before flash loan |
| 5 | AFTER_FLASH | After flash loan |
| 6 | AFTER_INIT | After pool initialization |
| 7 | DYNAMIC_FEE | Enable dynamic fee calculation |
TWAP Oracle
The VolatilityOracle plugin stores historical price observations. Query via:
// 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:
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)
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
function enterFarming(IncentiveKey memory key, uint256 tokenId) external;
tokenId: Your NFT position from NonfungiblePositionManager- The NFT must be approved for farming first
Exit Farming
function exitFarming(IncentiveKey memory key, uint256 tokenId) external;
Collect Farming Rewards
function collectRewards(IncentiveKey memory key, uint256 tokenId)
external returns (uint256 reward, uint256 bonusReward);
Claim Rewards
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
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_valueBARK tokens.
Increase Lock Amount
function increase_amount(uint256 _tokenId, uint256 _value) external;
Extend Lock Duration
function increase_unlock_time(uint256 _tokenId, uint256 _lock_duration) external;
Withdraw (After Lock Expires)
function withdraw(uint256 _tokenId) external;
// Requires: block.timestamp >= locked[_tokenId].end
Merge Two veNFTs
function merge(uint256 _from, uint256 _to) external;
// Combines lock amounts; uses the later expiry
Read Voting Power
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
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
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
function reset(uint256 _tokenId) external;
Poke (Refresh Vote Power)
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
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
function claimRewards(address[] memory _gauges) external;
// Claims earned emissions from gauges
Claim Bribes
function claimBribes(
address[] memory _bribes,
address[][] memory _tokens,
uint256 _tokenId
) external;
View Functions
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
function deposit(uint256 tokenId) external;
// Transfers NFT to gauge; starts earning rewards
Withdraw LP NFT
function withdraw(uint256 tokenId) external;
Claim Gauge Rewards
function getReward(address account, address[] memory tokens) external;
Claim Trading Fees
function claimFees() external returns (uint256 claimed0, uint256 claimed1);
Read Earned Rewards
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:
| Type | Source | Recipient |
|---|---|---|
| Internal Bribes | Trading fees from the pool | veNFT voters of that pool |
| External Bribes | Third-party incentives | veNFT voters of that pool |
Claim Bribe Rewards
// 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)
function update_period() external returns (uint256 period);
// Mints new BARK and distributes to Voter for gauge allocation
Key Parameters
| Parameter | Value | Description |
|---|---|---|
| Epoch Duration | 7 days | Weekly emission epochs |
| Emission Rate | 99% | Weekly emission = previous × 0.99 |
| Tail Emission | 2 bps | Minimum emission floor |
| Max Rebase | 3% | Maximum anti-dilution rebase |
| Max Team Rate | 5% | Maximum team allocation |
Read Functions
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.
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
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
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
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
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)
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
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
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
| Constant | Value | Description |
|---|---|---|
FEE_DENOMINATOR | 1,000,000 (1e6) | Fee precision. Fee of 500 = 0.05% |
INIT_DEFAULT_FEE | 500 (0.05%) | Default pool fee |
MAX_DEFAULT_FEE | 50,000 (5%) | Maximum allowed fee |
FLASH_FEE | 100 (0.01%) | Flash loan fee |
INIT_DEFAULT_TICK_SPACING | 60 | Default tick spacing |
MAX_TICK_SPACING | 500 | Maximum tick spacing |
MIN_TICK_SPACING | 1 | Minimum tick spacing |
MAX_COMMUNITY_FEE | 1000 (100%) | Max community fee share |
FEE_TRANSFER_FREQUENCY | 8 hours | Community fee transfer interval |
MAX_LIQUIDITY_PER_TICK | ~1.9 × 10^32 | Overflow protection |
Tick/Price Math
| Constant | Value |
|---|---|
MIN_TICK | -887272 |
MAX_TICK | 887272 |
MIN_SQRT_RATIO | 4295128739 |
MAX_SQRT_RATIO | 1461446703485210103287273052203988822378723970342 |
Q96 | 2^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
| Parameter | Value |
|---|---|
| Max Lock Duration | 2 years (730 days) |
| Epoch Duration | 7 days |
| Vote Delay | 1 day |
| Vote Weight Precision | 10,000 (basis points) |
| Emission Decay | 1% per epoch (99% retention) |
| Tail Emission | 0.02% of supply |
| Max Rebase | 3% |
| Max Team Rate | 5% |
10. Error Reference
Pool Errors
| Error | Cause |
|---|---|
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
| Error | Cause |
|---|---|
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
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
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
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
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
-
Read-Only Reentrancy: Always use
safelyGetStateOfAMM()instead ofglobalState()when reading pool state from external contracts. The safe variant checks the reentrancy lock. -
Slippage Protection: Always set
amountOutMinimum/amountInMaximum/amount0Min/amount1Minto non-zero values based on quotes. Never use 0 in production. -
Deadline: Always set reasonable deadlines. Stale transactions can be exploited by MEV bots.
-
Token Sorting:
token0 < token1is mandatory when calling pool-level functions or the position manager. The SwapRouter handles sorting internally. -
Approval Management: Use exact approval amounts or implement approval reset patterns. Avoid unlimited approvals to periphery contracts in production.
-
Price Manipulation: Pool prices can be manipulated within a single block. Use TWAP oracles for price-sensitive operations.
-
Flash Loan Attacks: If building on top of Barkswap, ensure your contract cannot be exploited via flash-loan-funded price manipulation.
-
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).