# AMM Solidity implementation
AMM_CONTRACT = '''
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
interface IERC20 {
function transferFrom(address from, address to, uint256 amount) external returns (bool);
function transfer(address to, uint256 amount) external returns (bool);
function balanceOf(address account) external view returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
}
contract SimpleAMM {
IERC20 public immutable token0; // Ex: WETH
IERC20 public immutable token1; // Ex: USDC
uint256 public reserve0;
uint256 public reserve1;
uint256 public totalLiquidity;
mapping(address => uint256) public liquidity;
uint256 private constant FEE_NUMERATOR = 3;
uint256 private constant FEE_DENOMINATOR = 1000; // 0.3%
event Swap(
address indexed sender,
uint256 amount0In,
uint256 amount1In,
uint256 amount0Out,
uint256 amount1Out
);
event LiquidityAdded(
address indexed provider,
uint256 amount0,
uint256 amount1,
uint256 liquidityMinted
);
event LiquidityRemoved(
address indexed provider,
uint256 amount0,
uint256 amount1,
uint256 liquidityBurned
);
constructor(address _token0, address _token1) {
token0 = IERC20(_token0);
token1 = IERC20(_token1);
}
// Ajouter de la liquidite
function addLiquidity(
uint256 amount0Desired,
uint256 amount1Desired,
uint256 amount0Min,
uint256 amount1Min
) external returns (uint256 liquidityMinted) {
// Transfer les tokens
token0.transferFrom(msg.sender, address(this), amount0Desired);
token1.transferFrom(msg.sender, address(this), amount1Desired);
// Calcul de la liquidite a mint
if (totalLiquidity == 0) {
// Premier fournisseur: geometric mean
liquidityMinted = sqrt(amount0Desired * amount1Desired);
} else {
// Proportionnel aux reserves existantes
uint256 liquidity0 = (amount0Desired * totalLiquidity) / reserve0;
uint256 liquidity1 = (amount1Desired * totalLiquidity) / reserve1;
liquidityMinted = liquidity0 < liquidity1 ? liquidity0 : liquidity1;
}
require(liquidityMinted > 0, "Insufficient liquidity minted");
require(amount0Desired >= amount0Min, "Slippage: amount0");
require(amount1Desired >= amount1Min, "Slippage: amount1");
liquidity[msg.sender] += liquidityMinted;
totalLiquidity += liquidityMinted;
reserve0 += amount0Desired;
reserve1 += amount1Desired;
emit LiquidityAdded(msg.sender, amount0Desired, amount1Desired, liquidityMinted);
}
// Retirer de la liquidite
function removeLiquidity(uint256 liquidityToRemove)
external
returns (uint256 amount0, uint256 amount1)
{
require(liquidity[msg.sender] >= liquidityToRemove, "Insufficient liquidity");
amount0 = (liquidityToRemove * reserve0) / totalLiquidity;
amount1 = (liquidityToRemove * reserve1) / totalLiquidity;
liquidity[msg.sender] -= liquidityToRemove;
totalLiquidity -= liquidityToRemove;
reserve0 -= amount0;
reserve1 -= amount1;
token0.transfer(msg.sender, amount0);
token1.transfer(msg.sender, amount1);
emit LiquidityRemoved(msg.sender, amount0, amount1, liquidityToRemove);
}
// Swap token0 -> token1
function swap0For1(uint256 amount0In, uint256 amount1OutMin)
external
returns (uint256 amount1Out)
{
token0.transferFrom(msg.sender, address(this), amount0In);
// Fee deduite
uint256 amount0InWithFee = (amount0In * (FEE_DENOMINATOR - FEE_NUMERATOR)) / FEE_DENOMINATOR;
// Constant product formula: (reserve0 + amountIn) * (reserve1 - amountOut) = k
// amountOut = reserve1 - (reserve0 * reserve1) / (reserve0 + amountInWithFee)
amount1Out = (amount0InWithFee * reserve1) / (reserve0 + amount0InWithFee);
require(amount1Out >= amount1OutMin, "Slippage: amount1Out too low");
require(amount1Out <= reserve1, "Insufficient reserve1");
reserve0 += amount0In;
reserve1 -= amount1Out;
token1.transfer(msg.sender, amount1Out);
emit Swap(msg.sender, amount0In, 0, 0, amount1Out);
}
// Swap token1 -> token0
function swap1For0(uint256 amount1In, uint256 amount0OutMin)
external
returns (uint256 amount0Out)
{
token1.transferFrom(msg.sender, address(this), amount1In);
uint256 amount1InWithFee = (amount1In * (FEE_DENOMINATOR - FEE_NUMERATOR)) / FEE_DENOMINATOR;
amount0Out = (amount1InWithFee * reserve0) / (reserve1 + amount1InWithFee);
require(amount0Out >= amount0OutMin, "Slippage: amount0Out too low");
require(amount0Out <= reserve0, "Insufficient reserve0");
reserve1 += amount1In;
reserve0 -= amount0Out;
token0.transfer(msg.sender, amount0Out);
emit Swap(msg.sender, 0, amount1In, amount0Out, 0);
}
// Prix actuel
function getPrice0() external view returns (uint256) {
require(reserve0 > 0, "No liquidity");
return (reserve1 * 1e18) / reserve0;
}
// Square root function (Babylonian method)
function sqrt(uint256 y) internal pure returns (uint256 z) {
if (y > 3) {
z = y;
uint256 x = y / 2 + 1;
while (x < z) {
z = x;
x = (y / x + x) / 2;
}
} else if (y != 0) {
z = 1;
}
}
}
'''
# Token ERC-20 minimal pour tester l'AMM
ERC20_HELPER = '''
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;
contract TestToken {
string public name;
string public symbol;
uint8 public constant decimals = 18;
uint256 public totalSupply;
mapping(address => uint256) public balanceOf;
mapping(address => mapping(address => uint256)) public allowance;
constructor(string memory _name, string memory _symbol, uint256 _initialSupply) {
name = _name;
symbol = _symbol;
totalSupply = _initialSupply;
balanceOf[msg.sender] = _initialSupply;
}
function transfer(address to, uint256 amount) external returns (bool) {
require(balanceOf[msg.sender] >= amount, "Insufficient");
balanceOf[msg.sender] -= amount;
balanceOf[to] += amount;
return true;
}
function approve(address spender, uint256 amount) external returns (bool) {
allowance[msg.sender][spender] = amount;
return true;
}
function transferFrom(address from, address to, uint256 amount) external returns (bool) {
require(allowance[from][msg.sender] >= amount, "Not approved");
require(balanceOf[from] >= amount, "Insufficient");
allowance[from][msg.sender] -= amount;
balanceOf[from] -= amount;
balanceOf[to] += amount;
return true;
}
}
'''
# Deployer deux tokens pour l'AMM
token0, _ = compile_and_deploy(w3, ERC20_HELPER, deployer, "Wrapped ETH", "WETH", 1_000_000 * 10**18)
token1, _ = compile_and_deploy(w3, ERC20_HELPER, deployer, "USD Coin", "USDC", 10_000_000 * 10**18)
# Deployer l'AMM
amm, _ = compile_and_deploy(w3, AMM_CONTRACT, deployer, token0.address, token1.address)
# --- Demonstration: ajouter de la liquidite ---
amount0 = 100 * 10**18 # 100 WETH
amount1 = 200_000 * 10**18 # 200,000 USDC (prix initial: 2000 USDC/WETH)
# Approuver l'AMM pour les transferts
token0.functions.approve(amm.address, amount0).transact({"from": deployer})
token1.functions.approve(amm.address, amount1).transact({"from": deployer})
# Ajouter la liquidite
tx = amm.functions.addLiquidity(amount0, amount1, 0, 0).transact({"from": deployer})
w3.eth.wait_for_transaction_receipt(tx)
print(f"\nLiquidite ajoutee:")
print(f" Reserve WETH : {amm.functions.reserve0().call() / 10**18:.0f}")
print(f" Reserve USDC : {amm.functions.reserve1().call() / 10**18:.0f}")
print(f" Prix WETH : {amm.functions.getPrice0().call() / 10**18:.2f} USDC")
# --- Demonstration: swap 5 WETH -> USDC ---
swap_amount = 5 * 10**18
token0.functions.approve(amm.address, swap_amount).transact({"from": deployer})
usdc_before = token1.functions.balanceOf(deployer).call()
tx = amm.functions.swap0For1(swap_amount, 0).transact({"from": deployer})
w3.eth.wait_for_transaction_receipt(tx)
usdc_after = token1.functions.balanceOf(deployer).call()
usdc_received = (usdc_after - usdc_before) / 10**18
print(f"\nSwap 5 WETH -> USDC:")
print(f" USDC recu : {usdc_received:.2f}")
print(f" Prix d'execution: {usdc_received / 5:.2f} USDC/WETH")
print(f" Nouveau prix : {amm.functions.getPrice0().call() / 10**18:.2f} USDC/WETH")