Example: Single-Pair Swap Lyquid
This example shows how to achieve composability through network functions like traditional contracts that can call each other directly on chain. The AMM logic follows the original Uniswap V2 Core implementation, while the pricing/slippage helpers are inspired by the Uniswap V2 periphery (Library/Router). We translate these ideas into a Lyquid to show how you can do the same. This only implements the core part that enables a standard and realistic AMM to allow swaps for one pair of tokens. It's not a ready-to-use product and does not include UI for simplicity of demonstration.
Prerequisites
Make sure you have started the local devnet.
Deployment
-
Deploy Token A (First ERC20 Token)
shaker deploy ~/.shakenup/ldk/lyquid-examples/erc20/Cargo.tomlThis command will give you something like
created Lyquid-Adm8r3j64FbzRjwcBK4ZZRFcgLQjTqtt => 0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512Keep both the Lyquid ID
LYQUID_A=Lyquid-Adm8r3j64FbzRjwcBK4ZZRFcgLQjTqtt, and the address:TOKEN_A=0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512 -
Deploy Token B (Second ERC20 Token)
shaker deploy ~/.shakenup/ldk/lyquid-examples/erc20/Cargo.tomlThis command will output a different set of Lyquid ID/address:
created Lyquid-HTZTGB7jxhgcKGWn7R9897gzuCZpPhL8C => 0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0Keep both the Lyquid ID
LYQUID_B=Lyquid-HTZTGB7jxhgcKGWn7R9897gzuCZpPhL8C, and this address:TOKEN_B=0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0 -
Deploy the BasicSwap Contract
The constructor of BasicSwap takes two LyquidIDs. In the ETH composability layer LyquidIDs are represented by
address, but should not be confused with the token address used by a user's wallet. You can useshaker to-hextool to convert LyquidID to hex format.LYQUID_A="Lyquid-Adm8r3j64FbzRjwcBK4ZZRFcgLQjTqtt"
LYQUID_B="Lyquid-HTZTGB7jxhgcKGWn7R9897gzuCZpPhL8C"
shaker deploy ~/.shakenup/ldk/lyquid-examples/basicswap/Cargo.toml --input $(cast abi-encode "constructor(address,address)" $(shaker to-hex "$LYQUID_A") $(shaker to-hex "$LYQUID_B"))This will output:
created Lyquid-Kpdt9vk6tiBdQpgv6kqHpLwG2Sg9na58Y => 0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9Keep this address:
PAIR=0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9. And convert the Lyquid ID to hex format.shaker to-hex Lyquid-Kpdt9vk6tiBdQpgv6kqHpLwG2Sg9na58Y
# Output: "0xce749A113606ee9E8afE4C0927A4ebCe63F0a447"
Play with the BasicSwap
Here is an example to try all the functions.
Liquidity Deposit
export FOUNDRY_ETH_RPC_URL="http://localhost:10087/api"
export KEY="0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"
YOUR_ADDR="0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"
RECIPIENT="0x27Ef5cDBe01777D62438AfFeb695e33fC2335979"
# Contract addresses (from deployment above)
TOKEN_A="0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512"
TOKEN_B="0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0"
PAIR="0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9"
PAIR_LYQUID="0xce749a113606ee9e8afe4c0927a4ebce63f0a447"
echo "=== Adding Liquidity ==="
# Transfer tokens to the pair
cast send $TOKEN_A "transfer(address, uint256)" $PAIR_LYQUID 1000000000000000000 --private-key "$KEY" -q
cast send $TOKEN_B "transfer(address, uint256)" $PAIR_LYQUID 1000000000000000000 --private-key "$KEY" -q
sleep 1
cast abi-decode "balanceOf(address) returns (uint256)" $(cast call 0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512 "balanceOf(address)" $PAIR_LYQUID)
# Output: "1000000000000000000 [1e18]"
cast abi-decode "balanceOf(address) returns (uint256)" $(cast call 0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0 "balanceOf(address)" $PAIR_LYQUID)
# Output: "1000000000000000000 [1e18]"
# Mint LP tokens to YOUR_ADDR
cast send $PAIR "mint(address)" $YOUR_ADDR --private-key "$KEY" -q
sleep 1
echo "=== LP Balance ==="
cast abi-decode "balanceOf(address) returns (uint256)" $(cast call $PAIR "balanceOf(address)" $YOUR_ADDR)
# Output: "999999999999999000 [9.999e17]"
echo "=== Initial Prices ==="
cast abi-decode "getPrice0() returns (uint256)" $(cast call $PAIR "getPrice0()")
# Output: "1000000000000000000 [1e18]"
cast abi-decode "getPrice1() returns (uint256)" $(cast call $PAIR "getPrice1()")
# Output: "1000000000000000000 [1e18]"
Swap (slippage-based, recommended)
Use the slippage-based swap helpers so you only specify the amount and a slippage cap.
Flow overview:
- Approve input token to the pair’s LyquidID address so the pair can pull tokens from you.
- Exact-in swap (
swapExactInWithSlippage): you provide how much you spend. The pair quotes the output and enforces the slippage cap (minimum output).token0_to_token1indicates the swap direction: set it to true to swap token0 for token1; set it to false to swap token1 for token0. - Exact-out swap (
swapExactOutWithSlippage): you provide how much you want. The pair quotes the required input and enforces the slippage cap (maximum input). - Observe prices after each swap to see the pool move.
# Token approval (approve the pair's LyquidID address for the input token)
cast send $TOKEN_A "approve(address, uint256)" $PAIR_LYQUID 2000000000000000000 --private-key "$KEY" -q
sleep 1
print_prices() {
echo "-- Price0 (token0 in token1) --"
cast abi-decode "getPrice0() returns (uint256)" $(cast call $PAIR "getPrice0()")
echo "-- Price1 (token1 in token0) --"
cast abi-decode "getPrice1() returns (uint256)" $(cast call $PAIR "getPrice1()")
}
echo "=== Swap 1: token0 -> token1 (exact in, 1% slippage) ==="
# token0_to_token1 = true, slippage_bps = 100 (1%), spend 0.1 token0
cast send $PAIR "swapExactInWithSlippage(bool,uint256,uint32,address)" true 100000000000000000 100 $RECIPIENT --private-key "$KEY" -q
sleep 1
print_prices
# -- Price0 (token0 in token1) --
# 826671736919986442 [8.266e17]
# -- Price1 (token1 in token0) --
# 1209669999999999999 [1.209e18]
echo "=== Swap 2: token1 -> token0 (exact out, 1% slippage) ==="
# Approve token1 as input
cast send $TOKEN_B "approve(address, uint256)" $PAIR_LYQUID 2000000000000000000 --private-key "$KEY" -q
sleep 1
# token0_to_token1 = false, slippage_bps = 100 (1%), want 0.05 token0
cast send $PAIR "swapExactOutWithSlippage(bool,uint256,uint32,address)" false 50000000000000000 100 $RECIPIENT --private-key "$KEY" -q
sleep 1
print_prices
# -- Price0 (token0 in token1) --
# 907401009472640909 [9.074e17]
# -- Price1 (token1 in token0) --
# 1102048586634453206 [1.102e18]
Liquidity Removal
echo "=== Liquidity Burn ==="
# Burn half of the LP tokens (500000000000000000)
cast send $PAIR "burn(address, uint256)" $YOUR_ADDR 500000000000000000 --private-key "$KEY" -q
sleep 1
# Check balances after burning
cast abi-decode "balanceOf(address) returns (uint256)" $(cast call $PAIR "balanceOf(address)" $YOUR_ADDR)
# === Liquidity Burn ===
# 499999999999999000 [4.999e17]