# PrivatePool

[Git Source](https://github.com/outdoteth/caviar-private-pools/blob/2cfa974fc146acb9dc53b7d410222483f4b15dd7/src/PrivatePool.sol)

**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.

```solidity
address public baseToken;
```

### nft

The address of the nft.

```solidity
address public nft;
```

### changeFee

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

```solidity
uint56 public changeFee;
```

### feeRate

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

```solidity
uint16 public feeRate;
```

### initialized

Whether or not the pool has been initialized.

```solidity
bool public initialized;
```

### payRoyalties

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

```solidity
bool public payRoyalties;
```

### useStolenNftOracle

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

```solidity
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.

```solidity
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.*

```solidity
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.

```solidity
bytes32 public merkleRoot;
```

### stolenNftOracle

The NFT oracle to check if an NFT is stolen.

```solidity
address public immutable stolenNftOracle;
```

### factory

The factory contract that created this pool.

```solidity
address payable public immutable factory;
```

### royaltyRegistry

The royalty registry from manifold.xyz.

```solidity
address public immutable royaltyRegistry;
```

## Functions

### onlyOwner

```solidity
modifier onlyOwner() virtual;
```

### receive

```solidity
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.\*

```solidity
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.

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

**Parameters**

| Name                        | Type      | Description                                                                     |
| --------------------------- | --------- | ------------------------------------------------------------------------------- |
| `_baseToken`                | `address` | The address of the base token                                                   |
| `_nft`                      | `address` | The address of the NFT                                                          |
| `_virtualBaseTokenReserves` | `uint128` | The virtual base token reserves                                                 |
| `_virtualNftReserves`       | `uint128` | The virtual NFT reserves                                                        |
| `_changeFee`                | `uint56`  |                                                                                 |
| `_feeRate`                  | `uint16`  | The fee rate (in basis points) 200 = 2%                                         |
| `_merkleRoot`               | `bytes32` | The merkle root                                                                 |
| `_useStolenNftOracle`       | `bool`    | Whether or not the pool uses the stolen NFT oracle to check if an NFT is stolen |
| `_payRoyalties`             | `bool`    |                                                                                 |

### 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.*

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

**Parameters**

| Name           | Type               | Description                                          |
| -------------- | ------------------ | ---------------------------------------------------- |
| `tokenIds`     | `uint256[]`        | The token IDs of the NFTs to buy.                    |
| `tokenWeights` | `uint256[]`        | The weights of the NFTs to buy.                      |
| `proof`        | `MerkleMultiProof` | The merkle proof for the weights of each NFT to buy. |

**Returns**

| Name                | Type      | Description                                        |
| ------------------- | --------- | -------------------------------------------------- |
| `netInputAmount`    | `uint256` | The amount of base tokens spent inclusive of fees. |
| `feeAmount`         | `uint256` | The amount of base tokens spent on fees.           |
| `protocolFeeAmount` | `uint256` |                                                    |

### 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.*

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

**Parameters**

| Name              | Type                         | Description                                           |
| ----------------- | ---------------------------- | ----------------------------------------------------- |
| `tokenIds`        | `uint256[]`                  | The token IDs of the NFTs to sell.                    |
| `tokenWeights`    | `uint256[]`                  | The weights of the NFTs to sell.                      |
| `proof`           | `MerkleMultiProof`           | The merkle proof for the weights of each NFT to sell. |
| `stolenNftProofs` | `Message.IStolenNftOracle[]` | The proofs that show each NFT is not stolen.          |

**Returns**

| Name                | Type      | Description                                           |
| ------------------- | --------- | ----------------------------------------------------- |
| `netOutputAmount`   | `uint256` | The amount of base tokens received inclusive of fees. |
| `feeAmount`         | `uint256` | The amount of base tokens to pay in fees.             |
| `protocolFeeAmount` | `uint256` |                                                       |

### 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.

```solidity
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**

| Name                 | Type                         | Description                                              |
| -------------------- | ---------------------------- | -------------------------------------------------------- |
| `inputTokenIds`      | `uint256[]`                  | The token IDs of the NFTs to change.                     |
| `inputTokenWeights`  | `uint256[]`                  | The weights of the NFTs to change.                       |
| `inputProof`         | `MerkleMultiProof`           | The merkle proof for the weights of each NFT to change.  |
| `stolenNftProofs`    | `Message.IStolenNftOracle[]` | The proofs that show each input NFT is not stolen.       |
| `outputTokenIds`     | `uint256[]`                  | The token IDs of the NFTs to receive.                    |
| `outputTokenWeights` | `uint256[]`                  | The weights of the NFTs to receive.                      |
| `outputProof`        | `MerkleMultiProof`           | The merkle proof for the weights of each NFT to receive. |

### 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.

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

**Parameters**

| Name     | Type      | Description                              |
| -------- | --------- | ---------------------------------------- |
| `target` | `address` | The address of the target contract.      |
| `data`   | `bytes`   | The data to send to the target contract. |

**Returns**

| Name     | Type    | Description                                    |
| -------- | ------- | ---------------------------------------------- |
| `<none>` | `bytes` | returnData The return data of the transaction. |

### 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.*

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

**Parameters**

| Name              | Type        | Description                           |
| ----------------- | ----------- | ------------------------------------- |
| `tokenIds`        | `uint256[]` | The token IDs of the NFTs to deposit. |
| `baseTokenAmount` | `uint256`   | The amount of base tokens to deposit. |

### withdraw

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

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

**Parameters**

| Name          | Type        | Description                            |
| ------------- | ----------- | -------------------------------------- |
| `_nft`        | `address`   | The address of the NFT.                |
| `tokenIds`    | `uint256[]` | The token IDs of the NFTs to withdraw. |
| `token`       | `address`   | The address of the token to withdraw.  |
| `tokenAmount` | `uint256`   | The amount of tokens to withdraw.      |

### 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.

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

**Parameters**

| Name                          | Type      | Description                          |
| ----------------------------- | --------- | ------------------------------------ |
| `newVirtualBaseTokenReserves` | `uint128` | The new virtual base token reserves. |
| `newVirtualNftReserves`       | `uint128` | The new virtual NFT reserves.        |

### 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.

```solidity
function setMerkleRoot(bytes32 newMerkleRoot) public onlyOwner;
```

**Parameters**

| Name            | Type      | Description          |
| --------------- | --------- | -------------------- |
| `newMerkleRoot` | `bytes32` | The new merkle root. |

### 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%.

```solidity
function setFeeRate(uint16 newFeeRate) public onlyOwner;
```

**Parameters**

| Name         | Type     | Description                        |
| ------------ | -------- | ---------------------------------- |
| `newFeeRate` | `uint16` | The new fee rate (in basis points) |

### 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.

```solidity
function setUseStolenNftOracle(bool newUseStolenNftOracle) public onlyOwner;
```

**Parameters**

| Name                    | Type   | Description                         |
| ----------------------- | ------ | ----------------------------------- |
| `newUseStolenNftOracle` | `bool` | The new use stolen NFT oracle flag. |

### 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.

```solidity
function setPayRoyalties(bool newPayRoyalties) public onlyOwner;
```

**Parameters**

| Name              | Type   | Description                 |
| ----------------- | ------ | --------------------------- |
| `newPayRoyalties` | `bool` | The new pay royalties flag. |

### setAllParameters

Updates all parameter settings in one go.

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

**Parameters**

| Name                          | Type      | Description                          |
| ----------------------------- | --------- | ------------------------------------ |
| `newVirtualBaseTokenReserves` | `uint128` | The new virtual base token reserves. |
| `newVirtualNftReserves`       | `uint128` | The new virtual NFT reserves.        |
| `newMerkleRoot`               | `bytes32` | The new merkle root.                 |
| `newFeeRate`                  | `uint16`  | The new fee rate (in basis points)   |
| `newUseStolenNftOracle`       | `bool`    | The new use stolen NFT oracle flag.  |
| `newPayRoyalties`             | `bool`    | The new pay royalties flag.          |

### flashLoan

Executes a flash loan.

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

**Parameters**

| Name       | Type                    | Description                       |
| ---------- | ----------------------- | --------------------------------- |
| `receiver` | `IERC3156FlashBorrower` | The receiver of the flash loan.   |
| `token`    | `address`               | The address of the NFT contract.  |
| `tokenId`  | `uint256`               | The ID of the NFT.                |
| `data`     | `bytes`                 | The data to pass to the receiver. |

**Returns**

| Name     | Type   | Description                                           |
| -------- | ------ | ----------------------------------------------------- |
| `<none>` | `bool` | success Whether or not the flash loan was successful. |

### sumWeightsAndValidateProof

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

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

**Parameters**

| Name           | Type               | Description                                       |
| -------------- | ------------------ | ------------------------------------------------- |
| `tokenIds`     | `uint256[]`        | The token IDs of the NFTs to sum the weights for. |
| `tokenWeights` | `uint256[]`        | The weights of each NFT in the token IDs array.   |
| `proof`        | `MerkleMultiProof` | The merkle proof for the weights of each NFT.     |

**Returns**

| Name     | Type      | Description                             |
| -------- | --------- | --------------------------------------- |
| `<none>` | `uint256` | sum The sum of the weights of each NFT. |

### \_getRoyalty

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

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

**Parameters**

| Name        | Type      | Description                |
| ----------- | --------- | -------------------------- |
| `tokenId`   | `uint256` | The token ID of the NFT.   |
| `salePrice` | `uint256` | The sale price of the NFT. |

**Returns**

| Name         | Type      | Description                            |
| ------------ | --------- | -------------------------------------- |
| `royaltyFee` | `uint256` | The royalty fee to pay.                |
| `recipient`  | `address` | The address to pay the royalty fee to. |

### 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.

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

**Parameters**

| Name           | Type      | Description                                   |
| -------------- | --------- | --------------------------------------------- |
| `outputAmount` | `uint256` | The amount of NFTs to buy multiplied by 1e18. |

**Returns**

| Name                | Type      | Description                                                    |
| ------------------- | --------- | -------------------------------------------------------------- |
| `netInputAmount`    | `uint256` | The required input amount of base tokens inclusive of the fee. |
| `feeAmount`         | `uint256` | The fee amount.                                                |
| `protocolFeeAmount` | `uint256` |                                                                |

### 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.

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

**Parameters**

| Name          | Type      | Description                                    |
| ------------- | --------- | ---------------------------------------------- |
| `inputAmount` | `uint256` | The amount of NFTs to sell multiplied by 1e18. |

**Returns**

| Name                | Type      | Description                                            |
| ------------------- | --------- | ------------------------------------------------------ |
| `netOutputAmount`   | `uint256` | The output amount of base tokens inclusive of the fee. |
| `feeAmount`         | `uint256` | The fee amount.                                        |
| `protocolFeeAmount` | `uint256` |                                                        |

### 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.

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

**Parameters**

| Name          | Type      | Description                                      |
| ------------- | --------- | ------------------------------------------------ |
| `inputAmount` | `uint256` | The amount of NFTs to change multiplied by 1e18. |

**Returns**

| Name                | Type      | Description              |
| ------------------- | --------- | ------------------------ |
| `feeAmount`         | `uint256` | The fee amount.          |
| `protocolFeeAmount` | `uint256` | The protocol fee amount. |

### price

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

```solidity
function price() public view returns (uint256);
```

**Returns**

| Name     | Type      | Description                  |
| -------- | --------- | ---------------------------- |
| `<none>` | `uint256` | price The price of the pool. |

### flashFee

Returns the fee required to flash swap a given NFT.

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

**Returns**

| Name     | Type      | Description               |
| -------- | --------- | ------------------------- |
| `<none>` | `uint256` | feeAmount The fee amount. |

### flashFeeToken

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

```solidity
function flashFeeToken() public view returns (address);
```

### availableForFlashLoan

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

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

**Parameters**

| Name      | Type      | Description                      |
| --------- | --------- | -------------------------------- |
| `token`   | `address` | The address of the NFT contract. |
| `tokenId` | `uint256` | The ID of the NFT.               |

**Returns**

| Name     | Type   | Description                                                     |
| -------- | ------ | --------------------------------------------------------------- |
| `<none>` | `bool` | available Whether or not the NFT is available for a flash loan. |

## Events

### Initialize

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

### Buy

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

### Sell

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

### Deposit

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

### Withdraw

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

### Change

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

### SetVirtualReserves

```solidity
event SetVirtualReserves(uint128 virtualBaseTokenReserves, uint128 virtualNftReserves);
```

### SetMerkleRoot

```solidity
event SetMerkleRoot(bytes32 merkleRoot);
```

### SetFeeRate

```solidity
event SetFeeRate(uint16 feeRate);
```

### SetUseStolenNftOracle

```solidity
event SetUseStolenNftOracle(bool useStolenNftOracle);
```

### SetPayRoyalties

```solidity
event SetPayRoyalties(bool payRoyalties);
```

## Errors

### AlreadyInitialized

```solidity
error AlreadyInitialized();
```

### Unauthorized

```solidity
error Unauthorized();
```

### InvalidEthAmount

```solidity
error InvalidEthAmount();
```

### InvalidMerkleProof

```solidity
error InvalidMerkleProof();
```

### InsufficientInputWeight

```solidity
error InsufficientInputWeight();
```

### FeeRateTooHigh

```solidity
error FeeRateTooHigh();
```

### NotAvailableForFlashLoan

```solidity
error NotAvailableForFlashLoan();
```

### FlashLoanFailed

```solidity
error FlashLoanFailed();
```

### InvalidRoyaltyFee

```solidity
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>

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