diff --git a/.prettierrc.yml b/.prettierrc.yml
index bd56d83..a1ecdbb 100644
--- a/.prettierrc.yml
+++ b/.prettierrc.yml
@@ -5,9 +5,3 @@ singleQuote: false
 tabWidth: 2
 trailingComma: "all"
 useTabs: false
-
-overrides:
-  - files: "*.sol"
-    options:
-      compiler: "0.8.17"
-      tabWidth: 4
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 35d160b..d559cc9 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -7,7 +7,7 @@
   // editor settings
   // "editor.defaultFormatter": "esbenp.prettier-vscode",
   "editor.formatOnSave": true,
-  "solidity.formatter": "prettier", // This is the default so it might be missing.
+  "solidity.formatter": "forge", // This is the default so it might be missing.
   "[solidity]": {
     "editor.defaultFormatter": "JuanBlanco.solidity"
   }
diff --git a/README.md b/README.md
index e258d56..add22e8 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1 @@
-Find safe transfer library
-find fixed point math lib that supports powu
\ No newline at end of file
+Find safe transfer library find fixed point math lib that supports powu
diff --git a/foundry.toml b/foundry.toml
index 9e20fd0..83101c2 100644
--- a/foundry.toml
+++ b/foundry.toml
@@ -16,6 +16,17 @@ test = "test"
 fuzz = { runs = 1_000 }
 verbosity = 4
 
+[fmt]
+bracket_spacing = true
+int_types = "long"
+line_length = 120
+multiline_func_header = "all"
+number_underscore = "thousands"
+quote_style = "double"
+tab_width = 2
+variable_override_spacing = false
+wrap_comments = true
+
 [rpc_endpoints]
 arbitrum = "${RPC_URL_ARBITRUM}"
 goerli = "${RPC_URL_GOERLI}"
diff --git a/src/core/ERC20.sol b/src/core/ERC20.sol
index 32b46f8..b9876c2 100644
--- a/src/core/ERC20.sol
+++ b/src/core/ERC20.sol
@@ -3,189 +3,188 @@ pragma solidity >=0.8.0;
 
 /// @notice Modern and gas efficient ERC20 + EIP-2612 implementation.
 /// @author Kyle Scott (https://github.com/numoen/core/blob/master/src/ERC20.sol)
-/// @author Modified from Solmate v6 (https://github.com/transmissions11/solmate/blob/a9e3ea26a2dc73bfa87f0cb189687d029028e0c5/src/tokens/ERC20.sol)
+/// @author Modified from Solmate v6
+/// (https://github.com/transmissions11/solmate/blob/a9e3ea26a2dc73bfa87f0cb189687d029028e0c5/src/tokens/ERC20.sol)
 /// and Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)
 /// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.
 abstract contract ERC20 {
-    /*///////////////////////////////////////////////////////////////
+  /*///////////////////////////////////////////////////////////////
                                   EVENTS
     //////////////////////////////////////////////////////////////*/
 
-    event Transfer(address indexed from, address indexed to, uint256 amount);
+  event Transfer(address indexed from, address indexed to, uint256 amount);
 
-    event Approval(address indexed owner, address indexed spender, uint256 amount);
+  event Approval(address indexed owner, address indexed spender, uint256 amount);
 
-    /*///////////////////////////////////////////////////////////////
+  /*///////////////////////////////////////////////////////////////
                              METADATA STORAGE
     //////////////////////////////////////////////////////////////*/
 
-    string public constant name = "Numoen Replicating Derivative";
+  string public constant name = "Numoen Replicating Derivative";
 
-    string public constant symbol = "NRD";
+  string public constant symbol = "NRD";
 
-    uint8 public constant decimals = 18;
+  uint8 public constant decimals = 18;
 
-    /*///////////////////////////////////////////////////////////////
+  /*///////////////////////////////////////////////////////////////
                               ERC20 STORAGE
     //////////////////////////////////////////////////////////////*/
 
-    uint256 public totalSupply;
+  uint256 public totalSupply;
 
-    mapping(address => uint256) public balanceOf;
+  mapping(address => uint256) public balanceOf;
 
-    mapping(address => mapping(address => uint256)) public allowance;
+  mapping(address => mapping(address => uint256)) public allowance;
 
-    /*///////////////////////////////////////////////////////////////
+  /*///////////////////////////////////////////////////////////////
                              EIP-2612 STORAGE
     //////////////////////////////////////////////////////////////*/
 
-    bytes32 public constant PERMIT_TYPEHASH =
-        keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
+  bytes32 public constant PERMIT_TYPEHASH =
+    keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
 
-    uint256 internal immutable INITIAL_CHAIN_ID;
+  uint256 internal immutable INITIAL_CHAIN_ID;
 
-    bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;
+  bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;
 
-    mapping(address => uint256) public nonces;
+  mapping(address => uint256) public nonces;
 
-    /*///////////////////////////////////////////////////////////////
+  /*///////////////////////////////////////////////////////////////
                                CONSTRUCTOR
     //////////////////////////////////////////////////////////////*/
 
-    constructor() {
-        INITIAL_CHAIN_ID = block.chainid;
-        INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
-    }
+  constructor() {
+    INITIAL_CHAIN_ID = block.chainid;
+    INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
+  }
 
-    /*///////////////////////////////////////////////////////////////
+  /*///////////////////////////////////////////////////////////////
                               ERC20 LOGIC
     //////////////////////////////////////////////////////////////*/
 
-    /// @dev changed visibility to external
-    function approve(address spender, uint256 amount) external virtual returns (bool) {
-        allowance[msg.sender][spender] = amount;
-
-        emit Approval(msg.sender, spender, amount);
-
-        return true;
-    }
+  /// @dev changed visibility to external
+  function approve(address spender, uint256 amount) external virtual returns (bool) {
+    allowance[msg.sender][spender] = amount;
 
-    /// @dev changed visibility to external
-    function transfer(address to, uint256 amount) external virtual returns (bool) {
-        balanceOf[msg.sender] -= amount;
+    emit Approval(msg.sender, spender, amount);
 
-        // Cannot overflow because the sum of all user
-        // balances can't exceed the max uint256 value.
-        unchecked {
-            balanceOf[to] += amount;
-        }
+    return true;
+  }
 
-        emit Transfer(msg.sender, to, amount);
+  /// @dev changed visibility to external
+  function transfer(address to, uint256 amount) external virtual returns (bool) {
+    balanceOf[msg.sender] -= amount;
 
-        return true;
+    // Cannot overflow because the sum of all user
+    // balances can't exceed the max uint256 value.
+    unchecked {
+      balanceOf[to] += amount;
     }
 
-    /// @dev changed visibility to external
-    function transferFrom(
-        address from,
-        address to,
-        uint256 amount
-    ) external virtual returns (bool) {
-        uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.
+    emit Transfer(msg.sender, to, amount);
 
-        if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;
+    return true;
+  }
 
-        balanceOf[from] -= amount;
+  /// @dev changed visibility to external
+  function transferFrom(address from, address to, uint256 amount) external virtual returns (bool) {
+    uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.
 
-        // Cannot overflow because the sum of all user
-        // balances can't exceed the max uint256 value.
-        unchecked {
-            balanceOf[to] += amount;
-        }
+    if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;
 
-        emit Transfer(from, to, amount);
+    balanceOf[from] -= amount;
 
-        return true;
+    // Cannot overflow because the sum of all user
+    // balances can't exceed the max uint256 value.
+    unchecked {
+      balanceOf[to] += amount;
     }
 
-    /*///////////////////////////////////////////////////////////////
-                              EIP-2612 LOGIC
-    //////////////////////////////////////////////////////////////*/
+    emit Transfer(from, to, amount);
 
-    function permit(
-        address owner,
-        address spender,
-        uint256 value,
-        uint256 deadline,
-        uint8 v,
-        bytes32 r,
-        bytes32 s
-    ) external virtual {
-        require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");
-
-        // Unchecked because the only math done is incrementing
-        // the owner's nonce which cannot realistically overflow.
-        unchecked {
-            bytes32 digest = keccak256(
-                abi.encodePacked(
-                    "\x19\x01",
-                    DOMAIN_SEPARATOR(),
-                    keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, nonces[owner]++, deadline))
-                )
-            );
-
-            address recoveredAddress = ecrecover(digest, v, r, s);
-
-            require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");
-
-            allowance[recoveredAddress][spender] = value;
-        }
-
-        emit Approval(owner, spender, value);
-    }
+    return true;
+  }
 
-    function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
-        return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
-    }
+  /*///////////////////////////////////////////////////////////////
+                              EIP-2612 LOGIC
+    //////////////////////////////////////////////////////////////*/
 
-    function computeDomainSeparator() internal view virtual returns (bytes32) {
-        return
-            keccak256(
-                abi.encode(
-                    keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
-                    keccak256(bytes(name)),
-                    keccak256("1"),
-                    block.chainid,
-                    address(this)
-                )
-            );
+  function permit(
+    address owner,
+    address spender,
+    uint256 value,
+    uint256 deadline,
+    uint8 v,
+    bytes32 r,
+    bytes32 s
+  )
+    external
+    virtual
+  {
+    require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");
+
+    // Unchecked because the only math done is incrementing
+    // the owner's nonce which cannot realistically overflow.
+    unchecked {
+      bytes32 digest = keccak256(
+        abi.encodePacked(
+          "\x19\x01",
+          DOMAIN_SEPARATOR(),
+          keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, nonces[owner]++, deadline))
+        )
+      );
+
+      address recoveredAddress = ecrecover(digest, v, r, s);
+
+      require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");
+
+      allowance[recoveredAddress][spender] = value;
     }
 
-    /*///////////////////////////////////////////////////////////////
+    emit Approval(owner, spender, value);
+  }
+
+  function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
+    return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
+  }
+
+  function computeDomainSeparator() internal view virtual returns (bytes32) {
+    return keccak256(
+      abi.encode(
+        keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
+        keccak256(bytes(name)),
+        keccak256("1"),
+        block.chainid,
+        address(this)
+      )
+    );
+  }
+
+  /*///////////////////////////////////////////////////////////////
                        INTERNAL MINT/BURN LOGIC
     //////////////////////////////////////////////////////////////*/
 
-    function _mint(address to, uint256 amount) internal virtual {
-        totalSupply += amount;
-
-        // Cannot overflow because the sum of all user
-        // balances can't exceed the max uint256 value.
-        unchecked {
-            balanceOf[to] += amount;
-        }
+  function _mint(address to, uint256 amount) internal virtual {
+    totalSupply += amount;
 
-        emit Transfer(address(0), to, amount);
+    // Cannot overflow because the sum of all user
+    // balances can't exceed the max uint256 value.
+    unchecked {
+      balanceOf[to] += amount;
     }
 
-    function _burn(address from, uint256 amount) internal virtual {
-        balanceOf[from] -= amount;
+    emit Transfer(address(0), to, amount);
+  }
 
-        // Cannot underflow because a user's balance
-        // will never be larger than the total supply.
-        unchecked {
-            totalSupply -= amount;
-        }
+  function _burn(address from, uint256 amount) internal virtual {
+    balanceOf[from] -= amount;
 
-        emit Transfer(from, address(0), amount);
+    // Cannot underflow because a user's balance
+    // will never be larger than the total supply.
+    unchecked {
+      totalSupply -= amount;
     }
+
+    emit Transfer(from, address(0), amount);
+  }
 }
diff --git a/src/core/Factory.sol b/src/core/Factory.sol
index 55ad173..9dfab6c 100644
--- a/src/core/Factory.sol
+++ b/src/core/Factory.sol
@@ -3,84 +3,87 @@ pragma solidity ^0.8.4;
 
 import { Lendgine } from "./Lendgine.sol";
 
-contract Factory {
-    /*//////////////////////////////////////////////////////////////
+import { IFactory } from "./interfaces/IFactory.sol";
+
+contract Factory is IFactory {
+  /*//////////////////////////////////////////////////////////////
                                  EVENTS
     //////////////////////////////////////////////////////////////*/
 
-    event LendgineCreated(
-        address indexed token0,
-        address indexed token1,
-        uint256 token0Exp,
-        uint256 token1Exp,
-        uint256 indexed upperBound,
-        address lendgine
-    );
+  event LendgineCreated(
+    address indexed token0,
+    address indexed token1,
+    uint256 token0Exp,
+    uint256 token1Exp,
+    uint256 indexed upperBound,
+    address lendgine
+  );
 
-    /*//////////////////////////////////////////////////////////////
+  /*//////////////////////////////////////////////////////////////
                                  ERRORS
     //////////////////////////////////////////////////////////////*/
 
-    error SameTokenError();
+  error SameTokenError();
 
-    error ZeroAddressError();
+  error ZeroAddressError();
 
-    error DeployedError();
+  error DeployedError();
 
-    error ScaleError();
+  error ScaleError();
 
-    /*//////////////////////////////////////////////////////////////
+  /*//////////////////////////////////////////////////////////////
                             FACTORY STORAGE
     //////////////////////////////////////////////////////////////*/
 
-    mapping(address => mapping(address => mapping(uint256 => mapping(uint256 => mapping(uint256 => address)))))
-        public getLendgine;
+  /// @inheritdoc IFactory
+  mapping(address => mapping(address => mapping(uint256 => mapping(uint256 => mapping(uint256 => address)))))
+    public
+    override getLendgine;
 
-    /*//////////////////////////////////////////////////////////////
+  /*//////////////////////////////////////////////////////////////
                         TEMPORARY DEPLOY STORAGE
     //////////////////////////////////////////////////////////////*/
 
-    struct Parameters {
-        address token0;
-        address token1;
-        uint256 token0Exp;
-        uint256 token1Exp;
-        uint256 upperBound;
-    }
+  struct Parameters {
+    address token0;
+    address token1;
+    uint128 token0Exp;
+    uint128 token1Exp;
+    uint256 upperBound;
+  }
 
-    Parameters public parameters;
+  /// @inheritdoc IFactory
+  Parameters public override parameters;
 
-    /*//////////////////////////////////////////////////////////////
+  /*//////////////////////////////////////////////////////////////
                               FACTORY LOGIC
     //////////////////////////////////////////////////////////////*/
 
-    function createLendgine(
-        address token0,
-        address token1,
-        uint8 token0Exp,
-        uint8 token1Exp,
-        uint256 upperBound
-    ) external returns (address lendgine) {
-        if (token0 == token1) revert SameTokenError();
-        if (token0 == address(0) || token1 == address(0)) revert ZeroAddressError();
-        if (getLendgine[token0][token1][token0Exp][token1Exp][upperBound] != address(0)) revert DeployedError();
-        if (token0Exp > 18 || token0Exp < 6 || token1Exp > 18 || token1Exp < 6) revert ScaleError();
-
-        parameters = Parameters({
-            token0: token0,
-            token1: token1,
-            token0Exp: token0Exp,
-            token1Exp: token1Exp,
-            upperBound: upperBound
-        });
-
-        lendgine = address(
-            new Lendgine{ salt: keccak256(abi.encode(token0, token1, token0Exp, token1Exp, upperBound)) }()
-        );
-
-        delete parameters;
-
-        getLendgine[token0][token1][token0Exp][token1Exp][upperBound] = lendgine;
-        emit LendgineCreated(token0, token1, token0Exp, token1Exp, upperBound, lendgine);
-    }
+  /// @inheritdoc IFactory
+  function createLendgine(
+    address token0,
+    address token1,
+    uint8 token0Exp,
+    uint8 token1Exp,
+    uint256 upperBound
+  )
+    external
+    override
+    returns (address lendgine)
+  {
+    if (token0 == token1) revert SameTokenError();
+    if (token0 == address(0) || token1 == address(0)) revert ZeroAddressError();
+    if (getLendgine[token0][token1][token0Exp][token1Exp][upperBound] != address(0)) revert DeployedError();
+    if (token0Exp > 18 || token0Exp < 6 || token1Exp > 18 || token1Exp < 6) revert ScaleError();
+
+    parameters =
+      Parameters({token0: token0, token1: token1, token0Exp: token0Exp, token1Exp: token1Exp, upperBound: upperBound});
+
+    lendgine = address(new Lendgine{ salt: keccak256(abi.encode(token0, token1, token0Exp, token1Exp, upperBound)) }());
+
+    delete parameters;
+
+    getLendgine[token0][token1][token0Exp][token1Exp][upperBound] = lendgine;
+    emit LendgineCreated(token0, token1, token0Exp, token1Exp, upperBound, lendgine);
+  }
 }
diff --git a/src/core/ImmutableState.sol b/src/core/ImmutableState.sol
index 352f4ad..f38c186 100644
--- a/src/core/ImmutableState.sol
+++ b/src/core/ImmutableState.sol
@@ -3,28 +3,36 @@ pragma solidity ^0.8.0;
 
 import { Factory } from "./Factory.sol";
 
-abstract contract ImmutableState {
-    address public immutable factory;
+import { IImmutableState } from "./interfaces/IImmutableState.sol";
 
-    address public immutable token0;
+abstract contract ImmutableState is IImmutableState {
+  /// @inheritdoc IImmutableState
+  address public immutable factory;
 
-    address public immutable token1;
+  /// @inheritdoc IImmutableState
+  address public immutable token0;
 
-    uint256 public immutable token0Scale;
+  /// @inheritdoc IImmutableState
+  address public immutable token1;
 
-    uint256 public immutable token1Scale;
+  /// @inheritdoc IImmutableState
+  uint256 public immutable token0Scale;
 
-    uint256 public immutable upperBound;
+  /// @inheritdoc IImmutableState
+  uint256 public immutable token1Scale;
 
-    constructor() {
-        factory = msg.sender;
+  /// @inheritdoc IImmutableState
+  uint256 public immutable upperBound;
 
-        uint256 _token0Exp;
-        uint256 _token1Exp;
+  constructor() {
+    factory = msg.sender;
 
-        (token0, token1, _token0Exp, _token1Exp, upperBound) = Factory(msg.sender).parameters();
+    uint128 _token0Exp;
+    uint128 _token1Exp;
 
-        token0Scale = 10**(18 - _token0Exp);
-        token1Scale = 10**(18 - _token1Exp);
-    }
+    (token0, token1, _token0Exp, _token1Exp, upperBound) = Factory(msg.sender).parameters();
+
+    token0Scale = 10 ** (18 - _token0Exp);
+    token1Scale = 10 ** (18 - _token1Exp);
+  }
 }
diff --git a/src/core/JumpRate.sol b/src/core/JumpRate.sol
index 876b826..5e734cd 100644
--- a/src/core/JumpRate.sol
+++ b/src/core/JumpRate.sol
@@ -4,43 +4,41 @@ pragma solidity ^0.8.0;
 import { IJumpRate } from "./interfaces/IJumpRate.sol";
 
 abstract contract JumpRate is IJumpRate {
-    uint256 public constant override kink = 0.8 ether;
-
-    uint256 public constant override multiplier = 1.375 ether;
-
-    uint256 public constant override jumpMultiplier = 44.5 ether;
-
-    function getBorrowRate(uint256 borrowedLiquidity, uint256 totalLiquidity)
-        public
-        pure
-        override
-        returns (uint256 rate)
-    {
-        uint256 util = utilizationRate(borrowedLiquidity, totalLiquidity);
-
-        if (util <= kink) {
-            return (util * multiplier) / 1e18;
-        } else {
-            uint256 normalRate = (kink * multiplier) / 1e18;
-            uint256 excessUtil = util - kink;
-            return ((excessUtil * jumpMultiplier) / 1e18) + normalRate;
-        }
-    }
+  uint256 public constant override kink = 0.8 ether;
 
-    function getSupplyRate(uint256 borrowedLiquidity, uint256 totalLiquidity)
-        external
-        pure
-        override
-        returns (uint256 rate)
-    {
-        uint256 util = utilizationRate(borrowedLiquidity, totalLiquidity);
-        uint256 borrowRate = getBorrowRate(borrowedLiquidity, totalLiquidity);
+  uint256 public constant override multiplier = 1.375 ether;
 
-        return (borrowRate * util) / 1e18;
-    }
+  uint256 public constant override jumpMultiplier = 44.5 ether;
+
+  function getBorrowRate(uint256 borrowedLiquidity, uint256 totalLiquidity) public pure override returns (uint256 rate) {
+    uint256 util = utilizationRate(borrowedLiquidity, totalLiquidity);
 
-    function utilizationRate(uint256 borrowedLiquidity, uint256 totalLiquidity) private pure returns (uint256 rate) {
-        if (totalLiquidity == 0) return 0;
-        return (borrowedLiquidity * 1e18) / totalLiquidity;
+    if (util <= kink) {
+      return (util * multiplier) / 1e18;
+    } else {
+      uint256 normalRate = (kink * multiplier) / 1e18;
+      uint256 excessUtil = util - kink;
+      return ((excessUtil * jumpMultiplier) / 1e18) + normalRate;
     }
+  }
+
+  function getSupplyRate(
+    uint256 borrowedLiquidity,
+    uint256 totalLiquidity
+  )
+    external
+    pure
+    override
+    returns (uint256 rate)
+  {
+    uint256 util = utilizationRate(borrowedLiquidity, totalLiquidity);
+    uint256 borrowRate = getBorrowRate(borrowedLiquidity, totalLiquidity);
+
+    return (borrowRate * util) / 1e18;
+  }
+
+  function utilizationRate(uint256 borrowedLiquidity, uint256 totalLiquidity) private pure returns (uint256 rate) {
+    if (totalLiquidity == 0) return 0;
+    return (borrowedLiquidity * 1e18) / totalLiquidity;
+  }
 }
diff --git a/src/core/Lendgine.sol b/src/core/Lendgine.sol
index cc8847a..12fa50c 100644
--- a/src/core/Lendgine.sol
+++ b/src/core/Lendgine.sol
@@ -14,246 +14,235 @@ import { SafeTransferLib } from "../libraries/SafeTransferLib.sol";
 import { SafeCast } from "../libraries/SafeCast.sol";
 
 contract Lendgine is ERC20, JumpRate, Pair {
-    using Position for mapping(address => Position.Info);
-    using Position for Position.Info;
+  using Position for mapping(address => Position.Info);
+  using Position for Position.Info;
 
-    /*//////////////////////////////////////////////////////////////
+  /*//////////////////////////////////////////////////////////////
                                  EVENTS
     //////////////////////////////////////////////////////////////*/
 
-    event Mint(address indexed sender, uint256 collateral, uint256 shares, uint256 liquidity, address indexed to);
+  event Mint(address indexed sender, uint256 collateral, uint256 shares, uint256 liquidity, address indexed to);
 
-    event Burn(address indexed sender, uint256 collateral, uint256 shares, uint256 liquidity, address indexed to);
+  event Burn(address indexed sender, uint256 collateral, uint256 shares, uint256 liquidity, address indexed to);
 
-    event Deposit(address indexed sender, uint256 size, uint256 liquidity, address indexed to);
+  event Deposit(address indexed sender, uint256 size, uint256 liquidity, address indexed to);
 
-    event Withdraw(address indexed sender, uint256 size, uint256 liquidity, address indexed to);
+  event Withdraw(address indexed sender, uint256 size, uint256 liquidity, address indexed to);
 
-    event AccrueInterest(uint256 timeElapsed, uint256 collateral, uint256 liquidity);
+  event AccrueInterest(uint256 timeElapsed, uint256 collateral, uint256 liquidity);
 
-    event AccruePositionInterest(address indexed owner, uint256 rewardPerPosition);
+  event AccruePositionInterest(address indexed owner, uint256 rewardPerPosition);
 
-    event Collect(address indexed owner, address indexed to, uint256 amount);
+  event Collect(address indexed owner, address indexed to, uint256 amount);
 
-    /*//////////////////////////////////////////////////////////////
+  /*//////////////////////////////////////////////////////////////
                                  ERRORS
     //////////////////////////////////////////////////////////////*/
 
-    error InputError();
+  error InputError();
 
-    error CompleteUtilizationError();
+  error CompleteUtilizationError();
 
-    error InsufficientInputError();
+  error InsufficientInputError();
 
-    error InsufficientPositionError();
+  error InsufficientPositionError();
 
-    /*//////////////////////////////////////////////////////////////
+  /*//////////////////////////////////////////////////////////////
                           LENDGINE STORAGE
     //////////////////////////////////////////////////////////////*/
 
-    mapping(address => Position.Info) public positions;
+  mapping(address => Position.Info) public positions;
 
-    uint256 public totalPositionSize;
+  uint256 public totalPositionSize;
 
-    uint256 public totalLiquidityBorrowed;
+  uint256 public totalLiquidityBorrowed;
 
-    uint256 public rewardPerPositionStored;
+  uint256 public rewardPerPositionStored;
 
-    /// @dev don't downsize because it takes up the last slot
-    uint256 public lastUpdate;
+  /// @dev don't downsize because it takes up the last slot
+  uint256 public lastUpdate;
 
-    function mint(
-        address to,
-        uint256 collateral,
-        bytes calldata data
-    ) external nonReentrant returns (uint256 shares) {
-        _accrueInterest();
+  function mint(address to, uint256 collateral, bytes calldata data) external nonReentrant returns (uint256 shares) {
+    _accrueInterest();
 
-        uint256 liquidity = convertCollateralToLiquidity(collateral);
-        shares = convertLiquidityToShare(liquidity);
+    uint256 liquidity = convertCollateralToLiquidity(collateral);
+    shares = convertLiquidityToShare(liquidity);
 
-        if (collateral == 0 || liquidity == 0 || shares == 0) revert InputError();
-        if (liquidity > totalLiquidity) revert CompleteUtilizationError();
-        if (totalSupply > 0 && totalLiquidityBorrowed == 0) revert CompleteUtilizationError();
+    if (collateral == 0 || liquidity == 0 || shares == 0) revert InputError();
+    if (liquidity > totalLiquidity) revert CompleteUtilizationError();
+    // next check is for the case when liquidity is borrowed but then was completely accrued
+    if (totalSupply > 0 && totalLiquidityBorrowed == 0) revert CompleteUtilizationError();
 
-        // update state
-        totalLiquidityBorrowed += liquidity;
-        (uint256 amount0, uint256 amount1) = burn(to, liquidity);
-        _mint(to, shares);
+    // update state
+    totalLiquidityBorrowed += liquidity;
+    (uint256 amount0, uint256 amount1) = burn(to, liquidity);
+    _mint(to, shares);
 
-        uint256 balanceBefore = Balance.balance(token1);
-        IMintCallback(msg.sender).mintCallback(collateral, amount0, amount1, liquidity, data);
-        uint256 balanceAfter = Balance.balance(token1);
+    uint256 balanceBefore = Balance.balance(token1);
+    IMintCallback(msg.sender).mintCallback(collateral, amount0, amount1, liquidity, data);
+    uint256 balanceAfter = Balance.balance(token1);
 
-        if (balanceAfter < balanceBefore + collateral) revert InsufficientInputError();
+    if (balanceAfter < balanceBefore + collateral) revert InsufficientInputError();
 
-        emit Mint(msg.sender, collateral, shares, liquidity, to);
-    }
+    emit Mint(msg.sender, collateral, shares, liquidity, to);
+  }
 
-    function burn(address to, bytes calldata data) external nonReentrant returns (uint256 collateral) {
-        _accrueInterest();
+  function burn(address to, bytes calldata data) external nonReentrant returns (uint256 collateral) {
+    _accrueInterest();
 
-        // calc shares and liquidity
-        uint256 shares = balanceOf[address(this)];
-        uint256 liquidity = convertShareToLiquidity(shares);
-        collateral = convertLiquidityToCollateral(liquidity);
+    // calc shares and liquidity
+    uint256 shares = balanceOf[address(this)];
+    uint256 liquidity = convertShareToLiquidity(shares);
+    collateral = convertLiquidityToCollateral(liquidity);
 
-        if (collateral == 0 || liquidity == 0 || shares == 0) revert InputError();
+    if (collateral == 0 || liquidity == 0 || shares == 0) revert InputError();
 
-        // update state
-        totalLiquidityBorrowed -= liquidity;
-        _burn(address(this), shares);
-        SafeTransferLib.safeTransfer(token1, to, collateral); // optimistically transfer
-        mint(liquidity, data);
+    // update state
+    totalLiquidityBorrowed -= liquidity;
+    _burn(address(this), shares);
+    SafeTransferLib.safeTransfer(token1, to, collateral); // optimistically transfer
+    mint(liquidity, data);
 
-        emit Burn(msg.sender, collateral, shares, liquidity, to);
-    }
+    emit Burn(msg.sender, collateral, shares, liquidity, to);
+  }
 
-    function deposit(
-        address to,
-        uint256 liquidity,
-        bytes calldata data
-    ) external nonReentrant returns (uint256 size) {
-        _accrueInterest();
+  function deposit(address to, uint256 liquidity, bytes calldata data) external nonReentrant returns (uint256 size) {
+    _accrueInterest();
 
-        uint256 _totalPositionSize = totalPositionSize; // SLOAD
-        uint256 totalLiquiditySupplied = totalLiquidity + totalLiquidityBorrowed;
+    uint256 _totalPositionSize = totalPositionSize; // SLOAD
+    uint256 totalLiquiditySupplied = totalLiquidity + totalLiquidityBorrowed;
 
-        // calculate position
-        size = Position.convertLiquidityToPosition(liquidity, totalLiquiditySupplied, _totalPositionSize);
+    // calculate position
+    size = Position.convertLiquidityToPosition(liquidity, totalLiquiditySupplied, _totalPositionSize);
 
-        // validate inputs
-        if (liquidity == 0 || size == 0) revert InputError();
-        // TODO: what if interest has been fully accrued
+    // validate inputs
+    if (liquidity == 0 || size == 0) revert InputError();
+    // next check is for the case when liquidity is borrowed but then was completely accrued
+    if (totalLiquiditySupplied == 0 && totalPositionSize > 0) revert CompleteUtilizationError();
 
-        // update state
-        positions.update(to, SafeCast.toInt256(size), rewardPerPositionStored);
-        totalPositionSize = _totalPositionSize + size;
-        mint(liquidity, data);
+    // update state
+    positions.update(to, SafeCast.toInt256(size), rewardPerPositionStored);
+    totalPositionSize = _totalPositionSize + size;
+    mint(liquidity, data);
 
-        emit Deposit(msg.sender, size, liquidity, to);
-    }
+    emit Deposit(msg.sender, size, liquidity, to);
+  }
 
-    function withdraw(address to, uint256 size)
-        external
-        nonReentrant
-        returns (
-            uint256 amount0,
-            uint256 amount1,
-            uint256 liquidity
-        )
-    {
-        _accrueInterest();
-
-        uint256 _totalPositionSize = totalPositionSize; // SLOAD
-        uint256 _totalLiquidity = totalLiquidity; // SLOAD
-        uint256 totalLiquiditySupplied = _totalLiquidity + totalLiquidityBorrowed;
-
-        // read position
-        Position.Info memory positionInfo = positions.get(msg.sender);
-        liquidity = Position.convertPositionToLiquidity(size, totalLiquiditySupplied, _totalPositionSize);
-
-        // validate inputs
-        if (liquidity == 0 || size == 0) revert InputError();
-
-        // check position
-        if (size > positionInfo.size) revert InsufficientPositionError();
-        if (liquidity > _totalLiquidity) revert CompleteUtilizationError();
-
-        // update state
-        positions.update(msg.sender, -SafeCast.toInt256(size), rewardPerPositionStored);
-        totalPositionSize -= size;
-        (amount0, amount1) = burn(to, liquidity);
-
-        emit Withdraw(msg.sender, size, liquidity, to);
-    }
+  function withdraw(
+    address to,
+    uint256 size
+  )
+    external
+    nonReentrant
+    returns (uint256 amount0, uint256 amount1, uint256 liquidity)
+  {
+    _accrueInterest();
 
-    function accrueInterest() external nonReentrant {
-        _accrueInterest();
-    }
+    uint256 _totalPositionSize = totalPositionSize; // SLOAD
+    uint256 _totalLiquidity = totalLiquidity; // SLOAD
+    uint256 totalLiquiditySupplied = _totalLiquidity + totalLiquidityBorrowed;
 
-    function accruePositionInterest() external nonReentrant {
-        _accrueInterest();
-        _accruePositionInterest(msg.sender);
-    }
+    // read position
+    Position.Info memory positionInfo = positions[msg.sender];
+    liquidity = Position.convertPositionToLiquidity(size, totalLiquiditySupplied, _totalPositionSize);
+
+    // validate inputs
+    if (liquidity == 0 || size == 0) revert InputError();
+
+    // check position
+    if (size > positionInfo.size) revert InsufficientPositionError();
+    if (liquidity > _totalLiquidity) revert CompleteUtilizationError();
+
+    // update state
+    positions.update(msg.sender, -SafeCast.toInt256(size), rewardPerPositionStored);
+    totalPositionSize -= size;
+    (amount0, amount1) = burn(to, liquidity);
+
+    emit Withdraw(msg.sender, size, liquidity, to);
+  }
 
-    function collect(address to, uint256 collateralRequested) external nonReentrant returns (uint256 collateral) {
-        Position.Info storage position = positions.get(msg.sender);
-        uint256 tokensOwed = position.tokensOwed; // SLOAD
+  function accrueInterest() external nonReentrant {
+    _accrueInterest();
+  }
 
-        collateral = collateralRequested > tokensOwed ? tokensOwed : collateralRequested;
+  function accruePositionInterest() external nonReentrant {
+    _accrueInterest();
+    _accruePositionInterest(msg.sender);
+  }
 
-        if (collateral > 0) {
-            position.tokensOwed = tokensOwed - collateral;
-            SafeTransferLib.safeTransfer(token1, to, collateral);
-        }
+  function collect(address to, uint256 collateralRequested) external nonReentrant returns (uint256 collateral) {
+    Position.Info storage position = positions[msg.sender]; // SLOAD
+    uint256 tokensOwed = position.tokensOwed;
 
-        emit Collect(msg.sender, to, collateral);
+    collateral = collateralRequested > tokensOwed ? tokensOwed : collateralRequested;
+
+    if (collateral > 0) {
+      position.tokensOwed = tokensOwed - collateral; // SSTORE
+      SafeTransferLib.safeTransfer(token1, to, collateral);
     }
 
-    /*//////////////////////////////////////////////////////////////
+    emit Collect(msg.sender, to, collateral);
+  }
+
+  /*//////////////////////////////////////////////////////////////
                             ACCOUNTING LOGIC
     //////////////////////////////////////////////////////////////*/
 
-    function convertLiquidityToShare(uint256 liquidity) public view returns (uint256) {
-        uint256 _totalLiquidityBorrowed = totalLiquidityBorrowed; // SLOAD
-        return
-            _totalLiquidityBorrowed == 0 ? liquidity : FullMath.mulDiv(liquidity, totalSupply, _totalLiquidityBorrowed);
-    }
+  function convertLiquidityToShare(uint256 liquidity) public view returns (uint256) {
+    uint256 _totalLiquidityBorrowed = totalLiquidityBorrowed; // SLOAD
+    return _totalLiquidityBorrowed == 0 ? liquidity : FullMath.mulDiv(liquidity, totalSupply, _totalLiquidityBorrowed);
+  }
 
-    function convertShareToLiquidity(uint256 shares) public view returns (uint256) {
-        return FullMath.mulDiv(totalLiquidityBorrowed, shares, totalSupply);
-    }
+  function convertShareToLiquidity(uint256 shares) public view returns (uint256) {
+    return FullMath.mulDiv(totalLiquidityBorrowed, shares, totalSupply);
+  }
 
-    function convertCollateralToLiquidity(uint256 collateral) public view returns (uint256) {
-        return FullMath.mulDiv(collateral * token1Scale, 1e18, 2 * upperBound);
-    }
+  function convertCollateralToLiquidity(uint256 collateral) public view returns (uint256) {
+    return FullMath.mulDiv(collateral * token1Scale, 1e18, 2 * upperBound);
+  }
 
-    function convertLiquidityToCollateral(uint256 liquidity) public view returns (uint256) {
-        return FullMath.mulDiv(liquidity, 2 * upperBound, 1e18) / token1Scale;
-    }
+  function convertLiquidityToCollateral(uint256 liquidity) public view returns (uint256) {
+    return FullMath.mulDiv(liquidity, 2 * upperBound, 1e18) / token1Scale;
+  }
 
-    /*//////////////////////////////////////////////////////////////
+  /*//////////////////////////////////////////////////////////////
                          INTERNAL INTEREST LOGIC
     //////////////////////////////////////////////////////////////*/
 
-    /// @notice Helper function for accruing lendgine interest
-    function _accrueInterest() private {
-        if (totalSupply == 0 || totalLiquidityBorrowed == 0) {
-            lastUpdate = block.timestamp;
-            return;
-        }
+  /// @notice Helper function for accruing lendgine interest
+  function _accrueInterest() private {
+    if (totalSupply == 0 || totalLiquidityBorrowed == 0) {
+      lastUpdate = block.timestamp;
+      return;
+    }
 
-        uint256 timeElapsed = block.timestamp - lastUpdate;
-        if (timeElapsed == 0) return;
+    uint256 timeElapsed = block.timestamp - lastUpdate;
+    if (timeElapsed == 0) return;
 
-        uint256 _totalLiquidityBorrowed = totalLiquidityBorrowed; // SLOAD
-        uint256 totalLiquiditySupplied = totalLiquidity + _totalLiquidityBorrowed; // SLOAD
+    uint256 _totalLiquidityBorrowed = totalLiquidityBorrowed; // SLOAD
+    uint256 totalLiquiditySupplied = totalLiquidity + _totalLiquidityBorrowed; // SLOAD
 
-        uint256 borrowRate = getBorrowRate(_totalLiquidityBorrowed, totalLiquiditySupplied);
+    uint256 borrowRate = getBorrowRate(_totalLiquidityBorrowed, totalLiquiditySupplied);
 
-        uint256 dilutionLPRequested = (FullMath.mulDiv(borrowRate, _totalLiquidityBorrowed, 1e18) * timeElapsed) /
-            365 days;
-        uint256 dilutionLP = dilutionLPRequested > _totalLiquidityBorrowed
-            ? _totalLiquidityBorrowed
-            : dilutionLPRequested;
-        uint256 dilutionSpeculative = convertLiquidityToCollateral(dilutionLP);
+    uint256 dilutionLPRequested = (FullMath.mulDiv(borrowRate, _totalLiquidityBorrowed, 1e18) * timeElapsed) / 365 days;
+    uint256 dilutionLP = dilutionLPRequested > _totalLiquidityBorrowed ? _totalLiquidityBorrowed : dilutionLPRequested;
+    uint256 dilutionSpeculative = convertLiquidityToCollateral(dilutionLP);
 
-        totalLiquidityBorrowed = _totalLiquidityBorrowed - dilutionLP;
-        rewardPerPositionStored += FullMath.mulDiv(dilutionSpeculative, 1e18, totalPositionSize);
-        lastUpdate = block.timestamp;
+    totalLiquidityBorrowed = _totalLiquidityBorrowed - dilutionLP;
+    rewardPerPositionStored += FullMath.mulDiv(dilutionSpeculative, 1e18, totalPositionSize);
+    lastUpdate = block.timestamp;
 
-        emit AccrueInterest(timeElapsed, dilutionSpeculative, dilutionLP);
-    }
+    emit AccrueInterest(timeElapsed, dilutionSpeculative, dilutionLP);
+  }
 
-    /// @notice Helper function for accruing interest to a position
-    /// @dev Assume the global interest is up to date
-    /// @param owner The address that this position belongs to
-    function _accruePositionInterest(address owner) private {
-        uint256 _rewardPerPositionStored = rewardPerPositionStored; // SLOAD
+  /// @notice Helper function for accruing interest to a position
+  /// @dev Assume the global interest is up to date
+  /// @param owner The address that this position belongs to
+  function _accruePositionInterest(address owner) private {
+    uint256 _rewardPerPositionStored = rewardPerPositionStored; // SLOAD
 
-        positions.update(owner, 0, _rewardPerPositionStored);
+    positions.update(owner, 0, _rewardPerPositionStored);
 
-        emit AccruePositionInterest(owner, _rewardPerPositionStored);
-    }
+    emit AccruePositionInterest(owner, _rewardPerPositionStored);
+  }
 }
diff --git a/src/core/Pair.sol b/src/core/Pair.sol
index 51ef395..ba4fa9e 100644
--- a/src/core/Pair.sol
+++ b/src/core/Pair.sol
@@ -4,6 +4,7 @@ pragma solidity ^0.8.4;
 import { ImmutableState } from "./ImmutableState.sol";
 import { ReentrancyGuard } from "./ReentrancyGuard.sol";
 
+import { IPair } from "./interfaces/IPair.sol";
 import { IPairMintCallback } from "./interfaces/callback/IPairMintCallback.sol";
 import { ISwapCallback } from "./interfaces/callback/ISwapCallback.sol";
 
@@ -12,130 +13,128 @@ import { FullMath } from "../libraries/FullMath.sol";
 import { SafeCast } from "../libraries/SafeCast.sol";
 import { SafeTransferLib } from "../libraries/SafeTransferLib.sol";
 
-abstract contract Pair is ImmutableState, ReentrancyGuard {
-    /*//////////////////////////////////////////////////////////////
+abstract contract Pair is ImmutableState, ReentrancyGuard, IPair {
+  /*//////////////////////////////////////////////////////////////
                                  EVENTS
     //////////////////////////////////////////////////////////////*/
 
-    event Mint(uint256 amount0In, uint256 amount1In, uint256 liquidity);
+  event Mint(uint256 amount0In, uint256 amount1In, uint256 liquidity);
 
-    event Burn(uint256 amount0Out, uint256 amount1Out, uint256 liquidity, address indexed to);
+  event Burn(uint256 amount0Out, uint256 amount1Out, uint256 liquidity, address indexed to);
 
-    event Swap(uint256 amount0Out, uint256 amount1Out, uint256 amount0In, uint256 amount1In, address indexed to);
+  event Swap(uint256 amount0Out, uint256 amount1Out, uint256 amount0In, uint256 amount1In, address indexed to);
 
-    /*//////////////////////////////////////////////////////////////
+  /*//////////////////////////////////////////////////////////////
                                  ERRORS
     //////////////////////////////////////////////////////////////*/
 
-    error InvariantError();
+  error InvariantError();
 
-    error InsufficientOutputError();
+  error InsufficientOutputError();
 
-    /*//////////////////////////////////////////////////////////////
+  /*//////////////////////////////////////////////////////////////
                                  STORAGE
     //////////////////////////////////////////////////////////////*/
 
-    uint120 public reserve0;
+  /// @inheritdoc IPair
+  uint120 public override reserve0;
 
-    uint120 public reserve1;
+  /// @inheritdoc IPair
+  uint120 public override reserve1;
 
-    uint256 public totalLiquidity;
+  /// @inheritdoc IPair
+  uint256 public override totalLiquidity;
 
-    /*//////////////////////////////////////////////////////////////
+  /*//////////////////////////////////////////////////////////////
                               PAIR LOGIC
     //////////////////////////////////////////////////////////////*/
 
-    function invariant(
-        uint256 amount0,
-        uint256 amount1,
-        uint256 liquidity
-    ) public view returns (bool) {
-        if (liquidity == 0) return (amount0 == 0 && amount1 == 0);
+  /// @inheritdoc IPair
+  function invariant(uint256 amount0, uint256 amount1, uint256 liquidity) public view override returns (bool) {
+    if (liquidity == 0) return (amount0 == 0 && amount1 == 0);
 
-        uint256 scale0 = FullMath.mulDiv(amount0, 1e18, liquidity) * token0Scale;
-        uint256 scale1 = FullMath.mulDiv(amount1, 1e18, liquidity) * token1Scale;
+    uint256 scale0 = FullMath.mulDiv(amount0, 1e18, liquidity) * token0Scale;
+    uint256 scale1 = FullMath.mulDiv(amount1, 1e18, liquidity) * token1Scale;
 
-        if (scale1 > 2 * upperBound) revert InvariantError();
+    if (scale1 > 2 * upperBound) revert InvariantError();
 
-        uint256 a = scale0 * 1e18;
-        uint256 b = scale1 * upperBound;
-        uint256 c = (scale1 * scale1) / 4;
-        uint256 d = upperBound * upperBound;
+    uint256 a = scale0 * 1e18;
+    uint256 b = scale1 * upperBound;
+    uint256 c = (scale1 * scale1) / 4;
+    uint256 d = upperBound * upperBound;
 
-        return a + b >= c + d;
-    }
-
-    /// @dev assumes liquidity is non-zero
-    function mint(uint256 liquidity, bytes calldata data) internal {
-        uint120 _reserve0 = reserve0; // SLOAD
-        uint120 _reserve1 = reserve1; // SLOAD
-        uint256 _totalLiquidity = totalLiquidity; // SLOAD
-
-        uint256 balance0Before = Balance.balance(token0);
-        uint256 balance1Before = Balance.balance(token1);
-        IPairMintCallback(msg.sender).pairMintCallback(liquidity, data);
-        uint256 amount0In = Balance.balance(token0) - balance0Before;
-        uint256 amount1In = Balance.balance(token1) - balance1Before;
+    return a + b >= c + d;
+  }
 
-        if (!invariant(_reserve0 + amount0In, _reserve1 + amount1In, _totalLiquidity + liquidity))
-            revert InvariantError();
+  /// @dev assumes liquidity is non-zero
+  function mint(uint256 liquidity, bytes calldata data) internal {
+    uint120 _reserve0 = reserve0; // SLOAD
+    uint120 _reserve1 = reserve1; // SLOAD
+    uint256 _totalLiquidity = totalLiquidity; // SLOAD
 
-        reserve0 = _reserve0 + SafeCast.toUint120(amount0In); // SSTORE
-        reserve1 = _reserve1 + SafeCast.toUint120(amount1In); // SSTORE
-        totalLiquidity = _totalLiquidity + liquidity; // SSTORE
+    uint256 balance0Before = Balance.balance(token0);
+    uint256 balance1Before = Balance.balance(token1);
+    IPairMintCallback(msg.sender).pairMintCallback(liquidity, data);
+    uint256 amount0In = Balance.balance(token0) - balance0Before;
+    uint256 amount1In = Balance.balance(token1) - balance1Before;
 
-        emit Mint(amount0In, amount1In, liquidity);
+    if (!invariant(_reserve0 + amount0In, _reserve1 + amount1In, _totalLiquidity + liquidity)) {
+      revert InvariantError();
     }
 
-    /// @dev assumes liquidity is non-zero
-    function burn(address to, uint256 liquidity) internal returns (uint256 amount0, uint256 amount1) {
-        uint120 _reserve0 = reserve0; // SLOAD
-        uint120 _reserve1 = reserve1; // SLOAD
-        uint256 _totalLiquidity = totalLiquidity; // SLOAD
+    reserve0 = _reserve0 + SafeCast.toUint120(amount0In); // SSTORE
+    reserve1 = _reserve1 + SafeCast.toUint120(amount1In); // SSTORE
+    totalLiquidity = _totalLiquidity + liquidity; // SSTORE
 
-        amount0 = FullMath.mulDiv(_reserve0, liquidity, _totalLiquidity);
-        amount1 = FullMath.mulDiv(_reserve1, liquidity, _totalLiquidity);
-        if (amount0 == 0 && amount1 == 0) revert InsufficientOutputError();
+    emit Mint(amount0In, amount1In, liquidity);
+  }
 
-        if (amount0 > 0) SafeTransferLib.safeTransfer(token0, to, amount0);
-        if (amount1 > 0) SafeTransferLib.safeTransfer(token1, to, amount1);
+  /// @dev assumes liquidity is non-zero
+  function burn(address to, uint256 liquidity) internal returns (uint256 amount0, uint256 amount1) {
+    uint120 _reserve0 = reserve0; // SLOAD
+    uint120 _reserve1 = reserve1; // SLOAD
+    uint256 _totalLiquidity = totalLiquidity; // SLOAD
 
-        // Extra check of the invariant
-        if (!invariant(_reserve0 - amount0, _reserve1 - amount1, _totalLiquidity - liquidity)) revert InvariantError();
+    amount0 = FullMath.mulDiv(_reserve0, liquidity, _totalLiquidity);
+    amount1 = FullMath.mulDiv(_reserve1, liquidity, _totalLiquidity);
+    if (amount0 == 0 && amount1 == 0) revert InsufficientOutputError();
 
-        reserve0 = _reserve0 - SafeCast.toUint120(amount0); // SSTORE
-        reserve1 = _reserve1 - SafeCast.toUint120(amount1); // SSTORE
-        totalLiquidity = _totalLiquidity - liquidity; // SSTORE
+    if (amount0 > 0) SafeTransferLib.safeTransfer(token0, to, amount0);
+    if (amount1 > 0) SafeTransferLib.safeTransfer(token1, to, amount1);
 
-        emit Burn(amount0, amount1, liquidity, to);
-    }
+    // Extra check of the invariant
+    if (!invariant(_reserve0 - amount0, _reserve1 - amount1, _totalLiquidity - liquidity)) revert InvariantError();
 
-    function swap(
-        address to,
-        uint256 amount0Out,
-        uint256 amount1Out,
-        bytes calldata data
-    ) external nonReentrant {
-        if (amount0Out == 0 && amount1Out == 0) revert InsufficientOutputError();
+    reserve0 = _reserve0 - SafeCast.toUint120(amount0); // SSTORE
+    reserve1 = _reserve1 - SafeCast.toUint120(amount1); // SSTORE
+    totalLiquidity = _totalLiquidity - liquidity; // SSTORE
 
-        uint120 _reserve0 = reserve0; // SLOAD
-        uint120 _reserve1 = reserve1; // SLOAD
+    emit Burn(amount0, amount1, liquidity, to);
+  }
 
-        if (amount0Out > 0) SafeTransferLib.safeTransfer(token0, to, amount0Out);
-        if (amount1Out > 0) SafeTransferLib.safeTransfer(token1, to, amount1Out);
+  /// @inheritdoc IPair
+  function swap(address to, uint256 amount0Out, uint256 amount1Out, bytes calldata data) external override nonReentrant {
+    if (amount0Out == 0 && amount1Out == 0) revert InsufficientOutputError();
 
-        uint256 balance0Before = Balance.balance(token0);
-        uint256 balance1Before = Balance.balance(token1);
-        ISwapCallback(msg.sender).swapCallback(amount0Out, amount1Out, data);
-        uint256 amount0In = Balance.balance(token0) - balance0Before;
-        uint256 amount1In = Balance.balance(token1) - balance1Before;
+    uint120 _reserve0 = reserve0; // SLOAD
+    uint120 _reserve1 = reserve1; // SLOAD
 
-        if (!invariant(_reserve0 + amount0In - amount0Out, _reserve1 + amount1In - amount1Out, totalLiquidity))
-            revert InvariantError();
+    if (amount0Out > 0) SafeTransferLib.safeTransfer(token0, to, amount0Out);
+    if (amount1Out > 0) SafeTransferLib.safeTransfer(token1, to, amount1Out);
 
-        reserve0 = _reserve0 + SafeCast.toUint120(amount0In) - SafeCast.toUint120(amount0Out); // SSTORE
-        reserve1 = _reserve1 + SafeCast.toUint120(amount1In) - SafeCast.toUint120(amount1Out); // SSTORE
+    uint256 balance0Before = Balance.balance(token0);
+    uint256 balance1Before = Balance.balance(token1);
+    ISwapCallback(msg.sender).swapCallback(amount0Out, amount1Out, data);
+    uint256 amount0In = Balance.balance(token0) - balance0Before;
+    uint256 amount1In = Balance.balance(token1) - balance1Before;
 
-        emit Swap(amount0Out, amount1Out, amount0In, amount1In, to);
+    if (!invariant(_reserve0 + amount0In - amount0Out, _reserve1 + amount1In - amount1Out, totalLiquidity)) {
+      revert InvariantError();
     }
+
+    reserve0 = _reserve0 + SafeCast.toUint120(amount0In) - SafeCast.toUint120(amount0Out); // SSTORE
+    reserve1 = _reserve1 + SafeCast.toUint120(amount1In) - SafeCast.toUint120(amount1Out); // SSTORE
+
+    emit Swap(amount0Out, amount1Out, amount0In, amount1In, to);
+  }
 }
diff --git a/src/core/ReentrancyGuard.sol b/src/core/ReentrancyGuard.sol
index 5c4f021..dc463ef 100644
--- a/src/core/ReentrancyGuard.sol
+++ b/src/core/ReentrancyGuard.sol
@@ -6,15 +6,15 @@ pragma solidity >=0.8.0;
 /// @author Modified from OpenZeppelin
 /// (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/security/ReentrancyGuard.sol)
 abstract contract ReentrancyGuard {
-    uint16 private locked = 1;
+  uint16 private locked = 1;
 
-    modifier nonReentrant() virtual {
-        require(locked == 1, "REENTRANCY");
+  modifier nonReentrant() virtual {
+    require(locked == 1, "REENTRANCY");
 
-        locked = 2;
+    locked = 2;
 
-        _;
+    _;
 
-        locked = 1;
-    }
+    locked = 1;
+  }
 }
diff --git a/src/core/interfaces/IFactory.sol b/src/core/interfaces/IFactory.sol
new file mode 100644
index 0000000..c5f54a2
--- /dev/null
+++ b/src/core/interfaces/IFactory.sol
@@ -0,0 +1,41 @@
+// SPDX-License-Identifier: GPL-3.0-only
+pragma solidity >=0.5.0;
+
+/// @notice Manages the recording and creation of Numoen markets
+/// @author Kyle Scott (https://github.com/numoen/contracts-mono/blob/master/src/Factory.sol)
+/// @author Modified from Uniswap (https://github.com/Uniswap/v2-core/blob/master/contracts/UniswapV2Factory.sol)
+/// and Primitive (https://github.com/primitivefinance/rmm-core/blob/main/contracts/PrimitiveFactory.sol)
+interface IFactory {
+  /// @notice Returns the lendgine address for a given pair of tokens and upper bound
+  /// @dev returns address 0 if it doesn't exist
+  function getLendgine(
+    address token0,
+    address token1,
+    uint256 token0Exp,
+    uint256 token1Exp,
+    uint256 upperBound
+  )
+    external
+    view
+    returns (address lendgine);
+
+  /// @notice Get the parameters to be used in constructing the lendgine, set
+  /// transiently during lendgine creation
+  /// @dev Called by the immutable state constructor to fetch the parameters of the lendgine
+  function parameters()
+    external
+    view
+    returns (address token0, address token1, uint128 token0Exp, uint128 token1Exp, uint256 upperBound);
+
+  /// @notice Deploys a lendgine contract by transiently setting the parameters storage slots
+  /// and clearing it after the lendgine has been deployed
+  function createLendgine(
+    address token0,
+    address token1,
+    uint8 token0Exp,
+    uint8 token1Exp,
+    uint256 upperBound
+  )
+    external
+    returns (address);
+}
diff --git a/src/core/interfaces/IImmutableState.sol b/src/core/interfaces/IImmutableState.sol
new file mode 100644
index 0000000..d0b2267
--- /dev/null
+++ b/src/core/interfaces/IImmutableState.sol
@@ -0,0 +1,24 @@
+// SPDX-License-Identifier: GPL-3.0-only
+pragma solidity >=0.5.0;
+
+/// @notice Immutable state interface
+/// @author Kyle Scott (kyle@numoen.com)
+interface IImmutableState {
+  /// @notice The contract that deployed the lendgine
+  function factory() external view returns (address);
+
+  /// @notice The "numeraire" or "base" token in the pair
+  function token0() external view returns (address);
+
+  /// @notice The "risky" or "speculative" token in the pair
+  function token1() external view returns (address);
+
+  /// @notice Scale required to make token 0 18 decimals
+  function token0Scale() external view returns (uint256);
+
+  /// @notice Scale required to make token 1 18 decimals
+  function token1Scale() external view returns (uint256);
+
+  /// @notice Maximum exchange rate (token0/token1)
+  function upperBound() external view returns (uint256);
+}
diff --git a/src/core/interfaces/IJumpRate.sol b/src/core/interfaces/IJumpRate.sol
index 6fcfc5d..d5ed344 100644
--- a/src/core/interfaces/IJumpRate.sol
+++ b/src/core/interfaces/IJumpRate.sol
@@ -6,13 +6,13 @@ pragma solidity >=0.5.0;
 /// @author Modified from Compound
 /// (https://github.com/compound-finance/compound-protocol/blob/master/contracts/JumpRateModel.sol)
 interface IJumpRate {
-    function kink() external view returns (uint256 kink);
+  function kink() external view returns (uint256 kink);
 
-    function multiplier() external view returns (uint256 multiplier);
+  function multiplier() external view returns (uint256 multiplier);
 
-    function jumpMultiplier() external view returns (uint256 jumpMultiplier);
+  function jumpMultiplier() external view returns (uint256 jumpMultiplier);
 
-    function getBorrowRate(uint256 borrowedLiquidity, uint256 totalLiquidity) external view returns (uint256 rate);
+  function getBorrowRate(uint256 borrowedLiquidity, uint256 totalLiquidity) external view returns (uint256 rate);
 
-    function getSupplyRate(uint256 borrowedLiquidity, uint256 totalLiquidity) external view returns (uint256 rate);
+  function getSupplyRate(uint256 borrowedLiquidity, uint256 totalLiquidity) external view returns (uint256 rate);
 }
diff --git a/src/core/interfaces/ILendgine.sol b/src/core/interfaces/ILendgine.sol
new file mode 100644
index 0000000..985ea59
--- /dev/null
+++ b/src/core/interfaces/ILendgine.sol
@@ -0,0 +1,50 @@
+// SPDX-License-Identifier: GPL-3.0-only
+pragma solidity >=0.5.0;
+
+import { IPair } from "./IPair.sol";
+
+interface ILendgine is IPair {
+  /// @notice
+  function positions(address) external view returns (uint256, uint256, uint256);
+
+  function totalPositionSize() external view returns (uint256);
+
+  function totalLiquidityBorrowed() external view returns (uint256);
+
+  function rewardPerPositionStored() external view returns (uint256);
+
+  function lastUpdate() external view returns (uint256);
+
+  function mint(address to, uint256 collateral, bytes calldata data) external returns (uint256 shares);
+
+  function burn(address to, bytes calldata data) external returns (uint256 collateral);
+
+  function deposit(address to, uint256 liquidity, bytes calldata data) external returns (uint256 size);
+
+  function withdraw(address to, uint256 size) external returns (uint256 amount0, uint256 amount1, uint256 liquidity);
+
+  function accrueInterest() external;
+
+  function accruePositionInterest() external;
+
+  function collect(address to, uint256 collateralRequested) external returns (uint256 collateral);
+
+  //         function convertLiquidityToShare(uint256 liquidity) public view returns (uint256) {
+  //     uint256 _totalLiquidityBorrowed = totalLiquidityBorrowed; // SLOAD
+  //     return
+  //         _totalLiquidityBorrowed == 0 ? liquidity : FullMath.mulDiv(liquidity, totalSupply,
+  // _totalLiquidityBorrowed);
+  // }
+
+  // function convertShareToLiquidity(uint256 shares) public view returns (uint256) {
+  //     return FullMath.mulDiv(totalLiquidityBorrowed, shares, totalSupply);
+  // }
+
+  // function convertCollateralToLiquidity(uint256 collateral) public view returns (uint256) {
+  //     return FullMath.mulDiv(collateral * token1Scale, 1e18, 2 * upperBound);
+  // }
+
+  // function convertLiquidityToCollateral(uint256 liquidity) public view returns (uint256) {
+  //     return FullMath.mulDiv(liquidity, 2 * upperBound, 1e18) / token1Scale;
+  // }
+}
diff --git a/src/core/interfaces/IPair.sol b/src/core/interfaces/IPair.sol
new file mode 100644
index 0000000..e3454e3
--- /dev/null
+++ b/src/core/interfaces/IPair.sol
@@ -0,0 +1,26 @@
+// SPDX-License-Identifier: GPL-3.0-only
+pragma solidity >=0.5.0;
+
+import { IImmutableState } from "./IImmutableState.sol";
+
+/// @notice AMM implementing the capped power invariant
+/// @author Kyle Scott (kyle@numoen.com)
+interface IPair is IImmutableState {
+  /// @notice The amount of token0 in the pair
+  function reserve0() external view returns (uint120);
+
+  /// @notice The amount of token1 in the pair
+  function reserve1() external view returns (uint120);
+
+  /// @notice The total amount of liquidity shares in the pair
+  function totalLiquidity() external view returns (uint256);
+
+  /// @notice The implementation of the capped power invariant
+  /// @return valid True if the invariant is satisfied
+  function invariant(uint256 amount0, uint256 amount1, uint256 liquidity) external view returns (bool);
+
+  /// @notice Exchange between token0 and token1, either accepts or rejects the proposed trade
+  /// @param data The data to be passed through to the callback
+  /// @dev A callback is invoked on the caller
+  function swap(address to, uint256 amount0Out, uint256 amount1Out, bytes calldata data) external;
+}
diff --git a/src/core/interfaces/callback/IMintCallback.sol b/src/core/interfaces/callback/IMintCallback.sol
index 826bba2..ee2fb06 100644
--- a/src/core/interfaces/callback/IMintCallback.sol
+++ b/src/core/interfaces/callback/IMintCallback.sol
@@ -2,15 +2,16 @@
 pragma solidity >=0.5.0;
 
 interface IMintCallback {
-    /// @notice Called to `msg.sender` after executing a mint via Lendgine
-    /// @dev In the implementation you must pay the speculative tokens owed for the mint.
-    /// The caller of this method must be checked to be a Lendgine deployed by the canonical Factory.
-    /// @param data Any data passed through by the caller via the Mint call
-    function mintCallback(
-        uint256 collateral,
-        uint256 amount0,
-        uint256 amount1,
-        uint256 liquidity,
-        bytes calldata data
-    ) external;
+  /// @notice Called to `msg.sender` after executing a mint via Lendgine
+  /// @dev In the implementation you must pay the speculative tokens owed for the mint.
+  /// The caller of this method must be checked to be a Lendgine deployed by the canonical Factory.
+  /// @param data Any data passed through by the caller via the Mint call
+  function mintCallback(
+    uint256 collateral,
+    uint256 amount0,
+    uint256 amount1,
+    uint256 liquidity,
+    bytes calldata data
+  )
+    external;
 }
diff --git a/src/core/interfaces/callback/IPairMintCallback.sol b/src/core/interfaces/callback/IPairMintCallback.sol
index 0cd5a93..5a5c135 100644
--- a/src/core/interfaces/callback/IPairMintCallback.sol
+++ b/src/core/interfaces/callback/IPairMintCallback.sol
@@ -2,9 +2,9 @@
 pragma solidity >=0.5.0;
 
 interface IPairMintCallback {
-    /// @notice Called to `msg.sender` after executing a mint via Pair
-    /// @dev In the implementation you must pay the pool tokens owed for the mint.
-    /// The caller of this method must be checked to be a Pair deployed by the canonical Factory.
-    /// @param data Any data passed through by the caller via the Mint call
-    function pairMintCallback(uint256 liquidity, bytes calldata data) external;
+  /// @notice Called to `msg.sender` after executing a mint via Pair
+  /// @dev In the implementation you must pay the pool tokens owed for the mint.
+  /// The caller of this method must be checked to be a Pair deployed by the canonical Factory.
+  /// @param data Any data passed through by the caller via the Mint call
+  function pairMintCallback(uint256 liquidity, bytes calldata data) external;
 }
diff --git a/src/core/interfaces/callback/ISwapCallback.sol b/src/core/interfaces/callback/ISwapCallback.sol
index 271915c..0fe1e62 100644
--- a/src/core/interfaces/callback/ISwapCallback.sol
+++ b/src/core/interfaces/callback/ISwapCallback.sol
@@ -2,13 +2,9 @@
 pragma solidity >=0.5.0;
 
 interface ISwapCallback {
-    /// @notice Called to `msg.sender` after executing a swap via Pair
-    /// @dev In the implementation you must pay the pool tokens owed for the swap.
-    /// The caller of this method must be checked to be a Pair deployed by the canonical Factory.
-    /// @param data Any data passed through by the caller via the Swap call
-    function swapCallback(
-        uint256 amount0Out,
-        uint256 amount1Out,
-        bytes calldata data
-    ) external;
+  /// @notice Called to `msg.sender` after executing a swap via Pair
+  /// @dev In the implementation you must pay the pool tokens owed for the swap.
+  /// The caller of this method must be checked to be a Pair deployed by the canonical Factory.
+  /// @param data Any data passed through by the caller via the Swap call
+  function swapCallback(uint256 amount0Out, uint256 amount1Out, bytes calldata data) external;
 }
diff --git a/src/core/libraries/Position.sol b/src/core/libraries/Position.sol
index 4c55b5c..11e672d 100644
--- a/src/core/libraries/Position.sol
+++ b/src/core/libraries/Position.sol
@@ -8,94 +8,90 @@ import { FullMath } from "../../libraries/FullMath.sol";
 /// @author Kyle Scott (https://github.com/Numoen/core/blob/master/src/libraries/Position.sol)
 /// @author Modified from Uniswap (https://github.com/Uniswap/v3-core/blob/main/contracts/libraries/Position.sol)
 library Position {
-    /*//////////////////////////////////////////////////////////////
+  /*//////////////////////////////////////////////////////////////
                                  ERRORS
     //////////////////////////////////////////////////////////////*/
 
-    error NoPositionError();
+  /// @notice Error for trying to update a position with no size
+  error NoPositionError();
 
-    /*//////////////////////////////////////////////////////////////
+  /*//////////////////////////////////////////////////////////////
                             POSITION STRUCT
     //////////////////////////////////////////////////////////////*/
 
-    /**
-     * @param size The size of the position
-     * @param rewardPerPositionPaid The reward per unit of size as of the last update to position or tokensOwed
-     * @param tokensOwed The fees owed to the position owner in `speculative` tokens
-     */
-    struct Info {
-        uint256 size;
-        uint256 rewardPerPositionPaid;
-        uint256 tokensOwed;
-    }
+  /**
+   * @param size The size of the position
+   * @param rewardPerPositionPaid The reward per unit of size as of the last update to position or tokensOwed
+   * @param tokensOwed The fees owed to the position owner in `speculative` tokens
+   */
+  struct Info {
+    uint256 size;
+    uint256 rewardPerPositionPaid;
+    uint256 tokensOwed;
+  }
 
-    /*//////////////////////////////////////////////////////////////
+  /*//////////////////////////////////////////////////////////////
                               POSITION LOGIC
     //////////////////////////////////////////////////////////////*/
 
-    /// @notice Helper function for determining the amount of tokens owed to a position
-    /// @dev Assumes the global interest is up to date
-    function update(
-        mapping(address => Position.Info) storage self,
-        address owner,
-        int256 sizeDelta,
-        uint256 rewardPerPosition
-    ) internal {
-        Position.Info storage positionInfo = self[owner];
-        Position.Info memory _positionInfo = positionInfo;
-
-        uint256 tokensOwed;
-        if (_positionInfo.size > 0) {
-            tokensOwed = newTokensOwed(_positionInfo, rewardPerPosition);
-        }
+  /// @notice Helper function for updating a position by increasing/decreasing its size or accruing interest
+  function update(
+    mapping(address => Position.Info) storage self,
+    address owner,
+    int256 sizeDelta,
+    uint256 rewardPerPosition
+  )
+    internal
+  {
+    Position.Info storage positionInfo = self[owner];
+    Position.Info memory _positionInfo = positionInfo;
 
-        uint256 sizeNext;
-        if (sizeDelta == 0) {
-            if (_positionInfo.size == 0) revert NoPositionError();
-            sizeNext = _positionInfo.size;
-        } else {
-            sizeNext = PositionMath.addDelta(_positionInfo.size, sizeDelta);
-        }
-
-        if (sizeDelta != 0) positionInfo.size = sizeNext;
-        positionInfo.rewardPerPositionPaid = rewardPerPosition;
-        if (tokensOwed > 0) positionInfo.tokensOwed = _positionInfo.tokensOwed + tokensOwed;
+    uint256 tokensOwed;
+    if (_positionInfo.size > 0) {
+      tokensOwed = newTokensOwed(_positionInfo, rewardPerPosition);
     }
 
-    /// @notice Helper function for determining the amount of tokens owed to a position
-    function newTokensOwed(Position.Info memory position, uint256 rewardPerPosition) internal pure returns (uint256) {
-        return FullMath.mulDiv(position.size, rewardPerPosition - position.rewardPerPositionPaid, 1 ether);
+    uint256 sizeNext;
+    if (sizeDelta == 0) {
+      if (_positionInfo.size == 0) revert NoPositionError();
+      sizeNext = _positionInfo.size;
+    } else {
+      sizeNext = PositionMath.addDelta(_positionInfo.size, sizeDelta);
     }
 
-    function convertLiquidityToPosition(
-        uint256 liquidity,
-        uint256 totalLiquiditySupplied,
-        uint256 totalPositionSize
-    ) internal pure returns (uint256) {
-        return
-            totalLiquiditySupplied == 0
-                ? liquidity
-                : FullMath.mulDiv(liquidity, totalPositionSize, totalLiquiditySupplied);
-    }
+    if (sizeDelta != 0) positionInfo.size = sizeNext;
+    positionInfo.rewardPerPositionPaid = rewardPerPosition;
+    if (tokensOwed > 0) positionInfo.tokensOwed = _positionInfo.tokensOwed + tokensOwed;
+  }
 
-    function convertPositionToLiquidity(
-        uint256 position,
-        uint256 totalLiquiditySupplied,
-        uint256 totalPositionSize
-    ) internal pure returns (uint256) {
-        return FullMath.mulDiv(position, totalLiquiditySupplied, totalPositionSize);
-    }
+  /// @notice Helper function for determining the amount of tokens owed to a position
+  /// @param rewardPerPosition The global accrued interest
+  function newTokensOwed(Position.Info memory position, uint256 rewardPerPosition) internal pure returns (uint256) {
+    return FullMath.mulDiv(position.size, rewardPerPosition - position.rewardPerPositionPaid, 1 ether);
+  }
 
-    /*//////////////////////////////////////////////////////////////
-                           POSITION VIEW
-    //////////////////////////////////////////////////////////////*/
+  function convertLiquidityToPosition(
+    uint256 liquidity,
+    uint256 totalLiquiditySupplied,
+    uint256 totalPositionSize
+  )
+    internal
+    pure
+    returns (uint256)
+  {
+    return
+      totalLiquiditySupplied == 0 ? liquidity : FullMath.mulDiv(liquidity, totalPositionSize, totalLiquiditySupplied);
+  }
 
-    /// @notice Return a position identified by its owner and tick
-    function get(mapping(address => Info) storage self, address owner)
-        internal
-        view
-        returns (Position.Info storage position)
-    {
-        position = self[owner];
-    }
+  function convertPositionToLiquidity(
+    uint256 position,
+    uint256 totalLiquiditySupplied,
+    uint256 totalPositionSize
+  )
+    internal
+    pure
+    returns (uint256)
+  {
+    return FullMath.mulDiv(position, totalLiquiditySupplied, totalPositionSize);
+  }
 }
diff --git a/src/core/libraries/PositionMath.sol b/src/core/libraries/PositionMath.sol
index 0ede692..f25bbac 100644
--- a/src/core/libraries/PositionMath.sol
+++ b/src/core/libraries/PositionMath.sol
@@ -5,15 +5,15 @@ pragma solidity >=0.5.0;
 /// @author Kyle Scott (https://github.com/Numoen/core/blob/master/src/libraries/PositionMath.sol)
 /// @author Modified from Uniswap (https://github.com/Uniswap/v3-core/blob/main/contracts/libraries/LiquidityMath.sol)
 library PositionMath {
-    /// @notice Add a signed size delta to size and revert if it overflows or underflows
-    /// @param x The size before change
-    /// @param y The delta by which size should be changed
-    /// @return z The sizes delta
-    function addDelta(uint256 x, int256 y) internal pure returns (uint256 z) {
-        if (y < 0) {
-            require((z = x - uint256(-y)) < x, "LS");
-        } else {
-            require((z = x + uint256(y)) >= x, "LA");
-        }
+  /// @notice Add a signed size delta to size and revert if it overflows or underflows
+  /// @param x The size before change
+  /// @param y The delta by which size should be changed
+  /// @return z The sizes delta
+  function addDelta(uint256 x, int256 y) internal pure returns (uint256 z) {
+    if (y < 0) {
+      require((z = x - uint256(-y)) < x, "LS");
+    } else {
+      require((z = x + uint256(y)) >= x, "LA");
     }
+  }
 }
diff --git a/src/libraries/Balance.sol b/src/libraries/Balance.sol
index f7a83b7..eeb4887 100644
--- a/src/libraries/Balance.sol
+++ b/src/libraries/Balance.sol
@@ -1,14 +1,18 @@
 // SPDX-License-Identifier: GPL-3.0-only
 pragma solidity ^0.8.4;
 
+/// @notice Library for safely and cheaply reading balances
+/// @author Kyle Scott (kyle@numoen.com)
+/// @author Modified from UniswapV3Pool
+/// (https://github.com/Uniswap/v3-core/blob/main/contracts/UniswapV3Pool.sol#L140-L145)
 library Balance {
-    error BalanceReturnError();
+  error BalanceReturnError();
 
-    function balance(address token) internal view returns (uint256) {
-        (bool success, bytes memory data) = token.staticcall(
-            abi.encodeWithSelector(bytes4(keccak256(bytes("balanceOf(address)"))), address(this))
-        );
-        if (!success || data.length < 32) revert BalanceReturnError();
-        return abi.decode(data, (uint256));
-    }
+  /// @notice Determine the callers balance of the specified token
+  function balance(address token) internal view returns (uint256) {
+    (bool success, bytes memory data) =
+      token.staticcall(abi.encodeWithSelector(bytes4(keccak256(bytes("balanceOf(address)"))), address(this)));
+    if (!success || data.length < 32) revert BalanceReturnError();
+    return abi.decode(data, (uint256));
+  }
 }
diff --git a/src/libraries/FullMath.sol b/src/libraries/FullMath.sol
index 38a138c..58ab181 100644
--- a/src/libraries/FullMath.sol
+++ b/src/libraries/FullMath.sol
@@ -1,137 +1,129 @@
 // SPDX-License-Identifier: MIT
 pragma solidity ^0.8.0;
 
-/**
- * @dev https://github.com/Uniswap/uniswap-v3-core/blob/v1.0.0/contracts/libraries/FullMath.sol
- * Added `unchecked` and changed line 76 for being compatible in solidity 0.8
- */
-
 // solhint-disable max-line-length
 
 /// @title Contains 512-bit math functions
-/// @notice Facilitates multiplication and division that can have overflow of an intermediate value without any loss of precision
-/// @dev Handles "phantom overflow" i.e., allows multiplication and division where an intermediate value overflows 256 bits
+/// @notice Facilitates multiplication and division that can have overflow of an intermediate value without any loss of
+/// precision
+/// @author Muffin (https://github.com/muffinfi/muffin/blob/master/contracts/libraries/math/FullMath.sol)
+/// @dev Handles "phantom overflow" i.e., allows multiplication and division where an intermediate value overflows 256
+/// bits
 library FullMath {
-    /// @notice Calculates floor(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
-    /// @param a The multiplicand
-    /// @param b The multiplier
-    /// @param denominator The divisor
-    /// @return result The 256-bit result
-    /// @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv
-    function mulDiv(
-        uint256 a,
-        uint256 b,
-        uint256 denominator
-    ) internal pure returns (uint256 result) {
-        unchecked {
-            // 512-bit multiply [prod1 prod0] = a * b
-            // Compute the product mod 2**256 and mod 2**256 - 1
-            // then use the Chinese Remainder Theorem to reconstruct
-            // the 512 bit result. The result is stored in two 256
-            // variables such that product = prod1 * 2**256 + prod0
-            uint256 prod0; // Least significant 256 bits of the product
-            uint256 prod1; // Most significant 256 bits of the product
-            assembly {
-                let mm := mulmod(a, b, not(0))
-                prod0 := mul(a, b)
-                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
-            }
+  /// @notice Calculates floor(a×b÷denominator) with full precision. Throws if result overflows a uint256 or
+  /// denominator == 0
+  /// @param a The multiplicand
+  /// @param b The multiplier
+  /// @param denominator The divisor
+  /// @return result The 256-bit result
+  /// @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv
+  function mulDiv(uint256 a, uint256 b, uint256 denominator) internal pure returns (uint256 result) {
+    unchecked {
+      // 512-bit multiply [prod1 prod0] = a * b
+      // Compute the product mod 2**256 and mod 2**256 - 1
+      // then use the Chinese Remainder Theorem to reconstruct
+      // the 512 bit result. The result is stored in two 256
+      // variables such that product = prod1 * 2**256 + prod0
+      uint256 prod0; // Least significant 256 bits of the product
+      uint256 prod1; // Most significant 256 bits of the product
+      assembly {
+        let mm := mulmod(a, b, not(0))
+        prod0 := mul(a, b)
+        prod1 := sub(sub(mm, prod0), lt(mm, prod0))
+      }
 
-            // Handle non-overflow cases, 256 by 256 division
-            if (prod1 == 0) {
-                require(denominator > 0);
-                assembly {
-                    result := div(prod0, denominator)
-                }
-                return result;
-            }
+      // Handle non-overflow cases, 256 by 256 division
+      if (prod1 == 0) {
+        require(denominator > 0);
+        assembly {
+          result := div(prod0, denominator)
+        }
+        return result;
+      }
 
-            // Make sure the result is less than 2**256.
-            // Also prevents denominator == 0
-            require(denominator > prod1);
+      // Make sure the result is less than 2**256.
+      // Also prevents denominator == 0
+      require(denominator > prod1);
 
-            ///////////////////////////////////////////////
-            // 512 by 256 division.
-            ///////////////////////////////////////////////
+      ///////////////////////////////////////////////
+      // 512 by 256 division.
+      ///////////////////////////////////////////////
 
-            // Make division exact by subtracting the remainder from [prod1 prod0]
-            // Compute remainder using mulmod
-            uint256 remainder;
-            assembly {
-                remainder := mulmod(a, b, denominator)
-            }
-            // Subtract 256 bit number from 512 bit number
-            assembly {
-                prod1 := sub(prod1, gt(remainder, prod0))
-                prod0 := sub(prod0, remainder)
-            }
+      // Make division exact by subtracting the remainder from [prod1 prod0]
+      // Compute remainder using mulmod
+      uint256 remainder;
+      assembly {
+        remainder := mulmod(a, b, denominator)
+      }
+      // Subtract 256 bit number from 512 bit number
+      assembly {
+        prod1 := sub(prod1, gt(remainder, prod0))
+        prod0 := sub(prod0, remainder)
+      }
 
-            // Factor powers of two out of denominator
-            // Compute largest power of two divisor of denominator.
-            // Always >= 1.
+      // Factor powers of two out of denominator
+      // Compute largest power of two divisor of denominator.
+      // Always >= 1.
 
-            // [*] The next line is edited to be compatible with solidity 0.8
-            // ref: https://ethereum.stackexchange.com/a/96646
-            // original: uint256 twos = -denominator & denominator;
-            uint256 twos = denominator & (~denominator + 1);
+      // [*] The next line is edited to be compatible with solidity 0.8
+      // ref: https://ethereum.stackexchange.com/a/96646
+      // original: uint256 twos = -denominator & denominator;
+      uint256 twos = denominator & (~denominator + 1);
 
-            // Divide denominator by power of two
-            assembly {
-                denominator := div(denominator, twos)
-            }
+      // Divide denominator by power of two
+      assembly {
+        denominator := div(denominator, twos)
+      }
 
-            // Divide [prod1 prod0] by the factors of two
-            assembly {
-                prod0 := div(prod0, twos)
-            }
-            // Shift in bits from prod1 into prod0. For this we need
-            // to flip `twos` such that it is 2**256 / twos.
-            // If twos is zero, then it becomes one
-            assembly {
-                twos := add(div(sub(0, twos), twos), 1)
-            }
-            prod0 |= prod1 * twos;
+      // Divide [prod1 prod0] by the factors of two
+      assembly {
+        prod0 := div(prod0, twos)
+      }
+      // Shift in bits from prod1 into prod0. For this we need
+      // to flip `twos` such that it is 2**256 / twos.
+      // If twos is zero, then it becomes one
+      assembly {
+        twos := add(div(sub(0, twos), twos), 1)
+      }
+      prod0 |= prod1 * twos;
 
-            // Invert denominator mod 2**256
-            // Now that denominator is an odd number, it has an inverse
-            // modulo 2**256 such that denominator * inv = 1 mod 2**256.
-            // Compute the inverse by starting with a seed that is correct
-            // correct for four bits. That is, denominator * inv = 1 mod 2**4
-            uint256 inv = (3 * denominator) ^ 2;
-            // Now use Newton-Raphson iteration to improve the precision.
-            // Thanks to Hensel's lifting lemma, this also works in modular
-            // arithmetic, doubling the correct bits in each step.
-            inv *= 2 - denominator * inv; // inverse mod 2**8
-            inv *= 2 - denominator * inv; // inverse mod 2**16
-            inv *= 2 - denominator * inv; // inverse mod 2**32
-            inv *= 2 - denominator * inv; // inverse mod 2**64
-            inv *= 2 - denominator * inv; // inverse mod 2**128
-            inv *= 2 - denominator * inv; // inverse mod 2**256
+      // Invert denominator mod 2**256
+      // Now that denominator is an odd number, it has an inverse
+      // modulo 2**256 such that denominator * inv = 1 mod 2**256.
+      // Compute the inverse by starting with a seed that is correct
+      // correct for four bits. That is, denominator * inv = 1 mod 2**4
+      uint256 inv = (3 * denominator) ^ 2;
+      // Now use Newton-Raphson iteration to improve the precision.
+      // Thanks to Hensel's lifting lemma, this also works in modular
+      // arithmetic, doubling the correct bits in each step.
+      inv *= 2 - denominator * inv; // inverse mod 2**8
+      inv *= 2 - denominator * inv; // inverse mod 2**16
+      inv *= 2 - denominator * inv; // inverse mod 2**32
+      inv *= 2 - denominator * inv; // inverse mod 2**64
+      inv *= 2 - denominator * inv; // inverse mod 2**128
+      inv *= 2 - denominator * inv; // inverse mod 2**256
 
-            // Because the division is now exact we can divide by multiplying
-            // with the modular inverse of denominator. This will give us the
-            // correct result modulo 2**256. Since the precoditions guarantee
-            // that the outcome is less than 2**256, this is the final result.
-            // We don't need to compute the high bits of the result and prod1
-            // is no longer required.
-            result = prod0 * inv;
-            return result;
-        }
+      // Because the division is now exact we can divide by multiplying
+      // with the modular inverse of denominator. This will give us the
+      // correct result modulo 2**256. Since the precoditions guarantee
+      // that the outcome is less than 2**256, this is the final result.
+      // We don't need to compute the high bits of the result and prod1
+      // is no longer required.
+      result = prod0 * inv;
+      return result;
     }
+  }
 
-    /// @notice Calculates ceil(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
-    /// @param a The multiplicand
-    /// @param b The multiplier
-    /// @param denominator The divisor
-    /// @return result The 256-bit result
-    function mulDivRoundingUp(
-        uint256 a,
-        uint256 b,
-        uint256 denominator
-    ) internal pure returns (uint256 result) {
-        result = mulDiv(a, b, denominator);
-        if (mulmod(a, b, denominator) > 0) {
-            result++;
-        }
+  /// @notice Calculates ceil(a×b÷denominator) with full precision. Throws if result overflows a uint256 or
+  /// denominator == 0
+  /// @param a The multiplicand
+  /// @param b The multiplier
+  /// @param denominator The divisor
+  /// @return result The 256-bit result
+  function mulDivRoundingUp(uint256 a, uint256 b, uint256 denominator) internal pure returns (uint256 result) {
+    result = mulDiv(a, b, denominator);
+    if (mulmod(a, b, denominator) > 0) {
+      result++;
     }
+  }
 }
diff --git a/src/libraries/SafeCast.sol b/src/libraries/SafeCast.sol
index 3a381dc..d118fb1 100644
--- a/src/libraries/SafeCast.sol
+++ b/src/libraries/SafeCast.sol
@@ -1,19 +1,19 @@
 // SPDX-License-Identifier: GPL-3.0-only
 pragma solidity ^0.8.0;
 
-/// @notice Casting library
+/// @notice Library for safely and cheaply casting solidity types
 /// @author Kyle Scott (https://github.com/Numoen/core/blob/master/src/libraries/SafeCast.sol)
 /// @author Modified from Uniswap (https://github.com/Uniswap/v3-core/blob/main/contracts/libraries/SafeCast.sol)
 library SafeCast {
-    function toUint120(uint256 y) internal pure returns (uint120 z) {
-        require((z = uint120(y)) == y);
-    }
+  function toUint120(uint256 y) internal pure returns (uint120 z) {
+    require((z = uint120(y)) == y);
+  }
 
-    /// @notice Cast a uint256 to a int256, revert on overflow
-    /// @param y The uint256 to be casted
-    /// @return z The casted integer, now type int256
-    function toInt256(uint256 y) internal pure returns (int256 z) {
-        require(y < 2**255);
-        z = int256(y);
-    }
+  /// @notice Cast a uint256 to a int256, revert on overflow
+  /// @param y The uint256 to be casted
+  /// @return z The casted integer, now type int256
+  function toInt256(uint256 y) internal pure returns (int256 z) {
+    require(y < 2 ** 255);
+    z = int256(y);
+  }
 }
diff --git a/src/libraries/SafeTransferLib.sol b/src/libraries/SafeTransferLib.sol
index d66b65d..0ed12e9 100644
--- a/src/libraries/SafeTransferLib.sol
+++ b/src/libraries/SafeTransferLib.sol
@@ -1,117 +1,116 @@
 // SPDX-License-Identifier: AGPL-3.0-only
-pragma solidity >=0.8.4;
+pragma solidity >=0.8.0;
 
-/// @dev Adapted from Rari's Solmate https://github.com/Rari-Capital/solmate/blob/main/src/utils/SafeTransferLib.sol
-/// Edited from using error message to custom error for lower bytecode size.
+// solhint-disable max-line-length
 
 /// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
-/// @author Modified from Gnosis (https://github.com/gnosis/gp-v2-contracts/blob/main/src/contracts/libraries/GPv2SafeERC20.sol)
-/// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer.
+/// @author Muffin (https://github.com/muffinfi/muffin/blob/master/contracts/libraries/utils/SafeTransferLib.sol)
+/// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free
+/// memory pointer.
 library SafeTransferLib {
-    error FailedTransferETH();
-    error FailedTransfer();
-    error FailedTransferFrom();
+  error FailedTransferETH();
+  error FailedTransfer();
+  error FailedTransferFrom();
+  error FailedApprove();
 
-    /*///////////////////////////////////////////////////////////////
+  /*///////////////////////////////////////////////////////////////
                             ETH OPERATIONS
     //////////////////////////////////////////////////////////////*/
 
-    function safeTransferETH(address to, uint256 amount) internal {
-        bool callStatus;
+  function safeTransferETH(address to, uint256 amount) internal {
+    bool callStatus;
 
-        assembly {
-            // Transfer the ETH and store if it succeeded or not.
-            callStatus := call(gas(), to, amount, 0, 0, 0, 0)
-        }
-
-        if (!callStatus) revert FailedTransferETH();
+    assembly {
+      // Transfer the ETH and store if it succeeded or not.
+      callStatus := call(gas(), to, amount, 0, 0, 0, 0)
     }
 
-    /*///////////////////////////////////////////////////////////////
+    if (!callStatus) revert FailedTransferETH();
+  }
+
+  /*///////////////////////////////////////////////////////////////
                            ERC20 OPERATIONS
     //////////////////////////////////////////////////////////////*/
 
-    function safeTransferFrom(
-        address token,
-        address from,
-        address to,
-        uint256 amount
-    ) internal {
-        bool callStatus;
-
-        assembly {
-            // Get a pointer to some free memory.
-            let freeMemoryPointer := mload(0x40)
-
-            // Write the abi-encoded calldata to memory piece by piece:
-            mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000) // Begin with the function selector.
-            mstore(add(freeMemoryPointer, 4), and(from, 0xffffffffffffffffffffffffffffffffffffffff)) // Mask and append the "from" argument.
-            mstore(add(freeMemoryPointer, 36), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Mask and append the "to" argument.
-            mstore(add(freeMemoryPointer, 68), amount) // Finally append the "amount" argument. No mask as it's a full 32 byte value.
-
-            // Call the token and store if it succeeded or not.
-            // We use 100 because the calldata length is 4 + 32 * 3.
-            callStatus := call(gas(), token, 0, freeMemoryPointer, 100, 0, 0)
-        }
-
-        if (!didLastOptionalReturnCallSucceed(callStatus)) revert FailedTransferFrom();
+  function safeTransferFrom(address token, address from, address to, uint256 amount) internal {
+    bool callStatus;
+
+    assembly {
+      // Get a pointer to some free memory.
+      let freeMemoryPointer := mload(0x40)
+
+      // Write the abi-encoded calldata to memory piece by piece:
+      mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000) // Begin with
+      // the function selector.
+      mstore(add(freeMemoryPointer, 4), and(from, 0xffffffffffffffffffffffffffffffffffffffff)) // Mask and append
+      // the "from" argument.
+      mstore(add(freeMemoryPointer, 36), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Mask and append
+      // the "to" argument.
+      mstore(add(freeMemoryPointer, 68), amount) // Finally append the "amount" argument. No mask as it's a full
+      // 32 byte value.
+
+      // Call the token and store if it succeeded or not.
+      // We use 100 because the calldata length is 4 + 32 * 3.
+      callStatus := call(gas(), token, 0, freeMemoryPointer, 100, 0, 0)
     }
 
-    function safeTransfer(
-        address token,
-        address to,
-        uint256 amount
-    ) internal {
-        bool callStatus;
+    if (!didLastOptionalReturnCallSucceed(callStatus)) revert FailedTransferFrom();
+  }
 
-        assembly {
-            // Get a pointer to some free memory.
-            let freeMemoryPointer := mload(0x40)
+  function safeTransfer(address token, address to, uint256 amount) internal {
+    bool callStatus;
 
-            // Write the abi-encoded calldata to memory piece by piece:
-            mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000) // Begin with the function selector.
-            mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Mask and append the "to" argument.
-            mstore(add(freeMemoryPointer, 36), amount) // Finally append the "amount" argument. No mask as it's a full 32 byte value.
+    assembly {
+      // Get a pointer to some free memory.
+      let freeMemoryPointer := mload(0x40)
 
-            // Call the token and store if it succeeded or not.
-            // We use 68 because the calldata length is 4 + 32 * 2.
-            callStatus := call(gas(), token, 0, freeMemoryPointer, 68, 0, 0)
-        }
+      // Write the abi-encoded calldata to memory piece by piece:
+      mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000) // Begin with
+      // the function selector.
+      mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Mask and append
+      // the "to" argument.
+      mstore(add(freeMemoryPointer, 36), amount) // Finally append the "amount" argument. No mask as it's a full
+      // 32 byte value.
 
-        if (!didLastOptionalReturnCallSucceed(callStatus)) revert FailedTransfer();
+      // Call the token and store if it succeeded or not.
+      // We use 68 because the calldata length is 4 + 32 * 2.
+      callStatus := call(gas(), token, 0, freeMemoryPointer, 68, 0, 0)
     }
 
-    /*///////////////////////////////////////////////////////////////
+    if (!didLastOptionalReturnCallSucceed(callStatus)) revert FailedTransfer();
+  }
+
+  /*///////////////////////////////////////////////////////////////
                          INTERNAL HELPER LOGIC
     //////////////////////////////////////////////////////////////*/
 
-    function didLastOptionalReturnCallSucceed(bool callStatus) private pure returns (bool success) {
-        assembly {
-            // If the call reverted:
-            if iszero(callStatus) {
-                // Copy the revert message into memory.
-                returndatacopy(0, 0, returndatasize())
-
-                // Revert with the same message.
-                revert(0, returndatasize())
-            }
-
-            switch returndatasize()
-            case 32 {
-                // Copy the return data into memory.
-                returndatacopy(0, 0, returndatasize())
-
-                // Set success to whether it returned true.
-                success := iszero(iszero(mload(0)))
-            }
-            case 0 {
-                // There was no return data.
-                success := 1
-            }
-            default {
-                // It returned some malformed input.
-                success := 0
-            }
-        }
+  function didLastOptionalReturnCallSucceed(bool callStatus) private pure returns (bool success) {
+    assembly {
+      // If the call reverted:
+      if iszero(callStatus) {
+        // Copy the revert message into memory.
+        returndatacopy(0, 0, returndatasize())
+
+        // Revert with the same message.
+        revert(0, returndatasize())
+      }
+
+      switch returndatasize()
+      case 32 {
+        // Copy the return data into memory.
+        returndatacopy(0, 0, returndatasize())
+
+        // Set success to whether it returned true.
+        success := iszero(iszero(mload(0)))
+      }
+      case 0 {
+        // There was no return data.
+        success := 1
+      }
+      default {
+        // It returned some malformed input.
+        success := 0
+      }
     }
+  }
 }
diff --git a/src/periphery/LendgineRouter.sol b/src/periphery/LendgineRouter.sol
index b59a7a3..f478b60 100644
--- a/src/periphery/LendgineRouter.sol
+++ b/src/periphery/LendgineRouter.sol
@@ -2,7 +2,7 @@
 pragma solidity ^0.8.4;
 
 import { Lendgine } from "../core/Lendgine.sol"; // TODO: use interface
-import { Payment } from "./base/Payment.sol";
+import { Payment } from "./Payment.sol";
 import { SwapHelper } from "./SwapHelper.sol";
 
 import { FullMath } from "../libraries/FullMath.sol";
@@ -13,280 +13,267 @@ import { SafeCast } from "../libraries/SafeCast.sol";
 import { SafeTransferLib } from "../libraries/SafeTransferLib.sol";
 
 contract LendgineRouter is SwapHelper, Payment, IMintCallback, IPairMintCallback {
-    /*//////////////////////////////////////////////////////////////
+  /*//////////////////////////////////////////////////////////////
                                  EVENTS
     //////////////////////////////////////////////////////////////*/
 
-    event Mint(address indexed from, address indexed lendgine, uint256 collateral, uint256 shares, address indexed to);
+  event Mint(address indexed from, address indexed lendgine, uint256 collateral, uint256 shares, address indexed to);
 
-    event Burn(address indexed from, address indexed lendgine, uint256 collateral, uint256 shares, address indexed to);
+  event Burn(address indexed from, address indexed lendgine, uint256 collateral, uint256 shares, address indexed to);
 
-    /*//////////////////////////////////////////////////////////////
+  /*//////////////////////////////////////////////////////////////
                                  ERRORS
     //////////////////////////////////////////////////////////////*/
 
-    error LivelinessError();
+  error LivelinessError();
 
-    error ValidationError();
+  error ValidationError();
 
-    error AmountError();
+  error AmountError();
 
-    /*//////////////////////////////////////////////////////////////
+  /*//////////////////////////////////////////////////////////////
                                 STORAGE
     //////////////////////////////////////////////////////////////*/
 
-    address public immutable factory;
+  address public immutable factory;
 
-    /*//////////////////////////////////////////////////////////////
+  /*//////////////////////////////////////////////////////////////
                               CONSTRUCTOR
     //////////////////////////////////////////////////////////////*/
 
-    constructor(
-        address _factory,
-        address _uniswapV2Factory,
-        address _uniswapV3Factory,
-        address _weth
-    ) SwapHelper(_uniswapV2Factory, _uniswapV3Factory) Payment(_weth) {
-        factory = _factory;
-    }
-
-    /*//////////////////////////////////////////////////////////////
+  constructor(
+    address _factory,
+    address _uniswapV2Factory,
+    address _uniswapV3Factory,
+    address _weth
+  )
+    SwapHelper(_uniswapV2Factory, _uniswapV3Factory)
+    Payment(_weth)
+  {
+    factory = _factory;
+  }
+
+  /*//////////////////////////////////////////////////////////////
                            LIVELINESS MODIFIER
     //////////////////////////////////////////////////////////////*/
 
-    modifier checkDeadline(uint256 deadline) {
-        if (deadline < block.timestamp) revert LivelinessError();
-        _;
-    }
+  modifier checkDeadline(uint256 deadline) {
+    if (deadline < block.timestamp) revert LivelinessError();
+    _;
+  }
 
-    /*//////////////////////////////////////////////////////////////
+  /*//////////////////////////////////////////////////////////////
                                MINT LOGIC
     //////////////////////////////////////////////////////////////*/
 
-    struct MintCallbackData {
-        address token0;
-        address token1;
-        uint256 token0Exp;
-        uint256 token1Exp;
-        uint256 upperBound;
-        uint256 collateralMax;
-        SwapType swapType;
-        bytes swapExtraData;
-        address payer;
-    }
-
-    function mintCallback(
-        uint256 collateralTotal,
-        uint256 amount0,
-        uint256 amount1,
-        uint256,
-        bytes calldata data
-    ) external override {
-        MintCallbackData memory decoded = abi.decode(data, (MintCallbackData));
-
-        address lendgine = LendgineAddress.computeAddress(
-            factory,
-            decoded.token0,
-            decoded.token1,
-            decoded.token0Exp,
-            decoded.token1Exp,
-            decoded.upperBound
-        );
-        if (lendgine != msg.sender) revert ValidationError();
-
-        // swap token0 to token1
-        uint256 collateralSwap = swap(
-            decoded.swapType,
-            SwapParams({
-                tokenIn: decoded.token0,
-                tokenOut: decoded.token1,
-                amount: SafeCast.toInt256(amount0),
-                recipient: msg.sender
-            }),
-            decoded.swapExtraData
-        );
-
-        // send token1 back
-        SafeTransferLib.safeTransfer(decoded.token1, msg.sender, amount1);
-
-        // pull the rest of tokens from the user
-        uint256 collateralIn = collateralTotal - amount1 - collateralSwap;
-        if (collateralIn > decoded.collateralMax) revert AmountError();
-
-        pay(decoded.token1, decoded.payer, msg.sender, collateralIn);
-    }
-
-    struct MintParams {
-        address token0;
-        address token1;
-        uint256 token0Exp;
-        uint256 token1Exp;
-        uint256 upperBound;
-        uint256 amountIn;
-        uint256 amountBorrow;
-        uint256 sharesMin;
-        SwapType swapType;
-        bytes swapExtraData;
-        address recipient;
-        uint256 deadline;
-    }
-
-    function mint(MintParams calldata params) external payable checkDeadline(params.deadline) returns (uint256 shares) {
-        address lendgine = LendgineAddress.computeAddress(
-            factory,
-            params.token0,
-            params.token1,
-            params.token0Exp,
-            params.token1Exp,
-            params.upperBound
-        );
-
-        shares = Lendgine(lendgine).mint(
-            address(this),
-            params.amountIn + params.amountBorrow,
-            abi.encode(
-                MintCallbackData({
-                    token0: params.token0,
-                    token1: params.token1,
-                    token0Exp: params.token0Exp,
-                    token1Exp: params.token1Exp,
-                    upperBound: params.upperBound,
-                    collateralMax: params.amountIn,
-                    swapType: params.swapType,
-                    swapExtraData: params.swapExtraData,
-                    payer: msg.sender
-                })
-            )
-        );
-        if (shares < params.sharesMin) revert AmountError();
-
-        Lendgine(lendgine).transfer(params.recipient, shares);
-
-        emit Mint(msg.sender, lendgine, params.amountIn, shares, params.recipient);
-    }
-
-    /*//////////////////////////////////////////////////////////////
+  struct MintCallbackData {
+    address token0;
+    address token1;
+    uint256 token0Exp;
+    uint256 token1Exp;
+    uint256 upperBound;
+    uint256 collateralMax;
+    SwapType swapType;
+    bytes swapExtraData;
+    address payer;
+  }
+
+  function mintCallback(
+    uint256 collateralTotal,
+    uint256 amount0,
+    uint256 amount1,
+    uint256,
+    bytes calldata data
+  )
+    external
+    override
+  {
+    MintCallbackData memory decoded = abi.decode(data, (MintCallbackData));
+
+    address lendgine = LendgineAddress.computeAddress(
+      factory, decoded.token0, decoded.token1, decoded.token0Exp, decoded.token1Exp, decoded.upperBound
+    );
+    if (lendgine != msg.sender) revert ValidationError();
+
+    // swap token0 to token1
+    uint256 collateralSwap = swap(
+      decoded.swapType,
+      SwapParams({
+        tokenIn: decoded.token0,
+        tokenOut: decoded.token1,
+        amount: SafeCast.toInt256(amount0),
+        recipient: msg.sender
+      }),
+      decoded.swapExtraData
+    );
+
+    // send token1 back
+    SafeTransferLib.safeTransfer(decoded.token1, msg.sender, amount1);
+
+    // pull the rest of tokens from the user
+    uint256 collateralIn = collateralTotal - amount1 - collateralSwap;
+    if (collateralIn > decoded.collateralMax) revert AmountError();
+
+    pay(decoded.token1, decoded.payer, msg.sender, collateralIn);
+  }
+
+  struct MintParams {
+    address token0;
+    address token1;
+    uint256 token0Exp;
+    uint256 token1Exp;
+    uint256 upperBound;
+    uint256 amountIn;
+    uint256 amountBorrow;
+    uint256 sharesMin;
+    SwapType swapType;
+    bytes swapExtraData;
+    address recipient;
+    uint256 deadline;
+  }
+
+  function mint(MintParams calldata params) external payable checkDeadline(params.deadline) returns (uint256 shares) {
+    address lendgine = LendgineAddress.computeAddress(
+      factory, params.token0, params.token1, params.token0Exp, params.token1Exp, params.upperBound
+    );
+
+    shares = Lendgine(lendgine).mint(
+      address(this),
+      params.amountIn + params.amountBorrow,
+      abi.encode(
+        MintCallbackData({
+          token0: params.token0,
+          token1: params.token1,
+          token0Exp: params.token0Exp,
+          token1Exp: params.token1Exp,
+          upperBound: params.upperBound,
+          collateralMax: params.amountIn,
+          swapType: params.swapType,
+          swapExtraData: params.swapExtraData,
+          payer: msg.sender
+        })
+      )
+    );
+    if (shares < params.sharesMin) revert AmountError();
+
+    Lendgine(lendgine).transfer(params.recipient, shares);
+
+    emit Mint(msg.sender, lendgine, params.amountIn, shares, params.recipient);
+  }
+
+  /*//////////////////////////////////////////////////////////////
                                BURN LOGIC
     //////////////////////////////////////////////////////////////*/
 
-    struct PairMintCallbackData {
-        address token0;
-        address token1;
-        uint256 token0Exp;
-        uint256 token1Exp;
-        uint256 upperBound;
-        uint256 collateralMin;
-        uint256 amount0Min;
-        uint256 amount1Min;
-        SwapType swapType;
-        bytes swapExtraData;
-        address recipient;
-    }
-
-    function pairMintCallback(uint256 liquidity, bytes calldata data) external override {
-        PairMintCallbackData memory decoded = abi.decode(data, (PairMintCallbackData));
-
-        address lendgine = LendgineAddress.computeAddress(
-            factory,
-            decoded.token0,
-            decoded.token1,
-            decoded.token0Exp,
-            decoded.token1Exp,
-            decoded.upperBound
-        );
-        if (lendgine != msg.sender) revert ValidationError();
-
-        // determine amounts
-        uint256 r0 = Lendgine(msg.sender).reserve0();
-        uint256 r1 = Lendgine(msg.sender).reserve1();
-        uint256 totalLiquidity = Lendgine(msg.sender).totalLiquidity();
-
-        uint256 amount0;
-        uint256 amount1;
-
-        if (totalLiquidity == 0) {
-            amount0 = decoded.amount0Min;
-            amount1 = decoded.amount1Min;
-        } else {
-            amount0 = FullMath.mulDivRoundingUp(liquidity, r0, totalLiquidity);
-            amount1 = FullMath.mulDivRoundingUp(liquidity, r1, totalLiquidity);
-        }
-
-        if (amount0 < decoded.amount0Min || amount1 < decoded.amount1Min) revert AmountError();
-
-        // swap for token0
-        uint256 collateralSwapped = swap(
-            decoded.swapType,
-            SwapParams({
-                tokenIn: decoded.token1,
-                tokenOut: decoded.token0,
-                amount: -SafeCast.toInt256(amount0),
-                recipient: msg.sender
-            }),
-            decoded.swapExtraData
-        );
-
-        // pay token1
-        SafeTransferLib.safeTransfer(decoded.token1, msg.sender, amount1);
-
-        // determine remaining and send to recipient
-        uint256 collateralTotal = Lendgine(msg.sender).convertLiquidityToCollateral(liquidity);
-        uint256 collateralOut = collateralTotal - amount1 - collateralSwapped;
-        if (collateralOut < decoded.collateralMin) revert AmountError();
-
-        if (decoded.recipient != address(this))
-            SafeTransferLib.safeTransfer(decoded.token1, decoded.recipient, collateralOut);
-    }
-
-    struct BurnParams {
-        address token0;
-        address token1;
-        uint256 token0Exp;
-        uint256 token1Exp;
-        uint256 upperBound;
-        uint256 shares;
-        uint256 collateralMin;
-        uint256 amount0Min;
-        uint256 amount1Min;
-        SwapType swapType;
-        bytes swapExtraData;
-        address recipient;
-        uint256 deadline;
+  struct PairMintCallbackData {
+    address token0;
+    address token1;
+    uint256 token0Exp;
+    uint256 token1Exp;
+    uint256 upperBound;
+    uint256 collateralMin;
+    uint256 amount0Min;
+    uint256 amount1Min;
+    SwapType swapType;
+    bytes swapExtraData;
+    address recipient;
+  }
+
+  function pairMintCallback(uint256 liquidity, bytes calldata data) external override {
+    PairMintCallbackData memory decoded = abi.decode(data, (PairMintCallbackData));
+
+    address lendgine = LendgineAddress.computeAddress(
+      factory, decoded.token0, decoded.token1, decoded.token0Exp, decoded.token1Exp, decoded.upperBound
+    );
+    if (lendgine != msg.sender) revert ValidationError();
+
+    // determine amounts
+    uint256 r0 = Lendgine(msg.sender).reserve0();
+    uint256 r1 = Lendgine(msg.sender).reserve1();
+    uint256 totalLiquidity = Lendgine(msg.sender).totalLiquidity();
+
+    uint256 amount0;
+    uint256 amount1;
+
+    if (totalLiquidity == 0) {
+      amount0 = decoded.amount0Min;
+      amount1 = decoded.amount1Min;
+    } else {
+      amount0 = FullMath.mulDivRoundingUp(liquidity, r0, totalLiquidity);
+      amount1 = FullMath.mulDivRoundingUp(liquidity, r1, totalLiquidity);
     }
 
-    function burn(BurnParams calldata params) external payable checkDeadline(params.deadline) returns (uint256 amount) {
-        address lendgine = LendgineAddress.computeAddress(
-            factory,
-            params.token0,
-            params.token1,
-            params.token0Exp,
-            params.token1Exp,
-            params.upperBound
-        );
-
-        address recipient = params.recipient == address(0) ? address(this) : params.recipient;
-
-        Lendgine(lendgine).transferFrom(msg.sender, lendgine, params.shares);
-
-        amount = Lendgine(lendgine).burn(
-            address(this),
-            abi.encode(
-                PairMintCallbackData({
-                    token0: params.token0,
-                    token1: params.token1,
-                    token0Exp: params.token0Exp,
-                    token1Exp: params.token1Exp,
-                    upperBound: params.upperBound,
-                    collateralMin: params.collateralMin,
-                    amount0Min: params.amount0Min,
-                    amount1Min: params.amount1Min,
-                    swapType: params.swapType,
-                    swapExtraData: params.swapExtraData,
-                    recipient: recipient
-                })
-            )
-        );
-
-        emit Burn(msg.sender, lendgine, amount, params.shares, recipient);
+    if (amount0 < decoded.amount0Min || amount1 < decoded.amount1Min) revert AmountError();
+
+    // swap for token0
+    uint256 collateralSwapped = swap(
+      decoded.swapType,
+      SwapParams({
+        tokenIn: decoded.token1,
+        tokenOut: decoded.token0,
+        amount: -SafeCast.toInt256(amount0),
+        recipient: msg.sender
+      }),
+      decoded.swapExtraData
+    );
+
+    // pay token1
+    SafeTransferLib.safeTransfer(decoded.token1, msg.sender, amount1);
+
+    // determine remaining and send to recipient
+    uint256 collateralTotal = Lendgine(msg.sender).convertLiquidityToCollateral(liquidity);
+    uint256 collateralOut = collateralTotal - amount1 - collateralSwapped;
+    if (collateralOut < decoded.collateralMin) revert AmountError();
+
+    if (decoded.recipient != address(this)) {
+      SafeTransferLib.safeTransfer(decoded.token1, decoded.recipient, collateralOut);
     }
+  }
+
+  struct BurnParams {
+    address token0;
+    address token1;
+    uint256 token0Exp;
+    uint256 token1Exp;
+    uint256 upperBound;
+    uint256 shares;
+    uint256 collateralMin;
+    uint256 amount0Min;
+    uint256 amount1Min;
+    SwapType swapType;
+    bytes swapExtraData;
+    address recipient;
+    uint256 deadline;
+  }
+
+  function burn(BurnParams calldata params) external payable checkDeadline(params.deadline) returns (uint256 amount) {
+    address lendgine = LendgineAddress.computeAddress(
+      factory, params.token0, params.token1, params.token0Exp, params.token1Exp, params.upperBound
+    );
+
+    address recipient = params.recipient == address(0) ? address(this) : params.recipient;
+
+    Lendgine(lendgine).transferFrom(msg.sender, lendgine, params.shares);
+
+    amount = Lendgine(lendgine).burn(
+      address(this),
+      abi.encode(
+        PairMintCallbackData({
+          token0: params.token0,
+          token1: params.token1,
+          token0Exp: params.token0Exp,
+          token1Exp: params.token1Exp,
+          upperBound: params.upperBound,
+          collateralMin: params.collateralMin,
+          amount0Min: params.amount0Min,
+          amount1Min: params.amount1Min,
+          swapType: params.swapType,
+          swapExtraData: params.swapExtraData,
+          recipient: recipient
+        })
+      )
+    );
+
+    emit Burn(msg.sender, lendgine, amount, params.shares, recipient);
+  }
 }
diff --git a/src/periphery/LiquidityManager.sol b/src/periphery/LiquidityManager.sol
index 03f931a..d8ff692 100644
--- a/src/periphery/LiquidityManager.sol
+++ b/src/periphery/LiquidityManager.sol
@@ -2,7 +2,7 @@
 pragma solidity ^0.8.4;
 
 import { Lendgine } from "../core/Lendgine.sol"; // TODO: use interface
-import { Payment } from "./base/Payment.sol";
+import { Payment } from "./Payment.sol";
 
 import { IPairMintCallback } from "../core/interfaces/callback/IPairMintCallback.sol";
 
@@ -10,261 +10,234 @@ import { FullMath } from "../libraries/FullMath.sol";
 import { LendgineAddress } from "./libraries/LendgineAddress.sol";
 
 contract LiquidityManager is Payment, IPairMintCallback {
-    /*//////////////////////////////////////////////////////////////
+  /*//////////////////////////////////////////////////////////////
                                  EVENTS
     //////////////////////////////////////////////////////////////*/
-    event AddLiquidity(
-        address indexed from,
-        address indexed lendgine,
-        uint256 liquidity,
-        uint256 size,
-        uint256 amount0,
-        uint256 amount1,
-        address indexed to
-    );
-
-    event RemoveLiquidity(
-        address indexed from,
-        address indexed lendgine,
-        uint256 liquidity,
-        uint256 size,
-        uint256 amount0,
-        uint256 amount1,
-        address indexed to
-    );
-
-    event Collect(address indexed from, address indexed lendgine, uint256 amount, address indexed to);
-
-    /*//////////////////////////////////////////////////////////////
+  event AddLiquidity(
+    address indexed from,
+    address indexed lendgine,
+    uint256 liquidity,
+    uint256 size,
+    uint256 amount0,
+    uint256 amount1,
+    address indexed to
+  );
+
+  event RemoveLiquidity(
+    address indexed from,
+    address indexed lendgine,
+    uint256 liquidity,
+    uint256 size,
+    uint256 amount0,
+    uint256 amount1,
+    address indexed to
+  );
+
+  event Collect(address indexed from, address indexed lendgine, uint256 amount, address indexed to);
+
+  /*//////////////////////////////////////////////////////////////
                                  ERRORS
     //////////////////////////////////////////////////////////////*/
 
-    error LivelinessError();
+  error LivelinessError();
 
-    error AmountError();
+  error AmountError();
 
-    error ValidationError();
+  error ValidationError();
 
-    error PositionInvalidError();
+  error PositionInvalidError();
 
-    error CollectError();
+  error CollectError();
 
-    /*//////////////////////////////////////////////////////////////
+  /*//////////////////////////////////////////////////////////////
                                 STORAGE
     //////////////////////////////////////////////////////////////*/
 
-    address public immutable factory;
+  address public immutable factory;
 
-    struct Position {
-        uint256 size;
-        uint256 rewardPerPositionPaid;
-        uint256 tokensOwed;
-    }
+  struct Position {
+    uint256 size;
+    uint256 rewardPerPositionPaid;
+    uint256 tokensOwed;
+  }
 
-    // owner to lendgine to position
-    mapping(address => mapping(address => Position)) public positions;
+  // owner to lendgine to position
+  mapping(address => mapping(address => Position)) public positions;
 
-    /*//////////////////////////////////////////////////////////////
+  /*//////////////////////////////////////////////////////////////
                               CONSTRUCTOR
     //////////////////////////////////////////////////////////////*/
 
-    constructor(address _factory, address _weth) Payment(_weth) {
-        factory = _factory;
-    }
+  constructor(address _factory, address _weth) Payment(_weth) {
+    factory = _factory;
+  }
 
-    /*//////////////////////////////////////////////////////////////
+  /*//////////////////////////////////////////////////////////////
                            LIVELINESS MODIFIER
     //////////////////////////////////////////////////////////////*/
 
-    modifier checkDeadline(uint256 deadline) {
-        if (deadline < block.timestamp) revert LivelinessError();
-        _;
-    }
+  modifier checkDeadline(uint256 deadline) {
+    if (deadline < block.timestamp) revert LivelinessError();
+    _;
+  }
 
-    /*//////////////////////////////////////////////////////////////
+  /*//////////////////////////////////////////////////////////////
                                 CALLBACK
     //////////////////////////////////////////////////////////////*/
 
-    struct PairMintCallbackData {
-        address token0;
-        address token1;
-        uint256 token0Exp;
-        uint256 token1Exp;
-        uint256 upperBound;
-        uint256 amount0;
-        uint256 amount1;
-        address payer;
-    }
+  struct PairMintCallbackData {
+    address token0;
+    address token1;
+    uint256 token0Exp;
+    uint256 token1Exp;
+    uint256 upperBound;
+    uint256 amount0;
+    uint256 amount1;
+    address payer;
+  }
+
+  function pairMintCallback(uint256, bytes calldata data) external {
+    PairMintCallbackData memory decoded = abi.decode(data, (PairMintCallbackData));
+
+    address lendgine = LendgineAddress.computeAddress(
+      factory, decoded.token0, decoded.token1, decoded.token0Exp, decoded.token1Exp, decoded.upperBound
+    );
+    if (lendgine != msg.sender) revert ValidationError();
 
-    function pairMintCallback(uint256, bytes calldata data) external {
-        PairMintCallbackData memory decoded = abi.decode(data, (PairMintCallbackData));
-
-        address lendgine = LendgineAddress.computeAddress(
-            factory,
-            decoded.token0,
-            decoded.token1,
-            decoded.token0Exp,
-            decoded.token1Exp,
-            decoded.upperBound
-        );
-        if (lendgine != msg.sender) revert ValidationError();
-
-        if (decoded.amount0 > 0) pay(decoded.token0, decoded.payer, msg.sender, decoded.amount0);
-        if (decoded.amount1 > 0) pay(decoded.token1, decoded.payer, msg.sender, decoded.amount1);
-    }
+    if (decoded.amount0 > 0) pay(decoded.token0, decoded.payer, msg.sender, decoded.amount0);
+    if (decoded.amount1 > 0) pay(decoded.token1, decoded.payer, msg.sender, decoded.amount1);
+  }
 
-    /*//////////////////////////////////////////////////////////////
+  /*//////////////////////////////////////////////////////////////
                         LIQUIDITY MANAGER LOGIC
     //////////////////////////////////////////////////////////////*/
 
-    struct AddLiquidityParams {
-        address token0;
-        address token1;
-        uint256 token0Exp;
-        uint256 token1Exp;
-        uint256 upperBound;
-        uint256 liquidity;
-        uint256 amount0Min;
-        uint256 amount1Min;
-        uint256 sizeMin;
-        address recipient;
-        uint256 deadline;
-    }
+  struct AddLiquidityParams {
+    address token0;
+    address token1;
+    uint256 token0Exp;
+    uint256 token1Exp;
+    uint256 upperBound;
+    uint256 liquidity;
+    uint256 amount0Min;
+    uint256 amount1Min;
+    uint256 sizeMin;
+    address recipient;
+    uint256 deadline;
+  }
+
+  function addLiquidity(AddLiquidityParams calldata params) external payable checkDeadline(params.deadline) {
+    address lendgine = LendgineAddress.computeAddress(
+      factory, params.token0, params.token1, params.token0Exp, params.token1Exp, params.upperBound
+    );
 
-    function addLiquidity(AddLiquidityParams calldata params) external payable checkDeadline(params.deadline) {
-        address lendgine = LendgineAddress.computeAddress(
-            factory,
-            params.token0,
-            params.token1,
-            params.token0Exp,
-            params.token1Exp,
-            params.upperBound
-        );
-
-        uint256 r0 = Lendgine(lendgine).reserve0();
-        uint256 r1 = Lendgine(lendgine).reserve1();
-        uint256 totalLiquidity = Lendgine(lendgine).totalLiquidity();
-
-        uint256 amount0;
-        uint256 amount1;
-
-        if (totalLiquidity == 0) {
-            amount0 = params.amount0Min;
-            amount1 = params.amount1Min;
-        } else {
-            amount0 = FullMath.mulDivRoundingUp(params.liquidity, r0, totalLiquidity);
-            amount1 = FullMath.mulDivRoundingUp(params.liquidity, r1, totalLiquidity);
-        }
-
-        if (amount0 < params.amount0Min || amount1 < params.amount1Min) revert AmountError();
-
-        uint256 size = Lendgine(lendgine).deposit(
-            address(this),
-            params.liquidity,
-            abi.encode(
-                PairMintCallbackData({
-                    token0: params.token0,
-                    token1: params.token1,
-                    token0Exp: params.token0Exp,
-                    token1Exp: params.token1Exp,
-                    upperBound: params.upperBound,
-                    amount0: amount0,
-                    amount1: amount1,
-                    payer: msg.sender
-                })
-            )
-        );
-        if (size < params.sizeMin) revert AmountError();
-
-        Position memory position = positions[params.recipient][lendgine]; // SLOAD
-
-        (, uint256 rewardPerPositionPaid, ) = Lendgine(lendgine).positions(address(this));
-        position.tokensOwed += FullMath.mulDiv(
-            position.size,
-            rewardPerPositionPaid - position.rewardPerPositionPaid,
-            1e18
-        );
-        position.rewardPerPositionPaid = rewardPerPositionPaid;
-        position.size += size;
-
-        positions[params.recipient][lendgine] = position; // SSTORE
-
-        emit AddLiquidity(msg.sender, lendgine, params.liquidity, size, amount0, amount1, params.recipient);
-    }
+    uint256 r0 = Lendgine(lendgine).reserve0();
+    uint256 r1 = Lendgine(lendgine).reserve1();
+    uint256 totalLiquidity = Lendgine(lendgine).totalLiquidity();
+
+    uint256 amount0;
+    uint256 amount1;
 
-    struct RemoveLiquidityParams {
-        address token0;
-        address token1;
-        uint256 token0Exp;
-        uint256 token1Exp;
-        uint256 upperBound;
-        uint256 size;
-        uint256 amount0Min;
-        uint256 amount1Min;
-        address recipient;
-        uint256 deadline;
+    if (totalLiquidity == 0) {
+      amount0 = params.amount0Min;
+      amount1 = params.amount1Min;
+    } else {
+      amount0 = FullMath.mulDivRoundingUp(params.liquidity, r0, totalLiquidity);
+      amount1 = FullMath.mulDivRoundingUp(params.liquidity, r1, totalLiquidity);
     }
 
-    function removeLiquidity(RemoveLiquidityParams calldata params) external payable checkDeadline(params.deadline) {
-        address lendgine = LendgineAddress.computeAddress(
-            factory,
-            params.token0,
-            params.token1,
-            params.token0Exp,
-            params.token1Exp,
-            params.upperBound
-        );
+    if (amount0 < params.amount0Min || amount1 < params.amount1Min) revert AmountError();
+
+    uint256 size = Lendgine(lendgine).deposit(
+      address(this),
+      params.liquidity,
+      abi.encode(
+        PairMintCallbackData({
+          token0: params.token0,
+          token1: params.token1,
+          token0Exp: params.token0Exp,
+          token1Exp: params.token1Exp,
+          upperBound: params.upperBound,
+          amount0: amount0,
+          amount1: amount1,
+          payer: msg.sender
+        })
+      )
+    );
+    if (size < params.sizeMin) revert AmountError();
+
+    Position memory position = positions[params.recipient][lendgine]; // SLOAD
+
+    (, uint256 rewardPerPositionPaid,) = Lendgine(lendgine).positions(address(this));
+    position.tokensOwed += FullMath.mulDiv(position.size, rewardPerPositionPaid - position.rewardPerPositionPaid, 1e18);
+    position.rewardPerPositionPaid = rewardPerPositionPaid;
+    position.size += size;
+
+    positions[params.recipient][lendgine] = position; // SSTORE
+
+    emit AddLiquidity(msg.sender, lendgine, params.liquidity, size, amount0, amount1, params.recipient);
+  }
+
+  struct RemoveLiquidityParams {
+    address token0;
+    address token1;
+    uint256 token0Exp;
+    uint256 token1Exp;
+    uint256 upperBound;
+    uint256 size;
+    uint256 amount0Min;
+    uint256 amount1Min;
+    address recipient;
+    uint256 deadline;
+  }
+
+  function removeLiquidity(RemoveLiquidityParams calldata params) external payable checkDeadline(params.deadline) {
+    address lendgine = LendgineAddress.computeAddress(
+      factory, params.token0, params.token1, params.token0Exp, params.token1Exp, params.upperBound
+    );
 
-        address recipient = params.recipient == address(0) ? address(this) : params.recipient;
+    address recipient = params.recipient == address(0) ? address(this) : params.recipient;
 
-        (uint256 amount0, uint256 amount1, uint256 liquidity) = Lendgine(lendgine).withdraw(recipient, params.size);
-        if (amount0 < params.amount0Min || amount1 < params.amount1Min) revert AmountError();
+    (uint256 amount0, uint256 amount1, uint256 liquidity) = Lendgine(lendgine).withdraw(recipient, params.size);
+    if (amount0 < params.amount0Min || amount1 < params.amount1Min) revert AmountError();
 
-        Position memory position = positions[msg.sender][lendgine]; // SLOAD
+    Position memory position = positions[msg.sender][lendgine]; // SLOAD
 
-        (, uint256 rewardPerPositionPaid, ) = Lendgine(lendgine).positions(address(this));
-        position.tokensOwed += FullMath.mulDiv(
-            position.size,
-            rewardPerPositionPaid - position.rewardPerPositionPaid,
-            1e18
-        );
-        position.rewardPerPositionPaid = rewardPerPositionPaid;
-        position.size -= params.size;
+    (, uint256 rewardPerPositionPaid,) = Lendgine(lendgine).positions(address(this));
+    position.tokensOwed += FullMath.mulDiv(position.size, rewardPerPositionPaid - position.rewardPerPositionPaid, 1e18);
+    position.rewardPerPositionPaid = rewardPerPositionPaid;
+    position.size -= params.size;
 
-        positions[msg.sender][lendgine] = position; // SSTORE
+    positions[msg.sender][lendgine] = position; // SSTORE
 
-        emit RemoveLiquidity(msg.sender, lendgine, liquidity, params.size, amount0, amount1, recipient);
-    }
+    emit RemoveLiquidity(msg.sender, lendgine, liquidity, params.size, amount0, amount1, recipient);
+  }
 
-    struct CollectParams {
-        address lendgine;
-        address recipient;
-        uint256 amountRequested;
-    }
+  struct CollectParams {
+    address lendgine;
+    address recipient;
+    uint256 amountRequested;
+  }
 
-    function collect(CollectParams calldata params) external payable returns (uint256 amount) {
-        Lendgine(params.lendgine).accruePositionInterest();
+  function collect(CollectParams calldata params) external payable returns (uint256 amount) {
+    Lendgine(params.lendgine).accruePositionInterest();
 
-        address recipient = params.recipient == address(0) ? address(this) : params.recipient;
+    address recipient = params.recipient == address(0) ? address(this) : params.recipient;
 
-        Position memory position = positions[msg.sender][params.lendgine]; // SLOAD
+    Position memory position = positions[msg.sender][params.lendgine]; // SLOAD
 
-        (, uint256 rewardPerPositionPaid, ) = Lendgine(params.lendgine).positions(address(this));
-        position.tokensOwed += FullMath.mulDiv(
-            position.size,
-            rewardPerPositionPaid - position.rewardPerPositionPaid,
-            1e18
-        );
-        position.rewardPerPositionPaid = rewardPerPositionPaid;
+    (, uint256 rewardPerPositionPaid,) = Lendgine(params.lendgine).positions(address(this));
+    position.tokensOwed += FullMath.mulDiv(position.size, rewardPerPositionPaid - position.rewardPerPositionPaid, 1e18);
+    position.rewardPerPositionPaid = rewardPerPositionPaid;
 
-        amount = params.amountRequested > position.tokensOwed ? position.tokensOwed : params.amountRequested;
-        position.tokensOwed -= amount;
+    amount = params.amountRequested > position.tokensOwed ? position.tokensOwed : params.amountRequested;
+    position.tokensOwed -= amount;
 
-        positions[msg.sender][params.lendgine] = position; // SSTORE
+    positions[msg.sender][params.lendgine] = position; // SSTORE
 
-        uint256 collectAmount = Lendgine(params.lendgine).collect(recipient, amount);
-        if (collectAmount != amount) revert CollectError(); // extra check for safety
+    uint256 collectAmount = Lendgine(params.lendgine).collect(recipient, amount);
+    if (collectAmount != amount) revert CollectError(); // extra check for safety
 
-        emit Collect(msg.sender, params.lendgine, amount, recipient);
-    }
+    emit Collect(msg.sender, params.lendgine, amount, recipient);
+  }
 }
diff --git a/src/periphery/Payment.sol b/src/periphery/Payment.sol
new file mode 100644
index 0000000..ef1b1c3
--- /dev/null
+++ b/src/periphery/Payment.sol
@@ -0,0 +1,65 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+pragma solidity ^0.8.4;
+
+import { IWETH9 } from "./interfaces/IWETH9.sol";
+
+import { SafeTransferLib } from "./../libraries/SafeTransferLib.sol";
+import { Balance } from "./../libraries/Balance.sol";
+
+/// @title   Payment contract
+/// @author  https://github.com/Uniswap/v3-periphery/blob/main/contracts/base/PeripheryPayments.sol
+/// @notice  Functions to ease deposits and withdrawals of ETH
+abstract contract Payment {
+  address public immutable weth;
+
+  error InsufficientOutputError();
+
+  constructor(address _weth) {
+    weth = _weth;
+  }
+
+  receive() external payable {
+    require(msg.sender == weth, "Not WETH9");
+  }
+
+  function unwrapWETH(uint256 amountMinimum, address recipient) public payable {
+    uint256 balanceWETH = Balance.balance(weth);
+    if (balanceWETH < amountMinimum) revert InsufficientOutputError();
+
+    if (balanceWETH > 0) {
+      IWETH9(weth).withdraw(balanceWETH);
+      SafeTransferLib.safeTransferETH(recipient, balanceWETH);
+    }
+  }
+
+  function sweepToken(address token, uint256 amountMinimum, address recipient) public payable {
+    uint256 balanceToken = Balance.balance(token);
+    if (balanceToken < amountMinimum) revert InsufficientOutputError();
+
+    if (balanceToken > 0) {
+      SafeTransferLib.safeTransfer(token, recipient, balanceToken);
+    }
+  }
+
+  function refundETH() external payable {
+    if (address(this).balance > 0) SafeTransferLib.safeTransferETH(msg.sender, address(this).balance);
+  }
+
+  /// @param token The token to pay
+  /// @param payer The entity that must pay
+  /// @param recipient The entity that will receive payment
+  /// @param value The amount to pay
+  function pay(address token, address payer, address recipient, uint256 value) internal {
+    if (token == weth && address(this).balance >= value) {
+      // pay with WETH
+      IWETH9(weth).deposit{value: value}(); // wrap only what is needed to pay
+      SafeTransferLib.safeTransfer(weth, recipient, value);
+    } else if (payer == address(this)) {
+      // pay with tokens already in the contract (for the exact input multihop case)
+      SafeTransferLib.safeTransfer(token, recipient, value);
+    } else {
+      // pull payment
+      SafeTransferLib.safeTransferFrom(token, payer, recipient, value);
+    }
+  }
+}
diff --git a/src/periphery/SwapHelper.sol b/src/periphery/SwapHelper.sol
index dc8c129..c88e54d 100644
--- a/src/periphery/SwapHelper.sol
+++ b/src/periphery/SwapHelper.sol
@@ -11,121 +11,103 @@ import { TickMath } from "./UniswapV3/libraries/TickMath.sol";
 import { UniswapV2Library } from "./UniswapV2/libraries/UniswapV2Library.sol";
 
 abstract contract SwapHelper is IUniswapV3SwapCallback {
-    /*//////////////////////////////////////////////////////////////
+  /*//////////////////////////////////////////////////////////////
                                 STORAGE
     //////////////////////////////////////////////////////////////*/
 
-    address public immutable uniswapV2Factory;
+  address public immutable uniswapV2Factory;
 
-    address public immutable uniswapV3Factory;
+  address public immutable uniswapV3Factory;
 
-    /*//////////////////////////////////////////////////////////////
+  /*//////////////////////////////////////////////////////////////
                               CONSTRUCTOR
     //////////////////////////////////////////////////////////////*/
 
-    constructor(address _uniswapV2Factory, address _uniswapV3Factory) {
-        uniswapV2Factory = _uniswapV2Factory;
-        uniswapV3Factory = _uniswapV3Factory;
-    }
+  constructor(address _uniswapV2Factory, address _uniswapV3Factory) {
+    uniswapV2Factory = _uniswapV2Factory;
+    uniswapV3Factory = _uniswapV3Factory;
+  }
 
-    /*//////////////////////////////////////////////////////////////
+  /*//////////////////////////////////////////////////////////////
                         UNISWAPV3 SWAP CALLBACK
     //////////////////////////////////////////////////////////////*/
 
-    function uniswapV3SwapCallback(
-        int256 amount0Delta,
-        int256 amount1Delta,
-        bytes calldata data
-    ) external override {
-        address tokenIn = abi.decode(data, (address));
-        // no validation because this contract should hold no tokens
-
-        SafeTransferLib.safeTransfer(
-            tokenIn,
-            msg.sender,
-            amount0Delta > 0 ? uint256(amount0Delta) : uint256(amount1Delta)
-        );
-    }
+  function uniswapV3SwapCallback(int256 amount0Delta, int256 amount1Delta, bytes calldata data) external override {
+    address tokenIn = abi.decode(data, (address));
+    // no validation because this contract should hold no tokens
+
+    SafeTransferLib.safeTransfer(tokenIn, msg.sender, amount0Delta > 0 ? uint256(amount0Delta) : uint256(amount1Delta));
+  }
 
-    /*//////////////////////////////////////////////////////////////
+  /*//////////////////////////////////////////////////////////////
                                SWAP LOGIC
     //////////////////////////////////////////////////////////////*/
 
-    enum SwapType {
-        UniswapV2,
-        UniswapV3
-    }
-
-    struct SwapParams {
-        address tokenIn;
-        address tokenOut;
-        int256 amount; // negative corresponds to exact out
-        address recipient;
-    }
-
-    struct UniV3Data {
-        uint24 fee;
-    }
-
-    function swap(
-        SwapType swapType,
-        SwapParams memory params,
-        bytes memory data
-    ) internal returns (uint256 amount) {
-        if (swapType == SwapType.UniswapV2) {
-            address pair = UniswapV2Library.pairFor(uniswapV2Factory, params.tokenIn, params.tokenOut);
-
-            (uint256 reserveIn, uint256 reserveOut) = UniswapV2Library.getReserves(
-                uniswapV2Factory,
-                params.tokenIn,
-                params.tokenOut
-            );
-
-            amount = params.amount > 0
-                ? UniswapV2Library.getAmountOut(uint256(params.amount), reserveIn, reserveOut)
-                : UniswapV2Library.getAmountIn(uint256(-params.amount), reserveIn, reserveOut);
-
-            (uint256 amountIn, uint256 amountOut) = params.amount > 0
-                ? (uint256(params.amount), amount)
-                : (amount, uint256(-params.amount));
-
-            (address token0, ) = UniswapV2Library.sortTokens(params.tokenIn, params.tokenOut);
-            (uint256 amount0Out, uint256 amount1Out) = params.tokenIn == token0
-                ? (uint256(0), amountOut)
-                : (amountOut, uint256(0));
-
-            SafeTransferLib.safeTransfer(params.tokenIn, pair, amountIn);
-            IUniswapV2Pair(pair).swap(amount0Out, amount1Out, params.recipient, bytes(""));
-        } else {
-            UniV3Data memory uniV3Data = abi.decode(data, (UniV3Data));
-
-            // Borrowed logic from https://github.com/Uniswap/v3-periphery/blob/main/contracts/SwapRouter.sol
-            // exactInputInternal and exactOutputInternal
-
-            bool zeroForOne = params.tokenIn < params.tokenOut;
-
-            IUniswapV3Pool pool = IUniswapV3Pool(
-                PoolAddress.computeAddress(
-                    uniswapV3Factory,
-                    PoolAddress.getPoolKey(params.tokenIn, params.tokenOut, uniV3Data.fee)
-                )
-            );
-
-            (int256 amount0, int256 amount1) = pool.swap(
-                params.recipient,
-                zeroForOne,
-                params.amount,
-                zeroForOne ? TickMath.MIN_SQRT_RATIO + 1 : TickMath.MAX_SQRT_RATIO - 1,
-                abi.encode(params.tokenIn)
-            );
-
-            if (params.amount > 0) {
-                amount = uint256(-(zeroForOne ? amount1 : amount0));
-            } else {
-                int256 amountOutReceived;
-                (amount, amountOutReceived) = zeroForOne ? (uint256(amount0), amount1) : (uint256(amount1), amount0);
-                require(amountOutReceived == params.amount);
-            }
-        }
+  enum SwapType {
+    UniswapV2,
+    UniswapV3
+  }
+
+  struct SwapParams {
+    address tokenIn;
+    address tokenOut;
+    int256 amount; // negative corresponds to exact out
+    address recipient;
+  }
+
+  struct UniV3Data {
+    uint24 fee;
+  }
+
+  function swap(SwapType swapType, SwapParams memory params, bytes memory data) internal returns (uint256 amount) {
+    if (swapType == SwapType.UniswapV2) {
+      address pair = UniswapV2Library.pairFor(uniswapV2Factory, params.tokenIn, params.tokenOut);
+
+      (uint256 reserveIn, uint256 reserveOut) =
+        UniswapV2Library.getReserves(uniswapV2Factory, params.tokenIn, params.tokenOut);
+
+      amount = params.amount > 0
+        ? UniswapV2Library.getAmountOut(uint256(params.amount), reserveIn, reserveOut)
+        : UniswapV2Library.getAmountIn(uint256(-params.amount), reserveIn, reserveOut);
+
+      (uint256 amountIn, uint256 amountOut) =
+        params.amount > 0 ? (uint256(params.amount), amount) : (amount, uint256(-params.amount));
+
+      (address token0,) = UniswapV2Library.sortTokens(params.tokenIn, params.tokenOut);
+      (uint256 amount0Out, uint256 amount1Out) =
+        params.tokenIn == token0 ? (uint256(0), amountOut) : (amountOut, uint256(0));
+
+      SafeTransferLib.safeTransfer(params.tokenIn, pair, amountIn);
+      IUniswapV2Pair(pair).swap(amount0Out, amount1Out, params.recipient, bytes(""));
+    } else {
+      UniV3Data memory uniV3Data = abi.decode(data, (UniV3Data));
+
+      // Borrowed logic from https://github.com/Uniswap/v3-periphery/blob/main/contracts/SwapRouter.sol
+      // exactInputInternal and exactOutputInternal
+
+      bool zeroForOne = params.tokenIn < params.tokenOut;
+
+      IUniswapV3Pool pool = IUniswapV3Pool(
+        PoolAddress.computeAddress(
+          uniswapV3Factory, PoolAddress.getPoolKey(params.tokenIn, params.tokenOut, uniV3Data.fee)
+        )
+      );
+
+      (int256 amount0, int256 amount1) = pool.swap(
+        params.recipient,
+        zeroForOne,
+        params.amount,
+        zeroForOne ? TickMath.MIN_SQRT_RATIO + 1 : TickMath.MAX_SQRT_RATIO - 1,
+        abi.encode(params.tokenIn)
+      );
+
+      if (params.amount > 0) {
+        amount = uint256(-(zeroForOne ? amount1 : amount0));
+      } else {
+        int256 amountOutReceived;
+        (amount, amountOutReceived) = zeroForOne ? (uint256(amount0), amount1) : (uint256(amount1), amount0);
+        require(amountOutReceived == params.amount);
+      }
     }
+  }
 }
diff --git a/src/periphery/UniswapV2/interfaces/IUniswapV2Factory.sol b/src/periphery/UniswapV2/interfaces/IUniswapV2Factory.sol
index 07d8f5e..d8943f6 100644
--- a/src/periphery/UniswapV2/interfaces/IUniswapV2Factory.sol
+++ b/src/periphery/UniswapV2/interfaces/IUniswapV2Factory.sol
@@ -1,25 +1,25 @@
 pragma solidity >=0.5.0;
 
 interface IUniswapV2Factory {
-    event PairCreated(address indexed token0, address indexed token1, address pair, uint256);
+  event PairCreated(address indexed token0, address indexed token1, address pair, uint256);
 
-    function feeTo() external view returns (address);
+  function feeTo() external view returns (address);
 
-    function feeToSetter() external view returns (address);
+  function feeToSetter() external view returns (address);
 
-    function migrator() external view returns (address);
+  function migrator() external view returns (address);
 
-    function getPair(address tokenA, address tokenB) external view returns (address pair);
+  function getPair(address tokenA, address tokenB) external view returns (address pair);
 
-    function allPairs(uint256) external view returns (address pair);
+  function allPairs(uint256) external view returns (address pair);
 
-    function allPairsLength() external view returns (uint256);
+  function allPairsLength() external view returns (uint256);
 
-    function createPair(address tokenA, address tokenB) external returns (address pair);
+  function createPair(address tokenA, address tokenB) external returns (address pair);
 
-    function setFeeTo(address) external;
+  function setFeeTo(address) external;
 
-    function setFeeToSetter(address) external;
+  function setFeeToSetter(address) external;
 
-    function setMigrator(address) external;
+  function setMigrator(address) external;
 }
diff --git a/src/periphery/UniswapV2/interfaces/IUniswapV2Pair.sol b/src/periphery/UniswapV2/interfaces/IUniswapV2Pair.sol
index bbbe75e..f166dd2 100644
--- a/src/periphery/UniswapV2/interfaces/IUniswapV2Pair.sol
+++ b/src/periphery/UniswapV2/interfaces/IUniswapV2Pair.sol
@@ -1,96 +1,81 @@
 pragma solidity >=0.5.0;
 
 interface IUniswapV2Pair {
-    event Approval(address indexed owner, address indexed spender, uint256 value);
-    event Transfer(address indexed from, address indexed to, uint256 value);
+  event Approval(address indexed owner, address indexed spender, uint256 value);
+  event Transfer(address indexed from, address indexed to, uint256 value);
 
-    function name() external pure returns (string memory);
+  function name() external pure returns (string memory);
 
-    function symbol() external pure returns (string memory);
+  function symbol() external pure returns (string memory);
 
-    function decimals() external pure returns (uint8);
+  function decimals() external pure returns (uint8);
 
-    function totalSupply() external view returns (uint256);
+  function totalSupply() external view returns (uint256);
 
-    function balanceOf(address owner) external view returns (uint256);
+  function balanceOf(address owner) external view returns (uint256);
 
-    function allowance(address owner, address spender) external view returns (uint256);
+  function allowance(address owner, address spender) external view returns (uint256);
 
-    function approve(address spender, uint256 value) external returns (bool);
+  function approve(address spender, uint256 value) external returns (bool);
 
-    function transfer(address to, uint256 value) external returns (bool);
+  function transfer(address to, uint256 value) external returns (bool);
 
-    function transferFrom(
-        address from,
-        address to,
-        uint256 value
-    ) external returns (bool);
+  function transferFrom(address from, address to, uint256 value) external returns (bool);
 
-    function DOMAIN_SEPARATOR() external view returns (bytes32);
+  function DOMAIN_SEPARATOR() external view returns (bytes32);
 
-    function PERMIT_TYPEHASH() external pure returns (bytes32);
+  function PERMIT_TYPEHASH() external pure returns (bytes32);
 
-    function nonces(address owner) external view returns (uint256);
+  function nonces(address owner) external view returns (uint256);
 
-    function permit(
-        address owner,
-        address spender,
-        uint256 value,
-        uint256 deadline,
-        uint8 v,
-        bytes32 r,
-        bytes32 s
-    ) external;
+  function permit(
+    address owner,
+    address spender,
+    uint256 value,
+    uint256 deadline,
+    uint8 v,
+    bytes32 r,
+    bytes32 s
+  )
+    external;
 
-    event Mint(address indexed sender, uint256 amount0, uint256 amount1);
-    event Burn(address indexed sender, uint256 amount0, uint256 amount1, address indexed to);
-    event Swap(
-        address indexed sender,
-        uint256 amount0In,
-        uint256 amount1In,
-        uint256 amount0Out,
-        uint256 amount1Out,
-        address indexed to
-    );
-    event Sync(uint112 reserve0, uint112 reserve1);
+  event Mint(address indexed sender, uint256 amount0, uint256 amount1);
+  event Burn(address indexed sender, uint256 amount0, uint256 amount1, address indexed to);
+  event Swap(
+    address indexed sender,
+    uint256 amount0In,
+    uint256 amount1In,
+    uint256 amount0Out,
+    uint256 amount1Out,
+    address indexed to
+  );
+  event Sync(uint112 reserve0, uint112 reserve1);
 
-    function MINIMUM_LIQUIDITY() external pure returns (uint256);
+  function MINIMUM_LIQUIDITY() external pure returns (uint256);
 
-    function factory() external view returns (address);
+  function factory() external view returns (address);
 
-    function token0() external view returns (address);
+  function token0() external view returns (address);
 
-    function token1() external view returns (address);
+  function token1() external view returns (address);
 
-    function getReserves()
-        external
-        view
-        returns (
-            uint112 reserve0,
-            uint112 reserve1,
-            uint32 blockTimestampLast
-        );
+  function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);
 
-    function price0CumulativeLast() external view returns (uint256);
+  function price0CumulativeLast() external view returns (uint256);
 
-    function price1CumulativeLast() external view returns (uint256);
+  function price1CumulativeLast() external view returns (uint256);
 
-    function kLast() external view returns (uint256);
+  function kLast() external view returns (uint256);
 
-    function mint(address to) external returns (uint256 liquidity);
+  function mint(address to) external returns (uint256 liquidity);
 
-    function burn(address to) external returns (uint256 amount0, uint256 amount1);
+  function burn(address to) external returns (uint256 amount0, uint256 amount1);
 
-    function swap(
-        uint256 amount0Out,
-        uint256 amount1Out,
-        address to,
-        bytes calldata data
-    ) external;
+  function swap(uint256 amount0Out, uint256 amount1Out, address to, bytes calldata data) external;
 
-    function skim(address to) external;
+  function skim(address to) external;
 
-    function sync() external;
+  function sync() external;
 
-    function initialize(address, address) external;
+  function initialize(address, address) external;
 }
diff --git a/src/periphery/UniswapV2/libraries/UniswapV2Library.sol b/src/periphery/UniswapV2/libraries/UniswapV2Library.sol
index 283b640..e43c8bb 100644
--- a/src/periphery/UniswapV2/libraries/UniswapV2Library.sol
+++ b/src/periphery/UniswapV2/libraries/UniswapV2Library.sol
@@ -3,71 +3,79 @@ pragma solidity >=0.5.0;
 import { IUniswapV2Pair } from "../interfaces/IUniswapV2Pair.sol";
 
 library UniswapV2Library {
-    // returns sorted token addresses, used to handle return values from pairs sorted in this order
-    function sortTokens(address tokenA, address tokenB) internal pure returns (address token0, address token1) {
-        require(tokenA != tokenB, "UniswapV2Library: IDENTICAL_ADDRESSES");
-        (token0, token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA);
-        require(token0 != address(0), "UniswapV2Library: ZERO_ADDRESS");
-    }
+  // returns sorted token addresses, used to handle return values from pairs sorted in this order
+  function sortTokens(address tokenA, address tokenB) internal pure returns (address token0, address token1) {
+    require(tokenA != tokenB, "UniswapV2Library: IDENTICAL_ADDRESSES");
+    (token0, token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA);
+    require(token0 != address(0), "UniswapV2Library: ZERO_ADDRESS");
+  }
 
-    // calculates the CREATE2 address for a pair without making any external calls
-    function pairFor(
-        address factory,
-        address tokenA,
-        address tokenB
-    ) internal pure returns (address pair) {
-        (address token0, address token1) = sortTokens(tokenA, tokenB);
-        pair = address(
-            uint160( // extra cast for newer solidity
-                uint256(
-                    keccak256(
-                        abi.encodePacked(
-                            hex"ff",
-                            factory,
-                            keccak256(abi.encodePacked(token0, token1)),
-                            hex"e18a34eb0e04b04f7a0ac29a6e80748dca96319b42c54d679cb821dca90c6303" // init code hash
-                        )
-                    )
-                )
+  // calculates the CREATE2 address for a pair without making any external calls
+  function pairFor(address factory, address tokenA, address tokenB) internal pure returns (address pair) {
+    (address token0, address token1) = sortTokens(tokenA, tokenB);
+    pair = address(
+      uint160( // extra cast for newer solidity
+        uint256(
+          keccak256(
+            abi.encodePacked(
+              hex"ff",
+              factory,
+              keccak256(abi.encodePacked(token0, token1)),
+              hex"e18a34eb0e04b04f7a0ac29a6e80748dca96319b42c54d679cb821dca90c6303" // init code hash
             )
-        );
-    }
+          )
+        )
+      )
+    );
+  }
 
-    // fetches and sorts the reserves for a pair
-    function getReserves(
-        address factory,
-        address tokenA,
-        address tokenB
-    ) internal view returns (uint256 reserveA, uint256 reserveB) {
-        (address token0, ) = sortTokens(tokenA, tokenB);
-        (uint256 reserve0, uint256 reserve1, ) = IUniswapV2Pair(pairFor(factory, tokenA, tokenB)).getReserves();
-        (reserveA, reserveB) = tokenA == token0 ? (reserve0, reserve1) : (reserve1, reserve0);
-    }
+  // fetches and sorts the reserves for a pair
+  function getReserves(
+    address factory,
+    address tokenA,
+    address tokenB
+  )
+    internal
+    view
+    returns (uint256 reserveA, uint256 reserveB)
+  {
+    (address token0,) = sortTokens(tokenA, tokenB);
+    (uint256 reserve0, uint256 reserve1,) = IUniswapV2Pair(pairFor(factory, tokenA, tokenB)).getReserves();
+    (reserveA, reserveB) = tokenA == token0 ? (reserve0, reserve1) : (reserve1, reserve0);
+  }
 
-    // given an input amount of an asset and pair reserves, returns the maximum output amount of the other asset
-    function getAmountOut(
-        uint256 amountIn,
-        uint256 reserveIn,
-        uint256 reserveOut
-    ) internal pure returns (uint256 amountOut) {
-        require(amountIn > 0, "UniswapV2Library: INSUFFICIENT_INPUT_AMOUNT");
-        require(reserveIn > 0 && reserveOut > 0, "UniswapV2Library: INSUFFICIENT_LIQUIDITY");
-        uint256 amountInWithFee = amountIn * 997;
-        uint256 numerator = amountInWithFee * reserveOut;
-        uint256 denominator = (reserveIn * 1000) + amountInWithFee;
-        amountOut = numerator / denominator;
-    }
+  // given an input amount of an asset and pair reserves, returns the maximum output amount of the other asset
+  function getAmountOut(
+    uint256 amountIn,
+    uint256 reserveIn,
+    uint256 reserveOut
+  )
+    internal
+    pure
+    returns (uint256 amountOut)
+  {
+    require(amountIn > 0, "UniswapV2Library: INSUFFICIENT_INPUT_AMOUNT");
+    require(reserveIn > 0 && reserveOut > 0, "UniswapV2Library: INSUFFICIENT_LIQUIDITY");
+    uint256 amountInWithFee = amountIn * 997;
+    uint256 numerator = amountInWithFee * reserveOut;
+    uint256 denominator = (reserveIn * 1000) + amountInWithFee;
+    amountOut = numerator / denominator;
+  }
 
-    // given an output amount of an asset and pair reserves, returns a required input amount of the other asset
-    function getAmountIn(
-        uint256 amountOut,
-        uint256 reserveIn,
-        uint256 reserveOut
-    ) internal pure returns (uint256 amountIn) {
-        require(amountOut > 0, "UniswapV2Library: INSUFFICIENT_OUTPUT_AMOUNT");
-        require(reserveIn > 0 && reserveOut > 0, "UniswapV2Library: INSUFFICIENT_LIQUIDITY");
-        uint256 numerator = reserveIn * amountOut * 1000;
-        uint256 denominator = (reserveOut - amountOut) * 997;
-        amountIn = (numerator / denominator) + 1;
-    }
+  // given an output amount of an asset and pair reserves, returns a required input amount of the other asset
+  function getAmountIn(
+    uint256 amountOut,
+    uint256 reserveIn,
+    uint256 reserveOut
+  )
+    internal
+    pure
+    returns (uint256 amountIn)
+  {
+    require(amountOut > 0, "UniswapV2Library: INSUFFICIENT_OUTPUT_AMOUNT");
+    require(reserveIn > 0 && reserveOut > 0, "UniswapV2Library: INSUFFICIENT_LIQUIDITY");
+    uint256 numerator = reserveIn * amountOut * 1000;
+    uint256 denominator = (reserveOut - amountOut) * 997;
+    amountIn = (numerator / denominator) + 1;
+  }
 }
diff --git a/src/periphery/UniswapV3/interfaces/IUniswapV3Factory.sol b/src/periphery/UniswapV3/interfaces/IUniswapV3Factory.sol
index 540cfdc..d366717 100644
--- a/src/periphery/UniswapV3/interfaces/IUniswapV3Factory.sol
+++ b/src/periphery/UniswapV3/interfaces/IUniswapV3Factory.sol
@@ -4,75 +4,63 @@ pragma solidity >=0.5.0;
 /// @title The interface for the Uniswap V3 Factory
 /// @notice The Uniswap V3 Factory facilitates creation of Uniswap V3 pools and control over the protocol fees
 interface IUniswapV3Factory {
-    /// @notice Emitted when the owner of the factory is changed
-    /// @param oldOwner The owner before the owner was changed
-    /// @param newOwner The owner after the owner was changed
-    event OwnerChanged(address indexed oldOwner, address indexed newOwner);
+  /// @notice Emitted when the owner of the factory is changed
+  /// @param oldOwner The owner before the owner was changed
+  /// @param newOwner The owner after the owner was changed
+  event OwnerChanged(address indexed oldOwner, address indexed newOwner);
 
-    /// @notice Emitted when a pool is created
-    /// @param token0 The first token of the pool by address sort order
-    /// @param token1 The second token of the pool by address sort order
-    /// @param fee The fee collected upon every swap in the pool, denominated in hundredths of a bip
-    /// @param tickSpacing The minimum number of ticks between initialized ticks
-    /// @param pool The address of the created pool
-    event PoolCreated(
-        address indexed token0,
-        address indexed token1,
-        uint24 indexed fee,
-        int24 tickSpacing,
-        address pool
-    );
+  /// @notice Emitted when a pool is created
+  /// @param token0 The first token of the pool by address sort order
+  /// @param token1 The second token of the pool by address sort order
+  /// @param fee The fee collected upon every swap in the pool, denominated in hundredths of a bip
+  /// @param tickSpacing The minimum number of ticks between initialized ticks
+  /// @param pool The address of the created pool
+  event PoolCreated(
+    address indexed token0, address indexed token1, uint24 indexed fee, int24 tickSpacing, address pool
+  );
 
-    /// @notice Emitted when a new fee amount is enabled for pool creation via the factory
-    /// @param fee The enabled fee, denominated in hundredths of a bip
-    /// @param tickSpacing The minimum number of ticks between initialized ticks for pools created with the given fee
-    event FeeAmountEnabled(uint24 indexed fee, int24 indexed tickSpacing);
+  /// @notice Emitted when a new fee amount is enabled for pool creation via the factory
+  /// @param fee The enabled fee, denominated in hundredths of a bip
+  /// @param tickSpacing The minimum number of ticks between initialized ticks for pools created with the given fee
+  event FeeAmountEnabled(uint24 indexed fee, int24 indexed tickSpacing);
 
-    /// @notice Returns the current owner of the factory
-    /// @dev Can be changed by the current owner via setOwner
-    /// @return The address of the factory owner
-    function owner() external view returns (address);
+  /// @notice Returns the current owner of the factory
+  /// @dev Can be changed by the current owner via setOwner
+  /// @return The address of the factory owner
+  function owner() external view returns (address);
 
-    /// @notice Returns the tick spacing for a given fee amount, if enabled, or 0 if not enabled
-    /// @dev A fee amount can never be removed, so this value should be hard coded or cached in the calling context
-    /// @param fee The enabled fee, denominated in hundredths of a bip. Returns 0 in case of unenabled fee
-    /// @return The tick spacing
-    function feeAmountTickSpacing(uint24 fee) external view returns (int24);
+  /// @notice Returns the tick spacing for a given fee amount, if enabled, or 0 if not enabled
+  /// @dev A fee amount can never be removed, so this value should be hard coded or cached in the calling context
+  /// @param fee The enabled fee, denominated in hundredths of a bip. Returns 0 in case of unenabled fee
+  /// @return The tick spacing
+  function feeAmountTickSpacing(uint24 fee) external view returns (int24);
 
-    /// @notice Returns the pool address for a given pair of tokens and a fee, or address 0 if it does not exist
-    /// @dev tokenA and tokenB may be passed in either token0/token1 or token1/token0 order
-    /// @param tokenA The contract address of either token0 or token1
-    /// @param tokenB The contract address of the other token
-    /// @param fee The fee collected upon every swap in the pool, denominated in hundredths of a bip
-    /// @return pool The pool address
-    function getPool(
-        address tokenA,
-        address tokenB,
-        uint24 fee
-    ) external view returns (address pool);
+  /// @notice Returns the pool address for a given pair of tokens and a fee, or address 0 if it does not exist
+  /// @dev tokenA and tokenB may be passed in either token0/token1 or token1/token0 order
+  /// @param tokenA The contract address of either token0 or token1
+  /// @param tokenB The contract address of the other token
+  /// @param fee The fee collected upon every swap in the pool, denominated in hundredths of a bip
+  /// @return pool The pool address
+  function getPool(address tokenA, address tokenB, uint24 fee) external view returns (address pool);
 
-    /// @notice Creates a pool for the given two tokens and fee
-    /// @param tokenA One of the two tokens in the desired pool
-    /// @param tokenB The other of the two tokens in the desired pool
-    /// @param fee The desired fee for the pool
-    /// @dev tokenA and tokenB may be passed in either order: token0/token1 or token1/token0. tickSpacing is retrieved
-    /// from the fee. The call will revert if the pool already exists, the fee is invalid, or the token arguments
-    /// are invalid.
-    /// @return pool The address of the newly created pool
-    function createPool(
-        address tokenA,
-        address tokenB,
-        uint24 fee
-    ) external returns (address pool);
+  /// @notice Creates a pool for the given two tokens and fee
+  /// @param tokenA One of the two tokens in the desired pool
+  /// @param tokenB The other of the two tokens in the desired pool
+  /// @param fee The desired fee for the pool
+  /// @dev tokenA and tokenB may be passed in either order: token0/token1 or token1/token0. tickSpacing is retrieved
+  /// from the fee. The call will revert if the pool already exists, the fee is invalid, or the token arguments
+  /// are invalid.
+  /// @return pool The address of the newly created pool
+  function createPool(address tokenA, address tokenB, uint24 fee) external returns (address pool);
 
-    /// @notice Updates the owner of the factory
-    /// @dev Must be called by the current owner
-    /// @param _owner The new owner of the factory
-    function setOwner(address _owner) external;
+  /// @notice Updates the owner of the factory
+  /// @dev Must be called by the current owner
+  /// @param _owner The new owner of the factory
+  function setOwner(address _owner) external;
 
-    /// @notice Enables a fee amount with the given tickSpacing
-    /// @dev Fee amounts may never be removed once enabled
-    /// @param fee The fee amount to enable, denominated in hundredths of a bip (i.e. 1e-6)
-    /// @param tickSpacing The spacing between ticks to be enforced for all pools created with the given fee amount
-    function enableFeeAmount(uint24 fee, int24 tickSpacing) external;
+  /// @notice Enables a fee amount with the given tickSpacing
+  /// @dev Fee amounts may never be removed once enabled
+  /// @param fee The fee amount to enable, denominated in hundredths of a bip (i.e. 1e-6)
+  /// @param tickSpacing The spacing between ticks to be enforced for all pools created with the given fee amount
+  function enableFeeAmount(uint24 fee, int24 tickSpacing) external;
 }
diff --git a/src/periphery/UniswapV3/interfaces/IUniswapV3Pool.sol b/src/periphery/UniswapV3/interfaces/IUniswapV3Pool.sol
index a2239bd..1999f7a 100644
--- a/src/periphery/UniswapV3/interfaces/IUniswapV3Pool.sol
+++ b/src/periphery/UniswapV3/interfaces/IUniswapV3Pool.sol
@@ -13,12 +13,10 @@ import "./pool/IUniswapV3PoolEvents.sol";
 /// to the ERC20 specification
 /// @dev The pool interface is broken up into many smaller pieces
 interface IUniswapV3Pool is
-    IUniswapV3PoolImmutables,
-    IUniswapV3PoolState,
-    IUniswapV3PoolDerivedState,
-    IUniswapV3PoolActions,
-    IUniswapV3PoolOwnerActions,
-    IUniswapV3PoolEvents
-{
-
-}
+  IUniswapV3PoolImmutables,
+  IUniswapV3PoolState,
+  IUniswapV3PoolDerivedState,
+  IUniswapV3PoolActions,
+  IUniswapV3PoolOwnerActions,
+  IUniswapV3PoolEvents
+{ }
diff --git a/src/periphery/UniswapV3/interfaces/callback/IUniswapV3SwapCallback.sol b/src/periphery/UniswapV3/interfaces/callback/IUniswapV3SwapCallback.sol
index 9f183b2..953c686 100644
--- a/src/periphery/UniswapV3/interfaces/callback/IUniswapV3SwapCallback.sol
+++ b/src/periphery/UniswapV3/interfaces/callback/IUniswapV3SwapCallback.sol
@@ -4,18 +4,14 @@ pragma solidity >=0.5.0;
 /// @title Callback for IUniswapV3PoolActions#swap
 /// @notice Any contract that calls IUniswapV3PoolActions#swap must implement this interface
 interface IUniswapV3SwapCallback {
-    /// @notice Called to `msg.sender` after executing a swap via IUniswapV3Pool#swap.
-    /// @dev In the implementation you must pay the pool tokens owed for the swap.
-    /// The caller of this method must be checked to be a UniswapV3Pool deployed by the canonical UniswapV3Factory.
-    /// amount0Delta and amount1Delta can both be 0 if no tokens were swapped.
-    /// @param amount0Delta The amount of token0 that was sent (negative) or must be received (positive) by the pool by
-    /// the end of the swap. If positive, the callback must send that amount of token0 to the pool.
-    /// @param amount1Delta The amount of token1 that was sent (negative) or must be received (positive) by the pool by
-    /// the end of the swap. If positive, the callback must send that amount of token1 to the pool.
-    /// @param data Any data passed through by the caller via the IUniswapV3PoolActions#swap call
-    function uniswapV3SwapCallback(
-        int256 amount0Delta,
-        int256 amount1Delta,
-        bytes calldata data
-    ) external;
+  /// @notice Called to `msg.sender` after executing a swap via IUniswapV3Pool#swap.
+  /// @dev In the implementation you must pay the pool tokens owed for the swap.
+  /// The caller of this method must be checked to be a UniswapV3Pool deployed by the canonical UniswapV3Factory.
+  /// amount0Delta and amount1Delta can both be 0 if no tokens were swapped.
+  /// @param amount0Delta The amount of token0 that was sent (negative) or must be received (positive) by the pool by
+  /// the end of the swap. If positive, the callback must send that amount of token0 to the pool.
+  /// @param amount1Delta The amount of token1 that was sent (negative) or must be received (positive) by the pool by
+  /// the end of the swap. If positive, the callback must send that amount of token1 to the pool.
+  /// @param data Any data passed through by the caller via the IUniswapV3PoolActions#swap call
+  function uniswapV3SwapCallback(int256 amount0Delta, int256 amount1Delta, bytes calldata data) external;
 }
diff --git a/src/periphery/UniswapV3/interfaces/pool/IUniswapV3PoolActions.sol b/src/periphery/UniswapV3/interfaces/pool/IUniswapV3PoolActions.sol
index 44fb61c..14be213 100644
--- a/src/periphery/UniswapV3/interfaces/pool/IUniswapV3PoolActions.sol
+++ b/src/periphery/UniswapV3/interfaces/pool/IUniswapV3PoolActions.sol
@@ -4,100 +4,100 @@ pragma solidity >=0.5.0;
 /// @title Permissionless pool actions
 /// @notice Contains pool methods that can be called by anyone
 interface IUniswapV3PoolActions {
-    /// @notice Sets the initial price for the pool
-    /// @dev Price is represented as a sqrt(amountToken1/amountToken0) Q64.96 value
-    /// @param sqrtPriceX96 the initial sqrt price of the pool as a Q64.96
-    function initialize(uint160 sqrtPriceX96) external;
+  /// @notice Sets the initial price for the pool
+  /// @dev Price is represented as a sqrt(amountToken1/amountToken0) Q64.96 value
+  /// @param sqrtPriceX96 the initial sqrt price of the pool as a Q64.96
+  function initialize(uint160 sqrtPriceX96) external;
 
-    /// @notice Adds liquidity for the given recipient/tickLower/tickUpper position
-    /// @dev The caller of this method receives a callback in the form of IUniswapV3MintCallback#uniswapV3MintCallback
-    /// in which they must pay any token0 or token1 owed for the liquidity. The amount of token0/token1 due depends
-    /// on tickLower, tickUpper, the amount of liquidity, and the current price.
-    /// @param recipient The address for which the liquidity will be created
-    /// @param tickLower The lower tick of the position in which to add liquidity
-    /// @param tickUpper The upper tick of the position in which to add liquidity
-    /// @param amount The amount of liquidity to mint
-    /// @param data Any data that should be passed through to the callback
-    /// @return amount0 The amount of token0 that was paid to mint the given amount of liquidity. Matches the value in the callback
-    /// @return amount1 The amount of token1 that was paid to mint the given amount of liquidity. Matches the value in the callback
-    function mint(
-        address recipient,
-        int24 tickLower,
-        int24 tickUpper,
-        uint128 amount,
-        bytes calldata data
-    ) external returns (uint256 amount0, uint256 amount1);
+  /// @notice Adds liquidity for the given recipient/tickLower/tickUpper position
+  /// @dev The caller of this method receives a callback in the form of IUniswapV3MintCallback#uniswapV3MintCallback
+  /// in which they must pay any token0 or token1 owed for the liquidity. The amount of token0/token1 due depends
+  /// on tickLower, tickUpper, the amount of liquidity, and the current price.
+  /// @param recipient The address for which the liquidity will be created
+  /// @param tickLower The lower tick of the position in which to add liquidity
+  /// @param tickUpper The upper tick of the position in which to add liquidity
+  /// @param amount The amount of liquidity to mint
+  /// @param data Any data that should be passed through to the callback
+  /// @return amount0 The amount of token0 that was paid to mint the given amount of liquidity. Matches the value in
+  /// the callback
+  /// @return amount1 The amount of token1 that was paid to mint the given amount of liquidity. Matches the value in
+  /// the callback
+  function mint(
+    address recipient,
+    int24 tickLower,
+    int24 tickUpper,
+    uint128 amount,
+    bytes calldata data
+  )
+    external
+    returns (uint256 amount0, uint256 amount1);
 
-    /// @notice Collects tokens owed to a position
-    /// @dev Does not recompute fees earned, which must be done either via mint or burn of any amount of liquidity.
-    /// Collect must be called by the position owner. To withdraw only token0 or only token1, amount0Requested or
-    /// amount1Requested may be set to zero. To withdraw all tokens owed, caller may pass any value greater than the
-    /// actual tokens owed, e.g. type(uint128).max. Tokens owed may be from accumulated swap fees or burned liquidity.
-    /// @param recipient The address which should receive the fees collected
-    /// @param tickLower The lower tick of the position for which to collect fees
-    /// @param tickUpper The upper tick of the position for which to collect fees
-    /// @param amount0Requested How much token0 should be withdrawn from the fees owed
-    /// @param amount1Requested How much token1 should be withdrawn from the fees owed
-    /// @return amount0 The amount of fees collected in token0
-    /// @return amount1 The amount of fees collected in token1
-    function collect(
-        address recipient,
-        int24 tickLower,
-        int24 tickUpper,
-        uint128 amount0Requested,
-        uint128 amount1Requested
-    ) external returns (uint128 amount0, uint128 amount1);
+  /// @notice Collects tokens owed to a position
+  /// @dev Does not recompute fees earned, which must be done either via mint or burn of any amount of liquidity.
+  /// Collect must be called by the position owner. To withdraw only token0 or only token1, amount0Requested or
+  /// amount1Requested may be set to zero. To withdraw all tokens owed, caller may pass any value greater than the
+  /// actual tokens owed, e.g. type(uint128).max. Tokens owed may be from accumulated swap fees or burned liquidity.
+  /// @param recipient The address which should receive the fees collected
+  /// @param tickLower The lower tick of the position for which to collect fees
+  /// @param tickUpper The upper tick of the position for which to collect fees
+  /// @param amount0Requested How much token0 should be withdrawn from the fees owed
+  /// @param amount1Requested How much token1 should be withdrawn from the fees owed
+  /// @return amount0 The amount of fees collected in token0
+  /// @return amount1 The amount of fees collected in token1
+  function collect(
+    address recipient,
+    int24 tickLower,
+    int24 tickUpper,
+    uint128 amount0Requested,
+    uint128 amount1Requested
+  )
+    external
+    returns (uint128 amount0, uint128 amount1);
 
-    /// @notice Burn liquidity from the sender and account tokens owed for the liquidity to the position
-    /// @dev Can be used to trigger a recalculation of fees owed to a position by calling with an amount of 0
-    /// @dev Fees must be collected separately via a call to #collect
-    /// @param tickLower The lower tick of the position for which to burn liquidity
-    /// @param tickUpper The upper tick of the position for which to burn liquidity
-    /// @param amount How much liquidity to burn
-    /// @return amount0 The amount of token0 sent to the recipient
-    /// @return amount1 The amount of token1 sent to the recipient
-    function burn(
-        int24 tickLower,
-        int24 tickUpper,
-        uint128 amount
-    ) external returns (uint256 amount0, uint256 amount1);
+  /// @notice Burn liquidity from the sender and account tokens owed for the liquidity to the position
+  /// @dev Can be used to trigger a recalculation of fees owed to a position by calling with an amount of 0
+  /// @dev Fees must be collected separately via a call to #collect
+  /// @param tickLower The lower tick of the position for which to burn liquidity
+  /// @param tickUpper The upper tick of the position for which to burn liquidity
+  /// @param amount How much liquidity to burn
+  /// @return amount0 The amount of token0 sent to the recipient
+  /// @return amount1 The amount of token1 sent to the recipient
+  function burn(int24 tickLower, int24 tickUpper, uint128 amount) external returns (uint256 amount0, uint256 amount1);
 
-    /// @notice Swap token0 for token1, or token1 for token0
-    /// @dev The caller of this method receives a callback in the form of IUniswapV3SwapCallback#uniswapV3SwapCallback
-    /// @param recipient The address to receive the output of the swap
-    /// @param zeroForOne The direction of the swap, true for token0 to token1, false for token1 to token0
-    /// @param amountSpecified The amount of the swap, which implicitly configures the swap as exact input (positive), or exact output (negative)
-    /// @param sqrtPriceLimitX96 The Q64.96 sqrt price limit. If zero for one, the price cannot be less than this
-    /// value after the swap. If one for zero, the price cannot be greater than this value after the swap
-    /// @param data Any data to be passed through to the callback
-    /// @return amount0 The delta of the balance of token0 of the pool, exact when negative, minimum when positive
-    /// @return amount1 The delta of the balance of token1 of the pool, exact when negative, minimum when positive
-    function swap(
-        address recipient,
-        bool zeroForOne,
-        int256 amountSpecified,
-        uint160 sqrtPriceLimitX96,
-        bytes calldata data
-    ) external returns (int256 amount0, int256 amount1);
+  /// @notice Swap token0 for token1, or token1 for token0
+  /// @dev The caller of this method receives a callback in the form of IUniswapV3SwapCallback#uniswapV3SwapCallback
+  /// @param recipient The address to receive the output of the swap
+  /// @param zeroForOne The direction of the swap, true for token0 to token1, false for token1 to token0
+  /// @param amountSpecified The amount of the swap, which implicitly configures the swap as exact input (positive),
+  /// or exact output (negative)
+  /// @param sqrtPriceLimitX96 The Q64.96 sqrt price limit. If zero for one, the price cannot be less than this
+  /// value after the swap. If one for zero, the price cannot be greater than this value after the swap
+  /// @param data Any data to be passed through to the callback
+  /// @return amount0 The delta of the balance of token0 of the pool, exact when negative, minimum when positive
+  /// @return amount1 The delta of the balance of token1 of the pool, exact when negative, minimum when positive
+  function swap(
+    address recipient,
+    bool zeroForOne,
+    int256 amountSpecified,
+    uint160 sqrtPriceLimitX96,
+    bytes calldata data
+  )
+    external
+    returns (int256 amount0, int256 amount1);
 
-    /// @notice Receive token0 and/or token1 and pay it back, plus a fee, in the callback
-    /// @dev The caller of this method receives a callback in the form of IUniswapV3FlashCallback#uniswapV3FlashCallback
-    /// @dev Can be used to donate underlying tokens pro-rata to currently in-range liquidity providers by calling
-    /// with 0 amount{0,1} and sending the donation amount(s) from the callback
-    /// @param recipient The address which will receive the token0 and token1 amounts
-    /// @param amount0 The amount of token0 to send
-    /// @param amount1 The amount of token1 to send
-    /// @param data Any data to be passed through to the callback
-    function flash(
-        address recipient,
-        uint256 amount0,
-        uint256 amount1,
-        bytes calldata data
-    ) external;
+  /// @notice Receive token0 and/or token1 and pay it back, plus a fee, in the callback
+  /// @dev The caller of this method receives a callback in the form of IUniswapV3FlashCallback#uniswapV3FlashCallback
+  /// @dev Can be used to donate underlying tokens pro-rata to currently in-range liquidity providers by calling
+  /// with 0 amount{0,1} and sending the donation amount(s) from the callback
+  /// @param recipient The address which will receive the token0 and token1 amounts
+  /// @param amount0 The amount of token0 to send
+  /// @param amount1 The amount of token1 to send
+  /// @param data Any data to be passed through to the callback
+  function flash(address recipient, uint256 amount0, uint256 amount1, bytes calldata data) external;
 
-    /// @notice Increase the maximum number of price and liquidity observations that this pool will store
-    /// @dev This method is no-op if the pool already has an observationCardinalityNext greater than or equal to
-    /// the input observationCardinalityNext.
-    /// @param observationCardinalityNext The desired minimum number of observations for the pool to store
-    function increaseObservationCardinalityNext(uint16 observationCardinalityNext) external;
+  /// @notice Increase the maximum number of price and liquidity observations that this pool will store
+  /// @dev This method is no-op if the pool already has an observationCardinalityNext greater than or equal to
+  /// the input observationCardinalityNext.
+  /// @param observationCardinalityNext The desired minimum number of observations for the pool to store
+  function increaseObservationCardinalityNext(uint16 observationCardinalityNext) external;
 }
diff --git a/src/periphery/UniswapV3/interfaces/pool/IUniswapV3PoolDerivedState.sol b/src/periphery/UniswapV3/interfaces/pool/IUniswapV3PoolDerivedState.sol
index eda3a00..da412ae 100644
--- a/src/periphery/UniswapV3/interfaces/pool/IUniswapV3PoolDerivedState.sol
+++ b/src/periphery/UniswapV3/interfaces/pool/IUniswapV3PoolDerivedState.sol
@@ -5,36 +5,39 @@ pragma solidity >=0.5.0;
 /// @notice Contains view functions to provide information about the pool that is computed rather than stored on the
 /// blockchain. The functions here may have variable gas costs.
 interface IUniswapV3PoolDerivedState {
-    /// @notice Returns the cumulative tick and liquidity as of each timestamp `secondsAgo` from the current block timestamp
-    /// @dev To get a time weighted average tick or liquidity-in-range, you must call this with two values, one representing
-    /// the beginning of the period and another for the end of the period. E.g., to get the last hour time-weighted average tick,
-    /// you must call it with secondsAgos = [3600, 0].
-    /// @dev The time weighted average tick represents the geometric time weighted average price of the pool, in
-    /// log base sqrt(1.0001) of token1 / token0. The TickMath library can be used to go from a tick value to a ratio.
-    /// @param secondsAgos From how long ago each cumulative tick and liquidity value should be returned
-    /// @return tickCumulatives Cumulative tick values as of each `secondsAgos` from the current block timestamp
-    /// @return secondsPerLiquidityCumulativeX128s Cumulative seconds per liquidity-in-range value as of each `secondsAgos` from the current block
-    /// timestamp
-    function observe(uint32[] calldata secondsAgos)
-        external
-        view
-        returns (int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulativeX128s);
+  /// @notice Returns the cumulative tick and liquidity as of each timestamp `secondsAgo` from the current block
+  /// timestamp
+  /// @dev To get a time weighted average tick or liquidity-in-range, you must call this with two values, one
+  /// representing
+  /// the beginning of the period and another for the end of the period. E.g., to get the last hour time-weighted
+  /// average tick,
+  /// you must call it with secondsAgos = [3600, 0].
+  /// @dev The time weighted average tick represents the geometric time weighted average price of the pool, in
+  /// log base sqrt(1.0001) of token1 / token0. The TickMath library can be used to go from a tick value to a ratio.
+  /// @param secondsAgos From how long ago each cumulative tick and liquidity value should be returned
+  /// @return tickCumulatives Cumulative tick values as of each `secondsAgos` from the current block timestamp
+  /// @return secondsPerLiquidityCumulativeX128s Cumulative seconds per liquidity-in-range value as of each
+  /// `secondsAgos` from the current block
+  /// timestamp
+  function observe(uint32[] calldata secondsAgos)
+    external
+    view
+    returns (int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulativeX128s);
 
-    /// @notice Returns a snapshot of the tick cumulative, seconds per liquidity and seconds inside a tick range
-    /// @dev Snapshots must only be compared to other snapshots, taken over a period for which a position existed.
-    /// I.e., snapshots cannot be compared if a position is not held for the entire period between when the first
-    /// snapshot is taken and the second snapshot is taken.
-    /// @param tickLower The lower tick of the range
-    /// @param tickUpper The upper tick of the range
-    /// @return tickCumulativeInside The snapshot of the tick accumulator for the range
-    /// @return secondsPerLiquidityInsideX128 The snapshot of seconds per liquidity for the range
-    /// @return secondsInside The snapshot of seconds per liquidity for the range
-    function snapshotCumulativesInside(int24 tickLower, int24 tickUpper)
-        external
-        view
-        returns (
-            int56 tickCumulativeInside,
-            uint160 secondsPerLiquidityInsideX128,
-            uint32 secondsInside
-        );
+  /// @notice Returns a snapshot of the tick cumulative, seconds per liquidity and seconds inside a tick range
+  /// @dev Snapshots must only be compared to other snapshots, taken over a period for which a position existed.
+  /// I.e., snapshots cannot be compared if a position is not held for the entire period between when the first
+  /// snapshot is taken and the second snapshot is taken.
+  /// @param tickLower The lower tick of the range
+  /// @param tickUpper The upper tick of the range
+  /// @return tickCumulativeInside The snapshot of the tick accumulator for the range
+  /// @return secondsPerLiquidityInsideX128 The snapshot of seconds per liquidity for the range
+  /// @return secondsInside The snapshot of seconds per liquidity for the range
+  function snapshotCumulativesInside(
+    int24 tickLower,
+    int24 tickUpper
+  )
+    external
+    view
+    returns (int56 tickCumulativeInside, uint160 secondsPerLiquidityInsideX128, uint32 secondsInside);
 }
diff --git a/src/periphery/UniswapV3/interfaces/pool/IUniswapV3PoolEvents.sol b/src/periphery/UniswapV3/interfaces/pool/IUniswapV3PoolEvents.sol
index 9d915dd..7059fad 100644
--- a/src/periphery/UniswapV3/interfaces/pool/IUniswapV3PoolEvents.sol
+++ b/src/periphery/UniswapV3/interfaces/pool/IUniswapV3PoolEvents.sol
@@ -4,118 +4,110 @@ pragma solidity >=0.5.0;
 /// @title Events emitted by a pool
 /// @notice Contains all events emitted by the pool
 interface IUniswapV3PoolEvents {
-    /// @notice Emitted exactly once by a pool when #initialize is first called on the pool
-    /// @dev Mint/Burn/Swap cannot be emitted by the pool before Initialize
-    /// @param sqrtPriceX96 The initial sqrt price of the pool, as a Q64.96
-    /// @param tick The initial tick of the pool, i.e. log base 1.0001 of the starting price of the pool
-    event Initialize(uint160 sqrtPriceX96, int24 tick);
+  /// @notice Emitted exactly once by a pool when #initialize is first called on the pool
+  /// @dev Mint/Burn/Swap cannot be emitted by the pool before Initialize
+  /// @param sqrtPriceX96 The initial sqrt price of the pool, as a Q64.96
+  /// @param tick The initial tick of the pool, i.e. log base 1.0001 of the starting price of the pool
+  event Initialize(uint160 sqrtPriceX96, int24 tick);
 
-    /// @notice Emitted when liquidity is minted for a given position
-    /// @param sender The address that minted the liquidity
-    /// @param owner The owner of the position and recipient of any minted liquidity
-    /// @param tickLower The lower tick of the position
-    /// @param tickUpper The upper tick of the position
-    /// @param amount The amount of liquidity minted to the position range
-    /// @param amount0 How much token0 was required for the minted liquidity
-    /// @param amount1 How much token1 was required for the minted liquidity
-    event Mint(
-        address sender,
-        address indexed owner,
-        int24 indexed tickLower,
-        int24 indexed tickUpper,
-        uint128 amount,
-        uint256 amount0,
-        uint256 amount1
-    );
+  /// @notice Emitted when liquidity is minted for a given position
+  /// @param sender The address that minted the liquidity
+  /// @param owner The owner of the position and recipient of any minted liquidity
+  /// @param tickLower The lower tick of the position
+  /// @param tickUpper The upper tick of the position
+  /// @param amount The amount of liquidity minted to the position range
+  /// @param amount0 How much token0 was required for the minted liquidity
+  /// @param amount1 How much token1 was required for the minted liquidity
+  event Mint(
+    address sender,
+    address indexed owner,
+    int24 indexed tickLower,
+    int24 indexed tickUpper,
+    uint128 amount,
+    uint256 amount0,
+    uint256 amount1
+  );
 
-    /// @notice Emitted when fees are collected by the owner of a position
-    /// @dev Collect events may be emitted with zero amount0 and amount1 when the caller chooses not to collect fees
-    /// @param owner The owner of the position for which fees are collected
-    /// @param tickLower The lower tick of the position
-    /// @param tickUpper The upper tick of the position
-    /// @param amount0 The amount of token0 fees collected
-    /// @param amount1 The amount of token1 fees collected
-    event Collect(
-        address indexed owner,
-        address recipient,
-        int24 indexed tickLower,
-        int24 indexed tickUpper,
-        uint128 amount0,
-        uint128 amount1
-    );
+  /// @notice Emitted when fees are collected by the owner of a position
+  /// @dev Collect events may be emitted with zero amount0 and amount1 when the caller chooses not to collect fees
+  /// @param owner The owner of the position for which fees are collected
+  /// @param tickLower The lower tick of the position
+  /// @param tickUpper The upper tick of the position
+  /// @param amount0 The amount of token0 fees collected
+  /// @param amount1 The amount of token1 fees collected
+  event Collect(
+    address indexed owner,
+    address recipient,
+    int24 indexed tickLower,
+    int24 indexed tickUpper,
+    uint128 amount0,
+    uint128 amount1
+  );
 
-    /// @notice Emitted when a position's liquidity is removed
-    /// @dev Does not withdraw any fees earned by the liquidity position, which must be withdrawn via #collect
-    /// @param owner The owner of the position for which liquidity is removed
-    /// @param tickLower The lower tick of the position
-    /// @param tickUpper The upper tick of the position
-    /// @param amount The amount of liquidity to remove
-    /// @param amount0 The amount of token0 withdrawn
-    /// @param amount1 The amount of token1 withdrawn
-    event Burn(
-        address indexed owner,
-        int24 indexed tickLower,
-        int24 indexed tickUpper,
-        uint128 amount,
-        uint256 amount0,
-        uint256 amount1
-    );
+  /// @notice Emitted when a position's liquidity is removed
+  /// @dev Does not withdraw any fees earned by the liquidity position, which must be withdrawn via #collect
+  /// @param owner The owner of the position for which liquidity is removed
+  /// @param tickLower The lower tick of the position
+  /// @param tickUpper The upper tick of the position
+  /// @param amount The amount of liquidity to remove
+  /// @param amount0 The amount of token0 withdrawn
+  /// @param amount1 The amount of token1 withdrawn
+  event Burn(
+    address indexed owner,
+    int24 indexed tickLower,
+    int24 indexed tickUpper,
+    uint128 amount,
+    uint256 amount0,
+    uint256 amount1
+  );
 
-    /// @notice Emitted by the pool for any swaps between token0 and token1
-    /// @param sender The address that initiated the swap call, and that received the callback
-    /// @param recipient The address that received the output of the swap
-    /// @param amount0 The delta of the token0 balance of the pool
-    /// @param amount1 The delta of the token1 balance of the pool
-    /// @param sqrtPriceX96 The sqrt(price) of the pool after the swap, as a Q64.96
-    /// @param liquidity The liquidity of the pool after the swap
-    /// @param tick The log base 1.0001 of price of the pool after the swap
-    event Swap(
-        address indexed sender,
-        address indexed recipient,
-        int256 amount0,
-        int256 amount1,
-        uint160 sqrtPriceX96,
-        uint128 liquidity,
-        int24 tick
-    );
+  /// @notice Emitted by the pool for any swaps between token0 and token1
+  /// @param sender The address that initiated the swap call, and that received the callback
+  /// @param recipient The address that received the output of the swap
+  /// @param amount0 The delta of the token0 balance of the pool
+  /// @param amount1 The delta of the token1 balance of the pool
+  /// @param sqrtPriceX96 The sqrt(price) of the pool after the swap, as a Q64.96
+  /// @param liquidity The liquidity of the pool after the swap
+  /// @param tick The log base 1.0001 of price of the pool after the swap
+  event Swap(
+    address indexed sender,
+    address indexed recipient,
+    int256 amount0,
+    int256 amount1,
+    uint160 sqrtPriceX96,
+    uint128 liquidity,
+    int24 tick
+  );
 
-    /// @notice Emitted by the pool for any flashes of token0/token1
-    /// @param sender The address that initiated the swap call, and that received the callback
-    /// @param recipient The address that received the tokens from flash
-    /// @param amount0 The amount of token0 that was flashed
-    /// @param amount1 The amount of token1 that was flashed
-    /// @param paid0 The amount of token0 paid for the flash, which can exceed the amount0 plus the fee
-    /// @param paid1 The amount of token1 paid for the flash, which can exceed the amount1 plus the fee
-    event Flash(
-        address indexed sender,
-        address indexed recipient,
-        uint256 amount0,
-        uint256 amount1,
-        uint256 paid0,
-        uint256 paid1
-    );
+  /// @notice Emitted by the pool for any flashes of token0/token1
+  /// @param sender The address that initiated the swap call, and that received the callback
+  /// @param recipient The address that received the tokens from flash
+  /// @param amount0 The amount of token0 that was flashed
+  /// @param amount1 The amount of token1 that was flashed
+  /// @param paid0 The amount of token0 paid for the flash, which can exceed the amount0 plus the fee
+  /// @param paid1 The amount of token1 paid for the flash, which can exceed the amount1 plus the fee
+  event Flash(
+    address indexed sender, address indexed recipient, uint256 amount0, uint256 amount1, uint256 paid0, uint256 paid1
+  );
 
-    /// @notice Emitted by the pool for increases to the number of observations that can be stored
-    /// @dev observationCardinalityNext is not the observation cardinality until an observation is written at the index
-    /// just before a mint/swap/burn.
-    /// @param observationCardinalityNextOld The previous value of the next observation cardinality
-    /// @param observationCardinalityNextNew The updated value of the next observation cardinality
-    event IncreaseObservationCardinalityNext(
-        uint16 observationCardinalityNextOld,
-        uint16 observationCardinalityNextNew
-    );
+  /// @notice Emitted by the pool for increases to the number of observations that can be stored
+  /// @dev observationCardinalityNext is not the observation cardinality until an observation is written at the index
+  /// just before a mint/swap/burn.
+  /// @param observationCardinalityNextOld The previous value of the next observation cardinality
+  /// @param observationCardinalityNextNew The updated value of the next observation cardinality
+  event IncreaseObservationCardinalityNext(uint16 observationCardinalityNextOld, uint16 observationCardinalityNextNew);
 
-    /// @notice Emitted when the protocol fee is changed by the pool
-    /// @param feeProtocol0Old The previous value of the token0 protocol fee
-    /// @param feeProtocol1Old The previous value of the token1 protocol fee
-    /// @param feeProtocol0New The updated value of the token0 protocol fee
-    /// @param feeProtocol1New The updated value of the token1 protocol fee
-    event SetFeeProtocol(uint8 feeProtocol0Old, uint8 feeProtocol1Old, uint8 feeProtocol0New, uint8 feeProtocol1New);
+  /// @notice Emitted when the protocol fee is changed by the pool
+  /// @param feeProtocol0Old The previous value of the token0 protocol fee
+  /// @param feeProtocol1Old The previous value of the token1 protocol fee
+  /// @param feeProtocol0New The updated value of the token0 protocol fee
+  /// @param feeProtocol1New The updated value of the token1 protocol fee
+  event SetFeeProtocol(uint8 feeProtocol0Old, uint8 feeProtocol1Old, uint8 feeProtocol0New, uint8 feeProtocol1New);
 
-    /// @notice Emitted when the collected protocol fees are withdrawn by the factory owner
-    /// @param sender The address that collects the protocol fees
-    /// @param recipient The address that receives the collected protocol fees
-    /// @param amount0 The amount of token0 protocol fees that is withdrawn
-    /// @param amount0 The amount of token1 protocol fees that is withdrawn
-    event CollectProtocol(address indexed sender, address indexed recipient, uint128 amount0, uint128 amount1);
+  /// @notice Emitted when the collected protocol fees are withdrawn by the factory owner
+  /// @param sender The address that collects the protocol fees
+  /// @param recipient The address that receives the collected protocol fees
+  /// @param amount0 The amount of token0 protocol fees that is withdrawn
+  /// @param amount0 The amount of token1 protocol fees that is withdrawn
+  event CollectProtocol(address indexed sender, address indexed recipient, uint128 amount0, uint128 amount1);
 }
diff --git a/src/periphery/UniswapV3/interfaces/pool/IUniswapV3PoolImmutables.sol b/src/periphery/UniswapV3/interfaces/pool/IUniswapV3PoolImmutables.sol
index c9beb15..0ba3c89 100644
--- a/src/periphery/UniswapV3/interfaces/pool/IUniswapV3PoolImmutables.sol
+++ b/src/periphery/UniswapV3/interfaces/pool/IUniswapV3PoolImmutables.sol
@@ -4,32 +4,32 @@ pragma solidity >=0.5.0;
 /// @title Pool state that never changes
 /// @notice These parameters are fixed for a pool forever, i.e., the methods will always return the same values
 interface IUniswapV3PoolImmutables {
-    /// @notice The contract that deployed the pool, which must adhere to the IUniswapV3Factory interface
-    /// @return The contract address
-    function factory() external view returns (address);
+  /// @notice The contract that deployed the pool, which must adhere to the IUniswapV3Factory interface
+  /// @return The contract address
+  function factory() external view returns (address);
 
-    /// @notice The first of the two tokens of the pool, sorted by address
-    /// @return The token contract address
-    function token0() external view returns (address);
+  /// @notice The first of the two tokens of the pool, sorted by address
+  /// @return The token contract address
+  function token0() external view returns (address);
 
-    /// @notice The second of the two tokens of the pool, sorted by address
-    /// @return The token contract address
-    function token1() external view returns (address);
+  /// @notice The second of the two tokens of the pool, sorted by address
+  /// @return The token contract address
+  function token1() external view returns (address);
 
-    /// @notice The pool's fee in hundredths of a bip, i.e. 1e-6
-    /// @return The fee
-    function fee() external view returns (uint24);
+  /// @notice The pool's fee in hundredths of a bip, i.e. 1e-6
+  /// @return The fee
+  function fee() external view returns (uint24);
 
-    /// @notice The pool tick spacing
-    /// @dev Ticks can only be used at multiples of this value, minimum of 1 and always positive
-    /// e.g.: a tickSpacing of 3 means ticks can be initialized every 3rd tick, i.e., ..., -6, -3, 0, 3, 6, ...
-    /// This value is an int24 to avoid casting even though it is always positive.
-    /// @return The tick spacing
-    function tickSpacing() external view returns (int24);
+  /// @notice The pool tick spacing
+  /// @dev Ticks can only be used at multiples of this value, minimum of 1 and always positive
+  /// e.g.: a tickSpacing of 3 means ticks can be initialized every 3rd tick, i.e., ..., -6, -3, 0, 3, 6, ...
+  /// This value is an int24 to avoid casting even though it is always positive.
+  /// @return The tick spacing
+  function tickSpacing() external view returns (int24);
 
-    /// @notice The maximum amount of position liquidity that can use any tick in the range
-    /// @dev This parameter is enforced per tick to prevent liquidity from overflowing a uint128 at any point, and
-    /// also prevents out-of-range liquidity from being used to prevent adding in-range liquidity to a pool
-    /// @return The max amount of liquidity per tick
-    function maxLiquidityPerTick() external view returns (uint128);
+  /// @notice The maximum amount of position liquidity that can use any tick in the range
+  /// @dev This parameter is enforced per tick to prevent liquidity from overflowing a uint128 at any point, and
+  /// also prevents out-of-range liquidity from being used to prevent adding in-range liquidity to a pool
+  /// @return The max amount of liquidity per tick
+  function maxLiquidityPerTick() external view returns (uint128);
 }
diff --git a/src/periphery/UniswapV3/interfaces/pool/IUniswapV3PoolOwnerActions.sol b/src/periphery/UniswapV3/interfaces/pool/IUniswapV3PoolOwnerActions.sol
index 2395ed3..e802db3 100644
--- a/src/periphery/UniswapV3/interfaces/pool/IUniswapV3PoolOwnerActions.sol
+++ b/src/periphery/UniswapV3/interfaces/pool/IUniswapV3PoolOwnerActions.sol
@@ -4,20 +4,22 @@ pragma solidity >=0.5.0;
 /// @title Permissioned pool actions
 /// @notice Contains pool methods that may only be called by the factory owner
 interface IUniswapV3PoolOwnerActions {
-    /// @notice Set the denominator of the protocol's % share of the fees
-    /// @param feeProtocol0 new protocol fee for token0 of the pool
-    /// @param feeProtocol1 new protocol fee for token1 of the pool
-    function setFeeProtocol(uint8 feeProtocol0, uint8 feeProtocol1) external;
+  /// @notice Set the denominator of the protocol's % share of the fees
+  /// @param feeProtocol0 new protocol fee for token0 of the pool
+  /// @param feeProtocol1 new protocol fee for token1 of the pool
+  function setFeeProtocol(uint8 feeProtocol0, uint8 feeProtocol1) external;
 
-    /// @notice Collect the protocol fee accrued to the pool
-    /// @param recipient The address to which collected protocol fees should be sent
-    /// @param amount0Requested The maximum amount of token0 to send, can be 0 to collect fees in only token1
-    /// @param amount1Requested The maximum amount of token1 to send, can be 0 to collect fees in only token0
-    /// @return amount0 The protocol fee collected in token0
-    /// @return amount1 The protocol fee collected in token1
-    function collectProtocol(
-        address recipient,
-        uint128 amount0Requested,
-        uint128 amount1Requested
-    ) external returns (uint128 amount0, uint128 amount1);
+  /// @notice Collect the protocol fee accrued to the pool
+  /// @param recipient The address to which collected protocol fees should be sent
+  /// @param amount0Requested The maximum amount of token0 to send, can be 0 to collect fees in only token1
+  /// @param amount1Requested The maximum amount of token1 to send, can be 0 to collect fees in only token0
+  /// @return amount0 The protocol fee collected in token0
+  /// @return amount1 The protocol fee collected in token1
+  function collectProtocol(
+    address recipient,
+    uint128 amount0Requested,
+    uint128 amount1Requested
+  )
+    external
+    returns (uint128 amount0, uint128 amount1);
 }
diff --git a/src/periphery/UniswapV3/interfaces/pool/IUniswapV3PoolState.sol b/src/periphery/UniswapV3/interfaces/pool/IUniswapV3PoolState.sol
index 620256c..0a999ae 100644
--- a/src/periphery/UniswapV3/interfaces/pool/IUniswapV3PoolState.sol
+++ b/src/periphery/UniswapV3/interfaces/pool/IUniswapV3PoolState.sol
@@ -5,112 +5,114 @@ pragma solidity >=0.5.0;
 /// @notice These methods compose the pool's state, and can change with any frequency including multiple times
 /// per transaction
 interface IUniswapV3PoolState {
-    /// @notice The 0th storage slot in the pool stores many values, and is exposed as a single method to save gas
-    /// when accessed externally.
-    /// @return sqrtPriceX96 The current price of the pool as a sqrt(token1/token0) Q64.96 value
-    /// tick The current tick of the pool, i.e. according to the last tick transition that was run.
-    /// This value may not always be equal to SqrtTickMath.getTickAtSqrtRatio(sqrtPriceX96) if the price is on a tick
-    /// boundary.
-    /// observationIndex The index of the last oracle observation that was written,
-    /// observationCardinality The current maximum number of observations stored in the pool,
-    /// observationCardinalityNext The next maximum number of observations, to be updated when the observation.
-    /// feeProtocol The protocol fee for both tokens of the pool.
-    /// Encoded as two 4 bit values, where the protocol fee of token1 is shifted 4 bits and the protocol fee of token0
-    /// is the lower 4 bits. Used as the denominator of a fraction of the swap fee, e.g. 4 means 1/4th of the swap fee.
-    /// unlocked Whether the pool is currently locked to reentrancy
-    function slot0()
-        external
-        view
-        returns (
-            uint160 sqrtPriceX96,
-            int24 tick,
-            uint16 observationIndex,
-            uint16 observationCardinality,
-            uint16 observationCardinalityNext,
-            uint8 feeProtocol,
-            bool unlocked
-        );
+  /// @notice The 0th storage slot in the pool stores many values, and is exposed as a single method to save gas
+  /// when accessed externally.
+  /// @return sqrtPriceX96 The current price of the pool as a sqrt(token1/token0) Q64.96 value
+  /// tick The current tick of the pool, i.e. according to the last tick transition that was run.
+  /// This value may not always be equal to SqrtTickMath.getTickAtSqrtRatio(sqrtPriceX96) if the price is on a tick
+  /// boundary.
+  /// observationIndex The index of the last oracle observation that was written,
+  /// observationCardinality The current maximum number of observations stored in the pool,
+  /// observationCardinalityNext The next maximum number of observations, to be updated when the observation.
+  /// feeProtocol The protocol fee for both tokens of the pool.
+  /// Encoded as two 4 bit values, where the protocol fee of token1 is shifted 4 bits and the protocol fee of token0
+  /// is the lower 4 bits. Used as the denominator of a fraction of the swap fee, e.g. 4 means 1/4th of the swap fee.
+  /// unlocked Whether the pool is currently locked to reentrancy
+  function slot0()
+    external
+    view
+    returns (
+      uint160 sqrtPriceX96,
+      int24 tick,
+      uint16 observationIndex,
+      uint16 observationCardinality,
+      uint16 observationCardinalityNext,
+      uint8 feeProtocol,
+      bool unlocked
+    );
 
-    /// @notice The fee growth as a Q128.128 fees of token0 collected per unit of liquidity for the entire life of the pool
-    /// @dev This value can overflow the uint256
-    function feeGrowthGlobal0X128() external view returns (uint256);
+  /// @notice The fee growth as a Q128.128 fees of token0 collected per unit of liquidity for the entire life of the
+  /// pool
+  /// @dev This value can overflow the uint256
+  function feeGrowthGlobal0X128() external view returns (uint256);
 
-    /// @notice The fee growth as a Q128.128 fees of token1 collected per unit of liquidity for the entire life of the pool
-    /// @dev This value can overflow the uint256
-    function feeGrowthGlobal1X128() external view returns (uint256);
+  /// @notice The fee growth as a Q128.128 fees of token1 collected per unit of liquidity for the entire life of the
+  /// pool
+  /// @dev This value can overflow the uint256
+  function feeGrowthGlobal1X128() external view returns (uint256);
 
-    /// @notice The amounts of token0 and token1 that are owed to the protocol
-    /// @dev Protocol fees will never exceed uint128 max in either token
-    function protocolFees() external view returns (uint128 token0, uint128 token1);
+  /// @notice The amounts of token0 and token1 that are owed to the protocol
+  /// @dev Protocol fees will never exceed uint128 max in either token
+  function protocolFees() external view returns (uint128 token0, uint128 token1);
 
-    /// @notice The currently in range liquidity available to the pool
-    /// @dev This value has no relationship to the total liquidity across all ticks
-    function liquidity() external view returns (uint128);
+  /// @notice The currently in range liquidity available to the pool
+  /// @dev This value has no relationship to the total liquidity across all ticks
+  function liquidity() external view returns (uint128);
 
-    /// @notice Look up information about a specific tick in the pool
-    /// @param tick The tick to look up
-    /// @return liquidityGross the total amount of position liquidity that uses the pool either as tick lower or
-    /// tick upper,
-    /// liquidityNet how much liquidity changes when the pool price crosses the tick,
-    /// feeGrowthOutside0X128 the fee growth on the other side of the tick from the current tick in token0,
-    /// feeGrowthOutside1X128 the fee growth on the other side of the tick from the current tick in token1,
-    /// tickCumulativeOutside the cumulative tick value on the other side of the tick from the current tick
-    /// secondsPerLiquidityOutsideX128 the seconds spent per liquidity on the other side of the tick from the current tick,
-    /// secondsOutside the seconds spent on the other side of the tick from the current tick,
-    /// initialized Set to true if the tick is initialized, i.e. liquidityGross is greater than 0, otherwise equal to false.
-    /// Outside values can only be used if the tick is initialized, i.e. if liquidityGross is greater than 0.
-    /// In addition, these values are only relative and must be used only in comparison to previous snapshots for
-    /// a specific position.
-    function ticks(int24 tick)
-        external
-        view
-        returns (
-            uint128 liquidityGross,
-            int128 liquidityNet,
-            uint256 feeGrowthOutside0X128,
-            uint256 feeGrowthOutside1X128,
-            int56 tickCumulativeOutside,
-            uint160 secondsPerLiquidityOutsideX128,
-            uint32 secondsOutside,
-            bool initialized
-        );
+  /// @notice Look up information about a specific tick in the pool
+  /// @param tick The tick to look up
+  /// @return liquidityGross the total amount of position liquidity that uses the pool either as tick lower or
+  /// tick upper,
+  /// liquidityNet how much liquidity changes when the pool price crosses the tick,
+  /// feeGrowthOutside0X128 the fee growth on the other side of the tick from the current tick in token0,
+  /// feeGrowthOutside1X128 the fee growth on the other side of the tick from the current tick in token1,
+  /// tickCumulativeOutside the cumulative tick value on the other side of the tick from the current tick
+  /// secondsPerLiquidityOutsideX128 the seconds spent per liquidity on the other side of the tick from the current
+  /// tick,
+  /// secondsOutside the seconds spent on the other side of the tick from the current tick,
+  /// initialized Set to true if the tick is initialized, i.e. liquidityGross is greater than 0, otherwise equal to
+  /// false.
+  /// Outside values can only be used if the tick is initialized, i.e. if liquidityGross is greater than 0.
+  /// In addition, these values are only relative and must be used only in comparison to previous snapshots for
+  /// a specific position.
+  function ticks(int24 tick)
+    external
+    view
+    returns (
+      uint128 liquidityGross,
+      int128 liquidityNet,
+      uint256 feeGrowthOutside0X128,
+      uint256 feeGrowthOutside1X128,
+      int56 tickCumulativeOutside,
+      uint160 secondsPerLiquidityOutsideX128,
+      uint32 secondsOutside,
+      bool initialized
+    );
 
-    /// @notice Returns 256 packed tick initialized boolean values. See TickBitmap for more information
-    function tickBitmap(int16 wordPosition) external view returns (uint256);
+  /// @notice Returns 256 packed tick initialized boolean values. See TickBitmap for more information
+  function tickBitmap(int16 wordPosition) external view returns (uint256);
 
-    /// @notice Returns the information about a position by the position's key
-    /// @param key The position's key is a hash of a preimage composed by the owner, tickLower and tickUpper
-    /// @return _liquidity The amount of liquidity in the position,
-    /// Returns feeGrowthInside0LastX128 fee growth of token0 inside the tick range as of the last mint/burn/poke,
-    /// Returns feeGrowthInside1LastX128 fee growth of token1 inside the tick range as of the last mint/burn/poke,
-    /// Returns tokensOwed0 the computed amount of token0 owed to the position as of the last mint/burn/poke,
-    /// Returns tokensOwed1 the computed amount of token1 owed to the position as of the last mint/burn/poke
-    function positions(bytes32 key)
-        external
-        view
-        returns (
-            uint128 _liquidity,
-            uint256 feeGrowthInside0LastX128,
-            uint256 feeGrowthInside1LastX128,
-            uint128 tokensOwed0,
-            uint128 tokensOwed1
-        );
+  /// @notice Returns the information about a position by the position's key
+  /// @param key The position's key is a hash of a preimage composed by the owner, tickLower and tickUpper
+  /// @return _liquidity The amount of liquidity in the position,
+  /// Returns feeGrowthInside0LastX128 fee growth of token0 inside the tick range as of the last mint/burn/poke,
+  /// Returns feeGrowthInside1LastX128 fee growth of token1 inside the tick range as of the last mint/burn/poke,
+  /// Returns tokensOwed0 the computed amount of token0 owed to the position as of the last mint/burn/poke,
+  /// Returns tokensOwed1 the computed amount of token1 owed to the position as of the last mint/burn/poke
+  function positions(bytes32 key)
+    external
+    view
+    returns (
+      uint128 _liquidity,
+      uint256 feeGrowthInside0LastX128,
+      uint256 feeGrowthInside1LastX128,
+      uint128 tokensOwed0,
+      uint128 tokensOwed1
+    );
 
-    /// @notice Returns data about a specific observation index
-    /// @param index The element of the observations array to fetch
-    /// @dev You most likely want to use #observe() instead of this method to get an observation as of some amount of time
-    /// ago, rather than at a specific index in the array.
-    /// @return blockTimestamp The timestamp of the observation,
-    /// Returns tickCumulative the tick multiplied by seconds elapsed for the life of the pool as of the observation timestamp,
-    /// Returns secondsPerLiquidityCumulativeX128 the seconds per in range liquidity for the life of the pool as of the observation timestamp,
-    /// Returns initialized whether the observation has been initialized and the values are safe to use
-    function observations(uint256 index)
-        external
-        view
-        returns (
-            uint32 blockTimestamp,
-            int56 tickCumulative,
-            uint160 secondsPerLiquidityCumulativeX128,
-            bool initialized
-        );
+  /// @notice Returns data about a specific observation index
+  /// @param index The element of the observations array to fetch
+  /// @dev You most likely want to use #observe() instead of this method to get an observation as of some amount of
+  /// time
+  /// ago, rather than at a specific index in the array.
+  /// @return blockTimestamp The timestamp of the observation,
+  /// Returns tickCumulative the tick multiplied by seconds elapsed for the life of the pool as of the observation
+  /// timestamp,
+  /// Returns secondsPerLiquidityCumulativeX128 the seconds per in range liquidity for the life of the pool as of the
+  /// observation timestamp,
+  /// Returns initialized whether the observation has been initialized and the values are safe to use
+  function observations(uint256 index)
+    external
+    view
+    returns (uint32 blockTimestamp, int56 tickCumulative, uint160 secondsPerLiquidityCumulativeX128, bool initialized);
 }
diff --git a/src/periphery/UniswapV3/libraries/PoolAddress.sol b/src/periphery/UniswapV3/libraries/PoolAddress.sol
index a746765..1263212 100644
--- a/src/periphery/UniswapV3/libraries/PoolAddress.sol
+++ b/src/periphery/UniswapV3/libraries/PoolAddress.sol
@@ -3,48 +3,41 @@ pragma solidity >=0.5.0;
 
 /// @title Provides functions for deriving a pool address from the factory, tokens, and the fee
 library PoolAddress {
-    bytes32 internal constant POOL_INIT_CODE_HASH = 0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54;
+  bytes32 internal constant POOL_INIT_CODE_HASH = 0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54;
 
-    /// @notice The identifying key of the pool
-    struct PoolKey {
-        address token0;
-        address token1;
-        uint24 fee;
-    }
+  /// @notice The identifying key of the pool
+  struct PoolKey {
+    address token0;
+    address token1;
+    uint24 fee;
+  }
 
-    /// @notice Returns PoolKey: the ordered tokens with the matched fee levels
-    /// @param tokenA The first token of a pool, unsorted
-    /// @param tokenB The second token of a pool, unsorted
-    /// @param fee The fee level of the pool
-    /// @return Poolkey The pool details with ordered token0 and token1 assignments
-    function getPoolKey(
-        address tokenA,
-        address tokenB,
-        uint24 fee
-    ) internal pure returns (PoolKey memory) {
-        if (tokenA > tokenB) (tokenA, tokenB) = (tokenB, tokenA);
-        return PoolKey({ token0: tokenA, token1: tokenB, fee: fee });
-    }
+  /// @notice Returns PoolKey: the ordered tokens with the matched fee levels
+  /// @param tokenA The first token of a pool, unsorted
+  /// @param tokenB The second token of a pool, unsorted
+  /// @param fee The fee level of the pool
+  /// @return Poolkey The pool details with ordered token0 and token1 assignments
+  function getPoolKey(address tokenA, address tokenB, uint24 fee) internal pure returns (PoolKey memory) {
+    if (tokenA > tokenB) (tokenA, tokenB) = (tokenB, tokenA);
+    return PoolKey({token0: tokenA, token1: tokenB, fee: fee});
+  }
 
-    /// @notice Deterministically computes the pool address given the factory and PoolKey
-    /// @param factory The Uniswap V3 factory contract address
-    /// @param key The PoolKey
-    /// @return pool The contract address of the V3 pool
-    function computeAddress(address factory, PoolKey memory key) internal pure returns (address pool) {
-        require(key.token0 < key.token1);
-        pool = address(
-            uint160(
-                uint256(
-                    keccak256(
-                        abi.encodePacked(
-                            hex"ff",
-                            factory,
-                            keccak256(abi.encode(key.token0, key.token1, key.fee)),
-                            POOL_INIT_CODE_HASH
-                        )
-                    )
-                )
+  /// @notice Deterministically computes the pool address given the factory and PoolKey
+  /// @param factory The Uniswap V3 factory contract address
+  /// @param key The PoolKey
+  /// @return pool The contract address of the V3 pool
+  function computeAddress(address factory, PoolKey memory key) internal pure returns (address pool) {
+    require(key.token0 < key.token1);
+    pool = address(
+      uint160(
+        uint256(
+          keccak256(
+            abi.encodePacked(
+              hex"ff", factory, keccak256(abi.encode(key.token0, key.token1, key.fee)), POOL_INIT_CODE_HASH
             )
-        );
-    }
+          )
+        )
+      )
+    );
+  }
 }
diff --git a/src/periphery/UniswapV3/libraries/TickMath.sol b/src/periphery/UniswapV3/libraries/TickMath.sol
index bee1fda..91ad99f 100644
--- a/src/periphery/UniswapV3/libraries/TickMath.sol
+++ b/src/periphery/UniswapV3/libraries/TickMath.sol
@@ -5,13 +5,13 @@ pragma solidity >=0.5.0;
 /// @notice Computes sqrt price for ticks of size 1.0001, i.e. sqrt(1.0001^tick) as fixed point Q64.96 numbers. Supports
 /// prices between 2**-128 and 2**128
 library TickMath {
-    /// @dev The minimum tick that may be passed to #getSqrtRatioAtTick computed from log base 1.0001 of 2**-128
-    int24 internal constant MIN_TICK = -887272;
-    /// @dev The maximum tick that may be passed to #getSqrtRatioAtTick computed from log base 1.0001 of 2**128
-    int24 internal constant MAX_TICK = -MIN_TICK;
+  /// @dev The minimum tick that may be passed to #getSqrtRatioAtTick computed from log base 1.0001 of 2**-128
+  int24 internal constant MIN_TICK = -887_272;
+  /// @dev The maximum tick that may be passed to #getSqrtRatioAtTick computed from log base 1.0001 of 2**128
+  int24 internal constant MAX_TICK = -MIN_TICK;
 
-    /// @dev The minimum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MIN_TICK)
-    uint160 internal constant MIN_SQRT_RATIO = 4295128739;
-    /// @dev The maximum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MAX_TICK)
-    uint160 internal constant MAX_SQRT_RATIO = 1461446703485210103287273052203988822378723970342;
+  /// @dev The minimum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MIN_TICK)
+  uint160 internal constant MIN_SQRT_RATIO = 4_295_128_739;
+  /// @dev The maximum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MAX_TICK)
+  uint160 internal constant MAX_SQRT_RATIO = 1_461_446_703_485_210_103_287_273_052_203_988_822_378_723_970_342;
 }
diff --git a/src/periphery/base/Payment.sol b/src/periphery/base/Payment.sol
deleted file mode 100644
index 15dc72b..0000000
--- a/src/periphery/base/Payment.sol
+++ /dev/null
@@ -1,74 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-pragma solidity ^0.8.4;
-
-import { IWETH9 } from "../interfaces/IWETH9.sol";
-
-import { SafeTransferLib } from "../../libraries/SafeTransferLib.sol";
-import { Balance } from "../../libraries/Balance.sol";
-
-/// @title   Payment contract
-/// @author  https://github.com/Uniswap/v3-periphery/blob/main/contracts/base/PeripheryPayments.sol
-/// @notice  Functions to ease deposits and withdrawals of ETH
-abstract contract Payment {
-    address public immutable weth;
-
-    error InsufficientOutputError();
-
-    constructor(address _weth) {
-        weth = _weth;
-    }
-
-    receive() external payable {
-        require(msg.sender == weth, "Not WETH9");
-    }
-
-    function unwrapWETH(uint256 amountMinimum, address recipient) public payable {
-        uint256 balanceWETH = Balance.balance(weth);
-        if (balanceWETH < amountMinimum) revert InsufficientOutputError();
-
-        if (balanceWETH > 0) {
-            IWETH9(weth).withdraw(balanceWETH);
-            SafeTransferLib.safeTransferETH(recipient, balanceWETH);
-        }
-    }
-
-    function sweepToken(
-        address token,
-        uint256 amountMinimum,
-        address recipient
-    ) public payable {
-        uint256 balanceToken = Balance.balance(token);
-        if (balanceToken < amountMinimum) revert InsufficientOutputError();
-
-        if (balanceToken > 0) {
-            SafeTransferLib.safeTransfer(token, recipient, balanceToken);
-        }
-    }
-
-    function refundETH() external payable {
-        if (address(this).balance > 0) SafeTransferLib.safeTransferETH(msg.sender, address(this).balance);
-    }
-
-    /// @param token The token to pay
-    /// @param payer The entity that must pay
-    /// @param recipient The entity that will receive payment
-    /// @param value The amount to pay
-    function pay(
-        address token,
-        address payer,
-        address recipient,
-        uint256 value
-    ) internal {
-        if (token == weth && address(this).balance >= value) {
-            // pay with WETH
-            IWETH9(weth).deposit{ value: value }(); // wrap only what is needed to pay
-            SafeTransferLib.safeTransfer(weth, recipient, value);
-        } else if (payer == address(this)) {
-            // pay with tokens already in the contract (for the exact input multihop case)
-            SafeTransferLib.safeTransfer(token, recipient, value);
-        } else {
-            // pull payment
-            SafeTransferLib.safeTransferFrom(token, payer, recipient, value);
-        }
-    }
-}
diff --git a/src/periphery/interfaces/IWETH9.sol b/src/periphery/interfaces/IWETH9.sol
index 4d05079..e6f15cd 100644
--- a/src/periphery/interfaces/IWETH9.sol
+++ b/src/periphery/interfaces/IWETH9.sol
@@ -4,9 +4,9 @@ pragma solidity >=0.8.6;
 /// @title   Interface for WETH9
 /// @author  Primitive
 interface IWETH9 {
-    /// @notice Wraps ETH into WETH
-    function deposit() external payable;
+  /// @notice Wraps ETH into WETH
+  function deposit() external payable;
 
-    /// @notice Unwraps WETH into ETH
-    function withdraw(uint256) external;
+  /// @notice Unwraps WETH into ETH
+  function withdraw(uint256) external;
 }
diff --git a/src/periphery/libraries/LendgineAddress.sol b/src/periphery/libraries/LendgineAddress.sol
index 2a343e3..bbab344 100644
--- a/src/periphery/libraries/LendgineAddress.sol
+++ b/src/periphery/libraries/LendgineAddress.sol
@@ -4,27 +4,31 @@ pragma solidity >=0.5.0;
 import { Lendgine } from "../../core/Lendgine.sol";
 
 library LendgineAddress {
-    function computeAddress(
-        address factory,
-        address token0,
-        address token1,
-        uint256 token0Exp,
-        uint256 token1Exp,
-        uint256 upperBound
-    ) internal pure returns (address lendgine) {
-        lendgine = address(
-            uint160(
-                uint256(
-                    keccak256(
-                        abi.encodePacked(
-                            hex"ff",
-                            factory,
-                            keccak256(abi.encode(token0, token1, token0Exp, token1Exp, upperBound)),
-                            keccak256(type(Lendgine).creationCode)
-                        )
-                    )
-                )
+  function computeAddress(
+    address factory,
+    address token0,
+    address token1,
+    uint256 token0Exp,
+    uint256 token1Exp,
+    uint256 upperBound
+  )
+    internal
+    pure
+    returns (address lendgine)
+  {
+    lendgine = address(
+      uint160(
+        uint256(
+          keccak256(
+            abi.encodePacked(
+              hex"ff",
+              factory,
+              keccak256(abi.encode(token0, token1, token0Exp, token1Exp, upperBound)),
+              keccak256(type(Lendgine).creationCode)
             )
-        );
-    }
+          )
+        )
+      )
+    );
+  }
 }
diff --git a/test/AccrueInterestTest.t.sol b/test/AccrueInterestTest.t.sol
index db43fdf..8b38451 100644
--- a/test/AccrueInterestTest.t.sol
+++ b/test/AccrueInterestTest.t.sol
@@ -6,73 +6,114 @@ import { Pair } from "../src/core/Pair.sol";
 import { TestHelper } from "./utils/TestHelper.sol";
 
 contract AccrueInterestTest is TestHelper {
-    event AccrueInterest(uint256 timeElapsed, uint256 collateral, uint256 liquidity);
+  event AccrueInterest(uint256 timeElapsed, uint256 collateral, uint256 liquidity);
 
-    function setUp() external {
-        _setUp();
-    }
+  function setUp() external {
+    _setUp();
+  }
 
-    function testAccrueNoLiquidity() external {
-        lendgine.accrueInterest();
+  function testAccrueNoLiquidity() external {
+    lendgine.accrueInterest();
 
-        assertEq(1, lendgine.lastUpdate());
-        assertEq(0, lendgine.rewardPerPositionStored());
-        assertEq(0, lendgine.totalLiquidityBorrowed());
-    }
+    assertEq(1, lendgine.lastUpdate());
+    assertEq(0, lendgine.rewardPerPositionStored());
+    assertEq(0, lendgine.totalLiquidityBorrowed());
+  }
 
-    function testAccrueNoTime() external {
-        _deposit(cuh, cuh, 1 ether, 8 ether, 1 ether);
-        _mint(cuh, cuh, 5 ether);
+  function testAccrueNoTime() external {
+    _deposit(cuh, cuh, 1 ether, 8 ether, 1 ether);
+    _mint(cuh, cuh, 5 ether);
 
-        lendgine.accrueInterest();
+    lendgine.accrueInterest();
 
-        assertEq(1, lendgine.lastUpdate());
-        assertEq(0, lendgine.rewardPerPositionStored());
-        assertEq(0.5 ether, lendgine.totalLiquidityBorrowed());
-    }
+    assertEq(1, lendgine.lastUpdate());
+    assertEq(0, lendgine.rewardPerPositionStored());
+    assertEq(0.5 ether, lendgine.totalLiquidityBorrowed());
+  }
 
-    function testAccrueInterest() external {
-        _deposit(cuh, cuh, 1 ether, 8 ether, 1 ether);
-        _mint(cuh, cuh, 5 ether);
+  function testAccrueInterest() external {
+    _deposit(cuh, cuh, 1 ether, 8 ether, 1 ether);
+    _mint(cuh, cuh, 5 ether);
 
-        vm.warp(365 days + 1);
+    vm.warp(365 days + 1);
 
-        lendgine.accrueInterest();
+    lendgine.accrueInterest();
 
-        uint256 borrowRate = lendgine.getBorrowRate(0.5 ether, 1 ether);
-        uint256 lpDilution = borrowRate / 2; // 0.5 lp for one year
-        uint256 token1Dilution = 10 * lpDilution; // same as rewardPerPosition because position size is 1
+    uint256 borrowRate = lendgine.getBorrowRate(0.5 ether, 1 ether);
+    uint256 lpDilution = borrowRate / 2; // 0.5 lp for one year
+    uint256 token1Dilution = 10 * lpDilution; // same as rewardPerPosition because position size is 1
 
-        assertEq(365 days + 1, lendgine.lastUpdate());
-        assertEq(0.5 ether - lpDilution, lendgine.totalLiquidityBorrowed());
-        assertEq(token1Dilution, lendgine.rewardPerPositionStored());
-    }
+    assertEq(365 days + 1, lendgine.lastUpdate());
+    assertEq(0.5 ether - lpDilution, lendgine.totalLiquidityBorrowed());
+    assertEq(token1Dilution, lendgine.rewardPerPositionStored());
+  }
 
-    function testMaxDilution() external {
-        _deposit(cuh, cuh, 1 ether, 8 ether, 1 ether);
-        _mint(cuh, cuh, 5 ether);
+  function testMaxDilution() external {
+    _deposit(cuh, cuh, 1 ether, 8 ether, 1 ether);
+    _mint(cuh, cuh, 5 ether);
 
-        vm.warp(730 days + 1);
+    vm.warp(730 days + 1);
 
-        lendgine.accrueInterest();
+    lendgine.accrueInterest();
 
-        assertEq(730 days + 1, lendgine.lastUpdate());
-        assertEq(0, lendgine.totalLiquidityBorrowed());
-        assertEq(5 ether, lendgine.rewardPerPositionStored());
-    }
+    assertEq(730 days + 1, lendgine.lastUpdate());
+    assertEq(0, lendgine.totalLiquidityBorrowed());
+    assertEq(5 ether, lendgine.rewardPerPositionStored());
+  }
 
-    function testLendgineEmit() external {
-        _deposit(cuh, cuh, 1 ether, 8 ether, 1 ether);
-        _mint(cuh, cuh, 5 ether);
+  function testLendgineEmit() external {
+    _deposit(cuh, cuh, 1 ether, 8 ether, 1 ether);
+    _mint(cuh, cuh, 5 ether);
 
-        vm.warp(365 days + 1);
+    vm.warp(365 days + 1);
 
-        uint256 borrowRate = lendgine.getBorrowRate(0.5 ether, 1 ether);
-        uint256 lpDilution = borrowRate / 2; // 0.5 lp for one year
-        uint256 token1Dilution = 10 * lpDilution; // same as rewardPerPosition because position size is 1
+    uint256 borrowRate = lendgine.getBorrowRate(0.5 ether, 1 ether);
+    uint256 lpDilution = borrowRate / 2; // 0.5 lp for one year
+    uint256 token1Dilution = 10 * lpDilution; // same as rewardPerPosition because position size is 1
 
-        vm.expectEmit(false, false, false, true, address(lendgine));
-        emit AccrueInterest(365 days, token1Dilution, lpDilution);
-        lendgine.accrueInterest();
-    }
+    vm.expectEmit(false, false, false, true, address(lendgine));
+    emit AccrueInterest(365 days, token1Dilution, lpDilution);
+    lendgine.accrueInterest();
+  }
+
+  function testNonStandardDecimals() external {
+    token1Scale = 9;
+
+    lendgine = Lendgine(factory.createLendgine(address(token0), address(token1), token0Scale, token1Scale, upperBound));
+
+    token0.mint(address(this), 1e18);
+    token1.mint(address(this), 8 * 1e9);
+
+    lendgine.deposit(
+      address(this),
+      1 ether,
+      abi.encode(
+        PairMintCallbackData({
+          token0: address(token0),
+          token1: address(token1),
+          amount0: 1e18,
+          amount1: 8 * 1e9,
+          payer: address(this)
+        })
+      )
+    );
+
+    token1.mint(cuh, 5 * 1e9);
+
+    vm.prank(cuh);
+    token1.approve(address(this), 5 * 1e9);
+    lendgine.mint(cuh, 5 * 1e9, abi.encode(MintCallbackData({token: address(token1), payer: cuh})));
+
+    vm.warp(365 days + 1);
+
+    lendgine.accrueInterest();
+
+    uint256 borrowRate = lendgine.getBorrowRate(0.5 ether, 1 ether);
+    uint256 lpDilution = borrowRate / 2; // 0.5 lp for one year
+    uint256 token1Dilution = lendgine.convertLiquidityToCollateral(lpDilution); // same as rewardPerPosition because
+    // position size is 1
+
+    assertEq(0.5 ether - lpDilution, lendgine.totalLiquidityBorrowed());
+    assertEq(token1Dilution, lendgine.rewardPerPositionStored());
+  }
 }
diff --git a/test/AccruePositionInterestTest.t.sol b/test/AccruePositionInterestTest.t.sol
index da2fcd3..48ea83e 100644
--- a/test/AccruePositionInterestTest.t.sol
+++ b/test/AccruePositionInterestTest.t.sol
@@ -7,63 +7,63 @@ import { Position } from "../src/core/libraries/Position.sol";
 import { TestHelper } from "./utils/TestHelper.sol";
 
 contract AccruePositionInterestTest is TestHelper {
-    event AccruePositionInterest(address indexed owner, uint256 rewardPerPosition);
+  event AccruePositionInterest(address indexed owner, uint256 rewardPerPosition);
 
-    function setUp() external {
-        _setUp();
-    }
+  function setUp() external {
+    _setUp();
+  }
 
-    function testNoPositionError() external {
-        vm.expectRevert(Position.NoPositionError.selector);
-        vm.prank(cuh);
-        lendgine.accruePositionInterest();
-    }
+  function testNoPositionError() external {
+    vm.expectRevert(Position.NoPositionError.selector);
+    vm.prank(cuh);
+    lendgine.accruePositionInterest();
+  }
 
-    function testNoTime() external {
-        _deposit(cuh, cuh, 1 ether, 8 ether, 1 ether);
-        _mint(cuh, cuh, 5 ether);
+  function testNoTime() external {
+    _deposit(cuh, cuh, 1 ether, 8 ether, 1 ether);
+    _mint(cuh, cuh, 5 ether);
 
-        vm.prank(cuh);
-        lendgine.accruePositionInterest();
+    vm.prank(cuh);
+    lendgine.accruePositionInterest();
 
-        (uint256 positionSize, uint256 rewardPerPositionPaid, uint256 tokensOwed) = lendgine.positions(cuh);
-        assertEq(1 ether, positionSize);
-        assertEq(0, rewardPerPositionPaid);
-        assertEq(0, tokensOwed);
-    }
+    (uint256 positionSize, uint256 rewardPerPositionPaid, uint256 tokensOwed) = lendgine.positions(cuh);
+    assertEq(1 ether, positionSize);
+    assertEq(0, rewardPerPositionPaid);
+    assertEq(0, tokensOwed);
+  }
 
-    function testAccruePositionInterest() external {
-        _deposit(cuh, cuh, 1 ether, 8 ether, 1 ether);
-        _mint(cuh, cuh, 5 ether);
+  function testAccruePositionInterest() external {
+    _deposit(cuh, cuh, 1 ether, 8 ether, 1 ether);
+    _mint(cuh, cuh, 5 ether);
 
-        vm.warp(365 days + 1);
+    vm.warp(365 days + 1);
 
-        vm.prank(cuh);
-        lendgine.accruePositionInterest();
+    vm.prank(cuh);
+    lendgine.accruePositionInterest();
 
-        uint256 borrowRate = lendgine.getBorrowRate(0.5 ether, 1 ether);
-        uint256 lpDilution = borrowRate / 2; // 0.5 lp for one year
-        uint256 token1Dilution = 10 * lpDilution; // same as rewardPerPosition because position size is 1
+    uint256 borrowRate = lendgine.getBorrowRate(0.5 ether, 1 ether);
+    uint256 lpDilution = borrowRate / 2; // 0.5 lp for one year
+    uint256 token1Dilution = 10 * lpDilution; // same as rewardPerPosition because position size is 1
 
-        (uint256 positionSize, uint256 rewardPerPositionPaid, uint256 tokensOwed) = lendgine.positions(cuh);
-        assertEq(1 ether, positionSize);
-        assertEq(token1Dilution, rewardPerPositionPaid);
-        assertEq(token1Dilution, tokensOwed);
-    }
+    (uint256 positionSize, uint256 rewardPerPositionPaid, uint256 tokensOwed) = lendgine.positions(cuh);
+    assertEq(1 ether, positionSize);
+    assertEq(token1Dilution, rewardPerPositionPaid);
+    assertEq(token1Dilution, tokensOwed);
+  }
 
-    function testLendgineEmit() external {
-        _deposit(cuh, cuh, 1 ether, 8 ether, 1 ether);
-        _mint(cuh, cuh, 5 ether);
+  function testLendgineEmit() external {
+    _deposit(cuh, cuh, 1 ether, 8 ether, 1 ether);
+    _mint(cuh, cuh, 5 ether);
 
-        vm.warp(365 days + 1);
+    vm.warp(365 days + 1);
 
-        uint256 borrowRate = lendgine.getBorrowRate(0.5 ether, 1 ether);
-        uint256 lpDilution = borrowRate / 2; // 0.5 lp for one year
-        uint256 token1Dilution = 10 * lpDilution; // same as rewardPerPosition because position size is 1
+    uint256 borrowRate = lendgine.getBorrowRate(0.5 ether, 1 ether);
+    uint256 lpDilution = borrowRate / 2; // 0.5 lp for one year
+    uint256 token1Dilution = 10 * lpDilution; // same as rewardPerPosition because position size is 1
 
-        vm.prank(cuh);
-        vm.expectEmit(true, false, false, true, address(lendgine));
-        emit AccruePositionInterest(cuh, token1Dilution);
-        lendgine.accruePositionInterest();
-    }
+    vm.prank(cuh);
+    vm.expectEmit(true, false, false, true, address(lendgine));
+    emit AccruePositionInterest(cuh, token1Dilution);
+    lendgine.accruePositionInterest();
+  }
 }
diff --git a/test/BurnTest.t.sol b/test/BurnTest.t.sol
index 12b72ae..aaf35db 100644
--- a/test/BurnTest.t.sol
+++ b/test/BurnTest.t.sol
@@ -7,261 +7,243 @@ import { TestHelper } from "./utils/TestHelper.sol";
 import { FullMath } from "../src/libraries/FullMath.sol";
 
 contract BurnTest is TestHelper {
-    event Burn(address indexed sender, uint256 collateral, uint256 shares, uint256 liquidity, address indexed to);
-
-    event Mint(uint256 amount0In, uint256 amount1In, uint256 liquidity);
-
-    function setUp() external {
-        _setUp();
-        _deposit(address(this), address(this), 1 ether, 8 ether, 1 ether);
-        _mint(cuh, cuh, 5 ether);
-    }
-
-    function testBurnPartial() external {
-        uint256 collateral = _burn(cuh, cuh, 0.25 ether, 0.25 ether, 2 ether);
-
-        // check returned tokens
-        assertEq(2.5 ether, collateral);
-        assertEq(0.25 ether, token0.balanceOf(cuh));
-        assertEq(2 ether + 2.5 ether, token1.balanceOf(cuh));
-
-        // check lendgine token
-        assertEq(0.25 ether, lendgine.totalSupply());
-        assertEq(0.25 ether, lendgine.balanceOf(cuh));
-
-        // check storage slots
-        assertEq(0.25 ether, lendgine.totalLiquidityBorrowed());
-        assertEq(0.75 ether, lendgine.totalLiquidity());
-        assertEq(0.75 ether, uint256(lendgine.reserve0()));
-        assertEq(6 ether, uint256(lendgine.reserve1()));
-
-        // check lendgine balances
-        assertEq(0.75 ether, token0.balanceOf(address(lendgine)));
-        assertEq(2.5 ether + 6 ether, token1.balanceOf(address(lendgine)));
-    }
-
-    function testBurnFull() external {
-        uint256 collateral = _burn(cuh, cuh, 0.5 ether, 0.5 ether, 4 ether);
-
-        // check returned tokens
-        assertEq(5 ether, collateral);
-        assertEq(0 ether, token0.balanceOf(cuh));
-        assertEq(5 ether, token1.balanceOf(cuh));
-
-        // check lendgine token
-        assertEq(0 ether, lendgine.totalSupply());
-        assertEq(0 ether, lendgine.balanceOf(cuh));
-
-        // check storage slots
-        assertEq(0 ether, lendgine.totalLiquidityBorrowed());
-        assertEq(1 ether, lendgine.totalLiquidity());
-        assertEq(1 ether, uint256(lendgine.reserve0()));
-        assertEq(8 ether, uint256(lendgine.reserve1()));
-
-        // check lendgine balances
-        assertEq(1 ether, token0.balanceOf(address(lendgine)));
-        assertEq(8 ether, token1.balanceOf(address(lendgine)));
-    }
-
-    function testZeroBurn() external {
-        vm.expectRevert(Lendgine.InputError.selector);
-        lendgine.burn(cuh, bytes(""));
-    }
-
-    function testUnderPay() external {
-        vm.prank(cuh);
-        lendgine.transfer(address(lendgine), 0.5 ether);
-
-        vm.startPrank(cuh);
-        token0.approve(address(this), 0.5 ether);
-        token1.approve(address(this), 3 ether);
-        vm.stopPrank();
-
-        vm.expectRevert(Pair.InvariantError.selector);
-        lendgine.burn(
-            cuh,
-            abi.encode(
-                PairMintCallbackData({
-                    token0: address(token0),
-                    token1: address(token1),
-                    amount0: 0.5 ether,
-                    amount1: 3 ether,
-                    payer: cuh
-                })
-            )
-        );
-    }
-
-    function testEmitLendgine() external {
-        vm.prank(cuh);
-        lendgine.transfer(address(lendgine), 0.5 ether);
-
-        vm.startPrank(cuh);
-        token0.approve(address(this), 0.5 ether);
-        token1.approve(address(this), 4 ether);
-        vm.stopPrank();
-
-        vm.expectEmit(true, true, false, true, address(lendgine));
-        emit Burn(address(this), 5 ether, 0.5 ether, 0.5 ether, cuh);
-        lendgine.burn(
-            cuh,
-            abi.encode(
-                PairMintCallbackData({
-                    token0: address(token0),
-                    token1: address(token1),
-                    amount0: 0.5 ether,
-                    amount1: 4 ether,
-                    payer: cuh
-                })
-            )
-        );
-    }
-
-    function testEmitPair() external {
-        vm.prank(cuh);
-        lendgine.transfer(address(lendgine), 0.5 ether);
-
-        vm.startPrank(cuh);
-        token0.approve(address(this), 0.5 ether);
-        token1.approve(address(this), 4 ether);
-        vm.stopPrank();
-
-        vm.expectEmit(false, false, false, true, address(lendgine));
-        emit Mint(0.5 ether, 4 ether, 0.5 ether);
-        lendgine.burn(
-            cuh,
-            abi.encode(
-                PairMintCallbackData({
-                    token0: address(token0),
-                    token1: address(token1),
-                    amount0: 0.5 ether,
-                    amount1: 4 ether,
-                    payer: cuh
-                })
-            )
-        );
-    }
-
-    function testAccrueOnBurn() external {
-        vm.warp(365 days + 1);
-        lendgine.accrueInterest();
-
-        uint256 reserve0 = lendgine.reserve0();
-        uint256 reserve1 = lendgine.reserve1();
-
-        uint256 amount0 = FullMath.mulDivRoundingUp(
-            reserve0,
-            lendgine.convertShareToLiquidity(0.5 ether),
-            lendgine.totalLiquidity()
-        );
-        uint256 amount1 = FullMath.mulDivRoundingUp(
-            reserve1,
-            lendgine.convertShareToLiquidity(0.5 ether),
-            lendgine.totalLiquidity()
-        );
-
-        _burn(cuh, cuh, 0.5 ether, amount0, amount1);
-
-        assertEq(365 days + 1, lendgine.lastUpdate());
-        assert(lendgine.rewardPerPositionStored() != 0);
-    }
-
-    function testProportionalBurn() external {
-        vm.warp(365 days + 1);
-        lendgine.accrueInterest();
-
-        uint256 borrowRate = lendgine.getBorrowRate(0.5 ether, 1 ether);
-        uint256 lpDilution = borrowRate / 2; // 0.5 lp for one year
-
-        uint256 reserve0 = lendgine.reserve0();
-        uint256 reserve1 = lendgine.reserve1();
-        uint256 shares = 0.25 ether;
-
-        uint256 amount0 = FullMath.mulDivRoundingUp(
-            reserve0,
-            lendgine.convertShareToLiquidity(shares),
-            lendgine.totalLiquidity()
-        );
-        uint256 amount1 = FullMath.mulDivRoundingUp(
-            reserve1,
-            lendgine.convertShareToLiquidity(shares),
-            lendgine.totalLiquidity()
-        );
-
-        uint256 collateral = _burn(cuh, cuh, shares, amount0, amount1);
-
-        // check collateral returned
-        assertEq(5 * (0.5 ether - lpDilution), collateral); // withdrew half the collateral
-
-        // check lendgine storage slots
-        assertEq((0.5 ether - lpDilution) / 2, lendgine.totalLiquidityBorrowed()); // withdrew half the liquidity
-        assertEq(0.5 ether + (0.5 ether - lpDilution) / 2, lendgine.totalLiquidity());
-    }
-
-    function testNonStandardDecimals() external {
-        token1Scale = 9;
-
-        lendgine = Lendgine(
-            factory.createLendgine(address(token0), address(token1), token0Scale, token1Scale, upperBound)
-        );
-
-        token0.mint(address(this), 1e18);
-        token1.mint(address(this), 8 * 1e9);
-
-        lendgine.deposit(
-            address(this),
-            1 ether,
-            abi.encode(
-                PairMintCallbackData({
-                    token0: address(token0),
-                    token1: address(token1),
-                    amount0: 1e18,
-                    amount1: 8 * 1e9,
-                    payer: address(this)
-                })
-            )
-        );
-
-        token1.mint(address(this), 5 * 1e9);
-
-        lendgine.mint(
-            address(this),
-            5 * 1e9,
-            abi.encode(MintCallbackData({ token: address(token1), payer: address(this) }))
-        );
-
-        lendgine.transfer(address(lendgine), 0.25 ether);
-
-        uint256 collateral = lendgine.burn(
-            address(this),
-            abi.encode(
-                PairMintCallbackData({
-                    token0: address(token0),
-                    token1: address(token1),
-                    amount0: 0.25 ether,
-                    amount1: 2 * 1e9,
-                    payer: address(this)
-                })
-            )
-        );
-
-        // check returned tokens
-        assertEq(2.5 * 1e9, collateral);
-        assertEq(0.25 ether, token0.balanceOf(address(this)));
-        assertEq(4.5 * 1e9, token1.balanceOf(address(this)));
-
-        // check lendgine token
-        assertEq(0.25 ether, lendgine.totalSupply());
-        assertEq(0.25 ether, lendgine.balanceOf(address(this)));
-
-        // check storage slots
-        assertEq(0.25 ether, lendgine.totalLiquidityBorrowed());
-        assertEq(0.75 ether, lendgine.totalLiquidity());
-        assertEq(0.75 ether, uint256(lendgine.reserve0()));
-        assertEq(6 * 1e9, uint256(lendgine.reserve1()));
-
-        // check lendgine balances
-        assertEq(0.75 ether, token0.balanceOf(address(lendgine)));
-        assertEq(8.5 * 1e9, token1.balanceOf(address(lendgine)));
-    }
+  event Burn(address indexed sender, uint256 collateral, uint256 shares, uint256 liquidity, address indexed to);
+
+  event Mint(uint256 amount0In, uint256 amount1In, uint256 liquidity);
+
+  function setUp() external {
+    _setUp();
+    _deposit(address(this), address(this), 1 ether, 8 ether, 1 ether);
+    _mint(cuh, cuh, 5 ether);
+  }
+
+  function testBurnPartial() external {
+    uint256 collateral = _burn(cuh, cuh, 0.25 ether, 0.25 ether, 2 ether);
+
+    // check returned tokens
+    assertEq(2.5 ether, collateral);
+    assertEq(0.25 ether, token0.balanceOf(cuh));
+    assertEq(2 ether + 2.5 ether, token1.balanceOf(cuh));
+
+    // check lendgine token
+    assertEq(0.25 ether, lendgine.totalSupply());
+    assertEq(0.25 ether, lendgine.balanceOf(cuh));
+
+    // check storage slots
+    assertEq(0.25 ether, lendgine.totalLiquidityBorrowed());
+    assertEq(0.75 ether, lendgine.totalLiquidity());
+    assertEq(0.75 ether, uint256(lendgine.reserve0()));
+    assertEq(6 ether, uint256(lendgine.reserve1()));
+
+    // check lendgine balances
+    assertEq(0.75 ether, token0.balanceOf(address(lendgine)));
+    assertEq(2.5 ether + 6 ether, token1.balanceOf(address(lendgine)));
+  }
+
+  function testBurnFull() external {
+    uint256 collateral = _burn(cuh, cuh, 0.5 ether, 0.5 ether, 4 ether);
+
+    // check returned tokens
+    assertEq(5 ether, collateral);
+    assertEq(0 ether, token0.balanceOf(cuh));
+    assertEq(5 ether, token1.balanceOf(cuh));
+
+    // check lendgine token
+    assertEq(0 ether, lendgine.totalSupply());
+    assertEq(0 ether, lendgine.balanceOf(cuh));
+
+    // check storage slots
+    assertEq(0 ether, lendgine.totalLiquidityBorrowed());
+    assertEq(1 ether, lendgine.totalLiquidity());
+    assertEq(1 ether, uint256(lendgine.reserve0()));
+    assertEq(8 ether, uint256(lendgine.reserve1()));
+
+    // check lendgine balances
+    assertEq(1 ether, token0.balanceOf(address(lendgine)));
+    assertEq(8 ether, token1.balanceOf(address(lendgine)));
+  }
+
+  function testZeroBurn() external {
+    vm.expectRevert(Lendgine.InputError.selector);
+    lendgine.burn(cuh, bytes(""));
+  }
+
+  function testUnderPay() external {
+    vm.prank(cuh);
+    lendgine.transfer(address(lendgine), 0.5 ether);
+
+    vm.startPrank(cuh);
+    token0.approve(address(this), 0.5 ether);
+    token1.approve(address(this), 3 ether);
+    vm.stopPrank();
+
+    vm.expectRevert(Pair.InvariantError.selector);
+    lendgine.burn(
+      cuh,
+      abi.encode(
+        PairMintCallbackData({
+          token0: address(token0),
+          token1: address(token1),
+          amount0: 0.5 ether,
+          amount1: 3 ether,
+          payer: cuh
+        })
+      )
+    );
+  }
+
+  function testEmitLendgine() external {
+    vm.prank(cuh);
+    lendgine.transfer(address(lendgine), 0.5 ether);
+
+    vm.startPrank(cuh);
+    token0.approve(address(this), 0.5 ether);
+    token1.approve(address(this), 4 ether);
+    vm.stopPrank();
+
+    vm.expectEmit(true, true, false, true, address(lendgine));
+    emit Burn(address(this), 5 ether, 0.5 ether, 0.5 ether, cuh);
+    lendgine.burn(
+      cuh,
+      abi.encode(
+        PairMintCallbackData({
+          token0: address(token0),
+          token1: address(token1),
+          amount0: 0.5 ether,
+          amount1: 4 ether,
+          payer: cuh
+        })
+      )
+    );
+  }
+
+  function testEmitPair() external {
+    vm.prank(cuh);
+    lendgine.transfer(address(lendgine), 0.5 ether);
+
+    vm.startPrank(cuh);
+    token0.approve(address(this), 0.5 ether);
+    token1.approve(address(this), 4 ether);
+    vm.stopPrank();
+
+    vm.expectEmit(false, false, false, true, address(lendgine));
+    emit Mint(0.5 ether, 4 ether, 0.5 ether);
+    lendgine.burn(
+      cuh,
+      abi.encode(
+        PairMintCallbackData({
+          token0: address(token0),
+          token1: address(token1),
+          amount0: 0.5 ether,
+          amount1: 4 ether,
+          payer: cuh
+        })
+      )
+    );
+  }
+
+  function testAccrueOnBurn() external {
+    vm.warp(365 days + 1);
+    lendgine.accrueInterest();
+
+    uint256 reserve0 = lendgine.reserve0();
+    uint256 reserve1 = lendgine.reserve1();
+
+    uint256 amount0 =
+      FullMath.mulDivRoundingUp(reserve0, lendgine.convertShareToLiquidity(0.5 ether), lendgine.totalLiquidity());
+    uint256 amount1 =
+      FullMath.mulDivRoundingUp(reserve1, lendgine.convertShareToLiquidity(0.5 ether), lendgine.totalLiquidity());
+
+    _burn(cuh, cuh, 0.5 ether, amount0, amount1);
+
+    assertEq(365 days + 1, lendgine.lastUpdate());
+    assert(lendgine.rewardPerPositionStored() != 0);
+  }
+
+  function testProportionalBurn() external {
+    vm.warp(365 days + 1);
+    lendgine.accrueInterest();
+
+    uint256 borrowRate = lendgine.getBorrowRate(0.5 ether, 1 ether);
+    uint256 lpDilution = borrowRate / 2; // 0.5 lp for one year
+
+    uint256 reserve0 = lendgine.reserve0();
+    uint256 reserve1 = lendgine.reserve1();
+    uint256 shares = 0.25 ether;
+
+    uint256 amount0 =
+      FullMath.mulDivRoundingUp(reserve0, lendgine.convertShareToLiquidity(shares), lendgine.totalLiquidity());
+    uint256 amount1 =
+      FullMath.mulDivRoundingUp(reserve1, lendgine.convertShareToLiquidity(shares), lendgine.totalLiquidity());
+
+    uint256 collateral = _burn(cuh, cuh, shares, amount0, amount1);
+
+    // check collateral returned
+    assertEq(5 * (0.5 ether - lpDilution), collateral); // withdrew half the collateral
+
+    // check lendgine storage slots
+    assertEq((0.5 ether - lpDilution) / 2, lendgine.totalLiquidityBorrowed()); // withdrew half the liquidity
+    assertEq(0.5 ether + (0.5 ether - lpDilution) / 2, lendgine.totalLiquidity());
+  }
+
+  function testNonStandardDecimals() external {
+    token1Scale = 9;
+
+    lendgine = Lendgine(factory.createLendgine(address(token0), address(token1), token0Scale, token1Scale, upperBound));
+
+    token0.mint(address(this), 1e18);
+    token1.mint(address(this), 8 * 1e9);
+
+    lendgine.deposit(
+      address(this),
+      1 ether,
+      abi.encode(
+        PairMintCallbackData({
+          token0: address(token0),
+          token1: address(token1),
+          amount0: 1e18,
+          amount1: 8 * 1e9,
+          payer: address(this)
+        })
+      )
+    );
+
+    token1.mint(address(this), 5 * 1e9);
+
+    lendgine.mint(address(this), 5 * 1e9, abi.encode(MintCallbackData({token: address(token1), payer: address(this)})));
+
+    lendgine.transfer(address(lendgine), 0.25 ether);
+
+    uint256 collateral = lendgine.burn(
+      address(this),
+      abi.encode(
+        PairMintCallbackData({
+          token0: address(token0),
+          token1: address(token1),
+          amount0: 0.25 ether,
+          amount1: 2 * 1e9,
+          payer: address(this)
+        })
+      )
+    );
+
+    // check returned tokens
+    assertEq(2.5 * 1e9, collateral);
+    assertEq(0.25 ether, token0.balanceOf(address(this)));
+    assertEq(4.5 * 1e9, token1.balanceOf(address(this)));
+
+    // check lendgine token
+    assertEq(0.25 ether, lendgine.totalSupply());
+    assertEq(0.25 ether, lendgine.balanceOf(address(this)));
+
+    // check storage slots
+    assertEq(0.25 ether, lendgine.totalLiquidityBorrowed());
+    assertEq(0.75 ether, lendgine.totalLiquidity());
+    assertEq(0.75 ether, uint256(lendgine.reserve0()));
+    assertEq(6 * 1e9, uint256(lendgine.reserve1()));
+
+    // check lendgine balances
+    assertEq(0.75 ether, token0.balanceOf(address(lendgine)));
+    assertEq(8.5 * 1e9, token1.balanceOf(address(lendgine)));
+  }
 }
diff --git a/test/CollectTest.t.sol b/test/CollectTest.t.sol
index 23dea4e..75c8c3e 100644
--- a/test/CollectTest.t.sol
+++ b/test/CollectTest.t.sol
@@ -6,87 +6,87 @@ import { Pair } from "../src/core/Pair.sol";
 import { TestHelper } from "./utils/TestHelper.sol";
 
 contract CollectTest is TestHelper {
-    event Collect(address indexed owner, address indexed to, uint256 amount);
+  event Collect(address indexed owner, address indexed to, uint256 amount);
 
-    function setUp() external {
-        _setUp();
-    }
+  function setUp() external {
+    _setUp();
+  }
 
-    function testZeroCollect() external {
-        uint256 collateral = lendgine.collect(cuh, 0);
-        assertEq(0, collateral);
+  function testZeroCollect() external {
+    uint256 collateral = lendgine.collect(cuh, 0);
+    assertEq(0, collateral);
 
-        collateral = lendgine.collect(cuh, 1 ether);
-        assertEq(0, collateral);
-    }
+    collateral = lendgine.collect(cuh, 1 ether);
+    assertEq(0, collateral);
+  }
 
-    function testCollectBasic() external {
-        _deposit(cuh, cuh, 1 ether, 8 ether, 1 ether);
-        _mint(address(this), address(this), 5 ether);
+  function testCollectBasic() external {
+    _deposit(cuh, cuh, 1 ether, 8 ether, 1 ether);
+    _mint(address(this), address(this), 5 ether);
 
-        vm.warp(365 days + 1);
+    vm.warp(365 days + 1);
 
-        uint256 borrowRate = lendgine.getBorrowRate(0.5 ether, 1 ether);
-        uint256 lpDilution = borrowRate / 2; // 0.5 lp for one year
+    uint256 borrowRate = lendgine.getBorrowRate(0.5 ether, 1 ether);
+    uint256 lpDilution = borrowRate / 2; // 0.5 lp for one year
 
-        vm.prank(cuh);
-        lendgine.accruePositionInterest();
+    vm.prank(cuh);
+    lendgine.accruePositionInterest();
 
-        vm.prank(cuh);
-        uint256 collateral = lendgine.collect(cuh, lpDilution * 5);
+    vm.prank(cuh);
+    uint256 collateral = lendgine.collect(cuh, lpDilution * 5);
 
-        // check return data
-        assertEq(lpDilution * 5, collateral);
+    // check return data
+    assertEq(lpDilution * 5, collateral);
 
-        // check position
-        (, , uint256 tokensOwed) = lendgine.positions(cuh);
-        assertEq(lpDilution * 5, tokensOwed);
+    // check position
+    (,, uint256 tokensOwed) = lendgine.positions(cuh);
+    assertEq(lpDilution * 5, tokensOwed);
 
-        // check token balances
-        assertEq(lpDilution * 5, token1.balanceOf(cuh));
-    }
+    // check token balances
+    assertEq(lpDilution * 5, token1.balanceOf(cuh));
+  }
 
-    function testOverCollect() external {
-        _deposit(cuh, cuh, 1 ether, 8 ether, 1 ether);
-        _mint(address(this), address(this), 5 ether);
+  function testOverCollect() external {
+    _deposit(cuh, cuh, 1 ether, 8 ether, 1 ether);
+    _mint(address(this), address(this), 5 ether);
 
-        vm.warp(365 days + 1);
+    vm.warp(365 days + 1);
 
-        uint256 borrowRate = lendgine.getBorrowRate(0.5 ether, 1 ether);
-        uint256 lpDilution = borrowRate / 2; // 0.5 lp for one year
+    uint256 borrowRate = lendgine.getBorrowRate(0.5 ether, 1 ether);
+    uint256 lpDilution = borrowRate / 2; // 0.5 lp for one year
 
-        vm.prank(cuh);
-        lendgine.accruePositionInterest();
+    vm.prank(cuh);
+    lendgine.accruePositionInterest();
 
-        vm.prank(cuh);
-        uint256 collateral = lendgine.collect(cuh, 100 ether);
+    vm.prank(cuh);
+    uint256 collateral = lendgine.collect(cuh, 100 ether);
 
-        // check return data
-        assertEq(lpDilution * 10, collateral);
+    // check return data
+    assertEq(lpDilution * 10, collateral);
 
-        // check position
-        (, , uint256 tokensOwed) = lendgine.positions(cuh);
-        assertEq(0, tokensOwed);
+    // check position
+    (,, uint256 tokensOwed) = lendgine.positions(cuh);
+    assertEq(0, tokensOwed);
 
-        // check token balances
-        assertEq(lpDilution * 10, token1.balanceOf(cuh));
-    }
+    // check token balances
+    assertEq(lpDilution * 10, token1.balanceOf(cuh));
+  }
 
-    function testEmit() external {
-        _deposit(cuh, cuh, 1 ether, 8 ether, 1 ether);
-        _mint(address(this), address(this), 5 ether);
+  function testEmit() external {
+    _deposit(cuh, cuh, 1 ether, 8 ether, 1 ether);
+    _mint(address(this), address(this), 5 ether);
 
-        vm.warp(365 days + 1);
+    vm.warp(365 days + 1);
 
-        uint256 borrowRate = lendgine.getBorrowRate(0.5 ether, 1 ether);
-        uint256 lpDilution = borrowRate / 2; // 0.5 lp for one year
+    uint256 borrowRate = lendgine.getBorrowRate(0.5 ether, 1 ether);
+    uint256 lpDilution = borrowRate / 2; // 0.5 lp for one year
 
-        vm.prank(cuh);
-        lendgine.accruePositionInterest();
+    vm.prank(cuh);
+    lendgine.accruePositionInterest();
 
-        vm.prank(cuh);
-        vm.expectEmit(true, true, false, true, address(lendgine));
-        emit Collect(cuh, cuh, lpDilution * 10);
-        lendgine.collect(cuh, lpDilution * 10);
-    }
+    vm.prank(cuh);
+    vm.expectEmit(true, true, false, true, address(lendgine));
+    emit Collect(cuh, cuh, lpDilution * 10);
+    lendgine.collect(cuh, lpDilution * 10);
+  }
 }
diff --git a/test/DepositTest.t.sol b/test/DepositTest.t.sol
index 4023512..153b64a 100644
--- a/test/DepositTest.t.sol
+++ b/test/DepositTest.t.sol
@@ -6,224 +6,231 @@ import { Pair } from "../src/core/Pair.sol";
 import { TestHelper } from "./utils/TestHelper.sol";
 
 contract DepositTest is TestHelper {
-    event Deposit(address indexed sender, uint256 size, uint256 liquidity, address indexed to);
-
-    event Mint(uint256 amount0In, uint256 amount1In, uint256 liquidity);
-
-    function setUp() external {
-        _setUp();
-    }
-
-    function testBasicDeposit() external {
-        uint256 size = _deposit(cuh, cuh, 1 ether, 8 ether, 1 ether);
-
-        // check lendgine storage slots
-        assertEq(1 ether, lendgine.totalLiquidity());
-        assertEq(1 ether, lendgine.totalPositionSize());
-        assertEq(1 ether, uint256(lendgine.reserve0()));
-        assertEq(8 ether, uint256(lendgine.reserve1()));
-
-        // check lendgine balances
-        assertEq(1 ether, token0.balanceOf(address(lendgine)));
-        assertEq(8 ether, token1.balanceOf(address(lendgine)));
-
-        // check position size
-        assertEq(1 ether, size);
-        (uint256 positionSize, , ) = lendgine.positions(cuh);
-        assertEq(1 ether, positionSize);
-    }
-
-    function testOverDeposit() external {
-        uint256 size = _deposit(cuh, cuh, 1 ether + 1, 8 ether + 1, 1 ether);
-
-        // check lendgine storage slots
-        assertEq(1 ether, lendgine.totalLiquidity());
-        assertEq(1 ether, lendgine.totalPositionSize());
-        assertEq(1 ether + 1, uint256(lendgine.reserve0()));
-        assertEq(8 ether + 1, uint256(lendgine.reserve1()));
-
-        // check lendgine balances
-        assertEq(1 ether + 1, token0.balanceOf(address(lendgine)));
-        assertEq(8 ether + 1, token1.balanceOf(address(lendgine)));
-
-        // check position size
-        assertEq(1 ether, size);
-        (uint256 positionSize, , ) = lendgine.positions(cuh);
-        assertEq(1 ether, positionSize);
-    }
-
-    function testZeroMint() external {
-        vm.expectRevert(Lendgine.InputError.selector);
-        lendgine.deposit(cuh, 0, bytes(""));
-    }
-
-    function testUnderPayment() external {
-        token0.mint(cuh, 1 ether);
-        token1.mint(cuh, 7 ether);
-
-        vm.startPrank(cuh);
-        token0.approve(address(this), 1 ether);
-        token1.approve(address(this), 7 ether);
-        vm.stopPrank();
-
-        vm.expectRevert(Pair.InvariantError.selector);
-        lendgine.deposit(
-            cuh,
-            1 ether,
-            abi.encode(
-                PairMintCallbackData({
-                    token0: address(token0),
-                    token1: address(token1),
-                    amount0: 1 ether,
-                    amount1: 7 ether,
-                    payer: cuh
-                })
-            )
-        );
-    }
-
-    function testEmitLendgine() external {
-        token0.mint(cuh, 1 ether);
-        token1.mint(cuh, 8 ether);
-
-        vm.startPrank(cuh);
-        token0.approve(address(this), 1 ether);
-        token1.approve(address(this), 8 ether);
-        vm.stopPrank();
-
-        vm.expectEmit(true, true, false, true, address(lendgine));
-        emit Deposit(address(this), 1 ether, 1 ether, cuh);
-        lendgine.deposit(
-            cuh,
-            1 ether,
-            abi.encode(
-                PairMintCallbackData({
-                    token0: address(token0),
-                    token1: address(token1),
-                    amount0: 1 ether,
-                    amount1: 8 ether,
-                    payer: cuh
-                })
-            )
-        );
-    }
-
-    function testEmitPair() external {
-        token0.mint(cuh, 1 ether);
-        token1.mint(cuh, 8 ether);
-
-        vm.startPrank(cuh);
-        token0.approve(address(this), 1 ether);
-        token1.approve(address(this), 8 ether);
-        vm.stopPrank();
-
-        vm.expectEmit(false, false, false, true, address(lendgine));
-        emit Mint(1 ether, 8 ether, 1 ether);
-        lendgine.deposit(
-            cuh,
-            1 ether,
-            abi.encode(
-                PairMintCallbackData({
-                    token0: address(token0),
-                    token1: address(token1),
-                    amount0: 1 ether,
-                    amount1: 8 ether,
-                    payer: cuh
-                })
-            )
-        );
-    }
-
-    function testAccrueOnDepositEmpty() external {
-        _deposit(cuh, cuh, 1 ether, 8 ether, 1 ether);
-
-        assertEq(1, lendgine.lastUpdate());
-    }
-
-    function testAccrueOnDeposit() external {
-        _deposit(address(this), address(this), 1 ether, 8 ether, 1 ether);
-        _mint(address(this), address(this), 5 ether);
-
-        vm.warp(365 days + 1);
-
-        _deposit(cuh, cuh, 1 ether, 8 ether, 1 ether);
-
-        assertEq(365 days + 1, lendgine.lastUpdate());
-        assert(lendgine.rewardPerPositionStored() != 0);
-    }
-
-    function testAccrueOnPositionDeposit() external {
-        _deposit(cuh, cuh, 1 ether, 8 ether, 1 ether);
-        _mint(address(this), address(this), 5 ether);
-
-        vm.warp(365 days + 1);
-
-        _deposit(cuh, cuh, 1 ether, 8 ether, 1 ether);
-
-        (, uint256 rewardPerPositionPaid, uint256 tokensOwed) = lendgine.positions(cuh);
-        assert(rewardPerPositionPaid != 0);
-        assert(tokensOwed != 0);
-    }
-
-    function testProportionPositionSize() external {
-        _deposit(address(this), address(this), 1 ether, 8 ether, 1 ether);
-        _mint(address(this), address(this), 5 ether);
-
-        vm.warp(365 days + 1);
-
-        uint256 size = _deposit(cuh, cuh, 1 ether, 8 ether, 1 ether);
-
-        uint256 borrowRate = lendgine.getBorrowRate(0.5 ether, 1 ether);
-        uint256 lpDilution = borrowRate / 2; // 0.5 lp for one year
-
-        // check position size
-        assertEq((1 ether * 1 ether) / (1 ether - lpDilution), size);
-        assertApproxEqAbs(1 ether, (size * (2 ether - lpDilution)) / (1 ether + size), 1);
-        (uint256 positionSize, , ) = lendgine.positions(cuh);
-        assertEq((1 ether * 1 ether) / (1 ether - lpDilution), positionSize);
-
-        // check lendgine storage slots
-        assertEq(1 ether + size, lendgine.totalPositionSize());
-        assertEq(1.5 ether, lendgine.totalLiquidity());
-    }
-
-    function testNonStandardDecimals() external {
-        token1Scale = 9;
-
-        lendgine = Lendgine(
-            factory.createLendgine(address(token0), address(token1), token0Scale, token1Scale, upperBound)
-        );
-
-        token0.mint(address(this), 1e18);
-        token1.mint(address(this), 8 * 1e9);
-
-        uint256 size = lendgine.deposit(
-            address(this),
-            1 ether,
-            abi.encode(
-                PairMintCallbackData({
-                    token0: address(token0),
-                    token1: address(token1),
-                    amount0: 1e18,
-                    amount1: 8 * 1e9,
-                    payer: address(this)
-                })
-            )
-        );
-
-        // check lendgine storage slots
-        assertEq(1 ether, lendgine.totalLiquidity());
-        assertEq(1 ether, lendgine.totalPositionSize());
-        assertEq(1 ether, uint256(lendgine.reserve0()));
-        assertEq(8 * 1e9, uint256(lendgine.reserve1()));
-
-        // check lendgine balances
-        assertEq(1 ether, token0.balanceOf(address(lendgine)));
-        assertEq(8 * 1e9, token1.balanceOf(address(lendgine)));
-
-        // check position size
-        assertEq(1 ether, size);
-        (uint256 positionSize, , ) = lendgine.positions(address(this));
-        assertEq(1 ether, positionSize);
-    }
+  event Deposit(address indexed sender, uint256 size, uint256 liquidity, address indexed to);
+
+  event Mint(uint256 amount0In, uint256 amount1In, uint256 liquidity);
+
+  function setUp() external {
+    _setUp();
+  }
+
+  function testBasicDeposit() external {
+    uint256 size = _deposit(cuh, cuh, 1 ether, 8 ether, 1 ether);
+
+    // check lendgine storage slots
+    assertEq(1 ether, lendgine.totalLiquidity());
+    assertEq(1 ether, lendgine.totalPositionSize());
+    assertEq(1 ether, uint256(lendgine.reserve0()));
+    assertEq(8 ether, uint256(lendgine.reserve1()));
+
+    // check lendgine balances
+    assertEq(1 ether, token0.balanceOf(address(lendgine)));
+    assertEq(8 ether, token1.balanceOf(address(lendgine)));
+
+    // check position size
+    assertEq(1 ether, size);
+    (uint256 positionSize,,) = lendgine.positions(cuh);
+    assertEq(1 ether, positionSize);
+  }
+
+  function testOverDeposit() external {
+    uint256 size = _deposit(cuh, cuh, 1 ether + 1, 8 ether + 1, 1 ether);
+
+    // check lendgine storage slots
+    assertEq(1 ether, lendgine.totalLiquidity());
+    assertEq(1 ether, lendgine.totalPositionSize());
+    assertEq(1 ether + 1, uint256(lendgine.reserve0()));
+    assertEq(8 ether + 1, uint256(lendgine.reserve1()));
+
+    // check lendgine balances
+    assertEq(1 ether + 1, token0.balanceOf(address(lendgine)));
+    assertEq(8 ether + 1, token1.balanceOf(address(lendgine)));
+
+    // check position size
+    assertEq(1 ether, size);
+    (uint256 positionSize,,) = lendgine.positions(cuh);
+    assertEq(1 ether, positionSize);
+  }
+
+  function testZeroMint() external {
+    vm.expectRevert(Lendgine.InputError.selector);
+    lendgine.deposit(cuh, 0, bytes(""));
+  }
+
+  function testUnderPayment() external {
+    token0.mint(cuh, 1 ether);
+    token1.mint(cuh, 7 ether);
+
+    vm.startPrank(cuh);
+    token0.approve(address(this), 1 ether);
+    token1.approve(address(this), 7 ether);
+    vm.stopPrank();
+
+    vm.expectRevert(Pair.InvariantError.selector);
+    lendgine.deposit(
+      cuh,
+      1 ether,
+      abi.encode(
+        PairMintCallbackData({
+          token0: address(token0),
+          token1: address(token1),
+          amount0: 1 ether,
+          amount1: 7 ether,
+          payer: cuh
+        })
+      )
+    );
+  }
+
+  function testEmitLendgine() external {
+    token0.mint(cuh, 1 ether);
+    token1.mint(cuh, 8 ether);
+
+    vm.startPrank(cuh);
+    token0.approve(address(this), 1 ether);
+    token1.approve(address(this), 8 ether);
+    vm.stopPrank();
+
+    vm.expectEmit(true, true, false, true, address(lendgine));
+    emit Deposit(address(this), 1 ether, 1 ether, cuh);
+    lendgine.deposit(
+      cuh,
+      1 ether,
+      abi.encode(
+        PairMintCallbackData({
+          token0: address(token0),
+          token1: address(token1),
+          amount0: 1 ether,
+          amount1: 8 ether,
+          payer: cuh
+        })
+      )
+    );
+  }
+
+  function testEmitPair() external {
+    token0.mint(cuh, 1 ether);
+    token1.mint(cuh, 8 ether);
+
+    vm.startPrank(cuh);
+    token0.approve(address(this), 1 ether);
+    token1.approve(address(this), 8 ether);
+    vm.stopPrank();
+
+    vm.expectEmit(false, false, false, true, address(lendgine));
+    emit Mint(1 ether, 8 ether, 1 ether);
+    lendgine.deposit(
+      cuh,
+      1 ether,
+      abi.encode(
+        PairMintCallbackData({
+          token0: address(token0),
+          token1: address(token1),
+          amount0: 1 ether,
+          amount1: 8 ether,
+          payer: cuh
+        })
+      )
+    );
+  }
+
+  function testAccrueOnDepositEmpty() external {
+    _deposit(cuh, cuh, 1 ether, 8 ether, 1 ether);
+
+    assertEq(1, lendgine.lastUpdate());
+  }
+
+  function testAccrueOnDeposit() external {
+    _deposit(address(this), address(this), 1 ether, 8 ether, 1 ether);
+    _mint(address(this), address(this), 5 ether);
+
+    vm.warp(365 days + 1);
+
+    _deposit(cuh, cuh, 1 ether, 8 ether, 1 ether);
+
+    assertEq(365 days + 1, lendgine.lastUpdate());
+    assert(lendgine.rewardPerPositionStored() != 0);
+  }
+
+  function testAccrueOnPositionDeposit() external {
+    _deposit(cuh, cuh, 1 ether, 8 ether, 1 ether);
+    _mint(address(this), address(this), 5 ether);
+
+    vm.warp(365 days + 1);
+
+    _deposit(cuh, cuh, 1 ether, 8 ether, 1 ether);
+
+    (, uint256 rewardPerPositionPaid, uint256 tokensOwed) = lendgine.positions(cuh);
+    assert(rewardPerPositionPaid != 0);
+    assert(tokensOwed != 0);
+  }
+
+  function testProportionPositionSize() external {
+    _deposit(address(this), address(this), 1 ether, 8 ether, 1 ether);
+    _mint(address(this), address(this), 5 ether);
+
+    vm.warp(365 days + 1);
+
+    uint256 size = _deposit(cuh, cuh, 1 ether, 8 ether, 1 ether);
+
+    uint256 borrowRate = lendgine.getBorrowRate(0.5 ether, 1 ether);
+    uint256 lpDilution = borrowRate / 2; // 0.5 lp for one year
+
+    // check position size
+    assertEq((1 ether * 1 ether) / (1 ether - lpDilution), size);
+    assertApproxEqAbs(1 ether, (size * (2 ether - lpDilution)) / (1 ether + size), 1);
+    (uint256 positionSize,,) = lendgine.positions(cuh);
+    assertEq((1 ether * 1 ether) / (1 ether - lpDilution), positionSize);
+
+    // check lendgine storage slots
+    assertEq(1 ether + size, lendgine.totalPositionSize());
+    assertEq(1.5 ether, lendgine.totalLiquidity());
+  }
+
+  function testNonStandardDecimals() external {
+    token1Scale = 9;
+
+    lendgine = Lendgine(factory.createLendgine(address(token0), address(token1), token0Scale, token1Scale, upperBound));
+
+    token0.mint(address(this), 1e18);
+    token1.mint(address(this), 8 * 1e9);
+
+    uint256 size = lendgine.deposit(
+      address(this),
+      1 ether,
+      abi.encode(
+        PairMintCallbackData({
+          token0: address(token0),
+          token1: address(token1),
+          amount0: 1e18,
+          amount1: 8 * 1e9,
+          payer: address(this)
+        })
+      )
+    );
+
+    // check lendgine storage slots
+    assertEq(1 ether, lendgine.totalLiquidity());
+    assertEq(1 ether, lendgine.totalPositionSize());
+    assertEq(1 ether, uint256(lendgine.reserve0()));
+    assertEq(8 * 1e9, uint256(lendgine.reserve1()));
+
+    // check lendgine balances
+    assertEq(1 ether, token0.balanceOf(address(lendgine)));
+    assertEq(8 * 1e9, token1.balanceOf(address(lendgine)));
+
+    // check position size
+    assertEq(1 ether, size);
+    (uint256 positionSize,,) = lendgine.positions(address(this));
+    assertEq(1 ether, positionSize);
+  }
+
+  function testDepositAfterFullAccrue() external {
+    _deposit(cuh, cuh, 1 ether, 8 ether, 1 ether);
+    _mint(address(this), address(this), 10 ether);
+    vm.warp(730 days + 1);
+
+    vm.expectRevert(Lendgine.CompleteUtilizationError.selector);
+    lendgine.deposit(cuh, 1 ether, bytes(""));
+  }
 }
diff --git a/test/FactoryTest.t.sol b/test/FactoryTest.t.sol
index 5830370..843568f 100644
--- a/test/FactoryTest.t.sol
+++ b/test/FactoryTest.t.sol
@@ -6,104 +6,104 @@ import { Lendgine } from "../src/core/Lendgine.sol";
 import { Test } from "forge-std/Test.sol";
 
 contract FactoryTest is Test {
-    event LendgineCreated(
-        address indexed token0,
-        address indexed token1,
-        uint256 token0Scale,
-        uint256 token1Scale,
-        uint256 indexed upperBound,
-        address lendgine
+  event LendgineCreated(
+    address indexed token0,
+    address indexed token1,
+    uint256 token0Scale,
+    uint256 token1Scale,
+    uint256 indexed upperBound,
+    address lendgine
+  );
+
+  Factory public factory;
+
+  function setUp() external {
+    factory = new Factory();
+  }
+
+  function testGetLendgine() external {
+    address lendgine = factory.createLendgine(address(1), address(2), 18, 18, 1e18);
+
+    assertEq(lendgine, factory.getLendgine(address(1), address(2), 18, 18, 1e18));
+  }
+
+  function testDeployAddress() external {
+    address lendgineEstimate = address(
+      uint160(
+        uint256(
+          keccak256(
+            abi.encodePacked(
+              hex"ff",
+              address(factory),
+              keccak256(abi.encode(address(1), address(2), 18, 18, 1e18)),
+              keccak256(type(Lendgine).creationCode)
+            )
+          )
+        )
+      )
     );
 
-    Factory public factory;
-
-    function setUp() external {
-        factory = new Factory();
-    }
-
-    function testGetLendgine() external {
-        address lendgine = factory.createLendgine(address(1), address(2), 18, 18, 1e18);
-
-        assertEq(lendgine, factory.getLendgine(address(1), address(2), 18, 18, 1e18));
-    }
-
-    function testDeployAddress() external {
-        address lendgineEstimate = address(
-            uint160(
-                uint256(
-                    keccak256(
-                        abi.encodePacked(
-                            hex"ff",
-                            address(factory),
-                            keccak256(abi.encode(address(1), address(2), 18, 18, 1e18)),
-                            keccak256(type(Lendgine).creationCode)
-                        )
-                    )
-                )
-            )
-        );
-
-        address lendgine = factory.createLendgine(address(1), address(2), 18, 18, 1e18);
-
-        assertEq(lendgine, lendgineEstimate);
-    }
-
-    function testSameTokenError() external {
-        vm.expectRevert(Factory.SameTokenError.selector);
-        factory.createLendgine(address(1), address(1), 18, 18, 1e18);
-    }
-
-    function testZeroAddressError() external {
-        vm.expectRevert(Factory.ZeroAddressError.selector);
-        factory.createLendgine(address(0), address(1), 18, 18, 1e18);
-
-        vm.expectRevert(Factory.ZeroAddressError.selector);
-        factory.createLendgine(address(1), address(0), 18, 18, 1e18);
-    }
-
-    function testDeployedError() external {
-        factory.createLendgine(address(1), address(2), 18, 18, 1e18);
-
-        vm.expectRevert(Factory.DeployedError.selector);
-        factory.createLendgine(address(1), address(2), 18, 18, 1e18);
-    }
-
-    function helpParametersZero() private {
-        (address token0, address token1, uint256 token0Scale, uint256 token1Scale, uint256 upperBound) = factory
-            .parameters();
-
-        assertEq(address(0), token0);
-        assertEq(address(0), token1);
-        assertEq(0, token0Scale);
-        assertEq(0, token1Scale);
-        assertEq(0, upperBound);
-    }
-
-    function testParameters() external {
-        helpParametersZero();
-
-        factory.createLendgine(address(1), address(2), 18, 18, 1e18);
-
-        helpParametersZero();
-    }
-
-    function testEmit() external {
-        address lendgineEstimate = address(
-            uint160(
-                uint256(
-                    keccak256(
-                        abi.encodePacked(
-                            hex"ff",
-                            address(factory),
-                            keccak256(abi.encode(address(1), address(2), 18, 18, 1e18)),
-                            keccak256(type(Lendgine).creationCode)
-                        )
-                    )
-                )
+    address lendgine = factory.createLendgine(address(1), address(2), 18, 18, 1e18);
+
+    assertEq(lendgine, lendgineEstimate);
+  }
+
+  function testSameTokenError() external {
+    vm.expectRevert(Factory.SameTokenError.selector);
+    factory.createLendgine(address(1), address(1), 18, 18, 1e18);
+  }
+
+  function testZeroAddressError() external {
+    vm.expectRevert(Factory.ZeroAddressError.selector);
+    factory.createLendgine(address(0), address(1), 18, 18, 1e18);
+
+    vm.expectRevert(Factory.ZeroAddressError.selector);
+    factory.createLendgine(address(1), address(0), 18, 18, 1e18);
+  }
+
+  function testDeployedError() external {
+    factory.createLendgine(address(1), address(2), 18, 18, 1e18);
+
+    vm.expectRevert(Factory.DeployedError.selector);
+    factory.createLendgine(address(1), address(2), 18, 18, 1e18);
+  }
+
+  function helpParametersZero() private {
+    (address token0, address token1, uint256 token0Scale, uint256 token1Scale, uint256 upperBound) =
+      factory.parameters();
+
+    assertEq(address(0), token0);
+    assertEq(address(0), token1);
+    assertEq(0, token0Scale);
+    assertEq(0, token1Scale);
+    assertEq(0, upperBound);
+  }
+
+  function testParameters() external {
+    helpParametersZero();
+
+    factory.createLendgine(address(1), address(2), 18, 18, 1e18);
+
+    helpParametersZero();
+  }
+
+  function testEmit() external {
+    address lendgineEstimate = address(
+      uint160(
+        uint256(
+          keccak256(
+            abi.encodePacked(
+              hex"ff",
+              address(factory),
+              keccak256(abi.encode(address(1), address(2), 18, 18, 1e18)),
+              keccak256(type(Lendgine).creationCode)
             )
-        );
-        vm.expectEmit(true, true, true, true, address(factory));
-        emit LendgineCreated(address(1), address(2), 18, 18, 1e18, lendgineEstimate);
-        factory.createLendgine(address(1), address(2), 18, 18, 1e18);
-    }
+          )
+        )
+      )
+    );
+    vm.expectEmit(true, true, true, true, address(factory));
+    emit LendgineCreated(address(1), address(2), 18, 18, 1e18, lendgineEstimate);
+    factory.createLendgine(address(1), address(2), 18, 18, 1e18);
+  }
 }
diff --git a/test/ImmutableStateTest.t.sol b/test/ImmutableStateTest.t.sol
index 02d8755..5ae04c7 100644
--- a/test/ImmutableStateTest.t.sol
+++ b/test/ImmutableStateTest.t.sol
@@ -6,19 +6,19 @@ import { Lendgine } from "../src/core/Lendgine.sol";
 import { Test } from "forge-std/Test.sol";
 
 contract ImmutableStateTest is Test {
-    Factory public factory;
-    Lendgine public lendgine;
+  Factory public factory;
+  Lendgine public lendgine;
 
-    function setUp() external {
-        factory = new Factory();
-        lendgine = Lendgine(factory.createLendgine(address(1), address(2), 18, 18, 1e18));
-    }
+  function setUp() external {
+    factory = new Factory();
+    lendgine = Lendgine(factory.createLendgine(address(1), address(2), 18, 18, 1e18));
+  }
 
-    function testImmutableState() external {
-        assertEq(address(1), lendgine.token0());
-        assertEq(address(2), lendgine.token1());
-        assertEq(1, lendgine.token0Scale());
-        assertEq(1, lendgine.token1Scale());
-        assertEq(1e18, lendgine.upperBound());
-    }
+  function testImmutableState() external {
+    assertEq(address(1), lendgine.token0());
+    assertEq(address(2), lendgine.token1());
+    assertEq(1, lendgine.token0Scale());
+    assertEq(1, lendgine.token1Scale());
+    assertEq(1e18, lendgine.upperBound());
+  }
 }
diff --git a/test/LendgineRouterTest.t.sol b/test/LendgineRouterTest.t.sol
index 0db76c6..2782b45 100644
--- a/test/LendgineRouterTest.t.sol
+++ b/test/LendgineRouterTest.t.sol
@@ -13,703 +13,705 @@ import { IUniswapV3Factory } from "../src/periphery/UniswapV3/interfaces/IUniswa
 import { IUniswapV3Pool } from "../src/periphery/UniswapV3/interfaces/IUniswapV3Pool.sol";
 
 contract LendgineRouterTest is TestHelper {
-    event Mint(address indexed from, address indexed lendgine, uint256 collateral, uint256 shares, address indexed to);
-
-    event Burn(address indexed from, address indexed lendgine, uint256 collateral, uint256 shares, address indexed to);
-
-    LendgineRouter public lendgineRouter;
-
-    IUniswapV2Factory public uniswapV2Factory = IUniswapV2Factory(0xc35DADB65012eC5796536bD9864eD8773aBc74C4);
-    IUniswapV2Pair public uniswapV2Pair;
-    IUniswapV3Factory public uniswapV3Factory = IUniswapV3Factory(0x1F98431c8aD98523631AE4a59f267346ea31F984);
-    IUniswapV3Pool public uniswapV3Pool = IUniswapV3Pool(0x07A4f63f643fE39261140DF5E613b9469eccEC86); // uni / eth 5 bps pool
-
-    function setUp() external {
-        vm.createSelectFork("goerli");
-
-        _setUp();
-        lendgineRouter = new LendgineRouter(
-            address(factory),
-            address(uniswapV2Factory),
-            address(uniswapV3Factory),
-            address(0)
-        );
-
-        // set up the uniswap v2 pair
-        uniswapV2Pair = IUniswapV2Pair(uniswapV2Factory.createPair(address(token0), address(token1)));
-        token0.mint(address(uniswapV2Pair), 100 ether);
-        token1.mint(address(uniswapV2Pair), 100 ether);
-        uniswapV2Pair.mint(address(this));
-
-        _deposit(address(this), address(this), 100 ether, 800 ether, 100 ether);
-    }
-
-    function setUpUniswapV3() internal {
-        // change tokens
-        token0 = MockERC20(0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984); // UNI
-        token1 = MockERC20(0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6); // WETH
-
-        // get tokens
-        vm.prank(0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984);
-        token0.transfer(cuh, 100 ether);
-
-        vm.prank(0xb3A16C2B68BBB0111EbD27871a5934b949837D95);
-        token1.transfer(cuh, 100 ether);
-
-        // deploy lendgine
-        lendgine = Lendgine(factory.createLendgine(address(token0), address(token1), 18, 18, 3 ether));
-
-        // deposit tokens
-        vm.startPrank(cuh);
-        token0.approve(address(this), 5.40225 ether);
-        token1.approve(address(this), 45.3 ether);
-        vm.stopPrank();
-
-        // price is .735 weth / uni
-        lendgine.deposit(
-            address(this),
-            10 ether,
-            abi.encode(
-                PairMintCallbackData({
-                    token0: address(token0),
-                    token1: address(token1),
-                    amount0: 5.40225 ether,
-                    amount1: 45.3 ether,
-                    payer: cuh
-                })
-            )
-        );
-    }
-
-    function testMintNoBorrow() external {
-        token1.mint(cuh, 1 ether);
-
-        vm.prank(cuh);
-        token1.approve(address(lendgineRouter), 1 ether);
-
-        vm.prank(cuh);
-        lendgineRouter.mint(
-            LendgineRouter.MintParams({
-                token0: address(token0),
-                token1: address(token1),
-                token0Exp: token0Scale,
-                token1Exp: token1Scale,
-                upperBound: upperBound,
-                amountIn: 1 ether,
-                amountBorrow: 0,
-                sharesMin: 0.1 ether,
-                swapType: SwapHelper.SwapType.UniswapV2,
-                swapExtraData: bytes(""),
-                recipient: cuh,
-                deadline: block.timestamp
-            })
-        );
-
-        // check option amounts
-        assertEq(0.1 ether, lendgine.totalSupply());
-        assertEq(0.1 ether, lendgine.balanceOf(cuh));
-
-        // check uniswap
-        // swap 0.1 ether of token 0 to token 1
-        assertEq(100.1 ether, token0.balanceOf(address(uniswapV2Pair)));
-        assertApproxEqRel(99.9 ether, token1.balanceOf(address(uniswapV2Pair)), .001 ether);
-
-        // check lendgine storage
-        assertEq(0.1 ether, lendgine.totalLiquidityBorrowed());
-
-        // check user balances
-        assertApproxEqRel(0.9 ether, token1.balanceOf(cuh), 1 ether);
-
-        // check router token balances
-        assertEq(0, token0.balanceOf(address(lendgineRouter)));
-        assertEq(0, token1.balanceOf(address(lendgineRouter)));
-        assertEq(0, lendgine.balanceOf(address(lendgineRouter)));
-    }
-
-    function testMintBorrow() external {
-        token1.mint(cuh, 1 ether);
-
-        vm.prank(cuh);
-        token1.approve(address(lendgineRouter), 1 ether);
-
-        vm.prank(cuh);
-        lendgineRouter.mint(
-            LendgineRouter.MintParams({
-                token0: address(token0),
-                token1: address(token1),
-                token0Exp: token0Scale,
-                token1Exp: token1Scale,
-                upperBound: upperBound,
-                amountIn: 1 ether,
-                amountBorrow: 8.8 ether,
-                sharesMin: 0,
-                swapType: SwapHelper.SwapType.UniswapV2,
-                swapExtraData: bytes(""),
-                recipient: cuh,
-                deadline: block.timestamp
-            })
-        );
-
-        // check option amounts
-        assertEq(0.98 ether, lendgine.totalSupply());
-        assertEq(0.98 ether, lendgine.balanceOf(cuh));
-
-        // check uniswap
-        // swap 0.98 ether of token 0 to token 1
-        assertEq(100.98 ether, token0.balanceOf(address(uniswapV2Pair)));
-        assertApproxEqRel(99.02 ether, token1.balanceOf(address(uniswapV2Pair)), .001 ether);
-
-        // check lendgine storage
-        assertEq(0.98 ether, lendgine.totalLiquidityBorrowed());
-
-        // check user balances
-        assertApproxEqRel(0, token1.balanceOf(cuh), 1 ether);
-
-        // check router token balances
-        assertEq(0, token0.balanceOf(address(lendgineRouter)));
-        assertEq(0, token1.balanceOf(address(lendgineRouter)));
-        assertEq(0, lendgine.balanceOf(address(lendgineRouter)));
-    }
-
-    function testMintV3() external {
-        setUpUniswapV3();
-
-        uint256 balance0Before = token0.balanceOf(address(uniswapV3Pool));
-        uint256 balance1Before = token1.balanceOf(address(uniswapV3Pool));
-
-        vm.prank(cuh);
-        token1.approve(address(lendgineRouter), 1 ether);
-
-        vm.prank(cuh);
-        lendgineRouter.mint(
-            LendgineRouter.MintParams({
-                token0: address(token0),
-                token1: address(token1),
-                token0Exp: 18,
-                token1Exp: 18,
-                upperBound: 3 ether,
-                amountIn: 1.2 ether,
-                amountBorrow: 0,
-                sharesMin: 0.2 ether,
-                swapType: SwapHelper.SwapType.UniswapV3,
-                swapExtraData: abi.encode(uint24(500)),
-                recipient: cuh,
-                deadline: block.timestamp
-            })
-        );
-
-        // check option amounts
-        assertEq(0.2 ether, lendgine.totalSupply());
-        assertEq(0.2 ether, lendgine.balanceOf(cuh));
-
-        // check uniswap
-        // swap (1.2 / 6) * .540 ether of token 0 to token 1
-        assertApproxEqRel(balance0Before + .108 ether, token0.balanceOf(address(uniswapV3Pool)), .001 ether);
-        assertApproxEqRel(balance1Before - .072 ether, token1.balanceOf(address(uniswapV3Pool)), .001 ether);
-
-        // check lendgine storage
-        assertEq(0.2 ether, lendgine.totalLiquidityBorrowed());
-
-        // check router token balances
-        assertEq(0, token0.balanceOf(address(lendgineRouter)));
-        assertEq(0, token1.balanceOf(address(lendgineRouter)));
-        assertEq(0, lendgine.balanceOf(address(lendgineRouter)));
-    }
-
-    function testAmountError() external {
-        token1.mint(cuh, 1 ether);
-
-        vm.prank(cuh);
-        token1.approve(address(lendgineRouter), 1 ether);
-
-        vm.prank(cuh);
-        vm.expectRevert(LendgineRouter.AmountError.selector);
-        lendgineRouter.mint(
-            LendgineRouter.MintParams({
-                token0: address(token0),
-                token1: address(token1),
-                token0Exp: token0Scale,
-                token1Exp: token1Scale,
-                upperBound: upperBound,
-                amountIn: 1 ether,
-                amountBorrow: 0,
-                sharesMin: 0.2 ether,
-                swapType: SwapHelper.SwapType.UniswapV2,
-                swapExtraData: bytes(""),
-                recipient: cuh,
-                deadline: block.timestamp
-            })
-        );
-    }
-
-    function testUserAmountError() external {
-        token1.mint(cuh, 1 ether);
-
-        vm.prank(cuh);
-        token1.approve(address(lendgineRouter), 1 ether);
-
-        vm.prank(cuh);
-        vm.expectRevert(LendgineRouter.AmountError.selector);
-        lendgineRouter.mint(
-            LendgineRouter.MintParams({
-                token0: address(token0),
-                token1: address(token1),
-                token0Exp: token0Scale,
-                token1Exp: token1Scale,
-                upperBound: upperBound,
-                amountIn: 1 ether,
-                amountBorrow: 10 ether,
-                sharesMin: 0,
-                swapType: SwapHelper.SwapType.UniswapV2,
-                swapExtraData: bytes(""),
-                recipient: cuh,
-                deadline: block.timestamp
-            })
-        );
-    }
-
-    function testMintEmit() external {
-        token1.mint(cuh, 1 ether);
-
-        vm.prank(cuh);
-        token1.approve(address(lendgineRouter), 1 ether);
-
-        vm.prank(cuh);
-        vm.expectEmit(true, true, true, true, address(lendgineRouter));
-        emit Mint(cuh, address(lendgine), 1 ether, 0.1 ether, cuh);
-        lendgineRouter.mint(
-            LendgineRouter.MintParams({
-                token0: address(token0),
-                token1: address(token1),
-                token0Exp: token0Scale,
-                token1Exp: token1Scale,
-                upperBound: upperBound,
-                amountIn: 1 ether,
-                amountBorrow: 0,
-                sharesMin: 0.1 ether,
-                swapType: SwapHelper.SwapType.UniswapV2,
-                swapExtraData: bytes(""),
-                recipient: cuh,
-                deadline: block.timestamp
-            })
-        );
-    }
-
-    function testBurn() external {
-        token1.mint(cuh, 1 ether);
-
-        vm.prank(cuh);
-        token1.approve(address(lendgineRouter), 1 ether);
-
-        vm.prank(cuh);
-        lendgineRouter.mint(
-            LendgineRouter.MintParams({
-                token0: address(token0),
-                token1: address(token1),
-                token0Exp: token0Scale,
-                token1Exp: token1Scale,
-                upperBound: upperBound,
-                amountIn: 1 ether,
-                amountBorrow: 8.8 ether,
-                sharesMin: 0,
-                swapType: SwapHelper.SwapType.UniswapV2,
-                swapExtraData: bytes(""),
-                recipient: cuh,
-                deadline: block.timestamp
-            })
-        );
-
-        vm.prank(cuh);
-        lendgine.approve(address(lendgineRouter), 0.98 ether);
-
-        vm.prank(cuh);
-        lendgineRouter.burn(
-            LendgineRouter.BurnParams({
-                token0: address(token0),
-                token1: address(token1),
-                token0Exp: token0Scale,
-                token1Exp: token1Scale,
-                upperBound: upperBound,
-                shares: .98 ether,
-                collateralMin: .96 ether,
-                amount0Min: .98 ether,
-                amount1Min: 8 * .98 ether,
-                swapType: SwapHelper.SwapType.UniswapV2,
-                swapExtraData: bytes(""),
-                recipient: cuh,
-                deadline: block.timestamp
-            })
-        );
-
-        // check lendgine token
-        assertEq(0, lendgine.balanceOf(cuh));
-        assertEq(0, lendgine.totalSupply());
-
-        // check lendgine storage slots
-        assertEq(0, lendgine.totalLiquidityBorrowed());
-
-        // check uniswap
-        assertApproxEqRel(100 ether, token0.balanceOf(address(uniswapV2Pair)), 0.001 ether);
-        assertApproxEqRel(100 ether, token1.balanceOf(address(uniswapV2Pair)), 0.001 ether);
-
-        // check user balances
-        assertApproxEqRel(0.1 ether, token1.balanceOf(cuh), 1 ether);
-
-        // check router token balances
-        assertEq(0, token0.balanceOf(address(lendgineRouter)));
-        assertEq(0, token1.balanceOf(address(lendgineRouter)));
-        assertEq(0, lendgine.balanceOf(address(lendgineRouter)));
-    }
-
-    function testBurnNoLiquidity() external {
-        token1.mint(cuh, 1 ether);
-
-        vm.prank(cuh);
-        token1.approve(address(lendgineRouter), 1 ether);
-
-        vm.prank(cuh);
-        lendgineRouter.mint(
-            LendgineRouter.MintParams({
-                token0: address(token0),
-                token1: address(token1),
-                token0Exp: token0Scale,
-                token1Exp: token1Scale,
-                upperBound: upperBound,
-                amountIn: 1 ether,
-                amountBorrow: 0,
-                sharesMin: 0,
-                swapType: SwapHelper.SwapType.UniswapV2,
-                swapExtraData: bytes(""),
-                recipient: cuh,
-                deadline: block.timestamp
-            })
-        );
-
-        _withdraw(address(this), address(this), 99.9 ether);
-
-        vm.prank(cuh);
-        lendgine.approve(address(lendgineRouter), 0.1 ether);
-
-        vm.prank(cuh);
-        lendgineRouter.burn(
-            LendgineRouter.BurnParams({
-                token0: address(token0),
-                token1: address(token1),
-                token0Exp: token0Scale,
-                token1Exp: token1Scale,
-                upperBound: upperBound,
-                shares: .1 ether,
-                collateralMin: 0,
-                amount0Min: .1 ether,
-                amount1Min: .8 ether,
-                swapType: SwapHelper.SwapType.UniswapV2,
-                swapExtraData: bytes(""),
-                recipient: cuh,
-                deadline: block.timestamp
-            })
-        );
-
-        // check lendgine token
-        assertEq(0, lendgine.balanceOf(cuh));
-        assertEq(0, lendgine.totalSupply());
-
-        // check lendgine storage slots
-        assertEq(0, lendgine.totalLiquidityBorrowed());
-
-        // check uniswap
-        assertApproxEqRel(100 ether, token0.balanceOf(address(uniswapV2Pair)), 0.001 ether);
-        assertApproxEqRel(100 ether, token1.balanceOf(address(uniswapV2Pair)), 0.001 ether);
-
-        // check user balances
-        assertApproxEqRel(0.1 ether, token1.balanceOf(cuh), 1 ether);
-
-        // check router token balances
-        assertEq(0, token0.balanceOf(address(lendgineRouter)));
-        assertEq(0, token1.balanceOf(address(lendgineRouter)));
-        assertEq(0, lendgine.balanceOf(address(lendgineRouter)));
-    }
-
-    function testBurnV3() external {
-        setUpUniswapV3();
-
-        uint256 balance0Before = token0.balanceOf(address(uniswapV3Pool));
-        uint256 balance1Before = token1.balanceOf(address(uniswapV3Pool));
-
-        uint256 userBalanceBefore = token1.balanceOf(cuh);
-
-        vm.prank(cuh);
-        token1.approve(address(lendgineRouter), 1 ether);
-
-        vm.prank(cuh);
-        lendgineRouter.mint(
-            LendgineRouter.MintParams({
-                token0: address(token0),
-                token1: address(token1),
-                token0Exp: 18,
-                token1Exp: 18,
-                upperBound: 3 ether,
-                amountIn: 1.2 ether,
-                amountBorrow: 0,
-                sharesMin: 0.2 ether,
-                swapType: SwapHelper.SwapType.UniswapV3,
-                swapExtraData: abi.encode(uint24(500)),
-                recipient: cuh,
-                deadline: block.timestamp
-            })
-        );
-
-        vm.prank(cuh);
-        lendgine.approve(address(lendgineRouter), 0.2 ether);
-
-        vm.prank(cuh);
-        lendgineRouter.burn(
-            LendgineRouter.BurnParams({
-                token0: address(token0),
-                token1: address(token1),
-                token0Exp: 18,
-                token1Exp: 18,
-                upperBound: 3 ether,
-                shares: .2 ether,
-                collateralMin: 0,
-                amount0Min: 0,
-                amount1Min: 0,
-                swapType: SwapHelper.SwapType.UniswapV3,
-                swapExtraData: abi.encode(uint24(500)),
-                recipient: cuh,
-                deadline: block.timestamp
-            })
-        );
-
-        // check lendgine token
-        assertEq(0, lendgine.balanceOf(cuh));
-        assertEq(0, lendgine.totalSupply());
-
-        // check lendgine storage slots
-        assertEq(0, lendgine.totalLiquidityBorrowed());
-
-        // check uniswap
-        assertApproxEqRel(balance0Before, token0.balanceOf(address(uniswapV3Pool)), 0.001 ether);
-        assertApproxEqRel(balance1Before, token1.balanceOf(address(uniswapV3Pool)), 0.001 ether);
-
-        // check user balance
-        assertApproxEqRel(userBalanceBefore, token1.balanceOf(cuh), .001 ether);
-
-        // check router token balances
-        assertEq(0, token0.balanceOf(address(lendgineRouter)));
-        assertEq(0, token1.balanceOf(address(lendgineRouter)));
-        assertEq(0, lendgine.balanceOf(address(lendgineRouter)));
-    }
-
-    function testBurnEmit() external {
-        token1.mint(cuh, 1 ether);
-
-        vm.prank(cuh);
-        token1.approve(address(lendgineRouter), 1 ether);
-
-        vm.prank(cuh);
-        lendgineRouter.mint(
-            LendgineRouter.MintParams({
-                token0: address(token0),
-                token1: address(token1),
-                token0Exp: token0Scale,
-                token1Exp: token1Scale,
-                upperBound: upperBound,
-                amountIn: 1 ether,
-                amountBorrow: 8.8 ether,
-                sharesMin: 0,
-                swapType: SwapHelper.SwapType.UniswapV2,
-                swapExtraData: bytes(""),
-                recipient: cuh,
-                deadline: block.timestamp
-            })
-        );
-
-        vm.prank(cuh);
-        lendgine.approve(address(lendgineRouter), 0.98 ether);
-
-        vm.prank(cuh);
-        vm.expectEmit(true, true, true, true, address(lendgineRouter));
-        emit Burn(cuh, address(lendgine), 9.8 ether, .98 ether, cuh);
-        lendgineRouter.burn(
-            LendgineRouter.BurnParams({
-                token0: address(token0),
-                token1: address(token1),
-                token0Exp: token0Scale,
-                token1Exp: token1Scale,
-                upperBound: upperBound,
-                shares: .98 ether,
-                collateralMin: .96 ether,
-                amount0Min: .98 ether,
-                amount1Min: 8 * .98 ether,
-                swapType: SwapHelper.SwapType.UniswapV2,
-                swapExtraData: bytes(""),
-                recipient: cuh,
-                deadline: block.timestamp
-            })
-        );
-    }
-
-    function testBurnAmountError() external {
-        token1.mint(cuh, 1 ether);
-
-        vm.prank(cuh);
-        token1.approve(address(lendgineRouter), 1 ether);
-
-        vm.prank(cuh);
-        lendgineRouter.mint(
-            LendgineRouter.MintParams({
-                token0: address(token0),
-                token1: address(token1),
-                token0Exp: token0Scale,
-                token1Exp: token1Scale,
-                upperBound: upperBound,
-                amountIn: 1 ether,
-                amountBorrow: 8.8 ether,
-                sharesMin: 0,
-                swapType: SwapHelper.SwapType.UniswapV2,
-                swapExtraData: bytes(""),
-                recipient: cuh,
-                deadline: block.timestamp
-            })
-        );
-
-        vm.prank(cuh);
-        lendgine.approve(address(lendgineRouter), 0.98 ether);
-
-        vm.prank(cuh);
-        vm.expectRevert(LendgineRouter.AmountError.selector);
-        lendgineRouter.burn(
-            LendgineRouter.BurnParams({
-                token0: address(token0),
-                token1: address(token1),
-                token0Exp: token0Scale,
-                token1Exp: token1Scale,
-                upperBound: upperBound,
-                shares: .98 ether,
-                collateralMin: 0,
-                amount0Min: .98 ether,
-                amount1Min: 8 * .98 ether + 1,
-                swapType: SwapHelper.SwapType.UniswapV2,
-                swapExtraData: bytes(""),
-                recipient: cuh,
-                deadline: block.timestamp
-            })
-        );
-    }
-
-    function testBurnUserAmountError() external {
-        token1.mint(cuh, 1 ether);
-
-        vm.prank(cuh);
-        token1.approve(address(lendgineRouter), 1 ether);
-
-        vm.prank(cuh);
-        lendgineRouter.mint(
-            LendgineRouter.MintParams({
-                token0: address(token0),
-                token1: address(token1),
-                token0Exp: token0Scale,
-                token1Exp: token1Scale,
-                upperBound: upperBound,
-                amountIn: 1 ether,
-                amountBorrow: 8.8 ether,
-                sharesMin: 0,
-                swapType: SwapHelper.SwapType.UniswapV2,
-                swapExtraData: bytes(""),
-                recipient: cuh,
-                deadline: block.timestamp
-            })
-        );
-
-        vm.prank(cuh);
-        lendgine.approve(address(lendgineRouter), 0.98 ether);
-
-        vm.prank(cuh);
-        vm.expectRevert(LendgineRouter.AmountError.selector);
-        lendgineRouter.burn(
-            LendgineRouter.BurnParams({
-                token0: address(token0),
-                token1: address(token1),
-                token0Exp: token0Scale,
-                token1Exp: token1Scale,
-                upperBound: upperBound,
-                shares: .98 ether,
-                collateralMin: 1 ether,
-                amount0Min: .98 ether,
-                amount1Min: 8 * .98 ether,
-                swapType: SwapHelper.SwapType.UniswapV2,
-                swapExtraData: bytes(""),
-                recipient: cuh,
-                deadline: block.timestamp
-            })
-        );
-    }
-
-    function testBurnNoRecipient() external {
-        token1.mint(cuh, 1 ether);
-
-        vm.prank(cuh);
-        token1.approve(address(lendgineRouter), 1 ether);
-
-        vm.prank(cuh);
-        lendgineRouter.mint(
-            LendgineRouter.MintParams({
-                token0: address(token0),
-                token1: address(token1),
-                token0Exp: token0Scale,
-                token1Exp: token1Scale,
-                upperBound: upperBound,
-                amountIn: 1 ether,
-                amountBorrow: 8.8 ether,
-                sharesMin: 0,
-                swapType: SwapHelper.SwapType.UniswapV2,
-                swapExtraData: bytes(""),
-                recipient: cuh,
-                deadline: block.timestamp
-            })
-        );
-
-        uint256 balanceBefore = token1.balanceOf(address(cuh));
-
-        vm.prank(cuh);
-        lendgine.approve(address(lendgineRouter), 0.98 ether);
-
-        vm.prank(cuh);
-        lendgineRouter.burn(
-            LendgineRouter.BurnParams({
-                token0: address(token0),
-                token1: address(token1),
-                token0Exp: token0Scale,
-                token1Exp: token1Scale,
-                upperBound: upperBound,
-                shares: .98 ether,
-                collateralMin: 0,
-                amount0Min: 0,
-                amount1Min: 0,
-                swapType: SwapHelper.SwapType.UniswapV2,
-                swapExtraData: bytes(""),
-                recipient: address(0),
-                deadline: block.timestamp
-            })
-        );
-
-        // check lendgine token
-        assertEq(0, lendgine.balanceOf(cuh));
-        assertEq(0, lendgine.totalSupply());
-
-        // check lendgine storage slots
-        assertEq(0, lendgine.totalLiquidityBorrowed());
-
-        // check uniswap
-        assertApproxEqRel(100 ether, token0.balanceOf(address(uniswapV2Pair)), 0.001 ether);
-        assertApproxEqRel(100 ether, token1.balanceOf(address(uniswapV2Pair)), 0.001 ether);
-
-        // check user balances
-        assertEq(balanceBefore, token1.balanceOf(address(cuh)));
-
-        // check router token balances
-        assertEq(0, token0.balanceOf(address(lendgineRouter)));
-        assertApproxEqRel(0.1 ether, token1.balanceOf(address(lendgineRouter)), 1 ether);
-        assertEq(0, lendgine.balanceOf(address(lendgineRouter)));
-    }
+  event Mint(address indexed from, address indexed lendgine, uint256 collateral, uint256 shares, address indexed to);
+
+  event Burn(address indexed from, address indexed lendgine, uint256 collateral, uint256 shares, address indexed to);
+
+  LendgineRouter public lendgineRouter;
+
+  IUniswapV2Factory public uniswapV2Factory = IUniswapV2Factory(0xc35DADB65012eC5796536bD9864eD8773aBc74C4);
+  IUniswapV2Pair public uniswapV2Pair;
+  IUniswapV3Factory public uniswapV3Factory = IUniswapV3Factory(0x1F98431c8aD98523631AE4a59f267346ea31F984);
+  IUniswapV3Pool public uniswapV3Pool = IUniswapV3Pool(0x07A4f63f643fE39261140DF5E613b9469eccEC86); // uni / eth 5 bps
+
+  // pool
+
+  function setUp() external {
+    vm.createSelectFork("goerli");
+
+    _setUp();
+    lendgineRouter = new LendgineRouter(
+      address(factory),
+      address(uniswapV2Factory),
+      address(uniswapV3Factory),
+      address(0)
+    );
+
+    // set up the uniswap v2 pair
+    uniswapV2Pair = IUniswapV2Pair(uniswapV2Factory.createPair(address(token0), address(token1)));
+    token0.mint(address(uniswapV2Pair), 100 ether);
+    token1.mint(address(uniswapV2Pair), 100 ether);
+    uniswapV2Pair.mint(address(this));
+
+    _deposit(address(this), address(this), 100 ether, 800 ether, 100 ether);
+  }
+
+  function setUpUniswapV3() internal {
+    // change tokens
+    token0 = MockERC20(0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984); // UNI
+    token1 = MockERC20(0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6); // WETH
+
+    // get tokens
+    vm.prank(0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984);
+    token0.transfer(cuh, 100 ether);
+
+    vm.prank(0xb3A16C2B68BBB0111EbD27871a5934b949837D95);
+    token1.transfer(cuh, 100 ether);
+
+    // deploy lendgine
+    lendgine = Lendgine(factory.createLendgine(address(token0), address(token1), 18, 18, 3 ether));
+
+    // deposit tokens
+    vm.startPrank(cuh);
+    token0.approve(address(this), 5.40225 ether);
+    token1.approve(address(this), 45.3 ether);
+    vm.stopPrank();
+
+    // price is .735 weth / uni
+    lendgine.deposit(
+      address(this),
+      10 ether,
+      abi.encode(
+        PairMintCallbackData({
+          token0: address(token0),
+          token1: address(token1),
+          amount0: 5.40225 ether,
+          amount1: 45.3 ether,
+          payer: cuh
+        })
+      )
+    );
+  }
+
+  function testMintNoBorrow() external {
+    token1.mint(cuh, 1 ether);
+
+    vm.prank(cuh);
+    token1.approve(address(lendgineRouter), 1 ether);
+
+    vm.prank(cuh);
+    lendgineRouter.mint(
+      LendgineRouter.MintParams({
+        token0: address(token0),
+        token1: address(token1),
+        token0Exp: token0Scale,
+        token1Exp: token1Scale,
+        upperBound: upperBound,
+        amountIn: 1 ether,
+        amountBorrow: 0,
+        sharesMin: 0.1 ether,
+        swapType: SwapHelper.SwapType.UniswapV2,
+        swapExtraData: bytes(""),
+        recipient: cuh,
+        deadline: block.timestamp
+      })
+    );
+
+    // check option amounts
+    assertEq(0.1 ether, lendgine.totalSupply());
+    assertEq(0.1 ether, lendgine.balanceOf(cuh));
+
+    // check uniswap
+    // swap 0.1 ether of token 0 to token 1
+    assertEq(100.1 ether, token0.balanceOf(address(uniswapV2Pair)));
+    assertApproxEqRel(99.9 ether, token1.balanceOf(address(uniswapV2Pair)), 0.001 ether);
+
+    // check lendgine storage
+    assertEq(0.1 ether, lendgine.totalLiquidityBorrowed());
+
+    // check user balances
+    assertApproxEqRel(0.9 ether, token1.balanceOf(cuh), 1 ether);
+
+    // check router token balances
+    assertEq(0, token0.balanceOf(address(lendgineRouter)));
+    assertEq(0, token1.balanceOf(address(lendgineRouter)));
+    assertEq(0, lendgine.balanceOf(address(lendgineRouter)));
+  }
+
+  function testMintBorrow() external {
+    token1.mint(cuh, 1 ether);
+
+    vm.prank(cuh);
+    token1.approve(address(lendgineRouter), 1 ether);
+
+    vm.prank(cuh);
+    lendgineRouter.mint(
+      LendgineRouter.MintParams({
+        token0: address(token0),
+        token1: address(token1),
+        token0Exp: token0Scale,
+        token1Exp: token1Scale,
+        upperBound: upperBound,
+        amountIn: 1 ether,
+        amountBorrow: 8.8 ether,
+        sharesMin: 0,
+        swapType: SwapHelper.SwapType.UniswapV2,
+        swapExtraData: bytes(""),
+        recipient: cuh,
+        deadline: block.timestamp
+      })
+    );
+
+    // check option amounts
+    assertEq(0.98 ether, lendgine.totalSupply());
+    assertEq(0.98 ether, lendgine.balanceOf(cuh));
+
+    // check uniswap
+    // swap 0.98 ether of token 0 to token 1
+    assertEq(100.98 ether, token0.balanceOf(address(uniswapV2Pair)));
+    assertApproxEqRel(99.02 ether, token1.balanceOf(address(uniswapV2Pair)), 0.001 ether);
+
+    // check lendgine storage
+    assertEq(0.98 ether, lendgine.totalLiquidityBorrowed());
+
+    // check user balances
+    assertApproxEqRel(0, token1.balanceOf(cuh), 1 ether);
+
+    // check router token balances
+    assertEq(0, token0.balanceOf(address(lendgineRouter)));
+    assertEq(0, token1.balanceOf(address(lendgineRouter)));
+    assertEq(0, lendgine.balanceOf(address(lendgineRouter)));
+  }
+
+  function testMintV3() external {
+    setUpUniswapV3();
+
+    uint256 balance0Before = token0.balanceOf(address(uniswapV3Pool));
+    uint256 balance1Before = token1.balanceOf(address(uniswapV3Pool));
+
+    vm.prank(cuh);
+    token1.approve(address(lendgineRouter), 1 ether);
+
+    vm.prank(cuh);
+    lendgineRouter.mint(
+      LendgineRouter.MintParams({
+        token0: address(token0),
+        token1: address(token1),
+        token0Exp: 18,
+        token1Exp: 18,
+        upperBound: 3 ether,
+        amountIn: 1.2 ether,
+        amountBorrow: 0,
+        sharesMin: 0.2 ether,
+        swapType: SwapHelper.SwapType.UniswapV3,
+        swapExtraData: abi.encode(uint24(500)),
+        recipient: cuh,
+        deadline: block.timestamp
+      })
+    );
+
+    // check option amounts
+    assertEq(0.2 ether, lendgine.totalSupply());
+    assertEq(0.2 ether, lendgine.balanceOf(cuh));
+
+    // check uniswap
+    // swap (1.2 / 6) * .540 ether of token 0 to token 1
+    assertApproxEqRel(balance0Before + 0.108 ether, token0.balanceOf(address(uniswapV3Pool)), 0.001 ether);
+    assertApproxEqRel(balance1Before - 0.072 ether, token1.balanceOf(address(uniswapV3Pool)), 0.001 ether);
+
+    // check lendgine storage
+    assertEq(0.2 ether, lendgine.totalLiquidityBorrowed());
+
+    // check router token balances
+    assertEq(0, token0.balanceOf(address(lendgineRouter)));
+    assertEq(0, token1.balanceOf(address(lendgineRouter)));
+    assertEq(0, lendgine.balanceOf(address(lendgineRouter)));
+  }
+
+  function testAmountError() external {
+    token1.mint(cuh, 1 ether);
+
+    vm.prank(cuh);
+    token1.approve(address(lendgineRouter), 1 ether);
+
+    vm.prank(cuh);
+    vm.expectRevert(LendgineRouter.AmountError.selector);
+    lendgineRouter.mint(
+      LendgineRouter.MintParams({
+        token0: address(token0),
+        token1: address(token1),
+        token0Exp: token0Scale,
+        token1Exp: token1Scale,
+        upperBound: upperBound,
+        amountIn: 1 ether,
+        amountBorrow: 0,
+        sharesMin: 0.2 ether,
+        swapType: SwapHelper.SwapType.UniswapV2,
+        swapExtraData: bytes(""),
+        recipient: cuh,
+        deadline: block.timestamp
+      })
+    );
+  }
+
+  function testUserAmountError() external {
+    token1.mint(cuh, 1 ether);
+
+    vm.prank(cuh);
+    token1.approve(address(lendgineRouter), 1 ether);
+
+    vm.prank(cuh);
+    vm.expectRevert(LendgineRouter.AmountError.selector);
+    lendgineRouter.mint(
+      LendgineRouter.MintParams({
+        token0: address(token0),
+        token1: address(token1),
+        token0Exp: token0Scale,
+        token1Exp: token1Scale,
+        upperBound: upperBound,
+        amountIn: 1 ether,
+        amountBorrow: 10 ether,
+        sharesMin: 0,
+        swapType: SwapHelper.SwapType.UniswapV2,
+        swapExtraData: bytes(""),
+        recipient: cuh,
+        deadline: block.timestamp
+      })
+    );
+  }
+
+  function testMintEmit() external {
+    token1.mint(cuh, 1 ether);
+
+    vm.prank(cuh);
+    token1.approve(address(lendgineRouter), 1 ether);
+
+    vm.prank(cuh);
+    vm.expectEmit(true, true, true, true, address(lendgineRouter));
+    emit Mint(cuh, address(lendgine), 1 ether, 0.1 ether, cuh);
+    lendgineRouter.mint(
+      LendgineRouter.MintParams({
+        token0: address(token0),
+        token1: address(token1),
+        token0Exp: token0Scale,
+        token1Exp: token1Scale,
+        upperBound: upperBound,
+        amountIn: 1 ether,
+        amountBorrow: 0,
+        sharesMin: 0.1 ether,
+        swapType: SwapHelper.SwapType.UniswapV2,
+        swapExtraData: bytes(""),
+        recipient: cuh,
+        deadline: block.timestamp
+      })
+    );
+  }
+
+  function testBurn() external {
+    token1.mint(cuh, 1 ether);
+
+    vm.prank(cuh);
+    token1.approve(address(lendgineRouter), 1 ether);
+
+    vm.prank(cuh);
+    lendgineRouter.mint(
+      LendgineRouter.MintParams({
+        token0: address(token0),
+        token1: address(token1),
+        token0Exp: token0Scale,
+        token1Exp: token1Scale,
+        upperBound: upperBound,
+        amountIn: 1 ether,
+        amountBorrow: 8.8 ether,
+        sharesMin: 0,
+        swapType: SwapHelper.SwapType.UniswapV2,
+        swapExtraData: bytes(""),
+        recipient: cuh,
+        deadline: block.timestamp
+      })
+    );
+
+    vm.prank(cuh);
+    lendgine.approve(address(lendgineRouter), 0.98 ether);
+
+    vm.prank(cuh);
+    lendgineRouter.burn(
+      LendgineRouter.BurnParams({
+        token0: address(token0),
+        token1: address(token1),
+        token0Exp: token0Scale,
+        token1Exp: token1Scale,
+        upperBound: upperBound,
+        shares: 0.98 ether,
+        collateralMin: 0.96 ether,
+        amount0Min: 0.98 ether,
+        amount1Min: 8 * 0.98 ether,
+        swapType: SwapHelper.SwapType.UniswapV2,
+        swapExtraData: bytes(""),
+        recipient: cuh,
+        deadline: block.timestamp
+      })
+    );
+
+    // check lendgine token
+    assertEq(0, lendgine.balanceOf(cuh));
+    assertEq(0, lendgine.totalSupply());
+
+    // check lendgine storage slots
+    assertEq(0, lendgine.totalLiquidityBorrowed());
+
+    // check uniswap
+    assertApproxEqRel(100 ether, token0.balanceOf(address(uniswapV2Pair)), 0.001 ether);
+    assertApproxEqRel(100 ether, token1.balanceOf(address(uniswapV2Pair)), 0.001 ether);
+
+    // check user balances
+    assertApproxEqRel(0.1 ether, token1.balanceOf(cuh), 1 ether);
+
+    // check router token balances
+    assertEq(0, token0.balanceOf(address(lendgineRouter)));
+    assertEq(0, token1.balanceOf(address(lendgineRouter)));
+    assertEq(0, lendgine.balanceOf(address(lendgineRouter)));
+  }
+
+  function testBurnNoLiquidity() external {
+    token1.mint(cuh, 1 ether);
+
+    vm.prank(cuh);
+    token1.approve(address(lendgineRouter), 1 ether);
+
+    vm.prank(cuh);
+    lendgineRouter.mint(
+      LendgineRouter.MintParams({
+        token0: address(token0),
+        token1: address(token1),
+        token0Exp: token0Scale,
+        token1Exp: token1Scale,
+        upperBound: upperBound,
+        amountIn: 1 ether,
+        amountBorrow: 0,
+        sharesMin: 0,
+        swapType: SwapHelper.SwapType.UniswapV2,
+        swapExtraData: bytes(""),
+        recipient: cuh,
+        deadline: block.timestamp
+      })
+    );
+
+    _withdraw(address(this), address(this), 99.9 ether);
+
+    vm.prank(cuh);
+    lendgine.approve(address(lendgineRouter), 0.1 ether);
+
+    vm.prank(cuh);
+    lendgineRouter.burn(
+      LendgineRouter.BurnParams({
+        token0: address(token0),
+        token1: address(token1),
+        token0Exp: token0Scale,
+        token1Exp: token1Scale,
+        upperBound: upperBound,
+        shares: 0.1 ether,
+        collateralMin: 0,
+        amount0Min: 0.1 ether,
+        amount1Min: 0.8 ether,
+        swapType: SwapHelper.SwapType.UniswapV2,
+        swapExtraData: bytes(""),
+        recipient: cuh,
+        deadline: block.timestamp
+      })
+    );
+
+    // check lendgine token
+    assertEq(0, lendgine.balanceOf(cuh));
+    assertEq(0, lendgine.totalSupply());
+
+    // check lendgine storage slots
+    assertEq(0, lendgine.totalLiquidityBorrowed());
+
+    // check uniswap
+    assertApproxEqRel(100 ether, token0.balanceOf(address(uniswapV2Pair)), 0.001 ether);
+    assertApproxEqRel(100 ether, token1.balanceOf(address(uniswapV2Pair)), 0.001 ether);
+
+    // check user balances
+    assertApproxEqRel(0.1 ether, token1.balanceOf(cuh), 1 ether);
+
+    // check router token balances
+    assertEq(0, token0.balanceOf(address(lendgineRouter)));
+    assertEq(0, token1.balanceOf(address(lendgineRouter)));
+    assertEq(0, lendgine.balanceOf(address(lendgineRouter)));
+  }
+
+  function testBurnV3() external {
+    setUpUniswapV3();
+
+    uint256 balance0Before = token0.balanceOf(address(uniswapV3Pool));
+    uint256 balance1Before = token1.balanceOf(address(uniswapV3Pool));
+
+    uint256 userBalanceBefore = token1.balanceOf(cuh);
+
+    vm.prank(cuh);
+    token1.approve(address(lendgineRouter), 1 ether);
+
+    vm.prank(cuh);
+    lendgineRouter.mint(
+      LendgineRouter.MintParams({
+        token0: address(token0),
+        token1: address(token1),
+        token0Exp: 18,
+        token1Exp: 18,
+        upperBound: 3 ether,
+        amountIn: 1.2 ether,
+        amountBorrow: 0,
+        sharesMin: 0.2 ether,
+        swapType: SwapHelper.SwapType.UniswapV3,
+        swapExtraData: abi.encode(uint24(500)),
+        recipient: cuh,
+        deadline: block.timestamp
+      })
+    );
+
+    vm.prank(cuh);
+    lendgine.approve(address(lendgineRouter), 0.2 ether);
+
+    vm.prank(cuh);
+    lendgineRouter.burn(
+      LendgineRouter.BurnParams({
+        token0: address(token0),
+        token1: address(token1),
+        token0Exp: 18,
+        token1Exp: 18,
+        upperBound: 3 ether,
+        shares: 0.2 ether,
+        collateralMin: 0,
+        amount0Min: 0,
+        amount1Min: 0,
+        swapType: SwapHelper.SwapType.UniswapV3,
+        swapExtraData: abi.encode(uint24(500)),
+        recipient: cuh,
+        deadline: block.timestamp
+      })
+    );
+
+    // check lendgine token
+    assertEq(0, lendgine.balanceOf(cuh));
+    assertEq(0, lendgine.totalSupply());
+
+    // check lendgine storage slots
+    assertEq(0, lendgine.totalLiquidityBorrowed());
+
+    // check uniswap
+    assertApproxEqRel(balance0Before, token0.balanceOf(address(uniswapV3Pool)), 0.001 ether);
+    assertApproxEqRel(balance1Before, token1.balanceOf(address(uniswapV3Pool)), 0.001 ether);
+
+    // check user balance
+    assertApproxEqRel(userBalanceBefore, token1.balanceOf(cuh), 0.001 ether);
+
+    // check router token balances
+    assertEq(0, token0.balanceOf(address(lendgineRouter)));
+    assertEq(0, token1.balanceOf(address(lendgineRouter)));
+    assertEq(0, lendgine.balanceOf(address(lendgineRouter)));
+  }
+
+  function testBurnEmit() external {
+    token1.mint(cuh, 1 ether);
+
+    vm.prank(cuh);
+    token1.approve(address(lendgineRouter), 1 ether);
+
+    vm.prank(cuh);
+    lendgineRouter.mint(
+      LendgineRouter.MintParams({
+        token0: address(token0),
+        token1: address(token1),
+        token0Exp: token0Scale,
+        token1Exp: token1Scale,
+        upperBound: upperBound,
+        amountIn: 1 ether,
+        amountBorrow: 8.8 ether,
+        sharesMin: 0,
+        swapType: SwapHelper.SwapType.UniswapV2,
+        swapExtraData: bytes(""),
+        recipient: cuh,
+        deadline: block.timestamp
+      })
+    );
+
+    vm.prank(cuh);
+    lendgine.approve(address(lendgineRouter), 0.98 ether);
+
+    vm.prank(cuh);
+    vm.expectEmit(true, true, true, true, address(lendgineRouter));
+    emit Burn(cuh, address(lendgine), 9.8 ether, 0.98 ether, cuh);
+    lendgineRouter.burn(
+      LendgineRouter.BurnParams({
+        token0: address(token0),
+        token1: address(token1),
+        token0Exp: token0Scale,
+        token1Exp: token1Scale,
+        upperBound: upperBound,
+        shares: 0.98 ether,
+        collateralMin: 0.96 ether,
+        amount0Min: 0.98 ether,
+        amount1Min: 8 * 0.98 ether,
+        swapType: SwapHelper.SwapType.UniswapV2,
+        swapExtraData: bytes(""),
+        recipient: cuh,
+        deadline: block.timestamp
+      })
+    );
+  }
+
+  function testBurnAmountError() external {
+    token1.mint(cuh, 1 ether);
+
+    vm.prank(cuh);
+    token1.approve(address(lendgineRouter), 1 ether);
+
+    vm.prank(cuh);
+    lendgineRouter.mint(
+      LendgineRouter.MintParams({
+        token0: address(token0),
+        token1: address(token1),
+        token0Exp: token0Scale,
+        token1Exp: token1Scale,
+        upperBound: upperBound,
+        amountIn: 1 ether,
+        amountBorrow: 8.8 ether,
+        sharesMin: 0,
+        swapType: SwapHelper.SwapType.UniswapV2,
+        swapExtraData: bytes(""),
+        recipient: cuh,
+        deadline: block.timestamp
+      })
+    );
+
+    vm.prank(cuh);
+    lendgine.approve(address(lendgineRouter), 0.98 ether);
+
+    vm.prank(cuh);
+    vm.expectRevert(LendgineRouter.AmountError.selector);
+    lendgineRouter.burn(
+      LendgineRouter.BurnParams({
+        token0: address(token0),
+        token1: address(token1),
+        token0Exp: token0Scale,
+        token1Exp: token1Scale,
+        upperBound: upperBound,
+        shares: 0.98 ether,
+        collateralMin: 0,
+        amount0Min: 0.98 ether,
+        amount1Min: 8 * 0.98 ether + 1,
+        swapType: SwapHelper.SwapType.UniswapV2,
+        swapExtraData: bytes(""),
+        recipient: cuh,
+        deadline: block.timestamp
+      })
+    );
+  }
+
+  function testBurnUserAmountError() external {
+    token1.mint(cuh, 1 ether);
+
+    vm.prank(cuh);
+    token1.approve(address(lendgineRouter), 1 ether);
+
+    vm.prank(cuh);
+    lendgineRouter.mint(
+      LendgineRouter.MintParams({
+        token0: address(token0),
+        token1: address(token1),
+        token0Exp: token0Scale,
+        token1Exp: token1Scale,
+        upperBound: upperBound,
+        amountIn: 1 ether,
+        amountBorrow: 8.8 ether,
+        sharesMin: 0,
+        swapType: SwapHelper.SwapType.UniswapV2,
+        swapExtraData: bytes(""),
+        recipient: cuh,
+        deadline: block.timestamp
+      })
+    );
+
+    vm.prank(cuh);
+    lendgine.approve(address(lendgineRouter), 0.98 ether);
+
+    vm.prank(cuh);
+    vm.expectRevert(LendgineRouter.AmountError.selector);
+    lendgineRouter.burn(
+      LendgineRouter.BurnParams({
+        token0: address(token0),
+        token1: address(token1),
+        token0Exp: token0Scale,
+        token1Exp: token1Scale,
+        upperBound: upperBound,
+        shares: 0.98 ether,
+        collateralMin: 1 ether,
+        amount0Min: 0.98 ether,
+        amount1Min: 8 * 0.98 ether,
+        swapType: SwapHelper.SwapType.UniswapV2,
+        swapExtraData: bytes(""),
+        recipient: cuh,
+        deadline: block.timestamp
+      })
+    );
+  }
+
+  function testBurnNoRecipient() external {
+    token1.mint(cuh, 1 ether);
+
+    vm.prank(cuh);
+    token1.approve(address(lendgineRouter), 1 ether);
+
+    vm.prank(cuh);
+    lendgineRouter.mint(
+      LendgineRouter.MintParams({
+        token0: address(token0),
+        token1: address(token1),
+        token0Exp: token0Scale,
+        token1Exp: token1Scale,
+        upperBound: upperBound,
+        amountIn: 1 ether,
+        amountBorrow: 8.8 ether,
+        sharesMin: 0,
+        swapType: SwapHelper.SwapType.UniswapV2,
+        swapExtraData: bytes(""),
+        recipient: cuh,
+        deadline: block.timestamp
+      })
+    );
+
+    uint256 balanceBefore = token1.balanceOf(address(cuh));
+
+    vm.prank(cuh);
+    lendgine.approve(address(lendgineRouter), 0.98 ether);
+
+    vm.prank(cuh);
+    lendgineRouter.burn(
+      LendgineRouter.BurnParams({
+        token0: address(token0),
+        token1: address(token1),
+        token0Exp: token0Scale,
+        token1Exp: token1Scale,
+        upperBound: upperBound,
+        shares: 0.98 ether,
+        collateralMin: 0,
+        amount0Min: 0,
+        amount1Min: 0,
+        swapType: SwapHelper.SwapType.UniswapV2,
+        swapExtraData: bytes(""),
+        recipient: address(0),
+        deadline: block.timestamp
+      })
+    );
+
+    // check lendgine token
+    assertEq(0, lendgine.balanceOf(cuh));
+    assertEq(0, lendgine.totalSupply());
+
+    // check lendgine storage slots
+    assertEq(0, lendgine.totalLiquidityBorrowed());
+
+    // check uniswap
+    assertApproxEqRel(100 ether, token0.balanceOf(address(uniswapV2Pair)), 0.001 ether);
+    assertApproxEqRel(100 ether, token1.balanceOf(address(uniswapV2Pair)), 0.001 ether);
+
+    // check user balances
+    assertEq(balanceBefore, token1.balanceOf(address(cuh)));
+
+    // check router token balances
+    assertEq(0, token0.balanceOf(address(lendgineRouter)));
+    assertApproxEqRel(0.1 ether, token1.balanceOf(address(lendgineRouter)), 1 ether);
+    assertEq(0, lendgine.balanceOf(address(lendgineRouter)));
+  }
 }
diff --git a/test/LiquidityManagerTest.t.sol b/test/LiquidityManagerTest.t.sol
index cf2c955..eeb4489 100644
--- a/test/LiquidityManagerTest.t.sol
+++ b/test/LiquidityManagerTest.t.sol
@@ -6,574 +6,559 @@ import { LiquidityManager } from "../src/periphery/LiquidityManager.sol";
 import { TestHelper } from "./utils/TestHelper.sol";
 
 contract LiquidityManagerTest is TestHelper {
-    event AddLiquidity(
-        address indexed from,
-        address indexed lendgine,
-        uint256 liquidity,
-        uint256 size,
-        uint256 amount0,
-        uint256 amount1,
-        address indexed to
-    );
-
-    event RemoveLiquidity(
-        address indexed from,
-        address indexed lendgine,
-        uint256 liquidity,
-        uint256 size,
-        uint256 amount0,
-        uint256 amount1,
-        address indexed to
-    );
-
-    event Collect(address indexed from, address indexed lendgine, uint256 amount, address indexed to);
-
-    LiquidityManager public liquidityManager;
-
-    function setUp() external {
-        _setUp();
-        liquidityManager = new LiquidityManager(address(factory), address(0));
-    }
-
-    function _addLiquidity(
-        address to,
-        address from,
-        uint256 amount0,
-        uint256 amount1,
-        uint256 liquidity
-    ) internal {
-        token0.mint(from, amount0);
-        token1.mint(from, amount1);
-
-        if (from != address(this)) {
-            vm.startPrank(from);
-            token0.approve(address(liquidityManager), amount0);
-            token1.approve(address(liquidityManager), amount1);
-            vm.stopPrank();
-        } else {
-            token0.approve(address(liquidityManager), amount0);
-            token1.approve(address(liquidityManager), amount1);
-        }
-
-        if (from != address(this)) vm.prank(from);
-        liquidityManager.addLiquidity(
-            LiquidityManager.AddLiquidityParams({
-                token0: address(token0),
-                token1: address(token1),
-                token0Exp: token0Scale,
-                token1Exp: token1Scale,
-                upperBound: upperBound,
-                liquidity: liquidity,
-                amount0Min: amount0,
-                amount1Min: amount1,
-                sizeMin: 0,
-                recipient: to,
-                deadline: block.timestamp
-            })
-        );
-    }
-
-    function testAddPositionEmpty() external {
-        _addLiquidity(cuh, cuh, 1 ether, 8 ether, 1 ether);
-
-        // test lendgine storage
-        assertEq(lendgine.totalLiquidity(), 1 ether);
-        assertEq(lendgine.totalPositionSize(), 1 ether);
-        assertEq(lendgine.reserve0(), 1 ether);
-        assertEq(lendgine.reserve1(), 8 ether);
-
-        // test lendgine position
-        (uint256 positionSize, , ) = lendgine.positions(address(liquidityManager));
-        assertEq(1 ether, positionSize);
-
-        // test balances
-        assertEq(0, token0.balanceOf(address(liquidityManager)));
-        assertEq(0, token1.balanceOf(address(liquidityManager)));
-
-        // test liquidity manager position
-        (positionSize, , ) = liquidityManager.positions(cuh, address(lendgine));
-        assertEq(1 ether, positionSize);
-    }
-
-    function testAddPosition() external {
-        _deposit(address(this), address(this), 1 ether, 8 ether, 1 ether);
-
-        _addLiquidity(cuh, cuh, 1 ether, 8 ether, 1 ether);
-
-        // test lendgine storage
-        assertEq(lendgine.totalLiquidity(), 2 ether);
-        assertEq(lendgine.totalPositionSize(), 2 ether);
-        assertEq(lendgine.reserve0(), 2 ether);
-        assertEq(lendgine.reserve1(), 16 ether);
-
-        // test lendgine position
-        (uint256 positionSize, , ) = lendgine.positions(address(liquidityManager));
-        assertEq(1 ether, positionSize);
-
-        // test balances
-        assertEq(0, token0.balanceOf(address(liquidityManager)));
-        assertEq(0, token1.balanceOf(address(liquidityManager)));
-
-        // test liquidity manager position
-        (positionSize, , ) = liquidityManager.positions(cuh, address(lendgine));
-        assertEq(1 ether, positionSize);
-    }
-
-    function testDeadline() external {
-        vm.warp(2);
-
-        vm.expectRevert(LiquidityManager.LivelinessError.selector);
-        liquidityManager.addLiquidity(
-            LiquidityManager.AddLiquidityParams({
-                token0: address(token0),
-                token1: address(token1),
-                token0Exp: token0Scale,
-                token1Exp: token1Scale,
-                upperBound: upperBound,
-                liquidity: 1 ether,
-                amount0Min: 1 ether,
-                amount1Min: 8 ether,
-                sizeMin: 1 ether,
-                recipient: cuh,
-                deadline: 1
-            })
-        );
-    }
-
-    function testAmountErrorAdd() external {
-        token0.mint(cuh, 1 ether);
-        token1.mint(cuh, 8 ether);
-
-        vm.startPrank(cuh);
-        token0.approve(address(liquidityManager), 1 ether);
-        token1.approve(address(liquidityManager), 8 ether);
-
-        vm.expectRevert(LiquidityManager.AmountError.selector);
-        liquidityManager.addLiquidity(
-            LiquidityManager.AddLiquidityParams({
-                token0: address(token0),
-                token1: address(token1),
-                token0Exp: token0Scale,
-                token1Exp: token1Scale,
-                upperBound: upperBound,
-                liquidity: 1 ether,
-                amount0Min: 1 ether,
-                amount1Min: 8 ether,
-                sizeMin: 1 ether + 1,
-                recipient: cuh,
-                deadline: block.timestamp
-            })
-        );
-        vm.stopPrank();
-    }
-
-    function testAccruePosition() external {
-        _addLiquidity(cuh, cuh, 1 ether, 8 ether, 1 ether);
-
-        _mint(address(this), address(this), 5 ether);
-
-        vm.warp(365 days + 1);
-
-        _addLiquidity(cuh, cuh, 1 ether, 8 ether, 1 ether);
-
-        uint256 borrowRate = lendgine.getBorrowRate(0.5 ether, 1 ether);
-        uint256 lpDilution = borrowRate / 2; // 0.5 lp for one year
-        uint256 size = (1 ether * 1 ether) / (1 ether - lpDilution);
-
-        // test lendgine storage
-        assertEq(lendgine.totalLiquidity(), 1.5 ether);
-        assertEq(lendgine.totalPositionSize(), 1 ether + size);
-        assertEq(lendgine.reserve0(), 1.5 ether);
-        assertEq(lendgine.reserve1(), 12 ether);
-
-        // test lendgine position
-        (uint256 positionSize, uint256 rewardPerPositionPaid, uint256 tokensOwed) = lendgine.positions(
-            address(liquidityManager)
-        );
-        assertEq(1 ether + size, positionSize);
-        assertEq(10 * lpDilution, rewardPerPositionPaid);
-        assertEq(10 * lpDilution, tokensOwed);
-
-        // test balances
-        assertEq(0, token0.balanceOf(address(liquidityManager)));
-        assertEq(0, token1.balanceOf(address(liquidityManager)));
-
-        // test liquidity manager position
-        (positionSize, rewardPerPositionPaid, tokensOwed) = liquidityManager.positions(cuh, address(lendgine));
-        assertEq(size + 1 ether, positionSize);
-        assertEq(10 * lpDilution, rewardPerPositionPaid);
-        assertEq(10 * lpDilution, tokensOwed);
-    }
-
-    function testNewPositionAccrued() external {
-        _addLiquidity(address(this), address(this), 1 ether, 8 ether, 1 ether);
-
-        _mint(address(this), address(this), 5 ether);
-
-        vm.warp(365 days + 1);
-
-        _addLiquidity(cuh, cuh, 1 ether, 8 ether, 1 ether);
-
-        uint256 borrowRate = lendgine.getBorrowRate(0.5 ether, 1 ether);
-        uint256 lpDilution = borrowRate / 2; // 0.5 lp for one year
-        uint256 size = (1 ether * 1 ether) / (1 ether - lpDilution);
-
-        // test lendgine storage
-        assertEq(lendgine.totalLiquidity(), 1.5 ether);
-        assertEq(lendgine.totalPositionSize(), 1 ether + size);
-        assertEq(lendgine.reserve0(), 1.5 ether);
-        assertEq(lendgine.reserve1(), 12 ether);
-
-        // test lendgine position
-        (uint256 positionSize, uint256 rewardPerPositionPaid, ) = lendgine.positions(address(liquidityManager));
-        assertEq(1 ether + size, positionSize);
-        assertEq(10 * lpDilution, rewardPerPositionPaid);
-
-        // test balances
-        assertEq(0, token0.balanceOf(address(liquidityManager)));
-        assertEq(0, token1.balanceOf(address(liquidityManager)));
-
-        // test liquidity manager position
-        (positionSize, rewardPerPositionPaid, ) = liquidityManager.positions(cuh, address(lendgine));
-        assertEq(size, positionSize);
-        assertEq(10 * lpDilution, rewardPerPositionPaid);
-    }
-
-    function testCallbackValidation() external {
-        vm.expectRevert(LiquidityManager.ValidationError.selector);
-        liquidityManager.pairMintCallback(
-            0,
-            abi.encode(
-                LiquidityManager.PairMintCallbackData({
-                    token0: address(token0),
-                    token1: address(token1),
-                    token0Exp: token0Scale,
-                    token1Exp: token1Scale,
-                    upperBound: upperBound,
-                    amount0: 0,
-                    amount1: 0,
-                    payer: address(this)
-                })
-            )
-        );
+  event AddLiquidity(
+    address indexed from,
+    address indexed lendgine,
+    uint256 liquidity,
+    uint256 size,
+    uint256 amount0,
+    uint256 amount1,
+    address indexed to
+  );
+
+  event RemoveLiquidity(
+    address indexed from,
+    address indexed lendgine,
+    uint256 liquidity,
+    uint256 size,
+    uint256 amount0,
+    uint256 amount1,
+    address indexed to
+  );
+
+  event Collect(address indexed from, address indexed lendgine, uint256 amount, address indexed to);
+
+  LiquidityManager public liquidityManager;
+
+  function setUp() external {
+    _setUp();
+    liquidityManager = new LiquidityManager(address(factory), address(0));
+  }
+
+  function _addLiquidity(address to, address from, uint256 amount0, uint256 amount1, uint256 liquidity) internal {
+    token0.mint(from, amount0);
+    token1.mint(from, amount1);
+
+    if (from != address(this)) {
+      vm.startPrank(from);
+      token0.approve(address(liquidityManager), amount0);
+      token1.approve(address(liquidityManager), amount1);
+      vm.stopPrank();
+    } else {
+      token0.approve(address(liquidityManager), amount0);
+      token1.approve(address(liquidityManager), amount1);
     }
 
-    function testEmitAdd() external {
-        token0.mint(cuh, 1 ether);
-        token1.mint(cuh, 8 ether);
-
-        vm.startPrank(cuh);
-        token0.approve(address(liquidityManager), 1 ether);
-        token1.approve(address(liquidityManager), 8 ether);
-
-        vm.expectEmit(true, true, true, true, address(liquidityManager));
-        emit AddLiquidity(cuh, address(lendgine), 1 ether, 1 ether, 1 ether, 8 ether, cuh);
-        liquidityManager.addLiquidity(
-            LiquidityManager.AddLiquidityParams({
-                token0: address(token0),
-                token1: address(token1),
-                token0Exp: token0Scale,
-                token1Exp: token1Scale,
-                upperBound: upperBound,
-                liquidity: 1 ether,
-                amount0Min: 1 ether,
-                amount1Min: 8 ether,
-                sizeMin: 1 ether,
-                recipient: cuh,
-                deadline: block.timestamp
-            })
-        );
-        vm.stopPrank();
-    }
-
-    function testRemovePosition() external {
-        _addLiquidity(cuh, cuh, 1 ether, 8 ether, 1 ether);
-
-        vm.prank(cuh);
-        liquidityManager.removeLiquidity(
-            LiquidityManager.RemoveLiquidityParams({
-                token0: address(token0),
-                token1: address(token1),
-                token0Exp: token0Scale,
-                token1Exp: token1Scale,
-                upperBound: upperBound,
-                size: 1 ether,
-                amount0Min: 1 ether,
-                amount1Min: 8 ether,
-                recipient: cuh,
-                deadline: block.timestamp
-            })
-        );
-
-        // test lendgine storage
-        assertEq(lendgine.totalLiquidity(), 0);
-        assertEq(lendgine.totalPositionSize(), 0);
-        assertEq(lendgine.reserve0(), 0);
-        assertEq(lendgine.reserve1(), 0);
-
-        // test lendgine position
-        (uint256 positionSize, , ) = lendgine.positions(address(liquidityManager));
-        assertEq(0, positionSize);
-
-        // test balances
-        assertEq(0, token0.balanceOf(address(liquidityManager)));
-        assertEq(0, token1.balanceOf(address(liquidityManager)));
-
-        // test liquidity manager position
-        (positionSize, , ) = liquidityManager.positions(cuh, address(lendgine));
-        assertEq(0, positionSize);
-    }
+    if (from != address(this)) vm.prank(from);
+    liquidityManager.addLiquidity(
+      LiquidityManager.AddLiquidityParams({
+        token0: address(token0),
+        token1: address(token1),
+        token0Exp: token0Scale,
+        token1Exp: token1Scale,
+        upperBound: upperBound,
+        liquidity: liquidity,
+        amount0Min: amount0,
+        amount1Min: amount1,
+        sizeMin: 0,
+        recipient: to,
+        deadline: block.timestamp
+      })
+    );
+  }
+
+  function testAddPositionEmpty() external {
+    _addLiquidity(cuh, cuh, 1 ether, 8 ether, 1 ether);
+
+    // test lendgine storage
+    assertEq(lendgine.totalLiquidity(), 1 ether);
+    assertEq(lendgine.totalPositionSize(), 1 ether);
+    assertEq(lendgine.reserve0(), 1 ether);
+    assertEq(lendgine.reserve1(), 8 ether);
+
+    // test lendgine position
+    (uint256 positionSize,,) = lendgine.positions(address(liquidityManager));
+    assertEq(1 ether, positionSize);
+
+    // test balances
+    assertEq(0, token0.balanceOf(address(liquidityManager)));
+    assertEq(0, token1.balanceOf(address(liquidityManager)));
+
+    // test liquidity manager position
+    (positionSize,,) = liquidityManager.positions(cuh, address(lendgine));
+    assertEq(1 ether, positionSize);
+  }
+
+  function testAddPosition() external {
+    _deposit(address(this), address(this), 1 ether, 8 ether, 1 ether);
+
+    _addLiquidity(cuh, cuh, 1 ether, 8 ether, 1 ether);
+
+    // test lendgine storage
+    assertEq(lendgine.totalLiquidity(), 2 ether);
+    assertEq(lendgine.totalPositionSize(), 2 ether);
+    assertEq(lendgine.reserve0(), 2 ether);
+    assertEq(lendgine.reserve1(), 16 ether);
+
+    // test lendgine position
+    (uint256 positionSize,,) = lendgine.positions(address(liquidityManager));
+    assertEq(1 ether, positionSize);
+
+    // test balances
+    assertEq(0, token0.balanceOf(address(liquidityManager)));
+    assertEq(0, token1.balanceOf(address(liquidityManager)));
+
+    // test liquidity manager position
+    (positionSize,,) = liquidityManager.positions(cuh, address(lendgine));
+    assertEq(1 ether, positionSize);
+  }
+
+  function testDeadline() external {
+    vm.warp(2);
+
+    vm.expectRevert(LiquidityManager.LivelinessError.selector);
+    liquidityManager.addLiquidity(
+      LiquidityManager.AddLiquidityParams({
+        token0: address(token0),
+        token1: address(token1),
+        token0Exp: token0Scale,
+        token1Exp: token1Scale,
+        upperBound: upperBound,
+        liquidity: 1 ether,
+        amount0Min: 1 ether,
+        amount1Min: 8 ether,
+        sizeMin: 1 ether,
+        recipient: cuh,
+        deadline: 1
+      })
+    );
+  }
+
+  function testAmountErrorAdd() external {
+    token0.mint(cuh, 1 ether);
+    token1.mint(cuh, 8 ether);
+
+    vm.startPrank(cuh);
+    token0.approve(address(liquidityManager), 1 ether);
+    token1.approve(address(liquidityManager), 8 ether);
+
+    vm.expectRevert(LiquidityManager.AmountError.selector);
+    liquidityManager.addLiquidity(
+      LiquidityManager.AddLiquidityParams({
+        token0: address(token0),
+        token1: address(token1),
+        token0Exp: token0Scale,
+        token1Exp: token1Scale,
+        upperBound: upperBound,
+        liquidity: 1 ether,
+        amount0Min: 1 ether,
+        amount1Min: 8 ether,
+        sizeMin: 1 ether + 1,
+        recipient: cuh,
+        deadline: block.timestamp
+      })
+    );
+    vm.stopPrank();
+  }
+
+  function testAccruePosition() external {
+    _addLiquidity(cuh, cuh, 1 ether, 8 ether, 1 ether);
+
+    _mint(address(this), address(this), 5 ether);
+
+    vm.warp(365 days + 1);
+
+    _addLiquidity(cuh, cuh, 1 ether, 8 ether, 1 ether);
+
+    uint256 borrowRate = lendgine.getBorrowRate(0.5 ether, 1 ether);
+    uint256 lpDilution = borrowRate / 2; // 0.5 lp for one year
+    uint256 size = (1 ether * 1 ether) / (1 ether - lpDilution);
+
+    // test lendgine storage
+    assertEq(lendgine.totalLiquidity(), 1.5 ether);
+    assertEq(lendgine.totalPositionSize(), 1 ether + size);
+    assertEq(lendgine.reserve0(), 1.5 ether);
+    assertEq(lendgine.reserve1(), 12 ether);
+
+    // test lendgine position
+    (uint256 positionSize, uint256 rewardPerPositionPaid, uint256 tokensOwed) =
+      lendgine.positions(address(liquidityManager));
+    assertEq(1 ether + size, positionSize);
+    assertEq(10 * lpDilution, rewardPerPositionPaid);
+    assertEq(10 * lpDilution, tokensOwed);
+
+    // test balances
+    assertEq(0, token0.balanceOf(address(liquidityManager)));
+    assertEq(0, token1.balanceOf(address(liquidityManager)));
+
+    // test liquidity manager position
+    (positionSize, rewardPerPositionPaid, tokensOwed) = liquidityManager.positions(cuh, address(lendgine));
+    assertEq(size + 1 ether, positionSize);
+    assertEq(10 * lpDilution, rewardPerPositionPaid);
+    assertEq(10 * lpDilution, tokensOwed);
+  }
+
+  function testNewPositionAccrued() external {
+    _addLiquidity(address(this), address(this), 1 ether, 8 ether, 1 ether);
+
+    _mint(address(this), address(this), 5 ether);
+
+    vm.warp(365 days + 1);
+
+    _addLiquidity(cuh, cuh, 1 ether, 8 ether, 1 ether);
+
+    uint256 borrowRate = lendgine.getBorrowRate(0.5 ether, 1 ether);
+    uint256 lpDilution = borrowRate / 2; // 0.5 lp for one year
+    uint256 size = (1 ether * 1 ether) / (1 ether - lpDilution);
+
+    // test lendgine storage
+    assertEq(lendgine.totalLiquidity(), 1.5 ether);
+    assertEq(lendgine.totalPositionSize(), 1 ether + size);
+    assertEq(lendgine.reserve0(), 1.5 ether);
+    assertEq(lendgine.reserve1(), 12 ether);
+
+    // test lendgine position
+    (uint256 positionSize, uint256 rewardPerPositionPaid,) = lendgine.positions(address(liquidityManager));
+    assertEq(1 ether + size, positionSize);
+    assertEq(10 * lpDilution, rewardPerPositionPaid);
+
+    // test balances
+    assertEq(0, token0.balanceOf(address(liquidityManager)));
+    assertEq(0, token1.balanceOf(address(liquidityManager)));
+
+    // test liquidity manager position
+    (positionSize, rewardPerPositionPaid,) = liquidityManager.positions(cuh, address(lendgine));
+    assertEq(size, positionSize);
+    assertEq(10 * lpDilution, rewardPerPositionPaid);
+  }
+
+  function testCallbackValidation() external {
+    vm.expectRevert(LiquidityManager.ValidationError.selector);
+    liquidityManager.pairMintCallback(
+      0,
+      abi.encode(
+        LiquidityManager.PairMintCallbackData({
+          token0: address(token0),
+          token1: address(token1),
+          token0Exp: token0Scale,
+          token1Exp: token1Scale,
+          upperBound: upperBound,
+          amount0: 0,
+          amount1: 0,
+          payer: address(this)
+        })
+      )
+    );
+  }
+
+  function testEmitAdd() external {
+    token0.mint(cuh, 1 ether);
+    token1.mint(cuh, 8 ether);
+
+    vm.startPrank(cuh);
+    token0.approve(address(liquidityManager), 1 ether);
+    token1.approve(address(liquidityManager), 8 ether);
+
+    vm.expectEmit(true, true, true, true, address(liquidityManager));
+    emit AddLiquidity(cuh, address(lendgine), 1 ether, 1 ether, 1 ether, 8 ether, cuh);
+    liquidityManager.addLiquidity(
+      LiquidityManager.AddLiquidityParams({
+        token0: address(token0),
+        token1: address(token1),
+        token0Exp: token0Scale,
+        token1Exp: token1Scale,
+        upperBound: upperBound,
+        liquidity: 1 ether,
+        amount0Min: 1 ether,
+        amount1Min: 8 ether,
+        sizeMin: 1 ether,
+        recipient: cuh,
+        deadline: block.timestamp
+      })
+    );
+    vm.stopPrank();
+  }
+
+  function testRemovePosition() external {
+    _addLiquidity(cuh, cuh, 1 ether, 8 ether, 1 ether);
+
+    vm.prank(cuh);
+    liquidityManager.removeLiquidity(
+      LiquidityManager.RemoveLiquidityParams({
+        token0: address(token0),
+        token1: address(token1),
+        token0Exp: token0Scale,
+        token1Exp: token1Scale,
+        upperBound: upperBound,
+        size: 1 ether,
+        amount0Min: 1 ether,
+        amount1Min: 8 ether,
+        recipient: cuh,
+        deadline: block.timestamp
+      })
+    );
 
-    function testRemoveAmountError() external {
-        _addLiquidity(cuh, cuh, 1 ether, 8 ether, 1 ether);
-
-        vm.prank(cuh);
-        vm.expectRevert(LiquidityManager.AmountError.selector);
-        liquidityManager.removeLiquidity(
-            LiquidityManager.RemoveLiquidityParams({
-                token0: address(token0),
-                token1: address(token1),
-                token0Exp: token0Scale,
-                token1Exp: token1Scale,
-                upperBound: upperBound,
-                size: 1 ether,
-                amount0Min: 1 ether + 1,
-                amount1Min: 8 ether + 1,
-                recipient: cuh,
-                deadline: block.timestamp
-            })
-        );
-    }
+    // test lendgine storage
+    assertEq(lendgine.totalLiquidity(), 0);
+    assertEq(lendgine.totalPositionSize(), 0);
+    assertEq(lendgine.reserve0(), 0);
+    assertEq(lendgine.reserve1(), 0);
+
+    // test lendgine position
+    (uint256 positionSize,,) = lendgine.positions(address(liquidityManager));
+    assertEq(0, positionSize);
+
+    // test balances
+    assertEq(0, token0.balanceOf(address(liquidityManager)));
+    assertEq(0, token1.balanceOf(address(liquidityManager)));
+
+    // test liquidity manager position
+    (positionSize,,) = liquidityManager.positions(cuh, address(lendgine));
+    assertEq(0, positionSize);
+  }
+
+  function testRemoveAmountError() external {
+    _addLiquidity(cuh, cuh, 1 ether, 8 ether, 1 ether);
+
+    vm.prank(cuh);
+    vm.expectRevert(LiquidityManager.AmountError.selector);
+    liquidityManager.removeLiquidity(
+      LiquidityManager.RemoveLiquidityParams({
+        token0: address(token0),
+        token1: address(token1),
+        token0Exp: token0Scale,
+        token1Exp: token1Scale,
+        upperBound: upperBound,
+        size: 1 ether,
+        amount0Min: 1 ether + 1,
+        amount1Min: 8 ether + 1,
+        recipient: cuh,
+        deadline: block.timestamp
+      })
+    );
+  }
+
+  function testRemoveNoRecipient() external {
+    _addLiquidity(cuh, cuh, 1 ether, 8 ether, 1 ether);
+
+    vm.prank(cuh);
+    liquidityManager.removeLiquidity(
+      LiquidityManager.RemoveLiquidityParams({
+        token0: address(token0),
+        token1: address(token1),
+        token0Exp: token0Scale,
+        token1Exp: token1Scale,
+        upperBound: upperBound,
+        size: 1 ether,
+        amount0Min: 1 ether,
+        amount1Min: 8 ether,
+        recipient: address(0),
+        deadline: block.timestamp
+      })
+    );
 
-    function testRemoveNoRecipient() external {
-        _addLiquidity(cuh, cuh, 1 ether, 8 ether, 1 ether);
-
-        vm.prank(cuh);
-        liquidityManager.removeLiquidity(
-            LiquidityManager.RemoveLiquidityParams({
-                token0: address(token0),
-                token1: address(token1),
-                token0Exp: token0Scale,
-                token1Exp: token1Scale,
-                upperBound: upperBound,
-                size: 1 ether,
-                amount0Min: 1 ether,
-                amount1Min: 8 ether,
-                recipient: address(0),
-                deadline: block.timestamp
-            })
-        );
-
-        // test lendgine storage
-        assertEq(lendgine.totalLiquidity(), 0);
-        assertEq(lendgine.totalPositionSize(), 0);
-        assertEq(lendgine.reserve0(), 0);
-        assertEq(lendgine.reserve1(), 0);
-
-        // test lendgine position
-        (uint256 positionSize, , ) = lendgine.positions(address(liquidityManager));
-        assertEq(0, positionSize);
-
-        // test balances
-        assertEq(1 ether, token0.balanceOf(address(liquidityManager)));
-        assertEq(8 ether, token1.balanceOf(address(liquidityManager)));
-
-        // test liquidity manager position
-        (positionSize, , ) = liquidityManager.positions(cuh, address(lendgine));
-        assertEq(0, positionSize);
-    }
+    // test lendgine storage
+    assertEq(lendgine.totalLiquidity(), 0);
+    assertEq(lendgine.totalPositionSize(), 0);
+    assertEq(lendgine.reserve0(), 0);
+    assertEq(lendgine.reserve1(), 0);
+
+    // test lendgine position
+    (uint256 positionSize,,) = lendgine.positions(address(liquidityManager));
+    assertEq(0, positionSize);
+
+    // test balances
+    assertEq(1 ether, token0.balanceOf(address(liquidityManager)));
+    assertEq(8 ether, token1.balanceOf(address(liquidityManager)));
+
+    // test liquidity manager position
+    (positionSize,,) = liquidityManager.positions(cuh, address(lendgine));
+    assertEq(0, positionSize);
+  }
+
+  function testOverRemove() external {
+    _addLiquidity(address(this), address(this), 1 ether, 8 ether, 1 ether);
+    _addLiquidity(cuh, cuh, 1 ether, 8 ether, 1 ether);
+
+    vm.prank(cuh);
+    vm.expectRevert();
+    liquidityManager.removeLiquidity(
+      LiquidityManager.RemoveLiquidityParams({
+        token0: address(token0),
+        token1: address(token1),
+        token0Exp: token0Scale,
+        token1Exp: token1Scale,
+        upperBound: upperBound,
+        size: 1.5 ether,
+        amount0Min: 1 ether,
+        amount1Min: 8 ether,
+        recipient: address(0),
+        deadline: block.timestamp
+      })
+    );
+  }
+
+  function testEmitRemove() external {
+    _addLiquidity(cuh, cuh, 1 ether, 8 ether, 1 ether);
+
+    vm.prank(cuh);
+    vm.expectEmit(true, true, true, true, address(liquidityManager));
+    emit RemoveLiquidity(cuh, address(lendgine), 1 ether, 1 ether, 1 ether, 8 ether, cuh);
+    liquidityManager.removeLiquidity(
+      LiquidityManager.RemoveLiquidityParams({
+        token0: address(token0),
+        token1: address(token1),
+        token0Exp: token0Scale,
+        token1Exp: token1Scale,
+        upperBound: upperBound,
+        size: 1 ether,
+        amount0Min: 1 ether,
+        amount1Min: 8 ether,
+        recipient: cuh,
+        deadline: block.timestamp
+      })
+    );
+  }
+
+  function testRemoveAccrue() external {
+    _addLiquidity(cuh, cuh, 1 ether, 8 ether, 1 ether);
+    _mint(address(this), address(this), 5 ether);
+    vm.warp(365 days + 1);
+
+    vm.prank(cuh);
+    liquidityManager.removeLiquidity(
+      LiquidityManager.RemoveLiquidityParams({
+        token0: address(token0),
+        token1: address(token1),
+        token0Exp: token0Scale,
+        token1Exp: token1Scale,
+        upperBound: upperBound,
+        size: 0.5 ether,
+        amount0Min: 0,
+        amount1Min: 0,
+        recipient: cuh,
+        deadline: block.timestamp
+      })
+    );
 
-    function testOverRemove() external {
-        _addLiquidity(address(this), address(this), 1 ether, 8 ether, 1 ether);
-        _addLiquidity(cuh, cuh, 1 ether, 8 ether, 1 ether);
-
-        vm.prank(cuh);
-        vm.expectRevert();
-        liquidityManager.removeLiquidity(
-            LiquidityManager.RemoveLiquidityParams({
-                token0: address(token0),
-                token1: address(token1),
-                token0Exp: token0Scale,
-                token1Exp: token1Scale,
-                upperBound: upperBound,
-                size: 1.5 ether,
-                amount0Min: 1 ether,
-                amount1Min: 8 ether,
-                recipient: address(0),
-                deadline: block.timestamp
-            })
-        );
-    }
+    uint256 borrowRate = lendgine.getBorrowRate(0.5 ether, 1 ether);
+    uint256 lpDilution = borrowRate / 2; // 0.5 lp for one year
+
+    // test lendgine storage
+    assertEq(lendgine.totalLiquidity(), lpDilution / 2);
+    assertEq(lendgine.totalPositionSize(), 0.5 ether);
+    assertEq(lendgine.reserve0(), lpDilution / 2);
+    assertEq(lendgine.reserve1(), lpDilution * 4);
+
+    // test lendgine position
+    (uint256 positionSize, uint256 rewardPerPositionPaid,) = lendgine.positions(address(liquidityManager));
+    assertEq(0.5 ether, positionSize);
+    assertEq(lpDilution * 10, rewardPerPositionPaid);
+
+    // test balances
+    assertEq(0, token0.balanceOf(address(liquidityManager)));
+    assertEq(0, token1.balanceOf(address(liquidityManager)));
+
+    // test liquidity manager position
+    (positionSize, rewardPerPositionPaid,) = liquidityManager.positions(cuh, address(lendgine));
+    assertEq(0.5 ether, positionSize);
+    assertEq(lpDilution * 10, rewardPerPositionPaid);
+  }
+
+  function testCollect() external {
+    _addLiquidity(cuh, cuh, 1 ether, 8 ether, 1 ether);
+    _mint(address(this), address(this), 5 ether);
+    vm.warp(365 days + 1);
+
+    uint256 borrowRate = lendgine.getBorrowRate(0.5 ether, 1 ether);
+    uint256 lpDilution = borrowRate / 2; // 0.5 lp for one year
+
+    vm.prank(cuh);
+    liquidityManager.collect(
+      LiquidityManager.CollectParams({lendgine: address(lendgine), recipient: cuh, amountRequested: lpDilution * 10})
+    );
 
-    function testEmitRemove() external {
-        _addLiquidity(cuh, cuh, 1 ether, 8 ether, 1 ether);
-
-        vm.prank(cuh);
-        vm.expectEmit(true, true, true, true, address(liquidityManager));
-        emit RemoveLiquidity(cuh, address(lendgine), 1 ether, 1 ether, 1 ether, 8 ether, cuh);
-        liquidityManager.removeLiquidity(
-            LiquidityManager.RemoveLiquidityParams({
-                token0: address(token0),
-                token1: address(token1),
-                token0Exp: token0Scale,
-                token1Exp: token1Scale,
-                upperBound: upperBound,
-                size: 1 ether,
-                amount0Min: 1 ether,
-                amount1Min: 8 ether,
-                recipient: cuh,
-                deadline: block.timestamp
-            })
-        );
-    }
+    // test lendgine storage slots
+    assertEq(lpDilution * 10, lendgine.rewardPerPositionStored());
 
-    function testRemoveAccrue() external {
-        _addLiquidity(cuh, cuh, 1 ether, 8 ether, 1 ether);
-        _mint(address(this), address(this), 5 ether);
-        vm.warp(365 days + 1);
-
-        vm.prank(cuh);
-        liquidityManager.removeLiquidity(
-            LiquidityManager.RemoveLiquidityParams({
-                token0: address(token0),
-                token1: address(token1),
-                token0Exp: token0Scale,
-                token1Exp: token1Scale,
-                upperBound: upperBound,
-                size: 0.5 ether,
-                amount0Min: 0,
-                amount1Min: 0,
-                recipient: cuh,
-                deadline: block.timestamp
-            })
-        );
-
-        uint256 borrowRate = lendgine.getBorrowRate(0.5 ether, 1 ether);
-        uint256 lpDilution = borrowRate / 2; // 0.5 lp for one year
-
-        // test lendgine storage
-        assertEq(lendgine.totalLiquidity(), lpDilution / 2);
-        assertEq(lendgine.totalPositionSize(), 0.5 ether);
-        assertEq(lendgine.reserve0(), lpDilution / 2);
-        assertEq(lendgine.reserve1(), lpDilution * 4);
-
-        // test lendgine position
-        (uint256 positionSize, uint256 rewardPerPositionPaid, ) = lendgine.positions(address(liquidityManager));
-        assertEq(0.5 ether, positionSize);
-        assertEq(lpDilution * 10, rewardPerPositionPaid);
-
-        // test balances
-        assertEq(0, token0.balanceOf(address(liquidityManager)));
-        assertEq(0, token1.balanceOf(address(liquidityManager)));
-
-        // test liquidity manager position
-        (positionSize, rewardPerPositionPaid, ) = liquidityManager.positions(cuh, address(lendgine));
-        assertEq(0.5 ether, positionSize);
-        assertEq(lpDilution * 10, rewardPerPositionPaid);
-    }
+    // test lendgine position
+    (, uint256 rewardPerPositionPaid, uint256 tokensOwed) = lendgine.positions(address(liquidityManager));
+    assertEq(lpDilution * 10, rewardPerPositionPaid);
+    assertEq(0, tokensOwed);
 
-    function testCollect() external {
-        _addLiquidity(cuh, cuh, 1 ether, 8 ether, 1 ether);
-        _mint(address(this), address(this), 5 ether);
-        vm.warp(365 days + 1);
-
-        uint256 borrowRate = lendgine.getBorrowRate(0.5 ether, 1 ether);
-        uint256 lpDilution = borrowRate / 2; // 0.5 lp for one year
-
-        vm.prank(cuh);
-        liquidityManager.collect(
-            LiquidityManager.CollectParams({
-                lendgine: address(lendgine),
-                recipient: cuh,
-                amountRequested: lpDilution * 10
-            })
-        );
-
-        // test lendgine storage slots
-        assertEq(lpDilution * 10, lendgine.rewardPerPositionStored());
-
-        // test lendgine position
-        (, uint256 rewardPerPositionPaid, uint256 tokensOwed) = lendgine.positions(address(liquidityManager));
-        assertEq(lpDilution * 10, rewardPerPositionPaid);
-        assertEq(0, tokensOwed);
-
-        // test liquidity manager position
-        (, rewardPerPositionPaid, tokensOwed) = liquidityManager.positions(cuh, address(lendgine));
-        assertEq(lpDilution * 10, rewardPerPositionPaid);
-        assertEq(0, tokensOwed);
-
-        // test user balances
-        assertEq(token1.balanceOf(cuh), lpDilution * 10);
-    }
+    // test liquidity manager position
+    (, rewardPerPositionPaid, tokensOwed) = liquidityManager.positions(cuh, address(lendgine));
+    assertEq(lpDilution * 10, rewardPerPositionPaid);
+    assertEq(0, tokensOwed);
 
-    function testOverCollect() external {
-        _addLiquidity(cuh, cuh, 1 ether, 8 ether, 1 ether);
-        _mint(address(this), address(this), 5 ether);
-        vm.warp(365 days + 1);
+    // test user balances
+    assertEq(token1.balanceOf(cuh), lpDilution * 10);
+  }
 
-        uint256 borrowRate = lendgine.getBorrowRate(0.5 ether, 1 ether);
-        uint256 lpDilution = borrowRate / 2; // 0.5 lp for one year
+  function testOverCollect() external {
+    _addLiquidity(cuh, cuh, 1 ether, 8 ether, 1 ether);
+    _mint(address(this), address(this), 5 ether);
+    vm.warp(365 days + 1);
 
-        vm.prank(cuh);
-        liquidityManager.collect(
-            LiquidityManager.CollectParams({ lendgine: address(lendgine), recipient: cuh, amountRequested: 100 ether })
-        );
+    uint256 borrowRate = lendgine.getBorrowRate(0.5 ether, 1 ether);
+    uint256 lpDilution = borrowRate / 2; // 0.5 lp for one year
 
-        // test lendgine storage slots
-        assertEq(lpDilution * 10, lendgine.rewardPerPositionStored());
+    vm.prank(cuh);
+    liquidityManager.collect(
+      LiquidityManager.CollectParams({lendgine: address(lendgine), recipient: cuh, amountRequested: 100 ether})
+    );
 
-        // test lendgine position
-        (, uint256 rewardPerPositionPaid, uint256 tokensOwed) = lendgine.positions(address(liquidityManager));
-        assertEq(lpDilution * 10, rewardPerPositionPaid);
-        assertEq(0, tokensOwed);
+    // test lendgine storage slots
+    assertEq(lpDilution * 10, lendgine.rewardPerPositionStored());
+
+    // test lendgine position
+    (, uint256 rewardPerPositionPaid, uint256 tokensOwed) = lendgine.positions(address(liquidityManager));
+    assertEq(lpDilution * 10, rewardPerPositionPaid);
+    assertEq(0, tokensOwed);
+
+    // test liquidity manager position
+    (, rewardPerPositionPaid, tokensOwed) = liquidityManager.positions(cuh, address(lendgine));
+    assertEq(lpDilution * 10, rewardPerPositionPaid);
+    assertEq(0, tokensOwed);
+
+    // test user balances
+    assertEq(token1.balanceOf(cuh), lpDilution * 10);
+  }
+
+  function testCollectNoRecipient() external {
+    _addLiquidity(cuh, cuh, 1 ether, 8 ether, 1 ether);
+    _mint(address(this), address(this), 5 ether);
+    vm.warp(365 days + 1);
+
+    uint256 borrowRate = lendgine.getBorrowRate(0.5 ether, 1 ether);
+    uint256 lpDilution = borrowRate / 2; // 0.5 lp for one year
+
+    vm.prank(cuh);
+    liquidityManager.collect(
+      LiquidityManager.CollectParams({
+        lendgine: address(lendgine),
+        recipient: address(0),
+        amountRequested: lpDilution * 10
+      })
+    );
 
-        // test liquidity manager position
-        (, rewardPerPositionPaid, tokensOwed) = liquidityManager.positions(cuh, address(lendgine));
-        assertEq(lpDilution * 10, rewardPerPositionPaid);
-        assertEq(0, tokensOwed);
+    // test user balances
+    assertEq(token1.balanceOf(address(liquidityManager)), lpDilution * 10);
+  }
 
-        // test user balances
-        assertEq(token1.balanceOf(cuh), lpDilution * 10);
-    }
+  function testEmitCollect() external {
+    _addLiquidity(cuh, cuh, 1 ether, 8 ether, 1 ether);
+    _mint(address(this), address(this), 5 ether);
+    vm.warp(365 days + 1);
 
-    function testCollectNoRecipient() external {
-        _addLiquidity(cuh, cuh, 1 ether, 8 ether, 1 ether);
-        _mint(address(this), address(this), 5 ether);
-        vm.warp(365 days + 1);
-
-        uint256 borrowRate = lendgine.getBorrowRate(0.5 ether, 1 ether);
-        uint256 lpDilution = borrowRate / 2; // 0.5 lp for one year
-
-        vm.prank(cuh);
-        liquidityManager.collect(
-            LiquidityManager.CollectParams({
-                lendgine: address(lendgine),
-                recipient: address(0),
-                amountRequested: lpDilution * 10
-            })
-        );
-
-        // test user balances
-        assertEq(token1.balanceOf(address(liquidityManager)), lpDilution * 10);
-    }
+    uint256 borrowRate = lendgine.getBorrowRate(0.5 ether, 1 ether);
+    uint256 lpDilution = borrowRate / 2; // 0.5 lp for one year
 
-    function testEmitCollect() external {
-        _addLiquidity(cuh, cuh, 1 ether, 8 ether, 1 ether);
-        _mint(address(this), address(this), 5 ether);
-        vm.warp(365 days + 1);
-
-        uint256 borrowRate = lendgine.getBorrowRate(0.5 ether, 1 ether);
-        uint256 lpDilution = borrowRate / 2; // 0.5 lp for one year
-
-        vm.prank(cuh);
-        vm.expectEmit(true, true, true, true, address(liquidityManager));
-        emit Collect(cuh, address(lendgine), lpDilution * 10, cuh);
-        liquidityManager.collect(
-            LiquidityManager.CollectParams({
-                lendgine: address(lendgine),
-                recipient: cuh,
-                amountRequested: lpDilution * 10
-            })
-        );
-    }
+    vm.prank(cuh);
+    vm.expectEmit(true, true, true, true, address(liquidityManager));
+    emit Collect(cuh, address(lendgine), lpDilution * 10, cuh);
+    liquidityManager.collect(
+      LiquidityManager.CollectParams({lendgine: address(lendgine), recipient: cuh, amountRequested: lpDilution * 10})
+    );
+  }
 }
diff --git a/test/MintTest.t.sol b/test/MintTest.t.sol
index 65b0b79..750ddf6 100644
--- a/test/MintTest.t.sol
+++ b/test/MintTest.t.sol
@@ -6,177 +6,179 @@ import { Pair } from "../src/core/Pair.sol";
 import { TestHelper } from "./utils/TestHelper.sol";
 
 contract MintTest is TestHelper {
-    event Mint(address indexed sender, uint256 collateral, uint256 shares, uint256 liquidity, address indexed to);
-
-    event Burn(uint256 amount0Out, uint256 amount1Out, uint256 liquidity, address indexed to);
-
-    function setUp() external {
-        _setUp();
-        _deposit(address(this), address(this), 1 ether, 8 ether, 1 ether);
-    }
-
-    function testMintPartial() external {
-        uint256 shares = _mint(cuh, cuh, 5 ether);
-
-        // check lendgine token
-        assertEq(0.5 ether, shares);
-        assertEq(0.5 ether, lendgine.totalSupply());
-        assertEq(0.5 ether, lendgine.balanceOf(cuh));
-
-        // check lendgine storage slots
-        assertEq(0.5 ether, lendgine.totalLiquidityBorrowed());
-        assertEq(0.5 ether, lendgine.totalLiquidity());
-        assertEq(0.5 ether, uint256(lendgine.reserve0()));
-        assertEq(4 ether, uint256(lendgine.reserve1()));
+  event Mint(address indexed sender, uint256 collateral, uint256 shares, uint256 liquidity, address indexed to);
+
+  event Burn(uint256 amount0Out, uint256 amount1Out, uint256 liquidity, address indexed to);
+
+  function setUp() external {
+    _setUp();
+    _deposit(address(this), address(this), 1 ether, 8 ether, 1 ether);
+  }
+
+  function testMintPartial() external {
+    uint256 shares = _mint(cuh, cuh, 5 ether);
+
+    // check lendgine token
+    assertEq(0.5 ether, shares);
+    assertEq(0.5 ether, lendgine.totalSupply());
+    assertEq(0.5 ether, lendgine.balanceOf(cuh));
+
+    // check lendgine storage slots
+    assertEq(0.5 ether, lendgine.totalLiquidityBorrowed());
+    assertEq(0.5 ether, lendgine.totalLiquidity());
+    assertEq(0.5 ether, uint256(lendgine.reserve0()));
+    assertEq(4 ether, uint256(lendgine.reserve1()));
 
-        // check lendgine balances
-        assertEq(0.5 ether, token0.balanceOf(address(lendgine)));
-        assertEq(4 ether + 5 ether, token1.balanceOf(address(lendgine)));
+    // check lendgine balances
+    assertEq(0.5 ether, token0.balanceOf(address(lendgine)));
+    assertEq(4 ether + 5 ether, token1.balanceOf(address(lendgine)));
 
-        // check user balances
-        assertEq(0.5 ether, token0.balanceOf(cuh));
-        assertEq(4 ether, token1.balanceOf(cuh));
-    }
-
-    function testMintFull() external {
-        uint256 shares = _mint(cuh, cuh, 10 ether);
-
-        // check lendgine token
-        assertEq(1 ether, shares);
-        assertEq(1 ether, lendgine.totalSupply());
-        assertEq(1 ether, lendgine.balanceOf(cuh));
-
-        // check lendgine storage slots
-        assertEq(1 ether, lendgine.totalLiquidityBorrowed());
-        assertEq(0, lendgine.totalLiquidity());
-        assertEq(0, uint256(lendgine.reserve0()));
-        assertEq(0, uint256(lendgine.reserve1()));
-
-        // check lendgine balances
-        assertEq(0, token0.balanceOf(address(lendgine)));
-        assertEq(10 ether, token1.balanceOf(address(lendgine)));
-
-        // check user balances
-        assertEq(1 ether, token0.balanceOf(cuh));
-        assertEq(8 ether, token1.balanceOf(cuh));
-    }
-
-    function testMintFullDouble() external {
-        _mint(cuh, cuh, 5 ether);
-        _mint(cuh, cuh, 5 ether);
-    }
-
-    function testZeroMint() external {
-        vm.expectRevert(Lendgine.InputError.selector);
-        lendgine.mint(cuh, 0, bytes(""));
-    }
-
-    function testOverMint() external {
-        _mint(address(this), address(this), 5 ether);
-
-        vm.expectRevert(Lendgine.CompleteUtilizationError.selector);
-        lendgine.mint(cuh, 5 ether + 10, bytes(""));
-    }
-
-    function testEmitLendgine() external {
-        token1.mint(cuh, 5 ether);
-
-        vm.prank(cuh);
-        token1.approve(address(this), 5 ether);
-
-        vm.expectEmit(true, true, false, true, address(lendgine));
-        emit Mint(address(this), 5 ether, 0.5 ether, 0.5 ether, cuh);
-        lendgine.mint(cuh, 5 ether, abi.encode(MintCallbackData({ token: address(token1), payer: cuh })));
-    }
-
-    function testEmitPair() external {
-        token1.mint(cuh, 5 ether);
-
-        vm.prank(cuh);
-        token1.approve(address(this), 5 ether);
-
-        vm.expectEmit(true, false, false, true, address(lendgine));
-        emit Burn(0.5 ether, 4 ether, 0.5 ether, cuh);
-        lendgine.mint(cuh, 5 ether, abi.encode(MintCallbackData({ token: address(token1), payer: cuh })));
-    }
-
-    function testAccrueOnMint() external {
-        _mint(cuh, cuh, 1 ether);
-        vm.warp(365 days + 1);
-        _mint(cuh, cuh, 1 ether);
-
-        assertEq(365 days + 1, lendgine.lastUpdate());
-        assert(lendgine.rewardPerPositionStored() != 0);
-    }
-
-    function testProportionalMint() external {
-        _mint(cuh, cuh, 5 ether);
-        vm.warp(365 days + 1);
-        uint256 shares = _mint(cuh, cuh, 1 ether);
-
-        uint256 borrowRate = lendgine.getBorrowRate(0.5 ether, 1 ether);
-        uint256 lpDilution = borrowRate / 2; // 0.5 lp for one year
-
-        // check mint amount
-        assertEq((0.1 ether * 0.5 ether) / (0.5 ether - lpDilution), shares);
-        assertEq(shares + 0.5 ether, lendgine.balanceOf(cuh));
-
-        // check lendgine storage slots
-        assertEq(0.6 ether - lpDilution, lendgine.totalLiquidityBorrowed());
-        assertEq(shares + 0.5 ether, lendgine.totalSupply());
-    }
-
-    function testNonStandardDecimals() external {
-        token1Scale = 9;
-
-        lendgine = Lendgine(
-            factory.createLendgine(address(token0), address(token1), token0Scale, token1Scale, upperBound)
-        );
-
-        token0.mint(address(this), 1e18);
-        token1.mint(address(this), 8 * 1e9);
-
-        lendgine.deposit(
-            address(this),
-            1 ether,
-            abi.encode(
-                PairMintCallbackData({
-                    token0: address(token0),
-                    token1: address(token1),
-                    amount0: 1e18,
-                    amount1: 8 * 1e9,
-                    payer: address(this)
-                })
-            )
-        );
-
-        token1.mint(cuh, 5 * 1e9);
-
-        vm.prank(cuh);
-        token1.approve(address(this), 5 * 1e9);
-        uint256 shares = lendgine.mint(
-            cuh,
-            5 * 1e9,
-            abi.encode(MintCallbackData({ token: address(token1), payer: cuh }))
-        );
-
-        // check lendgine token
-        assertEq(0.5 ether, shares);
-        assertEq(0.5 ether, lendgine.totalSupply());
-        assertEq(0.5 ether, lendgine.balanceOf(cuh));
-
-        // check lendgine storage slots
-        assertEq(0.5 ether, lendgine.totalLiquidityBorrowed());
-        assertEq(0.5 ether, lendgine.totalLiquidity());
-        assertEq(0.5 ether, uint256(lendgine.reserve0()));
-        assertEq(4 * 1e9, uint256(lendgine.reserve1()));
-
-        // check lendgine balances
-        assertEq(0.5 ether, token0.balanceOf(address(lendgine)));
-        assertEq(9 * 1e9, token1.balanceOf(address(lendgine)));
-
-        // check user balances
-        assertEq(0.5 ether, token0.balanceOf(cuh));
-        assertEq(4 * 1e9, token1.balanceOf(cuh));
-    }
+    // check user balances
+    assertEq(0.5 ether, token0.balanceOf(cuh));
+    assertEq(4 ether, token1.balanceOf(cuh));
+  }
+
+  function testMintFull() external {
+    uint256 shares = _mint(cuh, cuh, 10 ether);
+
+    // check lendgine token
+    assertEq(1 ether, shares);
+    assertEq(1 ether, lendgine.totalSupply());
+    assertEq(1 ether, lendgine.balanceOf(cuh));
+
+    // check lendgine storage slots
+    assertEq(1 ether, lendgine.totalLiquidityBorrowed());
+    assertEq(0, lendgine.totalLiquidity());
+    assertEq(0, uint256(lendgine.reserve0()));
+    assertEq(0, uint256(lendgine.reserve1()));
+
+    // check lendgine balances
+    assertEq(0, token0.balanceOf(address(lendgine)));
+    assertEq(10 ether, token1.balanceOf(address(lendgine)));
+
+    // check user balances
+    assertEq(1 ether, token0.balanceOf(cuh));
+    assertEq(8 ether, token1.balanceOf(cuh));
+  }
+
+  function testMintFullDouble() external {
+    _mint(cuh, cuh, 5 ether);
+    _mint(cuh, cuh, 5 ether);
+  }
+
+  function testZeroMint() external {
+    vm.expectRevert(Lendgine.InputError.selector);
+    lendgine.mint(cuh, 0, bytes(""));
+  }
+
+  function testOverMint() external {
+    _mint(address(this), address(this), 5 ether);
+
+    vm.expectRevert(Lendgine.CompleteUtilizationError.selector);
+    lendgine.mint(cuh, 5 ether + 10, bytes(""));
+  }
+
+  function testEmitLendgine() external {
+    token1.mint(cuh, 5 ether);
+
+    vm.prank(cuh);
+    token1.approve(address(this), 5 ether);
+
+    vm.expectEmit(true, true, false, true, address(lendgine));
+    emit Mint(address(this), 5 ether, 0.5 ether, 0.5 ether, cuh);
+    lendgine.mint(cuh, 5 ether, abi.encode(MintCallbackData({token: address(token1), payer: cuh})));
+  }
+
+  function testEmitPair() external {
+    token1.mint(cuh, 5 ether);
+
+    vm.prank(cuh);
+    token1.approve(address(this), 5 ether);
+
+    vm.expectEmit(true, false, false, true, address(lendgine));
+    emit Burn(0.5 ether, 4 ether, 0.5 ether, cuh);
+    lendgine.mint(cuh, 5 ether, abi.encode(MintCallbackData({token: address(token1), payer: cuh})));
+  }
+
+  function testAccrueOnMint() external {
+    _mint(cuh, cuh, 1 ether);
+    vm.warp(365 days + 1);
+    _mint(cuh, cuh, 1 ether);
+
+    assertEq(365 days + 1, lendgine.lastUpdate());
+    assert(lendgine.rewardPerPositionStored() != 0);
+  }
+
+  function testProportionalMint() external {
+    _mint(cuh, cuh, 5 ether);
+    vm.warp(365 days + 1);
+    uint256 shares = _mint(cuh, cuh, 1 ether);
+
+    uint256 borrowRate = lendgine.getBorrowRate(0.5 ether, 1 ether);
+    uint256 lpDilution = borrowRate / 2; // 0.5 lp for one year
+
+    // check mint amount
+    assertEq((0.1 ether * 0.5 ether) / (0.5 ether - lpDilution), shares);
+    assertEq(shares + 0.5 ether, lendgine.balanceOf(cuh));
+
+    // check lendgine storage slots
+    assertEq(0.6 ether - lpDilution, lendgine.totalLiquidityBorrowed());
+    assertEq(shares + 0.5 ether, lendgine.totalSupply());
+  }
+
+  function testNonStandardDecimals() external {
+    token1Scale = 9;
+
+    lendgine = Lendgine(factory.createLendgine(address(token0), address(token1), token0Scale, token1Scale, upperBound));
+
+    token0.mint(address(this), 1e18);
+    token1.mint(address(this), 8 * 1e9);
+
+    lendgine.deposit(
+      address(this),
+      1 ether,
+      abi.encode(
+        PairMintCallbackData({
+          token0: address(token0),
+          token1: address(token1),
+          amount0: 1e18,
+          amount1: 8 * 1e9,
+          payer: address(this)
+        })
+      )
+    );
+
+    token1.mint(cuh, 5 * 1e9);
+
+    vm.prank(cuh);
+    token1.approve(address(this), 5 * 1e9);
+    uint256 shares = lendgine.mint(cuh, 5 * 1e9, abi.encode(MintCallbackData({token: address(token1), payer: cuh})));
+
+    // check lendgine token
+    assertEq(0.5 ether, shares);
+    assertEq(0.5 ether, lendgine.totalSupply());
+    assertEq(0.5 ether, lendgine.balanceOf(cuh));
+
+    // check lendgine storage slots
+    assertEq(0.5 ether, lendgine.totalLiquidityBorrowed());
+    assertEq(0.5 ether, lendgine.totalLiquidity());
+    assertEq(0.5 ether, uint256(lendgine.reserve0()));
+    assertEq(4 * 1e9, uint256(lendgine.reserve1()));
+
+    // check lendgine balances
+    assertEq(0.5 ether, token0.balanceOf(address(lendgine)));
+    assertEq(9 * 1e9, token1.balanceOf(address(lendgine)));
+
+    // check user balances
+    assertEq(0.5 ether, token0.balanceOf(cuh));
+    assertEq(4 * 1e9, token1.balanceOf(cuh));
+  }
+
+  function testMintAfterFullAccrue() external {
+    _mint(address(this), address(this), 5 ether);
+    vm.warp(730 days + 1);
+
+    vm.expectRevert(Lendgine.CompleteUtilizationError.selector);
+    lendgine.mint(cuh, 1 ether, bytes(""));
+  }
 }
diff --git a/test/PrecisionTest.t.sol b/test/PrecisionTest.t.sol
index 9a8dfb9..87bdcf4 100644
--- a/test/PrecisionTest.t.sol
+++ b/test/PrecisionTest.t.sol
@@ -20,53 +20,49 @@ import { TestHelper } from "./utils/TestHelper.sol";
 // max tvl: $100,000,000
 
 contract PrecisionTest is TestHelper {
-    function setUp() external {
-        _setUp();
-    }
+  function setUp() external {
+    _setUp();
+  }
 
-    function testBaseline() external {
-        lendgine.invariant((upperBound * upperBound) / 1e18, 0, 1 ether);
+  function testBaseline() external {
+    lendgine.invariant((upperBound * upperBound) / 1e18, 0, 1 ether);
 
-        uint256 value = (upperBound * upperBound) / 1e18;
-        uint256 basePerDollar = 1e18;
+    uint256 value = (upperBound * upperBound) / 1e18;
+    uint256 basePerDollar = 1e18;
 
-        uint256 minLP = value / basePerDollar;
+    uint256 minLP = value / basePerDollar;
 
-        assert(type(uint120).max / basePerDollar >= 100_000_000);
-        assert(minLP < 1 ether);
-    }
+    assert(type(uint120).max / basePerDollar >= 100_000_000);
+    assert(minLP < 1 ether);
+  }
 
-    function testHighUpperBound() external {
-        upperBound = 1e27;
-        lendgine = Lendgine(
-            factory.createLendgine(address(token0), address(token1), token0Scale, token1Scale, upperBound)
-        );
+  function testHighUpperBound() external {
+    upperBound = 1e27;
+    lendgine = Lendgine(factory.createLendgine(address(token0), address(token1), token0Scale, token1Scale, upperBound));
 
-        lendgine.invariant((upperBound * upperBound) / 1e18, 0, 1 ether);
+    lendgine.invariant((upperBound * upperBound) / 1e18, 0, 1 ether);
 
-        uint256 value = (upperBound * upperBound) / 1e18;
-        uint256 basePerDollar = 1e21;
+    uint256 value = (upperBound * upperBound) / 1e18;
+    uint256 basePerDollar = 1e21;
 
-        uint256 minLP = value / basePerDollar;
+    uint256 minLP = value / basePerDollar;
 
-        assert(type(uint120).max / basePerDollar >= 100_000_000);
-        assert(minLP < 1 ether);
-    }
+    assert(type(uint120).max / basePerDollar >= 100_000_000);
+    assert(minLP < 1 ether);
+  }
 
-    function testLowUpperBound() external {
-        upperBound = 1e9;
-        lendgine = Lendgine(
-            factory.createLendgine(address(token0), address(token1), token0Scale, token1Scale, upperBound)
-        );
+  function testLowUpperBound() external {
+    upperBound = 1e9;
+    lendgine = Lendgine(factory.createLendgine(address(token0), address(token1), token0Scale, token1Scale, upperBound));
 
-        lendgine.invariant((upperBound * upperBound) / 1e18, 0, 1 ether);
+    lendgine.invariant((upperBound * upperBound) / 1e18, 0, 1 ether);
 
-        uint256 value = (upperBound * upperBound) / 1e18;
-        uint256 basePerDollar = 1e27;
+    uint256 value = (upperBound * upperBound) / 1e18;
+    uint256 basePerDollar = 1e27;
 
-        uint256 minLP = value / basePerDollar;
+    uint256 minLP = value / basePerDollar;
 
-        assert(type(uint120).max / basePerDollar >= 100_000_000);
-        assert(minLP < 1 ether);
-    }
+    assert(type(uint120).max / basePerDollar >= 100_000_000);
+    assert(minLP < 1 ether);
+  }
 }
diff --git a/test/SwapTest.t.sol b/test/SwapTest.t.sol
index b14d22f..bd85e80 100644
--- a/test/SwapTest.t.sol
+++ b/test/SwapTest.t.sol
@@ -6,87 +6,69 @@ import { Pair } from "../src/core/Pair.sol";
 import { TestHelper } from "./utils/TestHelper.sol";
 
 contract SwapTest is TestHelper {
-    event Swap(uint256 amount0Out, uint256 amount1Out, uint256 amount0In, uint256 amount1In, address indexed to);
+  event Swap(uint256 amount0Out, uint256 amount1Out, uint256 amount0In, uint256 amount1In, address indexed to);
 
-    function setUp() external {
-        _setUp();
-        _deposit(cuh, cuh, 1 ether, 8 ether, 1 ether);
-    }
+  function setUp() external {
+    _setUp();
+    _deposit(cuh, cuh, 1 ether, 8 ether, 1 ether);
+  }
 
-    function testSwapFull() external {
-        token0.mint(cuh, 24 ether);
+  function testSwapFull() external {
+    token0.mint(cuh, 24 ether);
 
-        vm.prank(cuh);
-        token0.approve(address(this), 24 ether);
+    vm.prank(cuh);
+    token0.approve(address(this), 24 ether);
 
-        lendgine.swap(
-            cuh,
-            0,
-            8 ether,
-            abi.encode(
-                SwapCallbackData({
-                    token0: address(token0),
-                    token1: address(token1),
-                    amount0: 24 ether,
-                    amount1: 0,
-                    payer: cuh
-                })
-            )
-        );
+    lendgine.swap(
+      cuh,
+      0,
+      8 ether,
+      abi.encode(
+        SwapCallbackData({token0: address(token0), token1: address(token1), amount0: 24 ether, amount1: 0, payer: cuh})
+      )
+    );
 
-        // check user balances
-        assertEq(0, token0.balanceOf(cuh));
-        assertEq(8 ether, token1.balanceOf(cuh));
+    // check user balances
+    assertEq(0, token0.balanceOf(cuh));
+    assertEq(8 ether, token1.balanceOf(cuh));
 
-        // check lendgine storage slots
-        assertEq(25 ether, lendgine.reserve0());
-        assertEq(0, lendgine.reserve1());
-    }
+    // check lendgine storage slots
+    assertEq(25 ether, lendgine.reserve0());
+    assertEq(0, lendgine.reserve1());
+  }
 
-    function testUnderPay() external {
-        token0.mint(cuh, 23 ether);
+  function testUnderPay() external {
+    token0.mint(cuh, 23 ether);
 
-        vm.prank(cuh);
-        token0.approve(address(this), 23 ether);
+    vm.prank(cuh);
+    token0.approve(address(this), 23 ether);
 
-        vm.expectRevert(Pair.InvariantError.selector);
-        lendgine.swap(
-            cuh,
-            0,
-            8 ether,
-            abi.encode(
-                SwapCallbackData({
-                    token0: address(token0),
-                    token1: address(token1),
-                    amount0: 23 ether,
-                    amount1: 0,
-                    payer: cuh
-                })
-            )
-        );
-    }
+    vm.expectRevert(Pair.InvariantError.selector);
+    lendgine.swap(
+      cuh,
+      0,
+      8 ether,
+      abi.encode(
+        SwapCallbackData({token0: address(token0), token1: address(token1), amount0: 23 ether, amount1: 0, payer: cuh})
+      )
+    );
+  }
 
-    function testEmit() external {
-        token0.mint(cuh, 24 ether);
+  function testEmit() external {
+    token0.mint(cuh, 24 ether);
 
-        vm.prank(cuh);
-        token0.approve(address(this), 24 ether);
+    vm.prank(cuh);
+    token0.approve(address(this), 24 ether);
 
-        vm.expectEmit(true, false, false, true, address(lendgine));
-        emit Swap(0, 8 ether, 24 ether, 0, cuh);
-        lendgine.swap(
-            cuh,
-            0,
-            8 ether,
-            abi.encode(
-                SwapCallbackData({
-                    token0: address(token0),
-                    token1: address(token1),
-                    amount0: 24 ether,
-                    amount1: 0,
-                    payer: cuh
-                })
-            )
-        );
-    }
+    vm.expectEmit(true, false, false, true, address(lendgine));
+    emit Swap(0, 8 ether, 24 ether, 0, cuh);
+    lendgine.swap(
+      cuh,
+      0,
+      8 ether,
+      abi.encode(
+        SwapCallbackData({token0: address(token0), token1: address(token1), amount0: 24 ether, amount1: 0, payer: cuh})
+      )
+    );
+  }
 }
diff --git a/test/WithdrawTest.t.sol b/test/WithdrawTest.t.sol
index fe6cbea..abc7eed 100644
--- a/test/WithdrawTest.t.sol
+++ b/test/WithdrawTest.t.sol
@@ -7,192 +7,184 @@ import { TestHelper } from "./utils/TestHelper.sol";
 import { FullMath } from "../src/libraries/FullMath.sol";
 
 contract WithdrawTest is TestHelper {
-    event Withdraw(address indexed sender, uint256 size, uint256 liquidity, address indexed to);
+  event Withdraw(address indexed sender, uint256 size, uint256 liquidity, address indexed to);
 
-    event Burn(uint256 amount0Out, uint256 amount1Out, uint256 liquidity, address indexed to);
+  event Burn(uint256 amount0Out, uint256 amount1Out, uint256 liquidity, address indexed to);
 
-    function setUp() external {
-        _setUp();
+  function setUp() external {
+    _setUp();
 
-        _deposit(cuh, cuh, 1 ether, 8 ether, 1 ether);
-    }
+    _deposit(cuh, cuh, 1 ether, 8 ether, 1 ether);
+  }
 
-    function testWithdrawPartial() external {
-        (uint256 amount0, uint256 amount1, uint256 liquidity) = _withdraw(cuh, cuh, 0.5 ether);
+  function testWithdrawPartial() external {
+    (uint256 amount0, uint256 amount1, uint256 liquidity) = _withdraw(cuh, cuh, 0.5 ether);
 
-        assertEq(liquidity, 0.5 ether);
-        assertEq(0.5 ether, amount0);
-        assertEq(4 ether, amount1);
+    assertEq(liquidity, 0.5 ether);
+    assertEq(0.5 ether, amount0);
+    assertEq(4 ether, amount1);
 
-        assertEq(0.5 ether, lendgine.totalLiquidity());
-        assertEq(0.5 ether, lendgine.totalPositionSize());
+    assertEq(0.5 ether, lendgine.totalLiquidity());
+    assertEq(0.5 ether, lendgine.totalPositionSize());
 
-        assertEq(0.5 ether, uint256(lendgine.reserve0()));
-        assertEq(4 ether, uint256(lendgine.reserve1()));
-        assertEq(0.5 ether, token0.balanceOf(address(lendgine)));
-        assertEq(4 ether, token1.balanceOf(address(lendgine)));
-
-        assertEq(0.5 ether, token0.balanceOf(address(cuh)));
-        assertEq(4 ether, token1.balanceOf(address(cuh)));
-
-        (uint256 positionSize, , ) = lendgine.positions(cuh);
-        assertEq(0.5 ether, positionSize);
-    }
-
-    function testWithdrawFull() external {
-        (uint256 amount0, uint256 amount1, uint256 liquidity) = _withdraw(cuh, cuh, 1 ether);
+    assertEq(0.5 ether, uint256(lendgine.reserve0()));
+    assertEq(4 ether, uint256(lendgine.reserve1()));
+    assertEq(0.5 ether, token0.balanceOf(address(lendgine)));
+    assertEq(4 ether, token1.balanceOf(address(lendgine)));
 
-        assertEq(liquidity, 1 ether);
-        assertEq(1 ether, amount0);
-        assertEq(8 ether, amount1);
+    assertEq(0.5 ether, token0.balanceOf(address(cuh)));
+    assertEq(4 ether, token1.balanceOf(address(cuh)));
 
-        assertEq(0, lendgine.totalLiquidity());
-        assertEq(0, lendgine.totalPositionSize());
-
-        assertEq(0, uint256(lendgine.reserve0()));
-        assertEq(0, uint256(lendgine.reserve1()));
-        assertEq(0, token0.balanceOf(address(lendgine)));
-        assertEq(0, token1.balanceOf(address(lendgine)));
+    (uint256 positionSize,,) = lendgine.positions(cuh);
+    assertEq(0.5 ether, positionSize);
+  }
 
-        assertEq(1 ether, token0.balanceOf(address(cuh)));
-        assertEq(8 ether, token1.balanceOf(address(cuh)));
-
-        (uint256 positionSize, , ) = lendgine.positions(cuh);
-        assertEq(0, positionSize);
-    }
+  function testWithdrawFull() external {
+    (uint256 amount0, uint256 amount1, uint256 liquidity) = _withdraw(cuh, cuh, 1 ether);
 
-    function testEmitLendgine() external {
-        vm.expectEmit(true, true, false, true, address(lendgine));
-        emit Withdraw(cuh, 1 ether, 1 ether, cuh);
-        _withdraw(cuh, cuh, 1 ether);
-    }
-
-    function testEmitPair() external {
-        vm.expectEmit(true, false, false, true, address(lendgine));
-        emit Burn(1 ether, 8 ether, 1 ether, cuh);
-        _withdraw(cuh, cuh, 1 ether);
-    }
-
-    function testZeroWithdraw() external {
-        vm.expectRevert(Lendgine.InputError.selector);
-        _withdraw(cuh, cuh, 0);
-    }
-
-    function testOverWithdraw() external {
-        vm.expectRevert(Lendgine.InsufficientPositionError.selector);
-        _withdraw(cuh, cuh, 2 ether);
-    }
-
-    function testMaxUtilizedWithdraw() external {
-        _mint(address(this), address(this), 5 ether);
-        _withdraw(cuh, cuh, 0.5 ether);
-    }
-
-    function testCompleteUtilization() external {
-        _mint(address(this), address(this), 5 ether);
-
-        vm.expectRevert(Lendgine.CompleteUtilizationError.selector);
-        _withdraw(cuh, cuh, 0.5 ether + 1);
-    }
-
-    function testAccrueOnWithdraw() external {
-        _mint(address(this), address(this), 1 ether);
-        vm.warp(365 days + 1);
-        _withdraw(cuh, cuh, .5 ether);
-
-        assertEq(365 days + 1, lendgine.lastUpdate());
-        assert(lendgine.rewardPerPositionStored() != 0);
-    }
-
-    function testAccrueOnPositionWithdraw() external {
-        _mint(address(this), address(this), 1 ether);
-        vm.warp(365 days + 1);
-        _withdraw(cuh, cuh, .5 ether);
-
-        (, uint256 rewardPerPositionPaid, uint256 tokensOwed) = lendgine.positions(cuh);
-        assert(rewardPerPositionPaid != 0);
-        assert(tokensOwed != 0);
-    }
-
-    function testProportionalPositionSize() external {
-        uint256 shares = _mint(address(this), address(this), 5 ether);
-        vm.warp(365 days + 1);
-        lendgine.accrueInterest();
-
-        uint256 borrowRate = lendgine.getBorrowRate(0.5 ether, 1 ether);
-        uint256 lpDilution = borrowRate / 2; // 0.5 lp for one year
-
-        uint256 reserve0 = lendgine.reserve0();
-        uint256 reserve1 = lendgine.reserve1();
-
-        uint256 _amount0 = FullMath.mulDivRoundingUp(
-            reserve0,
-            lendgine.convertShareToLiquidity(0.5 ether),
-            lendgine.totalLiquidity()
-        );
-        uint256 _amount1 = FullMath.mulDivRoundingUp(
-            reserve1,
-            lendgine.convertShareToLiquidity(0.5 ether),
-            lendgine.totalLiquidity()
-        );
-
-        _burn(address(this), address(this), 0.5 ether, _amount0, _amount1);
-
-        (, , uint256 liquidity) = _withdraw(cuh, cuh, 1 ether);
-
-        // check liquidity
-        assertEq(liquidity, 1 ether - lpDilution);
-
-        // check lendgine storage slots
-        assertEq(lendgine.totalLiquidity(), 0);
-        assertEq(lendgine.totalPositionSize(), 0);
-        assertEq(lendgine.totalLiquidityBorrowed(), 0);
-        assertEq(0, lendgine.reserve0());
-        assertEq(0, lendgine.reserve1());
-    }
-
-    function testNonStandardDecimals() external {
-        token1Scale = 9;
-
-        lendgine = Lendgine(
-            factory.createLendgine(address(token0), address(token1), token0Scale, token1Scale, upperBound)
-        );
-
-        token0.mint(address(this), 1e18);
-        token1.mint(address(this), 8 * 1e9);
-
-        lendgine.deposit(
-            address(this),
-            1 ether,
-            abi.encode(
-                PairMintCallbackData({
-                    token0: address(token0),
-                    token1: address(token1),
-                    amount0: 1e18,
-                    amount1: 8 * 1e9,
-                    payer: address(this)
-                })
-            )
-        );
-
-        (uint256 amount0, uint256 amount1, uint256 liquidity) = lendgine.withdraw(address(this), 0.5 ether);
-
-        assertEq(liquidity, 0.5 ether);
-        assertEq(0.5 ether, amount0);
-        assertEq(4 * 1e9, amount1);
-
-        assertEq(0.5 ether, lendgine.totalLiquidity());
-        assertEq(0.5 ether, lendgine.totalPositionSize());
-
-        assertEq(0.5 ether, uint256(lendgine.reserve0()));
-        assertEq(4 * 1e9, uint256(lendgine.reserve1()));
-        assertEq(0.5 ether, token0.balanceOf(address(lendgine)));
-        assertEq(4 * 1e9, token1.balanceOf(address(lendgine)));
-
-        assertEq(0.5 ether, token0.balanceOf(address(this)));
-        assertEq(4 * 1e9, token1.balanceOf(address(this)));
-
-        (uint256 positionSize, , ) = lendgine.positions(address(this));
-        assertEq(0.5 ether, positionSize);
-    }
+    assertEq(liquidity, 1 ether);
+    assertEq(1 ether, amount0);
+    assertEq(8 ether, amount1);
+
+    assertEq(0, lendgine.totalLiquidity());
+    assertEq(0, lendgine.totalPositionSize());
+
+    assertEq(0, uint256(lendgine.reserve0()));
+    assertEq(0, uint256(lendgine.reserve1()));
+    assertEq(0, token0.balanceOf(address(lendgine)));
+    assertEq(0, token1.balanceOf(address(lendgine)));
+
+    assertEq(1 ether, token0.balanceOf(address(cuh)));
+    assertEq(8 ether, token1.balanceOf(address(cuh)));
+
+    (uint256 positionSize,,) = lendgine.positions(cuh);
+    assertEq(0, positionSize);
+  }
+
+  function testEmitLendgine() external {
+    vm.expectEmit(true, true, false, true, address(lendgine));
+    emit Withdraw(cuh, 1 ether, 1 ether, cuh);
+    _withdraw(cuh, cuh, 1 ether);
+  }
+
+  function testEmitPair() external {
+    vm.expectEmit(true, false, false, true, address(lendgine));
+    emit Burn(1 ether, 8 ether, 1 ether, cuh);
+    _withdraw(cuh, cuh, 1 ether);
+  }
+
+  function testZeroWithdraw() external {
+    vm.expectRevert(Lendgine.InputError.selector);
+    _withdraw(cuh, cuh, 0);
+  }
+
+  function testOverWithdraw() external {
+    vm.expectRevert(Lendgine.InsufficientPositionError.selector);
+    _withdraw(cuh, cuh, 2 ether);
+  }
+
+  function testMaxUtilizedWithdraw() external {
+    _mint(address(this), address(this), 5 ether);
+    _withdraw(cuh, cuh, 0.5 ether);
+  }
+
+  function testCompleteUtilization() external {
+    _mint(address(this), address(this), 5 ether);
+
+    vm.expectRevert(Lendgine.CompleteUtilizationError.selector);
+    _withdraw(cuh, cuh, 0.5 ether + 1);
+  }
+
+  function testAccrueOnWithdraw() external {
+    _mint(address(this), address(this), 1 ether);
+    vm.warp(365 days + 1);
+    _withdraw(cuh, cuh, 0.5 ether);
+
+    assertEq(365 days + 1, lendgine.lastUpdate());
+    assert(lendgine.rewardPerPositionStored() != 0);
+  }
+
+  function testAccrueOnPositionWithdraw() external {
+    _mint(address(this), address(this), 1 ether);
+    vm.warp(365 days + 1);
+    _withdraw(cuh, cuh, 0.5 ether);
+
+    (, uint256 rewardPerPositionPaid, uint256 tokensOwed) = lendgine.positions(cuh);
+    assert(rewardPerPositionPaid != 0);
+    assert(tokensOwed != 0);
+  }
+
+  function testProportionalPositionSize() external {
+    uint256 shares = _mint(address(this), address(this), 5 ether);
+    vm.warp(365 days + 1);
+    lendgine.accrueInterest();
+
+    uint256 borrowRate = lendgine.getBorrowRate(0.5 ether, 1 ether);
+    uint256 lpDilution = borrowRate / 2; // 0.5 lp for one year
+
+    uint256 reserve0 = lendgine.reserve0();
+    uint256 reserve1 = lendgine.reserve1();
+
+    uint256 _amount0 =
+      FullMath.mulDivRoundingUp(reserve0, lendgine.convertShareToLiquidity(0.5 ether), lendgine.totalLiquidity());
+    uint256 _amount1 =
+      FullMath.mulDivRoundingUp(reserve1, lendgine.convertShareToLiquidity(0.5 ether), lendgine.totalLiquidity());
+
+    _burn(address(this), address(this), 0.5 ether, _amount0, _amount1);
+
+    (,, uint256 liquidity) = _withdraw(cuh, cuh, 1 ether);
+
+    // check liquidity
+    assertEq(liquidity, 1 ether - lpDilution);
+
+    // check lendgine storage slots
+    assertEq(lendgine.totalLiquidity(), 0);
+    assertEq(lendgine.totalPositionSize(), 0);
+    assertEq(lendgine.totalLiquidityBorrowed(), 0);
+    assertEq(0, lendgine.reserve0());
+    assertEq(0, lendgine.reserve1());
+  }
+
+  function testNonStandardDecimals() external {
+    token1Scale = 9;
+
+    lendgine = Lendgine(factory.createLendgine(address(token0), address(token1), token0Scale, token1Scale, upperBound));
+
+    token0.mint(address(this), 1e18);
+    token1.mint(address(this), 8 * 1e9);
+
+    lendgine.deposit(
+      address(this),
+      1 ether,
+      abi.encode(
+        PairMintCallbackData({
+          token0: address(token0),
+          token1: address(token1),
+          amount0: 1e18,
+          amount1: 8 * 1e9,
+          payer: address(this)
+        })
+      )
+    );
+
+    (uint256 amount0, uint256 amount1, uint256 liquidity) = lendgine.withdraw(address(this), 0.5 ether);
+
+    assertEq(liquidity, 0.5 ether);
+    assertEq(0.5 ether, amount0);
+    assertEq(4 * 1e9, amount1);
+
+    assertEq(0.5 ether, lendgine.totalLiquidity());
+    assertEq(0.5 ether, lendgine.totalPositionSize());
+
+    assertEq(0.5 ether, uint256(lendgine.reserve0()));
+    assertEq(4 * 1e9, uint256(lendgine.reserve1()));
+    assertEq(0.5 ether, token0.balanceOf(address(lendgine)));
+    assertEq(4 * 1e9, token1.balanceOf(address(lendgine)));
+
+    assertEq(0.5 ether, token0.balanceOf(address(this)));
+    assertEq(4 * 1e9, token1.balanceOf(address(this)));
+
+    (uint256 positionSize,,) = lendgine.positions(address(this));
+    assertEq(0.5 ether, positionSize);
+  }
 }
diff --git a/test/utils/CallbackHelper.sol b/test/utils/CallbackHelper.sol
index 7d12200..30bd9b5 100644
--- a/test/utils/CallbackHelper.sol
+++ b/test/utils/CallbackHelper.sol
@@ -8,72 +8,66 @@ import { ISwapCallback } from "../../src/core/interfaces/callback/ISwapCallback.
 import { SafeTransferLib } from "../../src/libraries/SafeTransferLib.sol";
 
 contract CallbackHelper is IPairMintCallback, IMintCallback, ISwapCallback {
-    struct PairMintCallbackData {
-        address token0;
-        address token1;
-        uint256 amount0;
-        uint256 amount1;
-        address payer;
-    }
+  struct PairMintCallbackData {
+    address token0;
+    address token1;
+    uint256 amount0;
+    uint256 amount1;
+    address payer;
+  }
 
-    function pairMintCallback(uint256, bytes calldata data) external override {
-        PairMintCallbackData memory decoded = abi.decode(data, (PairMintCallbackData));
+  function pairMintCallback(uint256, bytes calldata data) external override {
+    PairMintCallbackData memory decoded = abi.decode(data, (PairMintCallbackData));
 
-        if (decoded.payer == address(this)) {
-            if (decoded.amount0 > 0) SafeTransferLib.safeTransfer(decoded.token0, msg.sender, decoded.amount0);
-            if (decoded.amount1 > 0) SafeTransferLib.safeTransfer(decoded.token1, msg.sender, decoded.amount1);
-        } else {
-            if (decoded.amount0 > 0)
-                SafeTransferLib.safeTransferFrom(decoded.token0, decoded.payer, msg.sender, decoded.amount0);
-            if (decoded.amount1 > 0)
-                SafeTransferLib.safeTransferFrom(decoded.token1, decoded.payer, msg.sender, decoded.amount1);
-        }
+    if (decoded.payer == address(this)) {
+      if (decoded.amount0 > 0) SafeTransferLib.safeTransfer(decoded.token0, msg.sender, decoded.amount0);
+      if (decoded.amount1 > 0) SafeTransferLib.safeTransfer(decoded.token1, msg.sender, decoded.amount1);
+    } else {
+      if (decoded.amount0 > 0) {
+        SafeTransferLib.safeTransferFrom(decoded.token0, decoded.payer, msg.sender, decoded.amount0);
+      }
+      if (decoded.amount1 > 0) {
+        SafeTransferLib.safeTransferFrom(decoded.token1, decoded.payer, msg.sender, decoded.amount1);
+      }
     }
+  }
 
-    struct MintCallbackData {
-        address token;
-        address payer;
-    }
+  struct MintCallbackData {
+    address token;
+    address payer;
+  }
 
-    function mintCallback(
-        uint256 collateral,
-        uint256,
-        uint256,
-        uint256,
-        bytes calldata data
-    ) external override {
-        MintCallbackData memory decoded = abi.decode(data, (MintCallbackData));
+  function mintCallback(uint256 collateral, uint256, uint256, uint256, bytes calldata data) external override {
+    MintCallbackData memory decoded = abi.decode(data, (MintCallbackData));
 
-        if (decoded.payer == address(this)) {
-            SafeTransferLib.safeTransfer(decoded.token, msg.sender, collateral);
-        } else {
-            SafeTransferLib.safeTransferFrom(decoded.token, decoded.payer, msg.sender, collateral);
-        }
+    if (decoded.payer == address(this)) {
+      SafeTransferLib.safeTransfer(decoded.token, msg.sender, collateral);
+    } else {
+      SafeTransferLib.safeTransferFrom(decoded.token, decoded.payer, msg.sender, collateral);
     }
+  }
 
-    struct SwapCallbackData {
-        address token0;
-        address token1;
-        uint256 amount0;
-        uint256 amount1;
-        address payer;
-    }
+  struct SwapCallbackData {
+    address token0;
+    address token1;
+    uint256 amount0;
+    uint256 amount1;
+    address payer;
+  }
 
-    function swapCallback(
-        uint256,
-        uint256,
-        bytes calldata data
-    ) external override {
-        SwapCallbackData memory decoded = abi.decode(data, (SwapCallbackData));
+  function swapCallback(uint256, uint256, bytes calldata data) external override {
+    SwapCallbackData memory decoded = abi.decode(data, (SwapCallbackData));
 
-        if (decoded.payer == address(this)) {
-            if (decoded.amount0 > 0) SafeTransferLib.safeTransfer(decoded.token0, msg.sender, decoded.amount0);
-            if (decoded.amount1 > 0) SafeTransferLib.safeTransfer(decoded.token1, msg.sender, decoded.amount1);
-        } else {
-            if (decoded.amount0 > 0)
-                SafeTransferLib.safeTransferFrom(decoded.token0, decoded.payer, msg.sender, decoded.amount0);
-            if (decoded.amount1 > 0)
-                SafeTransferLib.safeTransferFrom(decoded.token1, decoded.payer, msg.sender, decoded.amount1);
-        }
+    if (decoded.payer == address(this)) {
+      if (decoded.amount0 > 0) SafeTransferLib.safeTransfer(decoded.token0, msg.sender, decoded.amount0);
+      if (decoded.amount1 > 0) SafeTransferLib.safeTransfer(decoded.token1, msg.sender, decoded.amount1);
+    } else {
+      if (decoded.amount0 > 0) {
+        SafeTransferLib.safeTransferFrom(decoded.token0, decoded.payer, msg.sender, decoded.amount0);
+      }
+      if (decoded.amount1 > 0) {
+        SafeTransferLib.safeTransferFrom(decoded.token1, decoded.payer, msg.sender, decoded.amount1);
+      }
     }
+  }
 }
diff --git a/test/utils/TestHelper.sol b/test/utils/TestHelper.sol
index 080353d..1c2eebb 100644
--- a/test/utils/TestHelper.sol
+++ b/test/utils/TestHelper.sol
@@ -9,137 +9,133 @@ import { CallbackHelper } from "./CallbackHelper.sol";
 import { MockERC20 } from "./mocks/MockERC20.sol";
 
 abstract contract TestHelper is Test, CallbackHelper {
-    MockERC20 public token0;
-    MockERC20 public token1;
+  MockERC20 public token0;
+  MockERC20 public token1;
 
-    uint8 public token0Scale;
-    uint8 public token1Scale;
+  uint8 public token0Scale;
+  uint8 public token1Scale;
 
-    uint256 public upperBound;
+  uint256 public upperBound;
 
-    Factory public factory;
-    Lendgine public lendgine;
+  Factory public factory;
+  Lendgine public lendgine;
 
-    address public cuh;
-    address public dennis;
+  address public cuh;
+  address public dennis;
 
-    function mkaddr(string memory name) public returns (address) {
-        address addr = address(uint160(uint256(keccak256(abi.encodePacked(name)))));
-        vm.label(addr, name);
-        return addr;
-    }
+  function mkaddr(string memory name) public returns (address) {
+    address addr = address(uint160(uint256(keccak256(abi.encodePacked(name)))));
+    vm.label(addr, name);
+    return addr;
+  }
 
-    constructor() {
-        cuh = mkaddr("cuh");
-        dennis = mkaddr("dennis");
+  constructor() {
+    cuh = mkaddr("cuh");
+    dennis = mkaddr("dennis");
 
-        token0Scale = 18;
-        token1Scale = 18;
+    token0Scale = 18;
+    token1Scale = 18;
 
-        upperBound = 5 * 1e18;
-    }
+    upperBound = 5 * 1e18;
+  }
 
-    function _setUp() internal {
-        token0 = new MockERC20();
-        token1 = new MockERC20();
+  function _setUp() internal {
+    token0 = new MockERC20();
+    token1 = new MockERC20();
 
-        factory = new Factory();
-        lendgine = Lendgine(
-            factory.createLendgine(address(token0), address(token1), token0Scale, token1Scale, upperBound)
-        );
-    }
+    factory = new Factory();
+    lendgine = Lendgine(factory.createLendgine(address(token0), address(token1), token0Scale, token1Scale, upperBound));
+  }
 
-    function _mint(
-        address from,
-        address to,
-        uint256 collateral
-    ) internal returns (uint256 shares) {
-        token1.mint(from, collateral);
+  function _mint(address from, address to, uint256 collateral) internal returns (uint256 shares) {
+    token1.mint(from, collateral);
 
-        if (from != address(this)) {
-            vm.prank(from);
-            token1.approve(address(this), collateral);
-        }
-
-        shares = lendgine.mint(to, collateral, abi.encode(MintCallbackData({ token: address(token1), payer: from })));
+    if (from != address(this)) {
+      vm.prank(from);
+      token1.approve(address(this), collateral);
     }
 
-    function _burn(
-        address to,
-        address from,
-        uint256 shares,
-        uint256 amount0,
-        uint256 amount1
-    ) internal returns (uint256 collateral) {
-        if (from != address(this)) {
-            vm.startPrank(from);
-            lendgine.transfer(address(lendgine), shares);
-            token0.approve(address(this), amount0);
-            token1.approve(address(this), amount1);
-            vm.stopPrank();
-        } else {
-            lendgine.transfer(address(lendgine), shares);
-        }
-
-        collateral = lendgine.burn(
-            to,
-            abi.encode(
-                PairMintCallbackData({
-                    token0: address(token0),
-                    token1: address(token1),
-                    amount0: amount0,
-                    amount1: amount1,
-                    payer: from
-                })
-            )
-        );
+    shares = lendgine.mint(to, collateral, abi.encode(MintCallbackData({token: address(token1), payer: from})));
+  }
+
+  function _burn(
+    address to,
+    address from,
+    uint256 shares,
+    uint256 amount0,
+    uint256 amount1
+  )
+    internal
+    returns (uint256 collateral)
+  {
+    if (from != address(this)) {
+      vm.startPrank(from);
+      lendgine.transfer(address(lendgine), shares);
+      token0.approve(address(this), amount0);
+      token1.approve(address(this), amount1);
+      vm.stopPrank();
+    } else {
+      lendgine.transfer(address(lendgine), shares);
     }
 
-    function _deposit(
-        address to,
-        address from,
-        uint256 amount0,
-        uint256 amount1,
-        uint256 liquidity
-    ) internal returns (uint256 size) {
-        token0.mint(from, amount0);
-        token1.mint(from, amount1);
-
-        if (from != address(this)) {
-            vm.startPrank(from);
-            token0.approve(address(this), amount0);
-            token1.approve(address(this), amount1);
-            vm.stopPrank();
-        }
-
-        size = lendgine.deposit(
-            to,
-            liquidity,
-            abi.encode(
-                PairMintCallbackData({
-                    token0: address(token0),
-                    token1: address(token1),
-                    amount0: amount0,
-                    amount1: amount1,
-                    payer: from
-                })
-            )
-        );
+    collateral = lendgine.burn(
+      to,
+      abi.encode(
+        PairMintCallbackData({
+          token0: address(token0),
+          token1: address(token1),
+          amount0: amount0,
+          amount1: amount1,
+          payer: from
+        })
+      )
+    );
+  }
+
+  function _deposit(
+    address to,
+    address from,
+    uint256 amount0,
+    uint256 amount1,
+    uint256 liquidity
+  )
+    internal
+    returns (uint256 size)
+  {
+    token0.mint(from, amount0);
+    token1.mint(from, amount1);
+
+    if (from != address(this)) {
+      vm.startPrank(from);
+      token0.approve(address(this), amount0);
+      token1.approve(address(this), amount1);
+      vm.stopPrank();
     }
 
-    function _withdraw(
-        address from,
-        address to,
-        uint256 size
-    )
-        internal
-        returns (
-            uint256 amount0,
-            uint256 amount1,
-            uint256 liquidity
-        )
-    {
-        vm.prank(from);
-        (amount0, amount1, liquidity) = lendgine.withdraw(to, size);
-    }
+    size = lendgine.deposit(
+      to,
+      liquidity,
+      abi.encode(
+        PairMintCallbackData({
+          token0: address(token0),
+          token1: address(token1),
+          amount0: amount0,
+          amount1: amount1,
+          payer: from
+        })
+      )
+    );
+  }
+
+  function _withdraw(
+    address from,
+    address to,
+    uint256 size
+  )
+    internal
+    returns (uint256 amount0, uint256 amount1, uint256 liquidity)
+  {
+    vm.prank(from);
+    (amount0, amount1, liquidity) = lendgine.withdraw(to, size);
+  }
 }
diff --git a/test/utils/mocks/MockERC20.sol b/test/utils/mocks/MockERC20.sol
index 08e0764..4c4178b 100644
--- a/test/utils/mocks/MockERC20.sol
+++ b/test/utils/mocks/MockERC20.sol
@@ -4,13 +4,13 @@ pragma solidity ^0.8.0;
 import { ERC20 } from "../../../src/core/ERC20.sol";
 
 contract MockERC20 is ERC20 {
-    constructor() ERC20() {}
+  constructor() ERC20() { }
 
-    function mint(address to, uint256 value) public virtual {
-        _mint(to, value);
-    }
+  function mint(address to, uint256 value) public virtual {
+    _mint(to, value);
+  }
 
-    function burn(address from, uint256 value) public virtual {
-        _burn(from, value);
-    }
+  function burn(address from, uint256 value) public virtual {
+    _burn(from, value);
+  }
 }