Blockchain / Global / Address
Contract
0x5A910fA5346b8Cdd0402b38fB159fe0729D2C2Da
- Balance
- 0 SOMI
- Transactions sent
- 1
- Type
- Contract · 4,757 bytes of code
ForgeHybrid Verified
- Compiler
- solc 0.8.28+commit.7893614a
- Language
- Solidity
- Optimization
- Yes, 200 runs
- EVM version
- cancun
- License
- —
- Verified
- 2026-10-12
SOMNIAForgeHybridtestV2.sol
Solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
/// @title ForgeHybrid - Burn Bronze ERC-1155 to get Gold ERC-721
/// @notice Burns 5 Bronze tokens (ERC-1155) and transfers 1 Gold token (ERC-721) from reserve
// Minimal ERC-1155 interface for Bronze
interface IERC1155 {
function balanceOf(address account, uint256 id) external view returns (uint256);
function safeBatchTransferFrom(
address from,
address to,
uint256[] calldata ids,
uint256[] calldata amounts,
bytes calldata data
) external;
function setApprovalForAll(address operator, bool approved) external;
function isApprovedForAll(address account, address operator) external view returns (bool);
}
// Minimal ERC-721 interface for Gold
interface IERC721 {
function ownerOf(uint256 tokenId) external view returns (address);
function transferFrom(address from, address to, uint256 tokenId) external;
function setApprovalForAll(address operator, bool approved) external;
function isApprovedForAll(address owner, address operator) external view returns (bool);
}
contract ForgeHybrid {
// Errors
error Unauthorized(address caller);
error InsufficientBronzeBalance(uint256 required, uint256 available);
error NoGoldAvailable();
error ContractPaused();
error InvalidAddress();
error Reentrancy();
error BronzeNotApproved();
error GoldNotInReserve(uint256 tokenId);
error InvalidTokenIdRange();
// Events
event Forged(address indexed user, uint256 bronzesBurned, uint256 goldTokenId, uint256 feePaid);
event ParamsUpdated(uint256 bronzePerGold, uint256 feeWei, address feeRecipient);
event OwnerUpdated(address indexed newOwner);
event Paused(bool paused);
event ReserveWalletUpdated(address indexed newReserveWallet);
event GoldTokenIdRangeUpdated(uint256 startId, uint256 endId);
// Immutable contract addresses
IERC1155 public immutable bronzeContract;
IERC721 public immutable goldContract;
uint256 public immutable BRONZE_TOKEN_ID;
// Admin/Config
address public owner;
address public reserveWallet; // Wallet holding the Gold NFTs
uint256 public bronzePerGold; // How many Bronze needed for 1 Gold (default: 5)
uint256 public feeWei; // Optional fee in native token
address public feeRecipient;
bool public paused;
// Gold Token ID range (which Gold NFTs are available for forging)
uint256 public goldStartTokenId; // e.g., 30
uint256 public goldEndTokenId; // e.g., 49
uint256 public nextGoldTokenId; // Tracks which Gold to give next
// Reentrancy guard
uint256 private _status;
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
modifier onlyOwner() {
if (msg.sender != owner) revert Unauthorized(msg.sender);
_;
}
modifier whenNotPaused() {
if (paused) revert ContractPaused();
_;
}
modifier nonReentrant() {
if (_status == _ENTERED) revert Reentrancy();
_status = _ENTERED;
_;
_status = _NOT_ENTERED;
}
constructor(
address _bronzeContract,
address _goldContract,
uint256 _bronzeTokenId,
uint256 _bronzePerGold,
address _reserveWallet,
address _feeRecipient,
uint256 _goldStartTokenId,
uint256 _goldEndTokenId
) {
if (_bronzeContract == address(0)) revert InvalidAddress();
if (_goldContract == address(0)) revert InvalidAddress();
if (_reserveWallet == address(0)) revert InvalidAddress();
if (_feeRecipient == address(0)) revert InvalidAddress();
if (_bronzePerGold == 0) revert InvalidTokenIdRange();
if (_goldEndTokenId < _goldStartTokenId) revert InvalidTokenIdRange();
owner = msg.sender;
bronzeContract = IERC1155(_bronzeContract);
goldContract = IERC721(_goldContract);
BRONZE_TOKEN_ID = _bronzeTokenId;
bronzePerGold = _bronzePerGold;
reserveWallet = _reserveWallet;
feeRecipient = _feeRecipient;
feeWei = 0;
paused = false;
_status = _NOT_ENTERED;
goldStartTokenId = _goldStartTokenId;
goldEndTokenId = _goldEndTokenId;
nextGoldTokenId = _goldStartTokenId;
}
/// @notice Burn Bronze tokens to receive 1 Gold token
function forge() external payable nonReentrant whenNotPaused {
// Check fee if required
if (feeWei > 0) {
require(msg.value == feeWei, "Incorrect fee");
}
// Check user has enough Bronze
uint256 userBronze = bronzeContract.balanceOf(msg.sender, BRONZE_TOKEN_ID);
if (userBronze < bronzePerGold) {
revert InsufficientBronzeBalance(bronzePerGold, userBronze);
}
// Check if user approved this contract to transfer their Bronze
if (!bronzeContract.isApprovedForAll(msg.sender, address(this))) {
revert BronzeNotApproved();
}
// Check if there are Gold tokens available
if (nextGoldTokenId > goldEndTokenId) {
revert NoGoldAvailable();
}
// Get the next Gold token ID to transfer
uint256 goldTokenId = nextGoldTokenId;
nextGoldTokenId++;
// Verify the Gold token is in reserve wallet
address goldOwner = goldContract.ownerOf(goldTokenId);
if (goldOwner != reserveWallet) {
revert GoldNotInReserve(goldTokenId);
}
// Verify reserve wallet approved this contract
if (!goldContract.isApprovedForAll(reserveWallet, address(this))) {
revert GoldNotInReserve(goldTokenId);
}
// Burn Bronze from user (transfer to 0x000...dead)
uint256[] memory ids = new uint256[](1);
uint256[] memory amounts = new uint256[](1);
ids[0] = BRONZE_TOKEN_ID;
amounts[0] = bronzePerGold;
bronzeContract.safeBatchTransferFrom(
msg.sender,
0x000000000000000000000000000000000000dEaD,
ids,
amounts,
""
);
// Transfer Gold from reserve to user
goldContract.transferFrom(reserveWallet, msg.sender, goldTokenId);
emit Forged(msg.sender, bronzePerGold, goldTokenId, feeWei);
}
// Admin functions
function setParams(
uint256 _bronzePerGold,
uint256 _feeWei,
address _feeRecipient
) external onlyOwner {
if (_bronzePerGold == 0) revert InvalidTokenIdRange();
if (_feeRecipient == address(0)) revert InvalidAddress();
bronzePerGold = _bronzePerGold;
feeWei = _feeWei;
feeRecipient = _feeRecipient;
emit ParamsUpdated(_bronzePerGold, _feeWei, _feeRecipient);
}
function setReserveWallet(address _reserveWallet) external onlyOwner {
if (_reserveWallet == address(0)) revert InvalidAddress();
reserveWallet = _reserveWallet;
emit ReserveWalletUpdated(_reserveWallet);
}
function setGoldTokenIdRange(uint256 _startId, uint256 _endId) external onlyOwner {
if (_endId < _startId) revert InvalidTokenIdRange();
goldStartTokenId = _startId;
goldEndTokenId = _endId;
// Reset counter if needed
if (nextGoldTokenId < _startId) {
nextGoldTokenId = _startId;
}
emit GoldTokenIdRangeUpdated(_startId, _endId);
}
function setPaused(bool _paused) external onlyOwner {
paused = _paused;
emit Paused(_paused);
}
function setOwner(address _newOwner) external onlyOwner {
if (_newOwner == address(0)) revert InvalidAddress();
owner = _newOwner;
emit OwnerUpdated(_newOwner);
}
function withdrawFees() external {
if (msg.sender != owner && msg.sender != feeRecipient) {
revert Unauthorized(msg.sender);
}
uint256 bal = address(this).balance;
require(bal > 0, "No fees");
(bool ok, ) = payable(feeRecipient).call{value: bal}("");
require(ok, "Transfer failed");
}
// View functions
function canForge(address user) external view returns (bool) {
if (paused) return false;
if (nextGoldTokenId > goldEndTokenId) return false;
if (bronzeContract.balanceOf(user, BRONZE_TOKEN_ID) < bronzePerGold) return false;
if (!bronzeContract.isApprovedForAll(user, address(this))) return false;
return true;
}
function getAvailableGoldCount() external view returns (uint256) {
if (nextGoldTokenId > goldEndTokenId) return 0;
return goldEndTokenId - nextGoldTokenId + 1;
}
function getStats() external view returns (
uint256 _bronzePerGold,
uint256 _feeWei,
uint256 _availableGold,
uint256 _nextGoldId,
bool _paused
) {
uint256 available = 0;
if (nextGoldTokenId <= goldEndTokenId) {
available = goldEndTokenId - nextGoldTokenId + 1;
}
return (bronzePerGold, feeWei, available, nextGoldTokenId, paused);
}
receive() external payable {}
}ABI · 39 entries
json
[
{
"type": "constructor",
"inputs": [
{
"name": "_bronzeContract",
"type": "address",
"internalType": "address"
},
{
"name": "_goldContract",
"type": "address",
"internalType": "address"
},
{
"name": "_bronzeTokenId",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "_bronzePerGold",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "_reserveWallet",
"type": "address",
"internalType": "address"
},
{
"name": "_feeRecipient",
"type": "address",
"internalType": "address"
},
{
"name": "_goldStartTokenId",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "_goldEndTokenId",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "nonpayable"
},
{
"name": "BronzeNotApproved",
"type": "error",
"inputs": []
},
{
"name": "ContractPaused",
"type": "error",
"inputs": []
},
{
"name": "GoldNotInReserve",
"type": "error",
"inputs": [
{
"name": "tokenId",
"type": "uint256",
"internalType": "uint256"
}
]
},
{
"name": "InsufficientBronzeBalance",
"type": "error",
"inputs": [
{
"name": "required",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "available",
"type": "uint256",
"internalType": "uint256"
}
]
},
{
"name": "InvalidAddress",
"type": "error",
"inputs": []
},
{
"name": "InvalidTokenIdRange",
"type": "error",
"inputs": []
},
{
"name": "NoGoldAvailable",
"type": "error",
"inputs": []
},
{
"name": "Reentrancy",
"type": "error",
"inputs": []
},
{
"name": "Unauthorized",
"type": "error",
"inputs": [
{
"name": "caller",
"type": "address",
"internalType": "address"
}
]
},
{
"name": "Forged",
"type": "event",
"inputs": [
{
"name": "user",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "bronzesBurned",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
},
{
"name": "goldTokenId",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
},
{
"name": "feePaid",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
}
],
"anonymous": false
},
{
"name": "GoldTokenIdRangeUpdated",
"type": "event",
"inputs": [
{
"name": "startId",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
},
{
"name": "endId",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
}
],
"anonymous": false
},
{
"name": "OwnerUpdated",
"type": "event",
"inputs": [
{
"name": "newOwner",
"type": "address",
"indexed": true,
"internalType": "address"
}
],
"anonymous": false
},
{
"name": "ParamsUpdated",
"type": "event",
"inputs": [
{
"name": "bronzePerGold",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
},
{
"name": "feeWei",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
},
{
"name": "feeRecipient",
"type": "address",
"indexed": false,
"internalType": "address"
}
],
"anonymous": false
},
{
"name": "Paused",
"type": "event",
"inputs": [
{
"name": "paused",
"type": "bool",
"indexed": false,
"internalType": "bool"
}
],
"anonymous": false
},
{
"name": "ReserveWalletUpdated",
"type": "event",
"inputs": [
{
"name": "newReserveWallet",
"type": "address",
"indexed": true,
"internalType": "address"
}
],
"anonymous": false
},
{
"name": "BRONZE_TOKEN_ID",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"name": "bronzeContract",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "address",
"internalType": "contract IERC1155"
}
],
"stateMutability": "view"
},
{
"name": "bronzePerGold",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"name": "canForge",
"type": "function",
"inputs": [
{
"name": "user",
"type": "address",
"internalType": "address"
}
],
"outputs": [
{
"name": "",
"type": "bool",
"internalType": "bool"
}
],
"stateMutability": "view"
},
{
"name": "feeRecipient",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "address",
"internalType": "address"
}
],
"stateMutability": "view"
},
{
"name": "feeWei",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"name": "forge",
"type": "function",
"inputs": [],
"outputs": [],
"stateMutability": "payable"
},
{
"name": "getAvailableGoldCount",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"name": "getStats",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "_bronzePerGold",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "_feeWei",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "_availableGold",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "_nextGoldId",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "_paused",
"type": "bool",
"internalType": "bool"
}
],
"stateMutability": "view"
},
{
"name": "goldContract",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "address",
"internalType": "contract IERC721"
}
],
"stateMutability": "view"
},
{
"name": "goldEndTokenId",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"name": "goldStartTokenId",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"name": "nextGoldTokenId",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"name": "owner",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "address",
"internalType": "address"
}
],
"stateMutability": "view"
},
{
"name": "paused",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "bool",
"internalType": "bool"
}
],
"stateMutability": "view"
},
{
"name": "reserveWallet",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "address",
"internalType": "address"
}
],
"stateMutability": "view"
},
{
"name": "setGoldTokenIdRange",
"type": "function",
"inputs": [
{
"name": "_startId",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "_endId",
"type": "uint256",
"internalType": "uint256"
}
],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"name": "setOwner",
"type": "function",
"inputs": [
{
"name": "_newOwner",
"type": "address",
"internalType": "address"
}
],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"name": "setParams",
"type": "function",
"inputs": [
{
"name": "_bronzePerGold",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "_feeWei",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "_feeRecipient",
"type": "address",
"internalType": "address"
}
],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"name": "setPaused",
"type": "function",
"inputs": [
{
"name": "_paused",
"type": "bool",
"internalType": "bool"
}
],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"name": "setReserveWallet",
"type": "function",
"inputs": [
{
"name": "_reserveWallet",
"type": "address",
"internalType": "address"
}
],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"name": "withdrawFees",
"type": "function",
"inputs": [],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"type": "receive",
"stateMutability": "payable"
}
]