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
2 changes: 1 addition & 1 deletion addresses/addresses-999.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"fulfillmentVaultAddress": "0x8Bc1F48Ce0D241BB2B7EC05AF202B7dA9aaBd1d9",
"rolloverVaultAddress": "0x5e4A85D1BaD334A2Fdd07856201136bC6eE2e302",
"routerAddress": "0xeE958d427aFEd8443F89209693EE6b7B989239fF"
"routerAddress": "0x58B6943Ed3a48981eA58c0918830eFd4febbe5a1"
}
253 changes: 253 additions & 0 deletions broadcast/DeployRouter.s.sol/999/run-1766029502056.json

Large diffs are not rendered by default.

202 changes: 102 additions & 100 deletions broadcast/DeployRouter.s.sol/999/run-latest.json

Large diffs are not rendered by default.

8 changes: 8 additions & 0 deletions pull_request_template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
## Changes
<!--- description of changes and screenshots --->

## Testing :
<!--- How changes were tested --->

## Reviewers:
<!--- Please tag your reviewers --->
15 changes: 13 additions & 2 deletions script/DeployRouter.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ pragma solidity ^0.8.20;
import {DeployFulfillmentVaultScript} from "./DeployFulfillmentVault.s.sol";
import {console} from "forge-std/console.sol";
import {Router} from "../src/Router.sol";
import {RolloverVault} from "../src/RolloverVault.sol";
import {FulfillmentVault} from "../src/FulfillmentVault.sol";

contract DeployRouterScript is DeployFulfillmentVaultScript {
Router public router;
Expand All @@ -13,7 +15,11 @@ contract DeployRouterScript is DeployFulfillmentVaultScript {
}

function run() public virtual override {
vm.startBroadcast(deployerPrivateKey);
rolloverVault = RolloverVault(payable(vm.envAddress("ROLLOVER_VAULT_ADDRESS")));
console.log("Rollover vault address: %s", address(rolloverVault));
fulfillmentVault = FulfillmentVault(payable(vm.envAddress("FULFILLMENT_VAULT_ADDRESS")));
console.log("Fulfillment vault address: %s", address(fulfillmentVault));
vm.startBroadcast();
deployRouter();
// logAddresses();
vm.stopBroadcast();
Expand All @@ -23,7 +29,12 @@ contract DeployRouterScript is DeployFulfillmentVaultScript {
if (address(rolloverVault) == address(0)) {
revert("Rollover vault not deployed");
}
router = new Router(wrappedNativeTokenAddress, generalManagerAddress, address(rolloverVault), pythAddress);
if (address(fulfillmentVault) == address(0)) {
revert("Fulfillment vault not deployed");
}
router = new Router(
wrappedNativeTokenAddress, generalManagerAddress, address(rolloverVault), address(fulfillmentVault), pythAddress
);
router.approveCollaterals();
router.approveUsdTokens();
}
Expand Down
43 changes: 35 additions & 8 deletions src/Router.sol
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import {IPyth} from "@pythnetwork/IPyth.sol";
import {PythErrors} from "@pythnetwork/PythErrors.sol";
import {MortgageMath} from "@core/libraries/MortgageMath.sol";
import {MortgagePosition} from "@core/types/MortgagePosition.sol";
import {IRolloverVault} from "./interfaces/IRolloverVault/IRolloverVault.sol";
import {ILiquidityVault} from "./interfaces/ILiquidityVault/ILiquidityVault.sol";

/**
* @title Router
Expand All @@ -53,6 +53,8 @@ contract Router is
/// @inheritdoc IRouter
address public immutable rolloverVault;
/// @inheritdoc IRouter
address public immutable fulfillmentVault;
/// @inheritdoc IRouter
address public immutable pyth;
/// @inheritdoc IRouter
address public immutable wrappedNativeToken;
Expand All @@ -67,12 +69,20 @@ contract Router is
* @param _wrappedNativeToken The address of the wrapped native token (i.e., whype: 0x555...)
* @param _generalManager The address of the general manager contract
* @param _rolloverVault The address of the rollover vault contract
* @param _fulfillmentVault The address of the fulfillment vault contract
* @param _pyth The address of the Pyth contract
*/
constructor(address _wrappedNativeToken, address _generalManager, address _rolloverVault, address _pyth) {
constructor(
address _wrappedNativeToken,
address _generalManager,
address _rolloverVault,
address _fulfillmentVault,
address _pyth
) {
wrappedNativeToken = _wrappedNativeToken;
generalManager = _generalManager;
rolloverVault = _rolloverVault;
fulfillmentVault = _fulfillmentVault;
pyth = _pyth;
usdx = IGeneralManager(_generalManager).usdx();
consol = IGeneralManager(_generalManager).consol();
Expand Down Expand Up @@ -450,20 +460,37 @@ contract Router is
}

/**
* @inheritdoc IRouter
* @dev Internal function to deposit into a liquidity vault
* @param vault The address of the liquidity vault to deposit into
* @param usdToken The address of the usdToken to pull in
* @param usdTokenAmount The amount of usdToken to pull in
*/
function rolloverVaultDeposit(address usdToken, uint256 usdTokenAmount) external {
function _vaultDeposit(address vault, address usdToken, uint256 usdTokenAmount) internal {
// Convert the usdTokenAmount to USDX
uint256 usdxAmount = convert(usdToken, usdx, usdTokenAmount);

// Pull in the usdToken from the user
_pullUsdToken(usdToken, usdxAmount);

// Deposit the USDX into the rollover vault
IUSDX(usdx).approve(rolloverVault, usdxAmount);
IRolloverVault(rolloverVault).deposit(usdx, usdxAmount);
IUSDX(usdx).approve(vault, usdxAmount);
ILiquidityVault(vault).deposit(usdx, usdxAmount);

// Transfer the rollover vault share tokens to the user
IRolloverVault(rolloverVault).transfer(msg.sender, IRolloverVault(rolloverVault).balanceOf(address(this)));
// Transfer the vault share tokens to the user
ILiquidityVault(vault).transfer(msg.sender, ILiquidityVault(vault).balanceOf(address(this)));
}

/**
* @inheritdoc IRouter
*/
function rolloverVaultDeposit(address usdToken, uint256 usdTokenAmount) external {
_vaultDeposit(rolloverVault, usdToken, usdTokenAmount);
}

/**
* @inheritdoc IRouter
*/
function fulfillmentVaultDeposit(address usdToken, uint256 usdTokenAmount) external {
_vaultDeposit(fulfillmentVault, usdToken, usdTokenAmount);
}
}
13 changes: 13 additions & 0 deletions src/interfaces/IRouter/IRouter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ interface IRouter is IRouterErrors {
*/
function rolloverVault() external view returns (address);

/**
* @notice The address of the fulfillment vault contract
* @return The address of the fulfillment vault contract
*/
function fulfillmentVault() external view returns (address);

/**
* @notice The address of the Pyth contract
* @return The address of the Pyth contract
Expand Down Expand Up @@ -178,4 +184,11 @@ interface IRouter is IRouterErrors {
* @param usdTokenAmount The amount of usdToken to pull in
*/
function rolloverVaultDeposit(address usdToken, uint256 usdTokenAmount) external;

/**
* @notice Deposit into the fulfillment vault
* @param usdToken The address of the usdToken to pull in
* @param usdTokenAmount The amount of usdToken to pull in
*/
function fulfillmentVaultDeposit(address usdToken, uint256 usdTokenAmount) external;
}
178 changes: 178 additions & 0 deletions test/BaseTest.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,17 @@ import {IPyth} from "@pythnetwork/IPyth.sol";
import {MockPyth} from "@pythnetwork/MockPyth.sol";
import {MockERC20} from "./mocks/MockERC20.sol";
import {RolloverVault} from "../src/RolloverVault.sol";
import {FulfillmentVault} from "../src/FulfillmentVault.sol";
import {CoreSimulatorLib} from "@hyper-evm-lib/test/simulation/CoreSimulatorLib.sol";
import {PrecompileLib} from "@hyper-evm-lib/src/PrecompileLib.sol";
import {HyperCore} from "@hyper-evm-lib/test/simulation/HyperCore.sol";
import {TokenRegistry} from "@hyper-evm-lib/src/registry/TokenRegistry.sol";
import {HLConversions} from "@hyper-evm-lib/src/common/HLConversions.sol";
import {HLConstants} from "@hyper-evm-lib/src/common/HLConstants.sol";

contract BaseTest is Test {
using OPoolConfigIdLibrary for OPoolConfigId;
using PrecompileLib for address;

// Actors
address public admin = makeAddr("admin");
Expand Down Expand Up @@ -112,6 +120,27 @@ contract BaseTest is Test {
uint8 ROLLLOVER_VAULT_DECIMALS = 24; // Make this 6 + usdx decimals
uint8 ROLLLOVER_VAULT_DECIMALS_OFFSET = 6;
RolloverVault public rolloverVault;
// Hyper-EVM-Lib Values
HyperCore public hyperCore;
TokenRegistry public tokenRegistry;
address public HYPER_CORE_ADDRESS = 0x9999999999999999999999999999999999999999;
address TOKEN_INFO_PRECOMPILE_ADDRESS = 0x000000000000000000000000000000000000080C;
address SPOT_INFO_PRECOMPILE_ADDRESS = 0x000000000000000000000000000000000000080b;
address SPOT_PX_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000808;
address public TOKEN_REGISTRY_ADDRESS = 0x0b51d1A9098cf8a72C325003F44C194D41d7A85B;
address public UBTC_SYSTEM_ADDRESS = 0x20000000000000000000000000000000000000c5;
uint32 public HYPE_TOKEN_INDEX = uint32(HLConstants.hypeTokenIndex());
uint32 public USDC_TOKEN_INDEX = 0;
uint32 public USDT_TOKEN_INDEX = 268;
uint32 public USDH_TOKEN_INDEX = 360;
uint32 public UBTC_TOKEN_INDEX = 197;
// FulfillmentVault Args
FulfillmentVault public fulfillmentVault;
string FULFILLMENT_VAULT_NAME = "Test Fulfillment Vault";
string FULFILLMENT_VAULT_SYMBOL = "tFT";
uint8 FULFILLMENT_VAULT_DECIMALS = 24;
uint8 FULFILLMENT_VAULT_DECIMALS_OFFSET = 6;
// Vault Priming Amount
uint256 public PRIME_AMOUNT = 1e18; // The initial amount of depositableAsset to prime the liquidityVault with (in depositableAsset decimals)

function _deployWHype() internal {
Expand Down Expand Up @@ -428,5 +457,154 @@ contract BaseTest is Test {
vm.stopPrank();
}

function mockTokenInfo(
uint32 tokenIndex,
address evmContract,
string memory name,
uint8 szDecimals,
uint8 weiDecimals,
int8 evmExtraWeiDecimals
) internal {
PrecompileLib.TokenInfo memory info = PrecompileLib.TokenInfo({
name: name,
spots: new uint64[](0),
deployerTradingFeeShare: 0,
deployer: address(0),
evmContract: evmContract,
szDecimals: szDecimals,
weiDecimals: weiDecimals,
evmExtraWeiDecimals: evmExtraWeiDecimals
});

vm.mockCall(TOKEN_INFO_PRECOMPILE_ADDRESS, abi.encode(tokenIndex), abi.encode(info));
if (tokenIndex != HYPE_TOKEN_INDEX && tokenIndex != USDC_TOKEN_INDEX) {
tokenRegistry.setTokenInfo(tokenIndex);
}
}

function mockSpotInfo(uint32 spotIndex, string memory name, uint64[2] memory tokens) internal {
PrecompileLib.SpotInfo memory info = PrecompileLib.SpotInfo({name: name, tokens: tokens});
vm.mockCall(SPOT_INFO_PRECOMPILE_ADDRESS, abi.encode(spotIndex), abi.encode(info));
}

function mockSpotPx(uint32 spotIndex, uint64 px) internal {
vm.mockCall(SPOT_PX_PRECOMPILE_ADDRESS, abi.encode(spotIndex), abi.encode(px));
}

function setupHyperCore() internal {
hyperCore = new HyperCore();
vm.etch(HYPER_CORE_ADDRESS, address(hyperCore).code);
vm.label(HYPER_CORE_ADDRESS, "HyperCore");
hyperCore = CoreSimulatorLib.init();
hyperCore.setUseRealL1Read(false);
}

function setupTokenRegistry() internal {
tokenRegistry = new TokenRegistry();
vm.etch(TOKEN_REGISTRY_ADDRESS, address(tokenRegistry).code);
vm.label(TOKEN_REGISTRY_ADDRESS, "TokenRegistry");
tokenRegistry = TokenRegistry(TOKEN_REGISTRY_ADDRESS);
mockTokenInfo(USDT_TOKEN_INDEX, address(usdt), "USDT", 2, 8, -2);
mockTokenInfo(USDH_TOKEN_INDEX, address(usdh), "USDH", 2, 8, -2);
mockTokenInfo(HYPE_TOKEN_INDEX, address(0), "HYPE", 2, 8, 0);
mockTokenInfo(USDC_TOKEN_INDEX, address(0), "USDC", 8, 8, 0);
mockTokenInfo(UBTC_TOKEN_INDEX, address(ubtc), "UBTC", 5, 10, -2);
}

function setupSpotInfo() internal {
uint64[2] memory tokens = [uint64(USDT_TOKEN_INDEX), uint64(USDC_TOKEN_INDEX)];
mockSpotInfo(USDT_TOKEN_INDEX, "@166", tokens);
tokens = [uint64(USDH_TOKEN_INDEX), uint64(USDC_TOKEN_INDEX)];
mockSpotInfo(USDH_TOKEN_INDEX, "@230", tokens);
tokens = [uint64(HYPE_TOKEN_INDEX), uint64(USDC_TOKEN_INDEX)];
mockSpotInfo(HYPE_TOKEN_INDEX, "@107", tokens);
tokens = [uint64(UBTC_TOKEN_INDEX), uint64(USDC_TOKEN_INDEX)];
mockSpotInfo(UBTC_TOKEN_INDEX, "@142", tokens);
}

function primeFulfillmentVault() public {
// Mint 0.5 PRIME_AMOUNT of usdt0 and usdh to the admin
vm.startPrank(admin);
uint256 usdtAmount = usdx.convertUnderlying(address(usdt), PRIME_AMOUNT / 2);
uint256 usdhAmount = usdx.convertUnderlying(address(usdh), PRIME_AMOUNT / 2);
deal(address(usdt), admin, usdtAmount);
deal(address(usdh), admin, usdhAmount);
vm.stopPrank();

// Admin primes the fulfillmentVault with PRIME_AMOUNT of usdx
vm.startPrank(admin);
usdt.approve(address(usdx), usdtAmount);
usdh.approve(address(usdx), usdhAmount);
usdx.deposit(address(usdt), usdtAmount);
usdx.deposit(address(usdh), usdhAmount);
usdx.approve(address(fulfillmentVault), PRIME_AMOUNT);
fulfillmentVault.deposit(address(usdx), PRIME_AMOUNT);
vm.stopPrank();

// Transfer the fulfillmentVault balance to the fulfillmentVault itself
vm.startPrank(admin);
fulfillmentVault.transfer(address(fulfillmentVault), fulfillmentVault.balanceOf(admin));
vm.stopPrank();
}

function setUpFulfillmentVault() public {
// Initialize the HyperCore simulator
// vm.createSelectFork(vm.rpcUrl("hyperliquid"), 17133085);
setupHyperCore();

// Setup core
setUpCore();

// Setup the mock token registry
setupTokenRegistry();

// Setup the mock spot info
setupSpotInfo();

// Deploy the fulfillmentVault
FulfillmentVault fulfillmentVaultImplementation = new FulfillmentVault();
bytes memory initializerData = abi.encodeWithSelector(
FulfillmentVault.initialize.selector,
FULFILLMENT_VAULT_NAME,
FULFILLMENT_VAULT_SYMBOL,
FULFILLMENT_VAULT_DECIMALS,
FULFILLMENT_VAULT_DECIMALS_OFFSET,
address(whype),
address(generalManager),
address(admin)
);
ERC1967Proxy proxy = new ERC1967Proxy(address(fulfillmentVaultImplementation), initializerData);
fulfillmentVault = FulfillmentVault(payable(address(proxy)));

// Prime the fulfillmentVault
primeFulfillmentVault();

// Grant the keeper the KEEPER_ROLE
vm.startPrank(admin);
fulfillmentVault.grantRole(fulfillmentVault.KEEPER_ROLE(), keeper);
vm.stopPrank();

// Force the fulfillmentVault to be activated on hypercore
vm.mockCall(
HLConstants.CORE_USER_EXISTS_PRECOMPILE_ADDRESS, abi.encode(address(fulfillmentVault)), abi.encode(true)
);
CoreSimulatorLib.forceAccountActivation(address(fulfillmentVault));

// Force activate the HYPE system address so bridging works
vm.mockCall(
HLConstants.CORE_USER_EXISTS_PRECOMPILE_ADDRESS, abi.encode(HLConstants.HYPE_SYSTEM_ADDRESS), abi.encode(true)
);
CoreSimulatorLib.forceAccountActivation(HLConstants.HYPE_SYSTEM_ADDRESS);

// Force activate the UBTC system address so bridging works
vm.mockCall(HLConstants.CORE_USER_EXISTS_PRECOMPILE_ADDRESS, abi.encode(UBTC_SYSTEM_ADDRESS), abi.encode(true));
CoreSimulatorLib.forceAccountActivation(UBTC_SYSTEM_ADDRESS);

// Grant the fulfillmentVault the orderPool's FULFILLMENT_VAULT_ROLE
vm.startPrank(admin);
IAccessControl(address(orderPool)).grantRole(Roles.FULFILLMENT_ROLE, address(fulfillmentVault));
vm.stopPrank();
}

function test_constructor() public view virtual {}
}
Loading
Loading