Skip to content

Instantly share code, notes, and snippets.

@rfikki
Created May 15, 2026 23:07
Show Gist options
  • Select an option

  • Save rfikki/649c683bc2475bb45932e6cfa27232a8 to your computer and use it in GitHub Desktop.

Select an option

Save rfikki/649c683bc2475bb45932e6cfa27232a8 to your computer and use it in GitHub Desktop.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
/**
* @title Dinosaur Amber Vault
* @notice Locks exactly 100 DINO to mint 1 dynamic Dinosaur NFT.
* The NFT acts as a physical receipt for the 100-token lot.
*/
contract DinosaurVault is ERC721, ReentrancyGuard {
IERC20 public immutable dinoToken;
uint256 public constant TOKENS_PER_NFT = 100 * 10**18;
uint256 private _nextId = 1;
// Events
event Incubated(address indexed user, uint256 indexed tokenId);
event Shattered(address indexed user, uint256 indexed tokenId);
constructor(address _dinoToken) ERC721("Dinosaur Vault", "DV-NFT") {
dinoToken = IERC20(_dinoToken);
}
/**
* @notice Locks 100 DINO and mints 1 NFT.
* @dev User must have called approve() on the DINO contract first.
*/
function incubate() external nonReentrant {
// Pull 100 DINO from the user and lock it in this contract
require(dinoToken.transferFrom(msg.sender, address(this), TOKENS_PER_NFT), "Transfer failed");
uint256 tokenId = _nextId++;
_mint(msg.sender, tokenId);
emit Incubated(msg.sender, tokenId);
}
/**
* @notice Burns the NFT and returns the 100 DINO lot to the owner.
*/
function shatter(uint256 tokenId) external nonReentrant {
// Ensure only the owner can unlock the lot
require(ownerOf(tokenId) == msg.sender, "Not the owner");
_burn(tokenId);
// Return the 100 DINO back to the user
require(dinoToken.transfer(msg.sender, TOKENS_PER_NFT), "Transfer failed");
emit Shattered(msg.sender, tokenId);
}
/**
* @notice This function would interact with your Uniswap V4 Hook
* to render the Upeg-style liquid metadata based on the current pool state.
*/
function tokenURI(uint256 tokenId) public view override returns (string memory) {
_requireOwned(tokenId);
// Here you will route to the Upeg SVG rendering logic
// which reads the Uniswap V4 Hook state.
return "data:application/json;base64,...";
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment