Integrate LiftUpin five minutes.
LiftUp is the stablecoin liquidity layer on Arc — Uniswap-V2- compatible AMM with a 0.05% fee tier across USDC / EURC / cirBTC pairs and a CCTP-native bridge. Any aggregator that speaks the V2 ABI can route through it — addresses, fee math, and integration samples below.
Contracts
Addresses & network
Everything is on Arc Testnet (chainId 5042002). Click an address to view on ArcScan.
Core contracts
Deploys pairs via CREATE2 · constructor(feeToSetter)
V2 router · swapExactTokensForTokens, addLiquidity, removeLiquidity
Receives 100% of pool fees + Circle 10 bps surcharge · 5 buckets
0.05% fee · standard V2 Swap/Mint/Burn events
0.05% fee · USDC-quoted BTC market via Circle Wrapped BTC
0.05% fee · EURC-quoted BTC market via Circle Wrapped BTC
Token contracts
6 decimals · Arc native gas token
8 decimals · Circle Wrapped Bitcoin
Lift Engine v1.1
Open-source primitive, fully specified.
Pro-rata distribution layer with per-swap cashback + claim mechanism. Replaces v0 lottery-style distributor. Currently in audit scope ($15k grant). Migration preserves all LP positions — no remove + re-add required.
Lift Engine Spec
Full specification of the v1.1 reward layer — 8 sub-buckets, pro-rata accrual, instant trader cashback, claim mechanism, bounded launch boost. The contract being audited.
Migration Plan (v0 → v1.1)
Step-by-step migration from the v0 lottery distributor to LiftEngine. LP positions stay intact (no remove + re-add); only factory.feeTo() switches in one tx.
| Aspect | v0 (current) | v1.1 (Lift Engine) |
|---|---|---|
| Selection | Random recipient sampling (PRNG) | Deterministic pro-rata by weight |
| Trader path | Random lottery winners | Instant cashback per swap + weekly/monthly bonus |
| LP path | Random + pro-rata split | Pure pro-rata weighted by tier × tenure |
| Payout flow | Pushed by owner-gated distribute* | User-pulled claim() — sub-cent gas |
| Multipliers | Soft tier + tenure (off-chain) | On-chain tier + tenure + bounded launch boost |
| Sub-buckets | 5 buckets | 8 (growth + bonus + 3 cadences × LP/Trader) |
Fee math
Standard V2 formula, 5 bps fee tier
The K-invariant is unchanged from Uniswap V2 — only the numerator/denominator differ. Plug the constants below into your existing V2 quote helper and LiftUp slots in.
// V2 swap math with LiftUp's 0.05% fee tier
const FEE_NUMERATOR = 9995n; // 10000 - 5 (5 bps fee)
const FEE_DENOMINATOR = 10000n;
function getAmountOut(amountIn, reserveIn, reserveOut) {
const amountInWithFee = amountIn * FEE_NUMERATOR;
const numerator = amountInWithFee * reserveOut;
const denominator = reserveIn * FEE_DENOMINATOR + amountInWithFee;
return numerator / denominator;
}5 basis points, matches Aerodrome / Velodrome stable tier
amount in × (10000 − 5) / 10000 = amount after fee
basis-points base
Heads up
LiftupPair transfers 100% of the fee out of the pool to factory.feeTo() on every swap. K stays constant after the swap (instead of growing like vanilla V2). If your aggregator monitors K-growth as a sanity check, treat LiftUp as a special source where K-growth equals zero.
Pair address derivation
Standard V2 CREATE2. Sort tokens ascending, hash as the salt, combine with the factory address and LiftupPair init-code hash.
// LiftupFactory uses standard V2 CREATE2 with sorted-token salt.
import { keccak256, encodePacked, getCreate2Address } from 'viem';
function liftupPairAddress(factory, tokenA, tokenB, initCodeHash) {
const [token0, token1] = tokenA.toLowerCase() < tokenB.toLowerCase()
? [tokenA, tokenB]
: [tokenB, tokenA];
const salt = keccak256(
encodePacked(['address', 'address'], [token0, token1])
);
return getCreate2Address({ from: factory, salt, bytecodeHash: initCodeHash });
}
// initCodeHash for LiftupPair: keccak256(LiftupPair_creationCode + abi.encode(token0, token1))
// available via factory.allPairs(i) iteration or precomputed off-chain.Sample code
Wire it in today.
TypeScript + viem snippets. The router exposes the same surface as Uniswap V2 — drop addresses in and ship.
// Read live reserves from a LiftupPair (viem)
import { createPublicClient, http } from 'viem';
const client = createPublicClient({
chain: { id: 5042002, name: 'Arc Testnet' },
transport: http('https://rpc.testnet.arc.network'),
});
const reserves = await client.readContract({
address: '0xc36B6B7f9F35A145E2E34c9452E99E57379aEBfF', // USDC/EURC pair
abi: [{
type: 'function',
name: 'getReserves',
inputs: [],
outputs: [
{ name: 'reserve0', type: 'uint112' },
{ name: 'reserve1', type: 'uint112' },
{ name: 'blockTimestampLast', type: 'uint32' },
],
stateMutability: 'view',
}],
functionName: 'getReserves',
});// Quote a swap using LiftupRouter (recommended over manual math)
const amountOut = await client.readContract({
address: '0x9bAca4056C278bA4f45bDd80B539322a1238c240', // Router
abi: [{
type: 'function',
name: 'getAmountOut',
inputs: [
{ name: 'amountIn', type: 'uint256' },
{ name: 'tokenIn', type: 'address' },
{ name: 'tokenOut', type: 'address' },
],
outputs: [{ name: 'amountOut', type: 'uint256' }],
stateMutability: 'view',
}],
functionName: 'getAmountOut',
args: [
1_000_000n, // 1 USDC (6 decimals)
'0x3600000000000000000000000000000000000000', // USDC
'0x89B50855Aa3bE2F677cD6303Cec089B5F319D72a', // EURC
],
});// Execute a swap (user must have approved Router to spend tokenIn)
import { encodeFunctionData } from 'viem';
const data = encodeFunctionData({
abi: [{
type: 'function',
name: 'swapExactTokensForTokens',
inputs: [
{ name: 'amountIn', type: 'uint256' },
{ name: 'amountOutMin', type: 'uint256' },
{ name: 'path', type: 'address[]' },
{ name: 'to', type: 'address' },
{ name: 'deadline', type: 'uint256' },
],
outputs: [{ type: 'uint256[]' }],
stateMutability: 'nonpayable',
}],
functionName: 'swapExactTokensForTokens',
args: [
1_000_000n, // amountIn
quotedOut * 9950n / 10000n, // 0.5% slippage
[USDC, EURC], // path
userAddress, // recipient
BigInt(Math.floor(Date.now() / 1000) + 600), // 10-min deadline
],
});
await wallet.sendTransaction({ to: '0x9bAca…', data });// Standard V2 events emitted by LiftupPair
event Swap(
address indexed sender,
uint256 amount0In,
uint256 amount1In,
uint256 amount0Out,
uint256 amount1Out,
address indexed to
);
event Mint(address indexed sender, uint256 amount0, uint256 amount1);
event Burn(
address indexed sender,
uint256 amount0,
uint256 amount1,
address indexed to
);
event Sync(uint112 reserve0, uint112 reserve1);
event Transfer(address indexed from, address indexed to, uint256 value);
// Bonus: LiftupRewardDistributor's Distributed event for indexing winners
event Distributed(
bytes32 indexed bucket, // "daily" / "weekly" / "monthly" / "bonus"
address indexed token,
address indexed recipient,
uint256 amount
);Partner with LiftUp
Aggregator? Front-end builder?
We'll co-promote any aggregator that lists LiftUp Pool as a routing source on Arc — protocol-level partnerships only. Reach out and we'll wire you into the next testnet snapshot + mainnet launch list.
Aggregator integrations (1inch · Paraswap · OpenOcean · Matcha) will be filed after the audited mainnet release.