Robinhood MainnetChain ID: 4663
Contract detail

NvdaUsdgV4VaultV6

NvdaUsdgV4VaultV6 is a verified Robinhood Chain smart contract at 0x0059f82a…bb3dfd6e with 2786 indexed transactions.

Address type
Smart contract
Transactions
2786
First indexed block
#17248569
Latest indexed block
#62421896
Native balance
0 ETH
Balance observed at
Block #31581901
Indexed token transfers
338
ERC-20 transfers
338
Emitted events
2976
Verification
Verified
Compiler
0.8.26+commit.8a97fa7a
Verification match
exact
Creation transaction
0x07532fe0…0336e9d0
Created at block
#17248569
Language
Solidity
EVM version
cancun
License
BUSL-1.1
Compiler optimization
Enabled · 200 runs
Deployed bytecode hash
0x5a0875f0fc4ad209ccc7e5e6f62627b5e85b942e6c67267d1bdf04a02d46d5ed
Related activity

Transactions

Verified contract

Contract source code

// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.26;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";

import {NvdaUsdgV4VaultV5} from "./NvdaUsdgV4VaultV5.sol";
import {V4VaultMath} from "./libraries/V4VaultMath.sol";

/// @notice Value-accounted NVDA/USDG vault whose accepted deposits are fully deployed to the active range.
/// @dev Existing idle reserves remain part of shareholder NAV, but do not dictate the ratio of new deposits.
///      The inherited owner-only idle-reserve swap remains available only while public deposits are paused.
contract NvdaUsdgV4VaultV6 is NvdaUsdgV4VaultV5 {
    using SafeERC20 for IERC20;

    uint256 private constant Q96 = 1 << 96;

    struct DepositRequest {
        uint256 amount0Max;
        uint256 amount1Max;
        uint256 minimumShares;
        address receiver;
        uint256 deadline;
    }

    struct DepositCache {
        uint256 supply;
        int24 valuationTick;
        uint256 totalValue0;
        uint128 liquidityAdded;
        uint256 base0;
        uint256 base1;
        uint256 contributionValue0;
        uint256 refund0;
        uint256 refund1;
    }

    error TransferMismatch();

    event ActiveDeposit(
        address indexed caller,
        address indexed receiver,
        uint256 shares,
        uint128 liquidityAdded,
        uint256 deployed0,
        uint256 deployed1,
        uint256 refunded0,
        uint256 refunded1,
        uint256 contributionValue0
    );

    constructor(
        address initialOwner,
        address initialFeeRecipient,
        address initialPriceUpdater,
        int24 initialReferenceTick,
        uint24 initialMaxTickDeviation,
        uint32 initialMaxReferenceAge,
        uint128 initialMaxTotalLiquidity
    )
        NvdaUsdgV4VaultV5(
            initialOwner,
            initialFeeRecipient,
            initialPriceUpdater,
            initialReferenceTick,
            initialMaxTickDeviation,
            initialMaxReferenceAge,
            initialMaxTotalLiquidity
        )
    {}

    function name() public pure override returns (string memory) {
        return "NVDA-USDG Vault Test V6";
    }

    function symbol() public pure override returns (string memory) {
        return "NVDA-USDG-T6";
    }

    /// @notice Returns the token amounts represented by the current active position at live pool spot.
    function activeUnderlyingAtSpot() public view returns (uint256 amount0, uint256 amount1) {
        if (managedLiquidity == 0) return (0, 0);
        (uint160 sqrtPriceX96,,,) = stateView.getSlot0(POOL_ID);
        return V4VaultMath.getAmountsForLiquidity(
            sqrtPriceX96,
            V4VaultMath.getSqrtPriceAtTick(tickLower),
            V4VaultMath.getSqrtPriceAtTick(tickUpper),
            managedLiquidity
        );
    }

    /// @notice Previews the maximum active liquidity and exact token use for a two-token deposit.
    function previewActiveDeposit(uint256 amount0Max, uint256 amount1Max)
        public
        view
        returns (uint128 liquidity, uint256 used0, uint256 used1)
    {
        if (managedLiquidity == 0) return (0, 0, 0);
        (uint160 sqrtPriceX96,,,) = stateView.getSlot0(POOL_ID);
        liquidity = V4VaultMath.getLiquidityForAmounts(
            sqrtPriceX96,
            V4VaultMath.getSqrtPriceAtTick(tickLower),
            V4VaultMath.getSqrtPriceAtTick(tickUpper),
            amount0Max,
            amount1Max
        );
        if (liquidity == 0) return (0, 0, 0);
        (used0, used1) = V4VaultMath.getAmountsForLiquidity(
            sqrtPriceX96,
            V4VaultMath.getSqrtPriceAtTick(tickLower),
            V4VaultMath.getSqrtPriceAtTick(tickUpper),
            liquidity
        );
    }

    /// @notice Bootstraps only the exact token amounts consumed by the first active range.
    function bootstrap(
        uint256 amount0Max,
        uint256 amount1Max,
        int24 lower,
        int24 upper,
        uint128 minimumLiquidity,
        address receiver,
        uint256 deadline
    ) external override onlyOwner nonReentrant returns (uint128 liquidity, uint256 used0, uint256 used1) {
        _checkDeadline(deadline);
        _requirePriceGuard();
        if (totalSupply() != 0) revert AlreadyInitialized();
        if (receiver == address(0)) revert InvalidAddress();
        if (amount0Max == 0 && amount1Max == 0) revert ZeroAmount();
        V4VaultMath.validateTicks(lower, upper, TICK_SPACING);

        uint256 base0 = token0.balanceOf(address(this));
        uint256 base1 = token1.balanceOf(address(this));
        token0.safeTransferFrom(msg.sender, address(this), amount0Max);
        token1.safeTransferFrom(msg.sender, address(this), amount1Max);
        _requireExactReceipts(base0, base1, amount0Max, amount1Max);

        tickLower = lower;
        tickUpper = upper;
        liquidity = _liquidityForBalances(amount0Max, amount1Max, lower, upper);
        if (liquidity < minimumLiquidity || liquidity <= MINIMUM_SHARES) revert ZeroLiquidity();
        if (liquidity > maxTotalLiquidity) revert CapExceeded();
        (used0, used1,) = _modifyLiquidity(int256(uint256(liquidity)), lower, upper);
        if (used0 > amount0Max || used1 > amount1Max) revert TransferMismatch();
        managedLiquidity = liquidity;

        uint256 refund0 = amount0Max - used0;
        uint256 refund1 = amount1Max - used1;
        if (refund0 > 0) token0.safeTransfer(msg.sender, refund0);
        if (refund1 > 0) token1.safeTransfer(msg.sender, refund1);

        _mint(address(0xdead), MINIMUM_SHARES);
        _mint(receiver, uint256(liquidity) - MINIMUM_SHARES);
        emit Bootstrapped(receiver, liquidity, liquidity, lower, upper);
        emit ActiveDeposit(
            msg.sender,
            receiver,
            liquidity,
            liquidity,
            used0,
            used1,
            refund0,
            refund1,
            _valueInToken0(used0, used1, _trustedTick())
        );
    }

    /// @notice Accepts only the exact amounts the active range can consume and values shares against total NAV.
    /// @dev Existing idle reserves remain untouched. Any maxima not used by the active range are never retained.
    function deposit(
        uint256 amount0Max,
        uint256 amount1Max,
        uint256 minimumShares,
        address receiver,
        uint256 deadline
    ) external override nonReentrant returns (uint256 shares, uint256 used0, uint256 used1) {
        return _deposit(DepositRequest(amount0Max, amount1Max, minimumShares, receiver, deadline));
    }

    function _deposit(DepositRequest memory request)
        internal
        returns (uint256 shares, uint256 used0, uint256 used1)
    {
        _checkDeadline(request.deadline);
        if (!depositsEnabled) revert DepositsDisabled();
        _requirePriceGuard();
        if (request.receiver == address(0)) revert InvalidAddress();
        if (totalSupply() == 0 || managedLiquidity == 0) revert NotInitialized();
        if (request.amount0Max == 0 && request.amount1Max == 0) revert ZeroAmount();

        (uint256 netFee0, uint256 netFee1) = _collectAndChargeFees();
        _compoundAmounts(netFee0, netFee1, false);

        DepositCache memory cache;
        cache.supply = totalSupply();
        cache.valuationTick = _trustedTick();
        (uint256 total0, uint256 total1) = totalUnderlyingAtReference();
        cache.totalValue0 = _valueInToken0(total0, total1, cache.valuationTick);
        if (cache.totalValue0 == 0) revert NotInitialized();

        (cache.liquidityAdded,,) = previewActiveDeposit(request.amount0Max, request.amount1Max);
        if (cache.liquidityAdded == 0) revert ZeroLiquidity();
        if (uint256(managedLiquidity) + cache.liquidityAdded > maxTotalLiquidity) revert CapExceeded();

        cache.base0 = token0.balanceOf(address(this));
        cache.base1 = token1.balanceOf(address(this));
        token0.safeTransferFrom(msg.sender, address(this), request.amount0Max);
        token1.safeTransferFrom(msg.sender, address(this), request.amount1Max);
        _requireExactReceipts(cache.base0, cache.base1, request.amount0Max, request.amount1Max);

        (used0, used1) = _addManagedLiquidity(cache.liquidityAdded);
        if (used0 > request.amount0Max || used1 > request.amount1Max) revert TransferMismatch();

        cache.contributionValue0 = _valueInToken0(used0, used1, cache.valuationTick);
        shares = Math.mulDiv(cache.contributionValue0, cache.supply, cache.totalValue0);
        if (shares < request.minimumShares || shares == 0) revert Slippage();

        cache.refund0 = request.amount0Max - used0;
        cache.refund1 = request.amount1Max - used1;
        if (cache.refund0 > 0) token0.safeTransfer(msg.sender, cache.refund0);
        if (cache.refund1 > 0) token1.safeTransfer(msg.sender, cache.refund1);
        _mint(request.receiver, shares);

        emit Deposit(msg.sender, request.receiver, used0, used1, shares);
        emit DepositLiquidityAdded(
            msg.sender, request.receiver, shares, cache.liquidityAdded, used0, used1, used0, used1
        );
        emit ActiveDeposit(
            msg.sender,
            request.receiver,
            shares,
            cache.liquidityAdded,
            used0,
            used1,
            cache.refund0,
            cache.refund1,
            cache.contributionValue0
        );
    }

    function _valueInToken0(uint256 amount0, uint256 amount1, int24 tick) internal pure returns (uint256 value0) {
        uint160 sqrtPriceX96 = V4VaultMath.getSqrtPriceAtTick(tick);
        uint256 token1Value0 = Math.mulDiv(amount1, Q96, sqrtPriceX96);
        token1Value0 = Math.mulDiv(token1Value0, Q96, sqrtPriceX96);
        value0 = amount0 + token1Value0;
    }

    function _requireExactReceipts(uint256 base0, uint256 base1, uint256 expected0, uint256 expected1)
        internal
        view
    {
        if (
            token0.balanceOf(address(this)) != base0 + expected0
                || token1.balanceOf(address(this)) != base1 + expected1
        ) revert TransferMismatch();
    }
}