Description:
Proxy contract enabling upgradeable smart contract patterns. Delegates calls to an implementation contract.
Blockchain: Ethereum
Source Code: View Code On The Blockchain
Solidity Source Code:
{{
"language": "Solidity",
"sources": {
"hopium/etf/main/etf-vault.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.30;
import "hopium/common/interface/imDirectory.sol";
import "hopium/etf/interface/imEtfRouter.sol";
interface IERC20 {
function transfer(address to, uint256 value) external returns (bool);
function balanceOf(address account) external view returns (uint256);
}
contract TransferHelpers {
error ZeroAmount();
error EthSendFailed();
function _sendEth(address to, uint256 amount) internal {
if (amount == 0) revert ZeroAmount();
(bool ok, ) = payable(to).call{value: amount}("");
if (!ok) revert EthSendFailed();
}
function _sendToken(address tokenAddress, address toAddress, uint256 amount) internal {
if (amount == 0) revert ZeroAmount();
IERC20(tokenAddress).transfer(toAddress, amount);
}
function _sendEthOrToken(address tokenAddress, address toAddress) internal {
if (tokenAddress == address(0)) {
_sendEth(toAddress, address(this).balance);
} else {
_sendToken(tokenAddress, toAddress, IERC20(tokenAddress).balanceOf(address(this)));
}
}
function _recoverAsset(address tokenAddress, address toAddress) internal {
_sendEthOrToken(tokenAddress, toAddress);
}
}
/// @notice Etf Vault implementation (logic). Clones delegatecall into this.
/// @dev Assumes ImEtfRouter -> ImDirectory for access control & Directory wiring.
contract EtfVault is ImEtfRouter, TransferHelpers {
bool private _initialized;
/// @notice Constructor runs once for the implementation only.
/// @dev You can pass an existing Directory here; clones still call initialize().
constructor(address _directory) ImDirectory(_directory) {}
error AlreadyInitialized();
/// @notice One-time initializer for clones (constructors don't run for proxies)
function initialize(address _directory) external {
if (_initialized) revert AlreadyInitialized();
_initialized = true;
_setDirectory(_directory); // from ImDirectory (no modifier)
}
/// @notice Redeem underlying tokens to receiver; callable only by EtfRouter
function redeem(address tokenAddress, uint256 amount, address receiver) external onlyEtfRouter {
_sendToken(tokenAddress, receiver, amount);
}
/// @notice Owner (Directory.owner()) can recover stuck ETH/ERC20
function recoverAsset(address tokenAddress, address toAddress) external onlyOwner {
_recoverAsset(tokenAddress, toAddress);
}
receive() external payable {}
}
"
},
"hopium/etf/interface/imEtfRouter.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.30;
import "hopium/common/interface/imDirectory.sol";
interface IEtfRouter {
function mintEtfTokens(uint256 etfId, address receiver, string calldata affiliateCode) external payable;
}
abstract contract ImEtfRouter is ImDirectory {
function getEtfRouter() internal view virtual returns (IEtfRouter) {
return IEtfRouter(fetchFromDirectory("etf-router"));
}
modifier onlyEtfRouter() {
require(msg.sender == fetchFromDirectory("etf-router"), "msg.sender is not etf router");
_;
}
}"
},
"hopium/common/interface/imDirectory.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.30;
/// @notice Interface used by the registry to talk to the external directory.
interface IDirectory {
function owner() external view returns (address);
function fetchFromDirectory(string memory _key) external view returns (address);
}
abstract contract ImDirectory {
IDirectory public Directory;
constructor(address _directory) {
_setDirectory(_directory); // no modifier here
}
function changeDirectoryAddress(address _directory) external onlyOwner {
_setDirectory(_directory);
}
function _setDirectory(address _directory) internal {
require(_directory != address(0), "Directory cannot be zero address");
require(_directory.code.length > 0, "Directory must be a contract");
// Sanity check the interface
try IDirectory(_directory).owner() returns (address) {
Directory = IDirectory(_directory);
} catch {
revert("Directory address does not implement owner()");
}
}
modifier onlyOwner() {
require(msg.sender == Directory.owner(), "Caller is not the owner");
_;
}
function owner() public view returns (address) {
return Directory.owner();
}
function fetchFromDirectory(string memory _key) public view returns (address) {
return Directory.fetchFromDirectory(_key);
}
}
"
}
},
"settings": {
"optimizer": {
"enabled": true,
"runs": 200
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"remappings": []
}
}}
Submitted on: 2025-10-27 13:14:56
Comments
Log in to comment.
No comments yet.