Skip to content

Repository files navigation

Deployed Contracts

Lithos DEX

A decentralized exchange (DEX) built on Plasma testnet featuring both stable and volatile AMM pools, inspired by Solidly's design with Uniswap V2-like interface.

Key Features

  • Dual AMM Types: Support for both stable (low-slippage) and volatile (standard xy=k) pools
  • Uniswap V2 Compatible: Standard interface for easy frontend integration
  • Fee-on-Transfer Support: Works with tokens that charge fees on transfers
  • Native Token Integration: Full XPL/WXPL support with automatic wrapping/unwrapping

Contract Addresses

Mainnet (Plasma)

Core Contracts

Tokens

Note: RouterV2 is the primary contract for frontend integration. It handles all liquidity and swap operations.

Pairs

  1. WXPL/USDT0 (Volatile)

  2. USDe/USDT0 (Stable)

  3. USDe/USDT0 (Volatile)

  4. USDai/USDT0 (Stable)

  5. WETH/weETH (Volatile)

  6. WXPL/WETH (Volatile)

  7. xUSD/tcUSDT0 (Stable)

ve(3,3) Governance

The ve(3,3) system is live on Plasma mainnet. Key governance contracts (synced to deployments/mainnet/state.json):

Concentrated Liquidity (CL)

CL Config
  • Pool Init Code Hash: 0x62441ebe4e4315cf3d49d5957f94d66b253dbabe7006f34ad7f70947e60bf15c
CL Periphery
Hypervisors

API Helper Proxies

Testnet (Plasma)

Core DEX

ve(3,3) Governance

Tokens

Note: RouterV2 is the primary contract for frontend integration. It handles all liquidity and swap operations.

Pairs/Gauge

LITHOS/WXPL Pair

Frontend Integration Guide

1. Pool Creation

Create trading pairs through the PairFactory contract:

// Create a new trading pair
function createPair(
    address tokenA,
    address tokenB,
    bool stable      // true for stable pool, false for volatile pool
) external returns (address pair)

JavaScript Example:

const pairFactory = new ethers.Contract(
  PAIR_FACTORY_ADDRESS,
  pairFactoryAbi,
  signer
);

// Create volatile pool (standard AMM)
const volatilePair = await pairFactory.createPair(tokenA, tokenB, false);

// Create stable pool (low slippage for similar assets)
const stablePair = await pairFactory.createPair(USDC, USDT, true);

2. Liquidity Management

All liquidity operations are handled through RouterV2:

Add Liquidity (ERC20 + ERC20)

function addLiquidity(
    address tokenA,
    address tokenB,
    bool stable,
    uint amountADesired,
    uint amountBDesired,
    uint amountAMin,      // Slippage protection
    uint amountBMin,      // Slippage protection
    address to,           // LP token recipient
    uint deadline
) external returns (uint amountA, uint amountB, uint liquidity)

Add Liquidity (Token + XPL)

function addLiquidityETH(
    address token,
    bool stable,
    uint amountTokenDesired,
    uint amountTokenMin,
    uint amountETHMin,
    address to,
    uint deadline
) external payable returns (uint amountToken, uint amountETH, uint liquidity)

Remove Liquidity

function removeLiquidity(
    address tokenA,
    address tokenB,
    bool stable,
    uint liquidity,       // LP tokens to burn
    uint amountAMin,
    uint amountBMin,
    address to,
    uint deadline
) external returns (uint amountA, uint amountB)

Remove Liquidity (Get XPL back)

function removeLiquidityETH(
    address token,
    bool stable,
    uint liquidity,
    uint amountTokenMin,
    uint amountETHMin,
    address to,
    uint deadline
) external returns (uint amountToken, uint amountETH)

JavaScript Example:

const router = new ethers.Contract(ROUTER_V2_ADDRESS, routerV2Abi, signer);

// Add liquidity to USDC/USDT stable pool
await tokenA.approve(ROUTER_V2_ADDRESS, amountADesired);
await tokenB.approve(ROUTER_V2_ADDRESS, amountBDesired);

const tx = await router.addLiquidity(
  USDC_ADDRESS,
  USDT_ADDRESS,
  true, // stable pool
  amountADesired,
  amountBDesired,
  amountAMin,
  amountBMin,
  userAddress,
  deadline
);

// Add liquidity with XPL
await token.approve(ROUTER_V2_ADDRESS, amountTokenDesired);
const tx2 = await router.addLiquidityETH(
  TOKEN_ADDRESS,
  false, // volatile pool
  amountTokenDesired,
  amountTokenMin,
  amountETHMin,
  userAddress,
  deadline,
  { value: ethers.utils.parseEther("1.0") }
);

3. Token Swaps

Basic Token Swaps

// Multi-hop swap with custom routing
function swapExactTokensForTokens(
    uint amountIn,
    uint amountOutMin,
    route[] calldata routes,  // Custom routing path
    address to,
    uint deadline
) external returns (uint[] memory amounts)

// Simple single-hop swap
function swapExactTokensForTokensSimple(
    uint amountIn,
    uint amountOutMin,
    address tokenFrom,
    address tokenTo,
    bool stable,      // Which pool type to use
    address to,
    uint deadline
) external returns (uint[] memory amounts)

XPL Swaps

// XPL -> Token
function swapExactETHForTokens(
    uint amountOutMin,
    route[] calldata routes,
    address to,
    uint deadline
) external payable returns (uint[] memory amounts)

// Token -> XPL
function swapExactTokensForETH(
    uint amountIn,
    uint amountOutMin,
    route[] calldata routes,
    address to,
    uint deadline
) external returns (uint[] memory amounts)

Route Structure

struct route {
    address from;    // Input token
    address to;      // Output token
    bool stable;     // Pool type (true = stable, false = volatile)
}

JavaScript Example (GlobalRouter):

const globalRouter = new ethers.Contract(
  GLOBAL_ROUTER_ADDRESS,
  globalRouterAbi,
  signer
);

// Simple swap: USDC -> USDT (stable pool)
await usdc.approve(GLOBAL_ROUTER_ADDRESS, amountIn);
const tx = await globalRouter.swapExactTokensForTokens(
  amountIn,
  amountOutMin,
  [{ from: USDC_ADDRESS, to: USDT_ADDRESS, stable: true }],
  userAddress,
  deadline,
  true // _type = true for V2 pools, false for V3 pools
);

// Multi-hop swap: TokenA -> TokenB -> TokenC
const routes = [
  { from: TOKEN_A, to: TOKEN_B, stable: false },
  { from: TOKEN_B, to: TOKEN_C, stable: true },
];

await tokenA.approve(GLOBAL_ROUTER_ADDRESS, amountIn);
const tx2 = await globalRouter.swapExactTokensForTokens(
  amountIn,
  amountOutMin,
  routes,
  userAddress,
  deadline,
  true // use V2 pools
);

// Get swap preview using GlobalRouter
const [amount, isStablePool] = await globalRouter.getAmountOut(
  amountIn,
  TOKEN_A,
  TOKEN_B
);

4. Price Queries & Calculations

Use TradeHelper for price calculations without executing trades:

// Get best rate between stable and volatile pools
function getAmountOut(
    uint amountIn,
    address tokenIn,
    address tokenOut
) external view returns (uint amount, bool stable)

// Calculate multi-hop swap output
function getAmountsOut(
    uint amountIn,
    route[] memory routes
) external view returns (uint[] memory amounts)

// Quote liquidity addition
function quoteAddLiquidity(
    address tokenA,
    address tokenB,
    bool stable,
    uint amountADesired,
    uint amountBDesired
) external view returns (uint amountA, uint amountB, uint liquidity)

5. Fee-on-Transfer Token Support

For tokens that charge fees on transfers, use the SupportingFeeOnTransferTokens variants:

function swapExactTokensForTokensSupportingFeeOnTransferTokens(
    uint amountIn,
    uint amountOutMin,
    route[] calldata routes,
    address to,
    uint deadline
) external

function swapExactETHForTokensSupportingFeeOnTransferTokens(
    uint amountOutMin,
    route[] calldata routes,
    address to,
    uint deadline
) external payable

Pool Types

Stable Pools (stable = true)

  • Use Case: Assets with similar values (USDC/USDT, ETH/stETH)
  • Algorithm: Curve-like stable swap math
  • Benefits: Lower slippage, better rates for similar assets
  • Fees: 0.04% (4 basis points)

Volatile Pools (stable = false)

  • Use Case: Standard trading pairs with different values
  • Algorithm: Uniswap V2 xy=k formula
  • Benefits: Standard AMM behavior, suitable for all assets
  • Fees: 0.18% (18 basis points)

Integration Checklist

  • Import RouterV2 ABI and connect to 0xb7Be9aB86d1A18c0425C3f6ABbbD58d0Ef19f1a9
  • Implement token approval flows before liquidity/swap operations
  • Add slippage tolerance settings (recommend 0.5% for stable, 2% for volatile)
  • Implement deadline parameter (recommend current timestamp + 20 minutes)
  • Handle both stable and volatile pool routing
  • Add support for XPL (native token) operations via *ETH functions
  • Implement price impact warnings for large trades
  • Add liquidity preview using quoteAddLiquidity

Voting Escrow (veNFT) System

Lock LITHOS tokens to receive veNFTs with voting power and revenue sharing rights:

Create Lock

// Create a new lock position
function create_lock(uint256 _value, uint256 _lock_duration) external returns (uint256)

// Create lock for another address
function create_lock_for(uint256 _value, uint256 _lock_duration, address _to) external returns (uint256)

Manage Existing Lock

// Add more tokens to existing lock
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

NFT Features

// Get voting power of NFT
function balanceOfNFT(uint256 _tokenId) external view returns (uint256)

// Transfer veNFT (standard ERC-721)
function transferFrom(address _from, address _to, uint256 _tokenId) external

// Merge two NFTs into one
function merge(uint256 _from, uint256 _to) external

// Split NFT into multiple (specify percentages)
function split(uint256[] memory amounts, uint256 _tokenId) external

JavaScript Example:

const votingEscrow = new ethers.Contract(
  VOTING_ESCROW_ADDRESS,
  votingEscrowAbi,
  signer
);

// Approve LITHOS tokens
await lithos.approve(VOTING_ESCROW_ADDRESS, lockAmount);

// Create 1 year lock
const lockDuration = 365 * 24 * 60 * 60; // 1 year in seconds
const tx = await votingEscrow.create_lock(lockAmount, lockDuration);

// Get veNFT ID from event
const receipt = await tx.wait();
const tokenId = receipt.events.find((e) => e.event === "Transfer").args.tokenId;

// Check voting power
const votingPower = await votingEscrow.balanceOfNFT(tokenId);

// Increase lock amount
await lithos.approve(VOTING_ESCROW_ADDRESS, additionalAmount);
await votingEscrow.increase_amount(tokenId, additionalAmount);

// Transfer veNFT
await votingEscrow.transferFrom(userAddress, recipientAddress, tokenId);

Lock Parameters:

  • Min Duration: 1 week
  • Max Duration: 2 years (104 weeks)
  • Voting Power: Linear decay over time
  • Revenue Sharing: Proportional to voting power

Integration Checklist

DEX Features

  • Import GlobalRouter ABI and connect to 0x88C19a127aa22C7826546F34E63FE0e8995c88d0
  • Implement token approval flows before liquidity/swap operations
  • Add slippage tolerance settings (recommend 0.5% for stable, 2% for volatile)
  • Implement deadline parameter (recommend current timestamp + 20 minutes)
  • Handle both stable and volatile pool routing
  • Add support for XPL (native token) operations via *ETH functions
  • Implement price impact warnings for large trades
  • Add liquidity preview using quoteAddLiquidity

Voting Escrow (veNFT) Features

  • Import VotingEscrow ABI and connect to 0x516C42d4BcF32531Cb7cf5Eb89Bb8870A4a60011
  • Import Lithos token ABI and connect to 0x3a6a2309Bc05b9798CF46699Bba9F6536039B72D
  • Implement LITHOS token approval for locking operations
  • Add lock duration selector (1 week to 2 years)
  • Display voting power decay over time
  • Implement veNFT transfer functionality (ERC-721 standard)
  • Add merge/split NFT features for advanced users
  • Show lock expiration dates and withdrawal eligibility

Error Handling

Common revert reasons:

  • BaseV1Router: EXPIRED - Transaction deadline passed
  • BaseV1Router: INSUFFICIENT_OUTPUT_AMOUNT - Slippage tolerance exceeded
  • BaseV1Router: INSUFFICIENT_A_AMOUNT - Minimum amount not met
  • BaseV1Router: INVALID_PATH - Routing path invalid (check WXPL address)
  • Pair: INSUFFICIENT_LIQUIDITY - Pool has no liquidity

Foundry

Foundry is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust.

Foundry consists of:

  • Forge: Ethereum testing framework (like Truffle, Hardhat and DappTools).
  • Cast: Swiss army knife for interacting with EVM smart contracts, sending transactions and getting chain data.
  • Anvil: Local Ethereum node, akin to Ganache, Hardhat Network.
  • Chisel: Fast, utilitarian, and verbose solidity REPL.

Documentation

https://book.getfoundry.sh/

Usage

Build

$ forge build

Test

$ forge test

Format

$ forge fmt

Gas Snapshots

$ forge snapshot

Anvil

$ anvil

Deploy

$ forge script script/Counter.s.sol:CounterScript --rpc-url <your_rpc_url> --private-key <your_private_key>

Cast

$ cast <subcommand>

Help

$ forge --help
$ anvil --help
$ cast --help

Subgraph Deployment

Deploy the subgraph to index blockchain data using Goldsky CLI:

Prerequisites

  1. Install Goldsky CLI (if not already installed):

    npm install -g @goldsky/cli
  2. Login to Goldsky (if not already logged in):

    goldsky login

    Use the shared API key provided by the team.

Deployment Steps

  1. Navigate to subgraph directory:

    cd subgraph
  2. Generate types from schema:

    yarn codegen
  3. Build the subgraph:

    yarn build
  4. Deploy to Goldsky:

    goldsky subgraph deploy <subgraph-name>/<version> --path .

Example Deployment Commands

For mainnet:

cd subgraph
yarn codegen
yarn build
goldsky subgraph deploy lithos-subgraph-mainnet/v1.0.0 --path .

Additional Goldsky Commands

List deployed subgraphs:

goldsky subgraph list

Get subgraph info:

goldsky subgraph info <subgraph-name>/<version>

Delete a subgraph:

goldsky subgraph delete <subgraph-name>/<version>

Note: Ensure your subgraph.yaml manifest has the correct contract addresses from your deployment state file before deploying.

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages