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": {
"safe.sol": {
"content": "// tesnet 0xe7B8730DD5cADAd3ec3E3C7E92510642237D49cc\r
\r
// SPDX-License-Identifier: MIT\r
pragma solidity ^0.8.24;\r
\r
abstract contract UUPSUpgradeable {\r
bytes32 private constant IMPLEMENTATION_SLOT =\r
0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;\r
\r
function upgradeTo(address newImplementation) external virtual {\r
_authorizeUpgrade(newImplementation);\r
_setImplementation(newImplementation);\r
}\r
\r
function _setImplementation(address newImpl) internal {\r
require(newImpl != address(0), "bad impl");\r
bytes32 slot = IMPLEMENTATION_SLOT;\r
assembly {\r
sstore(slot, newImpl)\r
}\r
}\r
\r
function _authorizeUpgrade(address newImplementation) internal virtual;\r
}\r
\r
/**\r
* @title SafeTableV5 — With variable fee and encryptedPhones support + getBySafeAddress\r
* @notice Fee-gated registry of "safe" rows. Anyone can insert/update rows by paying at least the configured fee per call.\r
* Collected ETH can be withdrawn only by a fixed treasury address.\r
* \r
* V3 Changes vs V2:\r
* - Replaced constant fee with storage variable `fee`\r
* - Default fee set to 0.1 ether in `initialize`\r
* - Added `setFee()` (treasury-only) and public getter via `fee` variable\r
* - Added row ownership (consumes one more slot)\r
* - Added `getOwner()` and `transferOwner()` (treasury-only)\r
*\r
* V5 Changes vs previous:\r
* - `rows` mapping is now keyed by `safe_address`: `mapping(address => Row)`.\r
* - Added `getBySafeAddress(address)` to fetch without owner context.\r
* - Legacy functions `get(owner, safe_address)` and `hasRow(owner, safe_address)` preserve behavior by\r
* returning data only when `rows[safe_address].owner == owner`.\r
*/\r
contract SafeTableV5 is UUPSUpgradeable {\r
// --- Minimal Initializable (proxy-safe) ---\r
bool private _initialized;\r
bool private _initializing;\r
modifier initializer() {\r
require(!_initialized || _initializing, "already initialized");\r
bool isTopLevel = !_initializing;\r
if (isTopLevel) {\r
_initializing = true;\r
_initialized = true;\r
}\r
_;\r
if (isTopLevel) {\r
_initializing = false;\r
}\r
}\r
\r
modifier whenInitialized() {\r
require(_initialized && !_initializing, "not initialized");\r
_;\r
}\r
\r
// --- Minimal ReentrancyGuard ---\r
uint256 private constant _NOT_ENTERED = 1;\r
uint256 private constant _ENTERED = 2;\r
uint256 private _reentrancyStatus = _NOT_ENTERED;\r
modifier nonReentrant() {\r
require(_reentrancyStatus != _ENTERED, "reentrancy");\r
_reentrancyStatus = _ENTERED;\r
_;\r
_reentrancyStatus = _NOT_ENTERED;\r
}\r
\r
// --- Fee/Treasury config ---\r
address public treasury;\r
\r
/// @notice Initializer to replace constructor for proxies.\r
function initialize(address _treasury) public initializer {\r
require(_treasury != address(0), "treasury=0");\r
treasury = _treasury;\r
fee = 0.1 ether;\r
}\r
\r
constructor() {\r
_initialized = true;\r
_reentrancyStatus = _NOT_ENTERED;\r
}\r
\r
struct Row {\r
address owner;\r
address safe_address;\r
uint8 waiting_period;\r
bool death_certificate;\r
address[] attesters;\r
string[] encryptedPhones;\r
uint256 feePaid;\r
uint64 createdAt;\r
}\r
\r
// rows[safe_address] => Row\r
mapping(address => Row) private rows;\r
// enumeration of safes per owner\r
mapping(address => address[]) private ownerSafes;\r
\r
uint256 public totalSafes;\r
\r
// Configurable fee\r
uint256 public fee; // default 0.1 ether\r
\r
\r
// --- Events ---\r
event RowInserted(\r
address indexed owner,\r
address indexed safe_address,\r
uint8 waiting_period,\r
bool death_certificate,\r
address[] attesters,\r
string[] encryptedPhones,\r
uint64 createdAt\r
);\r
event RowUpdated(\r
address indexed owner,\r
address indexed safe_address,\r
uint8 waiting_period,\r
bool death_certificate,\r
address[] attesters,\r
string[] encryptedPhones\r
);\r
event FeeCollected(address indexed payer, uint256 amount);\r
event Withdrawn(address indexed to, uint256 amount);\r
event TreasuryChanged(address indexed oldTreasury, address indexed newTreasury);\r
event FeeChanged(uint256 oldFee, uint256 newFee);\r
\r
// --- External API ---\r
\r
/// @notice Insert a new row with encrypted contact info (requires >= fee; excess kept in contract).\r
function insert(\r
address safe_address,\r
uint8 waiting_period,\r
bool death_certificate,\r
address[] calldata attesters,\r
string[] calldata encryptedPhones\r
) external payable whenInitialized {\r
Row storage r = rows[safe_address];\r
require(r.createdAt == 0, "exists");\r
require(msg.value >= fee, "fee not met");\r
require(attesters.length == encryptedPhones.length, "length mismatch");\r
\r
r.owner = msg.sender;\r
r.safe_address = safe_address;\r
r.waiting_period = waiting_period;\r
r.death_certificate = death_certificate;\r
r.createdAt = uint64(block.timestamp);\r
r.feePaid += msg.value;\r
\r
for (uint256 i = 0; i < attesters.length; i++) {\r
r.attesters.push(attesters[i]);\r
r.encryptedPhones.push(encryptedPhones[i]);\r
}\r
\r
ownerSafes[msg.sender].push(safe_address);\r
\r
totalSafes += 1;\r
\r
emit FeeCollected(msg.sender, msg.value);\r
emit RowInserted(msg.sender, safe_address, waiting_period, death_certificate, attesters, encryptedPhones, r.createdAt);\r
}\r
\r
/// @notice Update an existing row (requires >= fee; excess kept in contract).\r
function update(\r
address safe_address,\r
uint8 waiting_period,\r
bool death_certificate,\r
address[] calldata attesters,\r
string[] calldata encryptedPhones\r
) external payable whenInitialized {\r
Row storage r = rows[safe_address];\r
require(r.createdAt != 0, "missing");\r
require(r.owner == msg.sender, "not owner");\r
require(msg.value >= fee, "fee not met");\r
require(attesters.length == encryptedPhones.length, "length mismatch");\r
\r
r.waiting_period = waiting_period;\r
r.death_certificate = death_certificate;\r
r.feePaid += msg.value;\r
\r
delete r.attesters;\r
delete r.encryptedPhones;\r
for (uint256 i = 0; i < attesters.length; i++) {\r
r.attesters.push(attesters[i]);\r
r.encryptedPhones.push(encryptedPhones[i]);\r
}\r
\r
emit FeeCollected(msg.sender, msg.value);\r
emit RowUpdated(msg.sender, safe_address, waiting_period, death_certificate, attesters, encryptedPhones);\r
}\r
\r
\r
/// @notice New: Get a row directly by safe address.\r
function get(address safe_address) external view whenInitialized returns (\r
address owner,\r
address _safe_address,\r
uint8 waiting_period,\r
bool death_certificate,\r
address[] memory attesters,\r
string[] memory encryptedPhones,\r
uint64 createdAt\r
) {\r
Row storage r = rows[safe_address];\r
return (r.owner, r.safe_address, r.waiting_period, r.death_certificate, r.attesters, r.encryptedPhones, r.createdAt);\r
}\r
\r
/// @notice Check if an owner has a row for a given safe.\r
function hasRow(address safe_address) external view whenInitialized returns (bool) {\r
Row storage r = rows[safe_address];\r
return r.createdAt != 0;\r
}\r
\r
/// @notice List all safes registered by an owner.\r
function listSafes(address owner) external view whenInitialized returns (address[] memory) {\r
return ownerSafes[owner];\r
}\r
\r
/// @notice Count safes registered by an owner.\r
function countOwnerSafes(address owner) external view whenInitialized returns (uint256) {\r
return ownerSafes[owner].length;\r
}\r
\r
function countSafes() external view whenInitialized returns (uint256) {\r
return totalSafes;\r
}\r
\r
// --- Treasury-only config and withdrawals of native ETH ---\r
\r
modifier onlyTreasury() {\r
require(_initialized && !_initializing, "not initialized");\r
require(msg.sender == treasury, "not treasury");\r
_;\r
}\r
\r
function setTreasury(address newTreasury) external onlyTreasury {\r
require(newTreasury != address(0), "treasury=0");\r
emit TreasuryChanged(treasury, newTreasury);\r
treasury = newTreasury;\r
}\r
\r
function setFee(uint256 newFee) external onlyTreasury {\r
uint256 old = fee;\r
fee = newFee;\r
emit FeeChanged(old, newFee);\r
}\r
\r
function withdraw(uint256 amount) external onlyTreasury nonReentrant {\r
(bool ok, ) = payable(treasury).call{value: amount}("");\r
require(ok, "withdraw failed");\r
emit Withdrawn(treasury, amount);\r
}\r
\r
function withdrawAll() external onlyTreasury nonReentrant {\r
uint256 bal = address(this).balance;\r
(bool ok, ) = payable(treasury).call{value: bal}("");\r
require(ok, "withdraw failed");\r
emit Withdrawn(treasury, bal);\r
}\r
\r
receive() external payable whenInitialized {}\r
\r
function _authorizeUpgrade(address) internal override onlyTreasury {}\r
\r
uint256[50] private __gap;\r
}\r
"
}
},
"settings": {
"optimizer": {
"enabled": true,
"runs": 200
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"remappings": []
}
}}
Submitted on: 2025-10-30 12:48:59
Comments
Log in to comment.
No comments yet.