Pymich Use case on decentralized exchange - Part 1

Thomas Binetruy
-
October 27, 2022

PYMICH Use Case #1: Decentralized Exchange

Let's investigate how an automated market maker parametrized by the constant product formula behaves as we swap tokens from one side to the other! A quick and easy introduction, using Jupyter Notebooks to run some Pymich this time 🧐

On a constant product AMM, the price of an asset with respect to another is determined by the amount of tokens each pool contains. Let’s define a function that calculates how many token A’s one would get by exchanging N token B’s in, including a small fee of course.

Notice how Pymich requires functions to be annotated with Michelson types, which we’ve imported accordingly:

Our dex allows exchanging XTZ for tokens and back, though our above function only takes naturals as pool inputs. Let’s make some casting functions to convert between mutez and natural numbers 🙂

Stepping it up, we’ll also need to send XTZ from the contract when exchanging tokens for tezies. We define an Operations → Operations function. It’ll take a list of operations to be spawned by the contract and return a new one with the XTZ transfer appended to it. Notice how we type the contract using Contract(Unit)(to). Mypy will catch a type error if the third argument to push doesn’t match this type. Here it is typed as Unit since we are sending the operation to an implicit wallet, a user account:

We’ll also need our AMM to send some FA1.2 tokens when exchanging some tezies in. We implement a dataclass inheriting from Record to define the call parameter type needed to interact with an FA1.2 contract. The rest speaks from itself and Mypy will typecheck cross-contract call properly :)

Alright, so how do we define a contract? Using a regular Python dataclass. Its attributes define the contract storage keys. In the case of an AMM, we’ll want to keep track of the pool amounts and the address to the associated FA1.2 token. The BaseContract class defines an ops attribute which is initialized as an empty list of operations to be returned by the contract we can append the operations to:

Let’s now define our xtz_to_token and token_to_xtzcontract entrypoints. They’re implemented as regular class methods. Exceptions can be raised with an error message, and comments work as expected 😊

Now we’ve got a nice little AMM going on 🤠 Let’s plot it using Matplotlib! We’re in a Jupyter Notebook after all 🙂We start by instantiating our contract class:

We’re now free to plot it’s storage using Pandas! Let’s iterate over new instances of our dex, all initialized with the same pools, and record how many tokens and XTZs we get out when swapping one into another:

Graphing and surprise!! The now classic AMM constant product formula is in front of our eyes ❤️

Soon, We’ll check out how to deploy this smart contract ;)