Skip to content

Commit

Permalink
Merge pull request #17 from numotrade/base/tob
Browse files Browse the repository at this point in the history
Base/tob
  • Loading branch information
robertleifke authored Aug 5, 2024
2 parents 626e5d9 + 5eb6135 commit e95cad7
Show file tree
Hide file tree
Showing 41 changed files with 3,256 additions and 3,153 deletions.
20 changes: 7 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,9 @@

![numo_banner](images/numo_readme.png)

Numo uniquely enables **depeg insurance** on all stablecoins and pegged pairs. One could conversly use Numo to speculate on the fluctuations between pegged tokens.

The repository contains the smart contract suite for **Numo**, the solidity implementation of a [*replicating market maker*](https://arxiv.org/abs/2103.14769) on the EVM.

TLDR: Numo allows anyone to short or long any token with leverage using pooled liquidity.

## Replicating Market Maker

The principle idea of a replicating market maker is that any options strategy can be constructed on the EVM simply by altering the trading function of a CFMM. Thus, turning a CFMM into an *exotic leverage token*.

### Leverage Token

When someone uses **Numo** to get leverage, they are swapping their token into a leverage token which automatically rebalances at every price to reflect 100x leverage when it hits its strike. Think of it as a 2x leverage token on ETH (ex. ETH2x-FLI), but instead its 100x and supports any token.
The solidity implementation for Numo is contained in this repository and was inspired by math that shows virtually [any option strategy can be constructed using CFMMs](https://arxiv.org/abs/2103.14769).

## Local development

Expand All @@ -23,13 +14,12 @@ This project uses [Foundry](https://github.com/foundry-rs/foundry) as the develo

```bash
forge install foundry-rs/forge-std

```
To handle high-percision fixed point airithmic, Numo uses the `PRBMath` Library.
```bash
bun add @prb/math
```
Yu need to add this to your remappings.txt file:
You need to add this to your remappings.txt file:

```
@prb/math/=node_modules/@prb/math/
Expand Down Expand Up @@ -64,3 +54,7 @@ then, in a separate terminal,
```bash
sh setup.sh
```

## Licences

The smart contracts that make up Numo are licensed under the GPL-3.0 unless specified otherwise.
Binary file added bun.lockb
Binary file not shown.
2 changes: 1 addition & 1 deletion foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ libs = ["lib"]
optimizer = true
optimizer_runs = 10_000
out = "out"
solc = "0.8.17"
solc = "0.8.19"
src = "src"
test = "test"

Expand Down
6 changes: 3 additions & 3 deletions lib/create3-factory/lib/solmate/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions lib/forge-std/src/interfaces/IERC1155.sol
Original file line number Diff line number Diff line change
Expand Up @@ -82,10 +82,10 @@ interface IERC1155 is IERC165 {
/// @return The _owner's balance of the token type requested
function balanceOf(address _owner, uint256 _id) external view returns (uint256);

/// @notice Get the balance of multiple account/token pairs
/// @notice Get the balance of multiple account/token QFMMs
/// @param _owners The addresses of the token holders
/// @param _ids ID of the tokens
/// @return The _owner's balance of the token types requested (i.e. balance for each (owner, id) pair)
/// @return The _owner's balance of the token types requested (i.e. balance for each (owner, id) QFMM)
function balanceOfBatch(address[] calldata _owners, uint256[] calldata _ids)
external
view
Expand Down
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@
"prettier:write": "prettier --write \"**/*.{json,md,sol,yml}\""
},
"dependencies": {
"@openzeppelin/contracts": "^5.0.2"
"@openzeppelin/contracts": "^5.0.2",
"@prb/math": "^4.0.3",
"@prb/test": "^0.6.4"
}
}
4 changes: 3 additions & 1 deletion remappings.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
forge-std/=lib/forge-std/src/
create3-factory/=lib/create3-factory/src/
create3-factory/=lib/create3-factory/src/
@prb/math/=node_modules/@prb/math/
@prb/test/=node_modules/@prb/test/src/
2 changes: 1 addition & 1 deletion script/Deploy.s.sol
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.13;
pragma solidity 0.8.19;

import { Script } from "forge-std/Script.sol";
import { console2 } from "forge-std/console2.sol";
Expand Down
14 changes: 7 additions & 7 deletions script/SetupLocal.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,9 @@ contract SetupLocalScript is Script {
);

// deploy new lendgines
console2.log("usdc/weth lendgine: ", factory.createLendgine(usdc, weth, 6, 18, usdcWethBound));
console2.log("weth/uni lendgine: ", factory.createLendgine(weth, uni, 18, 18, wethUniBound));
console2.log("uni/weth lendgine: ", factory.createLendgine(uni, weth, 18, 18, uniWethBound));
console2.log("usdc/weth lendgine: ", factory.createLendgine(usdc, weth, usdcWethBound));
console2.log("weth/uni lendgine: ", factory.createLendgine(weth, uni, wethUniBound));
console2.log("uni/weth lendgine: ", factory.createLendgine(uni, weth, uniWethBound));

// mint tokens to addr
IWETH9(weth).deposit{ value: 100 ether }();
Expand All @@ -87,7 +87,7 @@ contract SetupLocalScript is Script {
token1: weth,
token0Exp: 6,
token1Exp: 18,
upperBound: usdcWethBound,
strike: usdcWethBound,
liquidity: 1 ether / 1e5,
amount0Min: (reserveUsdc / 1e17) + 1,
amount1Min: (reserveWeth / 1e5) + 1,
Expand All @@ -103,7 +103,7 @@ contract SetupLocalScript is Script {
token1: uni,
token0Exp: 18,
token1Exp: 18,
upperBound: wethUniBound,
strike: wethUniBound,
liquidity: 1 ether * 1e2,
amount0Min: (reserveWeth * 1e2) + 100,
amount1Min: (reserveUni * 1e2) + 100,
Expand All @@ -119,7 +119,7 @@ contract SetupLocalScript is Script {
token1: weth,
token0Exp: 18,
token1Exp: 18,
upperBound: uniWethBound,
strike: uniWethBound,
liquidity: 1 ether / 20,
amount0Min: (reserveWeth / 20) + 20,
amount1Min: (reserveUni / 20) + 20,
Expand All @@ -138,7 +138,7 @@ contract SetupLocalScript is Script {
token1: uni,
token0Exp: 18,
token1Exp: 18,
upperBound: wethUniBound,
strike: wethUniBound,
amountIn: 1e18,
amountBorrow: 4 * 1e18,
sharesMin: 0,
Expand Down
127 changes: 65 additions & 62 deletions src/core/Factory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,88 +2,91 @@
pragma solidity ^0.8.4;

import { Lendgine } from "./Lendgine.sol";

import { IFactory } from "./interfaces/IFactory.sol";
import { IERC20 } from "./interfaces/IERC20.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 indexed strike,
address lendgine
);

/*//////////////////////////////////////////////////////////////
/*//////////////////////////////////////////////////////////////
ERRORS
//////////////////////////////////////////////////////////////*/

error SameTokenError();

error ZeroAddressError();

error DeployedError();

error ScaleError();
error SameTokenError();
error ZeroAddressError();
error DeployedError();
error UnsupportedTokenError();

/*//////////////////////////////////////////////////////////////
/*//////////////////////////////////////////////////////////////
FACTORY STORAGE
//////////////////////////////////////////////////////////////*/

/// @inheritdoc IFactory
mapping(address => mapping(address => mapping(uint256 => mapping(uint256 => mapping(uint256 => address)))))
public
override getLendgine;
/// @inheritdoc IFactory
mapping(address => mapping(address => mapping(uint256 => address)))
public
override getLendgine;

/*//////////////////////////////////////////////////////////////
/*//////////////////////////////////////////////////////////////
TEMPORARY DEPLOY STORAGE
//////////////////////////////////////////////////////////////*/

struct Parameters {
address token0;
address token1;
uint128 token0Exp;
uint128 token1Exp;
uint256 upperBound;
}
struct Parameters {
address token0;
address token1;
uint256 strike;
uint8 token0Decimals;
uint8 token1Decimals;
}

/// @inheritdoc IFactory
Parameters public override parameters;
/// @inheritdoc IFactory
Parameters public override parameters;

/*//////////////////////////////////////////////////////////////
/*//////////////////////////////////////////////////////////////
FACTORY LOGIC
//////////////////////////////////////////////////////////////*/

/// @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);
}
}
/// @inheritdoc IFactory
function createLendgine(
address token0,
address token1,
uint256 strike
)
external
override
returns (address lendgine)
{
if (token0 == token1) revert SameTokenError();
if (token0 == address(0) || token1 == address(0)) revert ZeroAddressError();
if (getLendgine[token0][token1][strike] != address(0)) revert DeployedError();

// Ensure tokens implement the decimals method
uint8 token0Decimals = IERC20(token0).decimals();
uint8 token1Decimals = IERC20(token1).decimals();
if (token0Decimals == 0 || token1Decimals == 0) revert UnsupportedTokenError();

parameters = Parameters({
token0: token0,
token1: token1,
strike: strike,
token0Decimals: token0Decimals,
token1Decimals: token1Decimals
});

lendgine = address(new Lendgine{ salt: keccak256(abi.encode(token0, token1, strike)) }());

delete parameters;

getLendgine[token0][token1][strike] = lendgine;
emit LendgineCreated(token0, token1, strike, lendgine);
}
}
15 changes: 2 additions & 13 deletions src/core/ImmutableState.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,23 +16,12 @@ abstract contract ImmutableState is IImmutableState {
address public immutable override token1;

/// @inheritdoc IImmutableState
uint256 public immutable override token0Scale;

/// @inheritdoc IImmutableState
uint256 public immutable override token1Scale;

/// @inheritdoc IImmutableState
uint256 public immutable override upperBound;
uint256 public immutable override strike;

constructor() {
factory = msg.sender;

uint128 _token0Exp;
uint128 _token1Exp;

(token0, token1, _token0Exp, _token1Exp, upperBound) = Factory(msg.sender).parameters();
(token0, token1, strike, , ) = Factory(msg.sender).parameters();

token0Scale = 10 ** (18 - _token0Exp);
token1Scale = 10 ** (18 - _token1Exp);
}
}
Loading

0 comments on commit e95cad7

Please sign in to comment.