Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Vertex Protocol

This repository contains the smart contract implementations for the Vertex Protocol ecosystem.

## Project Structure

The repository is organized into two main projects:

- **[vertex-contracts/core](./vertex-contracts/core)**: EVM implementation of Vertex core functionality
- **[vertex-contracts/lba](./vertex-contracts/lba)**: Vertex LBA (Liquidity Bootstrap Auction) contracts

## Requirements

- Node.js >=16
- [Yarn](https://yarnpkg.com/)

## Getting Started

Each project has its own setup and development commands. Navigate to the respective directories for project-specific instructions:

```
# For Vertex EVM Core Contracts
cd vertex-contracts/core
yarn install
yarn compile

# For Vertex LBA Contracts
cd vertex-contracts/lba
yarn install
# Follow the .env setup instructions
```

## Available Commands

### Core Contracts

- `yarn compile`: Compile Vertex EVM contracts
- See project-specific README for more details

### LBA Contracts

- `yarn lint`: Run prettier & SolHint
- `yarn contracts:force-compile`: Compile contracts and generate TS bindings + ABIs
- `yarn run-local-node`: Run a persistent local Hardhat node for testing
- See project-specific README for more details

## Further Documentation

For more detailed information about each project, please refer to their respective README files.
6 changes: 2 additions & 4 deletions core/contracts/BaseEngine.sol
Original file line number Diff line number Diff line change
Expand Up @@ -253,10 +253,8 @@ abstract contract BaseEngine is IProductEngine, EndpointGated {
IOffchainExchange(IEndpoint(getEndpoint()).getOffchainExchange());
}

function updatePrice(uint32 productId, int128 priceX18)
external
onlyEndpoint
{
function updatePrice(uint32 productId, int128 priceX18) external {
require(msg.sender == address(_clearinghouse), ERR_UNAUTHORIZED);
_risk().value[productId].priceX18 = priceX18;
}

Expand Down
186 changes: 138 additions & 48 deletions core/contracts/Clearinghouse.sol
Original file line number Diff line number Diff line change
Expand Up @@ -24,29 +24,6 @@ interface IProxyManager {
function getCodeHash(string memory name) external view returns (bytes32);
}

enum YieldMode {
AUTOMATIC,
DISABLED,
CLAIMABLE
}

enum GasMode {
VOID,
CLAIMABLE
}

interface IBlastPoints {
function configurePointsOperator(address operator) external;
}

interface IBlast {
function configure(
YieldMode _yield,
GasMode gasMode,
address governor
) external;
}

contract Clearinghouse is EndpointGated, ClearinghouseStorage, IClearinghouse {
using MathSD21x18 for int128;
using ERC20Helper for IERC20Base;
Expand Down Expand Up @@ -369,6 +346,72 @@ contract Clearinghouse is EndpointGated, ClearinghouseStorage, IClearinghouse {
require(sumBase == 0, ERR_INVALID_HOLDER_LIST);
}

function rebalanceXWithdraw(bytes calldata transaction, uint64 nSubmissions)
external
onlyEndpoint
{
IEndpoint.RebalanceXWithdraw memory txn = abi.decode(
transaction[1:],
(IEndpoint.RebalanceXWithdraw)
);

withdrawCollateral(
X_ACCOUNT,
txn.productId,
txn.amount,
txn.sendTo,
nSubmissions
);
}

function updateMinDepositRate(bytes calldata transaction)
external
onlyEndpoint
{
IEndpoint.UpdateMinDepositRate memory txn = abi.decode(
transaction[1:],
(IEndpoint.UpdateMinDepositRate)
);
ISpotEngine spotEngine = ISpotEngine(
address(engineByType[IProductEngine.EngineType.SPOT])
);
spotEngine.updateMinDepositRate(txn.productId, txn.minDepositRateX18);
}

function updateFeeRates(bytes calldata transaction) external onlyEndpoint {
IEndpoint.UpdateFeeRates memory txn = abi.decode(
transaction[1:],
(IEndpoint.UpdateFeeRates)
);
address offchainExchange = IEndpoint(getEndpoint())
.getOffchainExchange();
IOffchainExchange(offchainExchange).updateFeeRates(
txn.user,
txn.productId,
txn.makerRateX18,
txn.takerRateX18
);
}

function updatePrice(bytes calldata transaction)
external
onlyEndpoint
returns (uint32, int128)
{
IEndpoint.UpdatePrice memory txn = abi.decode(
transaction[1:],
(IEndpoint.UpdatePrice)
);
require(txn.priceX18 > 0, ERR_INVALID_PRICE);
IProductEngine engine = productToEngine[txn.productId];
if (address(engine) != address(0)) {
engine.updatePrice(txn.productId, txn.priceX18);
return (txn.productId, txn.priceX18);
} else {
return (0, 0);
}
}

function handleWithdrawTransfer(
IERC20Base token,
address to,
Expand All @@ -389,7 +432,7 @@ contract Clearinghouse is EndpointGated, ClearinghouseStorage, IClearinghouse {
uint128 amount,
address sendTo,
uint64 idx
) external virtual onlyEndpoint {
) public virtual onlyEndpoint {
require(!RiskHelper.isIsolatedSubaccount(sender), ERR_UNAUTHORIZED);
require(amount <= INT128_MAX, ERR_CONVERSION_OVERFLOW);
ISpotEngine spotEngine = ISpotEngine(
Expand Down Expand Up @@ -447,6 +490,70 @@ contract Clearinghouse is EndpointGated, ClearinghouseStorage, IClearinghouse {
);
}

function mintVlp(IEndpoint.MintVlp calldata txn, int128 oraclePriceX18)
external
onlyEndpoint
{
require(!RiskHelper.isIsolatedSubaccount(txn.sender), ERR_UNAUTHORIZED);
ISpotEngine spotEngine = ISpotEngine(
address(engineByType[IProductEngine.EngineType.SPOT])
);
spotEngine.updatePrice(VLP_PRODUCT_ID, oraclePriceX18);
require(txn.quoteAmount <= INT128_MAX, ERR_CONVERSION_OVERFLOW);

int128 quoteAmount = int128(txn.quoteAmount);
spotEngine.updateBalance(QUOTE_PRODUCT_ID, txn.sender, -quoteAmount);
spotEngine.updateBalance(QUOTE_PRODUCT_ID, V_ACCOUNT, quoteAmount);

int128 vlpAmount = quoteAmount.div(oraclePriceX18);
spotEngine.updateBalance(VLP_PRODUCT_ID, txn.sender, vlpAmount);
spotEngine.updateBalance(VLP_PRODUCT_ID, V_ACCOUNT, -vlpAmount);

require(
getHealth(txn.sender, IProductEngine.HealthType.INITIAL) >= 0,
ERR_SUBACCT_HEALTH
);
}

function burnVlp(IEndpoint.BurnVlp calldata txn, int128 oraclePriceX18)
external
onlyEndpoint
{
require(!RiskHelper.isIsolatedSubaccount(txn.sender), ERR_UNAUTHORIZED);
ISpotEngine spotEngine = ISpotEngine(
address(engineByType[IProductEngine.EngineType.SPOT])
);
spotEngine.updatePrice(VLP_PRODUCT_ID, oraclePriceX18);
require(txn.vlpAmount <= INT128_MAX, ERR_CONVERSION_OVERFLOW);

int128 vlpAmount = int128(txn.vlpAmount);
spotEngine.updateBalance(VLP_PRODUCT_ID, txn.sender, -vlpAmount);
spotEngine.updateBalance(VLP_PRODUCT_ID, V_ACCOUNT, vlpAmount);

int128 quoteAmount = vlpAmount.mul(oraclePriceX18);
spotEngine.updateBalance(QUOTE_PRODUCT_ID, txn.sender, quoteAmount);
spotEngine.updateBalance(QUOTE_PRODUCT_ID, V_ACCOUNT, -quoteAmount);

require(
spotEngine.getBalance(VLP_PRODUCT_ID, txn.sender).amount >= 0,
ERR_SUBACCT_HEALTH
);
}

function rebalanceVlp(bytes calldata transaction) external onlyEndpoint {
IEndpoint.RebalanceVlp memory txn = abi.decode(
transaction[1:],
(IEndpoint.RebalanceVlp)
);
ISpotEngine spotEngine = ISpotEngine(
address(engineByType[IProductEngine.EngineType.SPOT])
);
spotEngine.updateBalance(QUOTE_PRODUCT_ID, V_ACCOUNT, txn.quoteAmount);
spotEngine.updateBalance(QUOTE_PRODUCT_ID, X_ACCOUNT, -txn.quoteAmount);
spotEngine.updateBalance(VLP_PRODUCT_ID, V_ACCOUNT, txn.vlpAmount);
spotEngine.updateBalance(VLP_PRODUCT_ID, X_ACCOUNT, -txn.vlpAmount);
}

function burnLpAndTransfer(IEndpoint.BurnLpAndTransfer calldata txn)
external
virtual
Expand All @@ -473,18 +580,14 @@ contract Clearinghouse is EndpointGated, ClearinghouseStorage, IClearinghouse {
require(_isAboveInitial(txn.sender), ERR_SUBACCT_HEALTH);
}

function claimSequencerFees(
IEndpoint.ClaimSequencerFees calldata txn,
int128[] calldata fees
) external virtual onlyEndpoint {
require(
!RiskHelper.isIsolatedSubaccount(txn.subaccount),
ERR_UNAUTHORIZED
);
function claimSequencerFees(int128[] calldata fees)
external
virtual
onlyEndpoint
{
ISpotEngine spotEngine = ISpotEngine(
address(engineByType[IProductEngine.EngineType.SPOT])
);

IPerpEngine perpEngine = IPerpEngine(
address(engineByType[IProductEngine.EngineType.PERP])
);
Expand All @@ -497,13 +600,11 @@ contract Clearinghouse is EndpointGated, ClearinghouseStorage, IClearinghouse {
spotIds[i],
FEES_ACCOUNT
);

spotEngine.updateBalance(
spotIds[i],
txn.subaccount,
X_ACCOUNT,
fees[i] + feeBalance.amount
);

spotEngine.updateBalance(
spotIds[i],
FEES_ACCOUNT,
Expand All @@ -516,14 +617,12 @@ contract Clearinghouse is EndpointGated, ClearinghouseStorage, IClearinghouse {
perpIds[i],
FEES_ACCOUNT
);

perpEngine.updateBalance(
perpIds[i],
txn.subaccount,
X_ACCOUNT,
feeBalance.amount,
feeBalance.vQuoteBalance
);

perpEngine.updateBalance(
perpIds[i],
FEES_ACCOUNT,
Expand Down Expand Up @@ -617,15 +716,6 @@ contract Clearinghouse is EndpointGated, ClearinghouseStorage, IClearinghouse {
return spreads;
}

function configurePoints(
address blastPoints,
address blast,
address gov
) external onlyOwner {
IBlastPoints(blastPoints).configurePointsOperator(gov);
IBlast(blast).configure(YieldMode.CLAIMABLE, GasMode.CLAIMABLE, gov);
}

function requireMinDeposit(uint32 productId, uint128 amount) external {
require(amount <= INT128_MAX, ERR_CONVERSION_OVERFLOW);
uint8 decimals = _decimals(productId);
Expand Down
5 changes: 4 additions & 1 deletion core/contracts/ClearinghouseLiq.sol
Original file line number Diff line number Diff line change
Expand Up @@ -715,7 +715,10 @@ contract ClearinghouseLiq is
require(!RiskHelper.isIsolatedSubaccount(txn.sender), ERR_UNAUTHORIZED);
require(txn.sender != txn.liquidatee, ERR_UNAUTHORIZED);
require(isUnderMaintenance(txn.liquidatee), ERR_NOT_LIQUIDATABLE);
require(txn.liquidatee != X_ACCOUNT, ERR_NOT_LIQUIDATABLE);
require(
txn.liquidatee != X_ACCOUNT && txn.liquidatee != V_ACCOUNT,
ERR_NOT_LIQUIDATABLE
);
require(
txn.productId != QUOTE_PRODUCT_ID,
ERR_INVALID_LIQUIDATION_PARAMS
Expand Down
Loading
Loading