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
- Token tracker
- NVDA-USDG Vault Test V6 (NVDA-USDG-T6)
- 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();
}
}