Skip to content

LithosDex/lithos-smart-contracts

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

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 4

  •  
  •  
  •  
  •