This post is part of a series on DeFi. Here is the previous post, and this is the first post in the series.
We’ve seen how buying and selling from the Automated Market Maker (AMM) alters the token price. Now let’s see what it looks like when we have multiple people (counterparties) buying from and selling to the AMM.
Counterparty
We’ll create a Counterparty
class to represent our traders. Each Counterparty
has a name, and balances of ether and tokens.
class Counterparty
attr_accessor :name, :ether, :tokens
def initialize(params)
@name = params.fetch(:name)
@ether = params.fetch(:ether, 0).to_f
@tokens = params.fetch(:tokens, 0).to_f
end
end
Trading
Instead of entering commands like buy 1
, and sell 9
, we want to be able to
say who is buying/selling, so we want commands like Alice buy 1
and Bob sell 9
.
This means we need a way to refer to a particular Counterparty
by its name.
We’ll use a simple manager class for this, called Counterparties
.
# Manager class for Counterparty objects
class Counterparties
# list: Array of Counterparty objects
def initialize(list)
@counterparties = list.inject({}) { |acc, c| acc[c.name] = c; acc }
end
def find(name)
@counterparties[name]
end
end
This just takes a list of Counterparty
objects and creates a hash keyed on
the names, so we can easily refer to a specific Counterparty
.
Adjusting counterparties` balances
We need to adapt the buy
and sell
methods of our AMM to adjust
counterparties` balances of tokens and ether when they trade:
def buy(counterparty, amount)
ether = amount.to_f
tokens_bought, @ether_reserve, @token_reserve = trade(ether, @ether_reserve, @token_reserve)
counterparty.ether -= ether
counterparty.tokens += tokens_bought
return tokens_bought
end
def sell(counterparty, amount)
tokens = amount.to_f
ether, @token_reserve, @ether_reserve = trade(tokens, @token_reserve, @ether_reserve)
counterparty.ether += ether
counterparty.tokens -= tokens
return ether
end
Now we need to adjust our bin/amm.rb
script to create some counterparties:
alice = Counterparty.new(name: "Alice", ether: 10)
bob = Counterparty.new(name: "Bob", ether: 10)
counterparties = Counterparties.new([alice, bob])
When we get buy and sell commands, we figure out who is trading, and send the
Counterparty
object in our call to buy
or sell
:
when /(.*) buy (.*)/
name = $1
ether = $2.to_f
counterparty = counterparties.find(name)
amm.buy(counterparty, ether)
when /(.*) sell (.*)/
name = $1
counterparty = counterparties.find(name)
tokens = $2 == "all" ? counterparty.tokens : $2.to_f
amm.sell(counterparty, tokens)
I’ve added a convenience amount all
, to sell all of a Counterparty’s tokens,
so we don’t have to copy/paste long floating-point numbers.
I’ve also added a bit of code to output all counterparties and their token/ether balances, so we can see what’s going on.
You can see the full code here.
Let’s see what happens when multiple counterparties trade on our AMM:
$ bin/amm.rb
Pool eth: 10.0, tokens: 1000.0
> Alice buy 1
Alice gets 90.90909090909088 tokens for 1.0 ether, price 0.0110
Pool eth: 11.0, tokens: 909.0909090909091
> Bob buy 1
Bob gets 75.75757575757575 tokens for 1.0 ether, price 0.0132
Pool eth: 12.0, tokens: 833.3333333333334
> Alice sell all
Alice gets 1.1803278688524586 ether for 90.90909090909088 tokens, price 0.0130
Pool eth: 10.819672131147541, tokens: 924.2424242424242
> Bob sell all
Bob gets 0.8196721311475414 ether for 75.75757575757575 tokens, price 0.0108
Pool eth: 10.0, tokens: 1000.0
> counterparties
Alice ether: 10.1803, tokens: 0.0000
Bob ether: 9.8197, tokens: 0.0000
> q
bye.
Both counterparties buy 1 ETH worth of tokens, and then sell it back again, but because of the actions of the other traders in the market (i.e. Alice), Bob ends up losing some ether, and Alice gains it.
This shows how a very simple algorithm, x * y = k
can form the basis for a
marketplace where traders can win or lose by trading against one another.
So far, we’ve just looked at a very simple case where we spin up an AMM with a fixed pool of assets (tokens and ether). In practice, this isn’t very interesting because it suffers from slippage, and all traders can really do is redistribute that fixed supply of ether and tokens amongst themselves.
In the next post, we’ll look at how we can enable and incentivise people to add assets to our AMM, so that slippage is minimised and our AMM becomes more appealing to traders (counterparties).
2 thoughts on “Trading the Automated Market Maker”