Money Market Hook

Money market hook is a liquidity hook contract to handle basic interactions (create position, deposit/withdraw, add/remove collateral, and borrow/repay) via multicall to InitCore in a single transaction. Money market hook stores its running position id for each user, starting from 1, when a user creates a new position via the contract.

The interaction flow consists of:

  1. Create a position on the hook and InitCore, if not existed

  2. Perform multicall to InitCore, which performs:

    1. Decollateralize inToken from the position and redeem token in lending pool

    2. Withdraw from lending pool

    3. Change position mode, if specified

    4. Borrow tokens from lending pool

    5. Mint inToken from lending pool and collateralize to the position

  3. Unwrap rebase tokens, if specified

  4. Unwrap wrapped native token to native token, if specified

The interaction is done via execute function.

struct OperationParams {
    uint posId; //  position id to execute (0 to create new position)
    address viewer; // address to view position
    uint16 mode; // position mode to be used
    DepositParams[] depositParams; // deposit parameters
    WithdrawParams[] withdrawParams; // withdraw parameters
    BorrowParams[] borrowParams; // borrow parameters
    RepayParams[] repayParams; // repay parameters
    uint minHealth_e18; // minimum health to maintain after execute
    bool returnNative; // return native token or not (using balanceOf(address(this)))
}

function execute(OperationParams calldata _params)
    external
    payable
    nonReentrant
    returns (uint posId, uint initPosId, bytes[] memory results)
{
    // create position if not exist
    if (_params.posId == 0) {
        (posId, initPosId) = createPos(_params.mode, _params.viewer);
    } else {
        // for existing position, only owner can execute
        posId = _params.posId;
        initPosId = initPosIds[msg.sender][posId];
        _require(IERC721(POS_MANAGER).ownerOf(initPosId) == address(this), Errors.NOT_OWNER);
    }
    results = _handleMulticall(initPosId, _params);
    // check slippage
    _require(_params.minHealth_e18 <= IInitCore(CORE).getPosHealthCurrent_e18(initPosId), Errors.SLIPPAGE_CONTROL);
    // unwrap token if needed
    for (uint i; i < _params.withdrawParams.length; i = i.uinc()) {
        address helper = _params.withdrawParams[i].rebaseHelperParams.helper;
        if (helper != address(0)) IRebaseHelper(helper).unwrap(_params.withdrawParams[i].to);
    }
    // return native token
    if (_params.returnNative) {
        uint wNativeBal = IERC20(WNATIVE).balanceOf(address(this));
        // NOTE: no need receive function since we will use TransparentUpgradeableProxyReceiveETH
        if (wNativeBal != 0) IWNative(WNATIVE).withdraw(wNativeBal);
        uint nativeBal = address(this).balance;
        if (nativeBal != 0) {
            (bool success,) = payable(msg.sender).call{value: address(this).balance}('');
            _require(success, Errors.CALL_FAILED);
        }
    }
}

Last updated