Example: Single-Pair Swap Lyquid
This example shows how to achieve composability through network functions like the traditional contracts that can call each other directly on chain. We followed the code from the original Uniswap V2 Core implementation and translate that into a Lyquid to show case how you can easily 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 ETH composability layer LyquidIDs are represented by
address, but should not be confused with the token address used by 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 (multiple rounds)
# Token approval
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: 0.10 token0 -> 0.09 token1 ==="
cast send $PAIR "swap(uint256, uint256, address, uint256, uint256)" 0 90000000000000000 $RECIPIENT 100000000000000000 0 --private-key "$KEY" -q
sleep 1
print_prices
# -- Price0 (token0 in token1) --
# 827272727272727272 [8.272e17]
# -- Price1 (token1 in token0) --
# 1208791208791208791 [1.208e18]
echo "=== Swap 2: 0.05 token0 -> 0.0394 token1 ==="
cast send $PAIR "swap(uint256, uint256, address, uint256, uint256)" 0 39400000000000000 $RECIPIENT 50000000000000000 0 --private-key "$KEY" -q
sleep 1
print_prices
# -- Price0 (token0 in token1) --
# 757043478260869565 [7.57e17]
# -- Price1 (token1 in token0) --
# 1320928095566276131 [1.32e18]
echo "=== Swap 3: 0.20 token0 -> 0.128 token1 ==="
cast send $PAIR "swap(uint256, uint256, address, uint256, uint256)" 0 128000000000000000 $RECIPIENT 200000000000000000 0 --private-key "$KEY" -q
sleep 1
print_prices
# -- Price0 (token0 in token1) --
# 550074074074074074 [5.5e17]
# -- Price1 (token1 in token0) --
# 1817936978184756261 [1.817e18]
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)
# Output: "499999999999999000 [4.999e17]"