SafeTableV6

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": {
    "safesv6.sol": {
      "content": "// tesnet 0xE06c6F03Eb924050e8663A36D40A42F4aCDBc886\r
// mainnet 0x34235314409a75bf2A1A6a46f819a4c1b070C70f\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 SafeTableV6 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
        string[] encryptedProtocolPhrases;\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
        string[] encryptedProtocolPhrases,\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
        string[] encryptedProtocolPhrases\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
        string[] calldata encryptedProtocolPhrases\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.encryptedProtocolPhrases.push(encryptedProtocolPhrases[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, encryptedProtocolPhrases, 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
        string[] calldata encryptedProtocolPhrases\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
        delete r.encryptedProtocolPhrases;\r
        for (uint256 i = 0; i < attesters.length; i++) {\r
            r.attesters.push(attesters[i]);\r
            r.encryptedPhones.push(encryptedPhones[i]);\r
            r.encryptedProtocolPhrases.push(encryptedProtocolPhrases[i]);\r
        }\r
\r
        emit FeeCollected(msg.sender, msg.value);\r
        emit RowUpdated(msg.sender, safe_address, waiting_period, death_certificate, attesters, encryptedPhones, encryptedProtocolPhrases);\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
        string[] memory encryptedProtocolPhrases,\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.encryptedProtocolPhrases, 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": false,
      "runs": 200
    },
    "outputSelection": {
      "*": {
        "*": [
          "evm.bytecode",
          "evm.deployedBytecode",
          "devdoc",
          "userdoc",
          "metadata",
          "abi"
        ]
      }
    },
    "remappings": []
  }
}}

Tags:
Proxy, Upgradeable, Factory|addr:0x617242104212250bd3e3d337290b29fe0cfa0a08|verified:true|block:23708842|tx:0xcfd08d12be8138e1f32a9ea3d8a8ea29272b768051b4973483f4888f77994ead|first_check:1762078489

Submitted on: 2025-11-02 11:14:51

Comments

Log in to comment.

No comments yet.