PrivatePool

Git Source

Inherits: ERC721TokenReceiver

Author: out.eth (@outdoteth)

A private pool is a an NFT AMM controlled by a single owner with concentrated liquidity, custom fee rates, stolen NFT filtering, custom NFT weightings, royalty support, and flash loans. You can create a pool and change these parameters to your liking. Deposit NFTs and base tokens (or ETH) into the pool to enable trading. Earn fees on each trade.

State Variables

baseToken

The address of the base ERC20 token.

address public baseToken;

nft

The address of the nft.

address public nft;

changeFee

The change/flash fee to 4 decimals of precision. For example, 0.0025 ETH = 25. 500 USDC = 5_000_000.

uint56 public changeFee;

feeRate

The buy/sell fee rate (in basis points) 200 = 2%

uint16 public feeRate;

initialized

Whether or not the pool has been initialized.

bool public initialized;

payRoyalties

Whether or not the pool pays royalties to the NFT creator on each trade.

bool public payRoyalties;

useStolenNftOracle

Whether or not the pool uses the stolen NFT oracle to check if an NFT is stolen.

bool public useStolenNftOracle;

virtualBaseTokenReserves

The virtual base token reserves used in the xy=k invariant. Changing this will change the liquidity depth and price of the pool.

uint128 public virtualBaseTokenReserves;

virtualNftReserves

The virtual nft reserves used in the xy=k invariant. Changing this will change the liquidity depth and price of the pool.

The virtual NFT reserves that a user sets. If it's desired to set the reserves to match 16 NFTs then the virtual reserves should be set to 16e18. If weights are enabled by setting the merkle root to be non-zero then the virtual reserves should be set to the sum of the weights of the NFTs; where floor NFTs all have a weight of 1e18. A rarer NFT may have a weight of 2.3e18 if it's 2.3x more valuable than a floor.

uint128 public virtualNftReserves;

merkleRoot

The merkle root of all the token weights in the pool. If the merkle root is set to bytes32(0) then all NFTs are set to have a weight of 1e18.

bytes32 public merkleRoot;

stolenNftOracle

The NFT oracle to check if an NFT is stolen.

address public immutable stolenNftOracle;

factory

The factory contract that created this pool.

address payable public immutable factory;

royaltyRegistry

The royalty registry from manifold.xyz.

address public immutable royaltyRegistry;

Functions

onlyOwner

modifier onlyOwner() virtual;

receive

receive() external payable;

constructor

*This is only called when the base implementation contract is deployed. The following immutable parameters are set:

  • factory: The address of the factory contract

  • royaltyRegistry: The address of the royalty registry from manifold.xyz

  • stolenNftOracle: The address of the stolen NFT oracle These are all stored in immutable storage, which enables all minimal proxy contracts to read them without incurring additional deployment costs and re-initializing them at point of creation in the factory contract.*

constructor(address _factory, address _royaltyRegistry, address _stolenNftOracle);

initialize

Initializes the private pool and sets the initial parameters. Should only be called once by the factory.

function initialize(
    address _baseToken,
    address _nft,
    uint128 _virtualBaseTokenReserves,
    uint128 _virtualNftReserves,
    uint56 _changeFee,
    uint16 _feeRate,
    bytes32 _merkleRoot,
    bool _useStolenNftOracle,
    bool _payRoyalties
) public;

Parameters

buy

Buys NFTs from the pool, paying with base tokens from the caller. Then transfers the bought NFTs to the caller. The net cost depends on the current price, fee rate and assigned NFT weights.

DO NOT call this function directly unless you know what you are doing. Instead, use a wrapper contract that will check the max input amount and revert if the slippage is too high.

function buy(uint256[] calldata tokenIds, uint256[] calldata tokenWeights, MerkleMultiProof calldata proof)
    public
    payable
    returns (uint256 netInputAmount, uint256 feeAmount, uint256 protocolFeeAmount);

Parameters

Returns

sell

Sells NFTs into the pool and transfers base tokens to the caller. NFTs are transferred from the caller to the pool. The net sale amount depends on the current price, fee rate and assigned NFT weights.

DO NOT call this function directly unless you know what you are doing. Instead, use a wrapper contract that will check the min output amount and revert if the slippage is too high.

function sell(
    uint256[] calldata tokenIds,
    uint256[] calldata tokenWeights,
    MerkleMultiProof calldata proof,
    IStolenNftOracle.Message[] memory stolenNftProofs
) public returns (uint256 netOutputAmount, uint256 feeAmount, uint256 protocolFeeAmount);

Parameters

Returns

change

Changes a set of NFTs that the caller owns for another set of NFTs in the pool. The caller must approve the pool to transfer the NFTs. The sum of the caller's NFT weights must be less than or equal to the sum of the output pool NFTs weights. The caller must also pay a fee depending the net input weight and change fee amount.

function change(
    uint256[] memory inputTokenIds,
    uint256[] memory inputTokenWeights,
    MerkleMultiProof memory inputProof,
    IStolenNftOracle.Message[] memory stolenNftProofs,
    uint256[] memory outputTokenIds,
    uint256[] memory outputTokenWeights,
    MerkleMultiProof memory outputProof
) public payable returns (uint256 feeAmount, uint256 protocolFeeAmount);

Parameters

execute

Executes a transaction from the pool account to a target contract. The caller must be the owner of the pool. This allows for use cases such as claiming airdrops.

function execute(address target, bytes memory data) public payable onlyOwner returns (bytes memory);

Parameters

Returns

deposit

Deposits base tokens and NFTs into the pool. The caller must approve the pool to transfer their NFTs and base tokens.

DO NOT call this function directly unless you know what you are doing. Instead, use a wrapper contract that will check the current price is within the desired bounds.

function deposit(uint256[] calldata tokenIds, uint256 baseTokenAmount) public payable;

Parameters

withdraw

Withdraws NFTs and tokens from the pool. Can only be called by the owner of the pool.

function withdraw(address _nft, uint256[] calldata tokenIds, address token, uint256 tokenAmount) public onlyOwner;

Parameters

setVirtualReserves

Sets the virtual base token reserves and virtual NFT reserves. Can only be called by the owner of the pool. These parameters affect the price and liquidity depth of the pool.

function setVirtualReserves(uint128 newVirtualBaseTokenReserves, uint128 newVirtualNftReserves) public onlyOwner;

Parameters

setMerkleRoot

Sets the merkle root. Can only be called by the owner of the pool. The merkle root is used to validate the NFT weights.

function setMerkleRoot(bytes32 newMerkleRoot) public onlyOwner;

Parameters

setFeeRate

Sets the fee rate. Can only be called by the owner of the pool. The fee rate is used to calculate the fee amount when swapping or changing NFTs. The fee rate is in basis points (1/100th of a percent). For example, 10_000 == 100%, 200 == 2%, 1 == 0.01%.

function setFeeRate(uint16 newFeeRate) public onlyOwner;

Parameters

setUseStolenNftOracle

Sets the whether or not to use the stolen NFT oracle. Can only be called by the owner of the pool. The stolen NFT oracle is used to check if an NFT is stolen.

function setUseStolenNftOracle(bool newUseStolenNftOracle) public onlyOwner;

Parameters

setPayRoyalties

Sets the pay royalties flag. Can only be called by the owner of the pool. If royalties are enabled then the pool will pay royalties when buying or selling NFTs.

function setPayRoyalties(bool newPayRoyalties) public onlyOwner;

Parameters

setAllParameters

Updates all parameter settings in one go.

function setAllParameters(
    uint128 newVirtualBaseTokenReserves,
    uint128 newVirtualNftReserves,
    bytes32 newMerkleRoot,
    uint16 newFeeRate,
    bool newUseStolenNftOracle,
    bool newPayRoyalties
) public;

Parameters

flashLoan

Executes a flash loan.

function flashLoan(IERC3156FlashBorrower receiver, address token, uint256 tokenId, bytes calldata data)
    external
    payable
    returns (bool);

Parameters

Returns

sumWeightsAndValidateProof

Sums the weights of each NFT and validates that the weights are correct by verifying the merkle proof.

function sumWeightsAndValidateProof(
    uint256[] memory tokenIds,
    uint256[] memory tokenWeights,
    MerkleMultiProof memory proof
) public view returns (uint256);

Parameters

Returns

_getRoyalty

Gets the royalty and recipient for a given NFT and sale price. Looks up the royalty info from the manifold registry.

function _getRoyalty(uint256 tokenId, uint256 salePrice)
    internal
    view
    returns (uint256 royaltyFee, address recipient);

Parameters

Returns

buyQuote

Returns the required input of buying a given amount of NFTs inclusive of the fee which is dependent on the currently set fee rate.

function buyQuote(uint256 outputAmount)
    public
    view
    returns (uint256 netInputAmount, uint256 feeAmount, uint256 protocolFeeAmount);

Parameters

Returns

sellQuote

Returns the output amount of selling a given amount of NFTs inclusive of the fee which is dependent on the currently set fee rate.

function sellQuote(uint256 inputAmount)
    public
    view
    returns (uint256 netOutputAmount, uint256 feeAmount, uint256 protocolFeeAmount);

Parameters

Returns

changeFeeQuote

Returns the fee required to change a given amount of NFTs. The fee is based on the current changeFee (which contains 4 decimals of precision) multiplied by some exponent depending on the base token decimals.

function changeFeeQuote(uint256 inputAmount) public view returns (uint256 feeAmount, uint256 protocolFeeAmount);

Parameters

Returns

price

Returns the price of the pool to 18 decimals of accuracy.

function price() public view returns (uint256);

Returns

flashFee

Returns the fee required to flash swap a given NFT.

function flashFee(address, uint256) public view returns (uint256);

Returns

flashFeeToken

Returns the token that is used to pay the flash fee.

function flashFeeToken() public view returns (address);

availableForFlashLoan

Returns whether or not an NFT is available for a flash loan.

function availableForFlashLoan(address token, uint256 tokenId) public view returns (bool);

Parameters

Returns

Events

Initialize

event Initialize(
    address indexed baseToken,
    address indexed nft,
    uint128 virtualBaseTokenReserves,
    uint128 virtualNftReserves,
    uint56 changeFee,
    uint16 feeRate,
    bytes32 merkleRoot,
    bool useStolenNftOracle,
    bool payRoyalties
);

Buy

event Buy(
    uint256[] tokenIds,
    uint256[] tokenWeights,
    uint256 inputAmount,
    uint256 feeAmount,
    uint256 protocolFeeAmount,
    uint256 royaltyFeeAmount
);

Sell

event Sell(
    uint256[] tokenIds,
    uint256[] tokenWeights,
    uint256 outputAmount,
    uint256 feeAmount,
    uint256 protocolFeeAmount,
    uint256 royaltyFeeAmount
);

Deposit

event Deposit(uint256[] tokenIds, uint256 baseTokenAmount);

Withdraw

event Withdraw(address indexed nft, uint256[] tokenIds, address token, uint256 amount);

Change

event Change(
    uint256[] inputTokenIds,
    uint256[] inputTokenWeights,
    uint256[] outputTokenIds,
    uint256[] outputTokenWeights,
    uint256 feeAmount,
    uint256 protocolFeeAmount
);

SetVirtualReserves

event SetVirtualReserves(uint128 virtualBaseTokenReserves, uint128 virtualNftReserves);

SetMerkleRoot

event SetMerkleRoot(bytes32 merkleRoot);

SetFeeRate

event SetFeeRate(uint16 feeRate);

SetUseStolenNftOracle

event SetUseStolenNftOracle(bool useStolenNftOracle);

SetPayRoyalties

event SetPayRoyalties(bool payRoyalties);

Errors

AlreadyInitialized

error AlreadyInitialized();

Unauthorized

error Unauthorized();

InvalidEthAmount

error InvalidEthAmount();

InvalidMerkleProof

error InvalidMerkleProof();

InsufficientInputWeight

error InsufficientInputWeight();

FeeRateTooHigh

error FeeRateTooHigh();

NotAvailableForFlashLoan

error NotAvailableForFlashLoan();

FlashLoanFailed

error FlashLoanFailed();

InvalidRoyaltyFee

error InvalidRoyaltyFee();

Structs

MerkleMultiProof

Merkle proof input for a sparse merkle multi proof. It can be generated with a library like: https://github.com/OpenZeppelin/merkle-tree#treegetmultiproof

struct MerkleMultiProof {
    bytes32[] proof;
    bool[] flags;
}

Last updated