On-Chain Market MakerΒΆ
Warning
This is example code for learning purposes. Do not use in production without thorough review and testing.
A simple automated market maker (AMM) using the constant product formula (x * y = k).
1#pragma version >0.3.10
2
3from ethereum.ercs import IERC20
4
5
6totalEthQty: public(uint256)
7totalTokenQty: public(uint256)
8# Constant set in `initiate` that's used to calculate
9# the amount of ether/tokens that are exchanged
10invariant: public(uint256)
11token: IERC20
12owner: public(address)
13finalized: bool
14
15# Sets the on chain market maker with its owner, initial token quantity,
16# and initial ether quantity
17@external
18@payable
19def initiate(token_addr: address, token_quantity: uint256):
20 assert self.invariant == 0
21 self.token = IERC20(token_addr)
22 extcall self.token.transferFrom(msg.sender, self, token_quantity)
23 self.owner = msg.sender
24 self.totalEthQty = msg.value
25 self.totalTokenQty = token_quantity
26 self.invariant = msg.value * token_quantity
27 assert self.invariant > 0
28
29# Sells ether to the contract in exchange for tokens (minus a fee)
30@external
31@payable
32def ethToTokens():
33 assert not self.finalized
34
35 fee: uint256 = msg.value // 500
36 eth_in_purchase: uint256 = msg.value - fee
37 new_total_eth: uint256 = self.totalEthQty + eth_in_purchase
38 new_total_tokens: uint256 = self.invariant // new_total_eth
39 extcall self.token.transfer(msg.sender, self.totalTokenQty - new_total_tokens)
40 self.totalEthQty = new_total_eth
41 self.totalTokenQty = new_total_tokens
42
43# Sells tokens to the contract in exchange for ether
44@external
45def tokensToEth(sell_quantity: uint256):
46 assert not self.finalized
47
48 extcall self.token.transferFrom(msg.sender, self, sell_quantity)
49 new_total_tokens: uint256 = self.totalTokenQty + sell_quantity
50 new_total_eth: uint256 = self.invariant // new_total_tokens
51 eth_to_send: uint256 = self.totalEthQty - new_total_eth
52 send(msg.sender, eth_to_send)
53 self.totalEthQty = new_total_eth
54 self.totalTokenQty = new_total_tokens
55
56# Owner can withdraw their funds and stop the exchange
57@external
58def ownerWithdraw():
59 assert self.owner == msg.sender
60
61 self.finalized = True
62
63 extcall self.token.transfer(self.owner, self.totalTokenQty)
64
65 if self.balance > 0:
66 send(self.owner, self.balance)
How it works:
Owner calls
initiate()with initial ETH and tokens, setting the invariant (k = ETH * tokens)Users swap ETH for tokens via
ethToTokens()Users swap tokens for ETH via
tokensToEth()The invariant is maintained: more ETH in = fewer tokens out
The 0.2% fee (msg.value // 500) on ETH-to-token swaps goes to the liquidity provider.
Note
Production AMMs need price oracles, slippage protection, and liquidity management. This example demonstrates the core swap mechanism only.