An implementation of the AMM idea described here.
Liquidity providers use LSSVMPairFactory
to deploy a minimal proxy LSSVMPair
for a specific NFT collection. From there, the deployed pool maintains its own TOKEN/NFT inventory. Users can then call the various swap
functions on the pool to trade TOKEN/NFTs.
LSSVMPair
s are either LSSVMPairEnumerable
or LSSVMPairMissingEnumerable
depending on whether or not the pair's ERC721
contract supports Enumerable
or not. If it doesn't, we implement our own ID set to allow for easy access to NFT IDs in the pool.
For the actual token, NFTs are paired either with an ERC20
or ETH
, so there are 4 types of pairs:
- Missing Enumerable | ETH
- Missing Enumerable | ERC20
- Enumerable | ETH
- Enumerable | ERC20
The LSSVMRouter
allows splitting swaps across multiple LSSVMPair
s to purchase multiple NFTs.
An LSSVMPair
can be TOKEN, NFT, or TRADE.
The type refers to what the pool holds:
- a TOKEN pool has tokens that it is willing to give to traders in exchange for NFTs
- an NFT pool has NFTs that it is willing to give to traders in exchange for tokens
- a TRADE pools allow for both TOKEN-->NFT and NFT-->TOKEN swaps.
The LSSVMPair
swap
functions are named from the perspective of the end user. EX: swapTokenForAnyNFTs
means the caller is sending ETH and receiving NFTs.
In order to determine how many NFTs or tokens to give or receive, each LSSVMPair
calls a discrete bonding curve contract that conforms to the ICurve
interface. Bonding curve contracts are intended to be pure; it is the responsibility of LSSVMPair
to update its state and perform input/output validation.
See inline comments for more on swap/bonding curve logic.
To help with code reuse, base
contains actual swap logic to be tested in the form of abstract contracts, while actual test files inherit from various parent contracts found in mixins
to implement the different choices of bonding curve, NFT, or token.
testgen-scripts
contains a Python script used to generate all combinations of bonding curve, NFT, and token for each test in base
. They are then placed into test-cases
, which are the actual test files that get run.
Test files are prefixed with a shortened name of the base test they implememnt (e.g. NoArb for NoArbBondingCurve), which is then followed by the specific bonding curve type, then NFT type, then token type, e.g. NoArbLinearMissingEnumerableETH
.
Our testing setup is compatible with both forge
and dapptools
. We recommend forge
as it is significantly faster to run.
Template repository for getting started quickly with DappTools
make
make test
Contracts can be deployed via the make deploy
command. Addresses are automatically
written in a name-address json file stored under out/addresses.json
.
We recommend testing your deployments and provide an example under scripts/test-deploy.sh
which will launch a local testnet, deploy the contracts, and do some sanity checks.
Environment variables under the .env
file are automatically loaded (see .env.example
).
Be careful of the precedence in which env vars are read.
We assume ETH_FROM
is an address you own and is part of your keystore.
If not, use ethsign import
to import your private key.
See the Makefile
for more context on how this works under the hood
We use Alchemy as a remote node provider for the Mainnet & Rinkeby network deployments.
You must have set your API key as the ALCHEMY_API_KEY
enviroment variable in order to
deploy to these networks
ETH_FROM=0x3538b6eF447f244268BCb2A0E1796fEE7c45002D make deploy-mainnet
ETH_FROM=0x3538b6eF447f244268BCb2A0E1796fEE7c45002D make deploy-rinkeby
ETH_RPC_URL=<your network> make deploy
# on one terminal
dapp testnet
# get the printed account address from the testnet, and set it as ETH_FROM. Then:
make deploy
If you do not have DappTools already installed, you'll need to run the below commands
# User must be in sudoers
curl -L https://nixos.org/nix/install | sh
# Run this or login again to use Nix
. "$HOME/.nix-profile/etc/profile.d/nix.sh"
curl https://dapp.tools/install | sh