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": {
"claims.sol": {
"content": "// tesnet 0x7010996c5638C3b557cA1B7f69AD18991821eA82 (v1)\r
// tesnet v2 0xAb9F4f36897d108ffc89aaDDc4B06928edAf1c75 (v2)\r
// tesnet v3 0x21952de9a37BA2fD014E36f553C42Df208db1818 (v3)\r
// testnet v3.1 0x81bE3f271eF26CE80835A5B335a3aAA6C84a7418 (v3.1)\r
// mainnet 0xCBED2362c00587720aC216C37E4b62bCAB2F53E1\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
// --- Minimal ERC20 interface ---\r
interface IERC20 {\r
function decimals() external view returns (uint8);\r
function balanceOf(address) external view returns (uint256);\r
function allowance(address owner, address spender) external view returns (uint256);\r
function transfer(address to, uint256 value) external returns (bool);\r
function transferFrom(address from, address to, uint256 value) external returns (bool);\r
}\r
\r
/// @title ClaimsDB V2 — ERC20 fee (default 10 tokens) instead of 1 ETH (UUPS)\r
/// @notice Mirrors v1 features but collects an ERC-20 token as the creation fee.\r
/// @dev Hardened for fee-on-transfer tokens (balance-delta), tolerant ERC-20 calls, and reentrancy-guarded.\r
/// `initializeDefault` derives the default fee from token decimals (falls back to 18).\r
/*\r
* @changelog\r
* - v2: ERC20 fee (10 tokens by default not 1 ether) + Slashing (UUPS)\r
*\r
* - v3: encrypts safe address\r
*/\r
contract ClaimsDBV3_1 is UUPSUpgradeable {\r
\r
\r
// --- Data model (same as v1) ---\r
enum Status { Pending, Approved, Rejected, Cancelled }\r
\r
struct Claim {\r
address claimer; // who created the claim (payer)\r
string encryptedSafe; // encrypted safe identifier (replaces address safe)\r
address attestor; // designated attestor\r
string encryptedPhone; // encrypted phone blob (e.g., ciphertext)\r
uint64 createdAt; // timestamp at creation\r
Status status; // current status\r
}\r
\r
// --- Config / admin ---\r
address public treasury; // admin/withdrawer (same role as v1)\r
IERC20 public feeToken; // ERC-20 used for fee (e.g., MRS)\r
uint256 public registerFee; // fee in token units (e.g., 10 * 10**decimals)\r
bool private _initialized;\r
\r
// --- Storage ---\r
uint256 public nextId; // auto-incremented claim id\r
mapping(uint256 => Claim) private claims;\r
uint256[] public ids; // optional enumeration\r
\r
// --- Reentrancy guard ---\r
uint256 private _entered; // 0 = unlocked, 1 = locked\r
modifier nonReentrant() {\r
require(_entered == 0, "reentrancy");\r
_entered = 1;\r
_;\r
_entered = 0;\r
}\r
\r
modifier whenInitialized() {\r
require(_initialized, "not initialized");\r
_;\r
}\r
\r
modifier onlyTreasury() {\r
require(_initialized, "not initialized");\r
require(msg.sender == treasury, "not treasury");\r
_;\r
}\r
\r
// --- Events (aligned with v1 updated events) ---\r
event ClaimCreated(\r
uint256 indexed id,\r
address indexed claimer,\r
string encryptedSafe,\r
address attestor,\r
string encryptedPhone,\r
uint64 createdAt\r
);\r
event StatusChanged(uint256 indexed id, Status fromStatus, Status toStatus);\r
event PhoneUpdated(uint256 indexed id, string oldEncryptedPhone, string newEncryptedPhone);\r
event AttestorUpdated(uint256 indexed id, address oldAttestor, address newAttestor);\r
event Withdrawn(address indexed to, uint256 amount);\r
event TreasuryChanged(address indexed oldTreasury, address indexed newTreasury);\r
event RegisterFeeUpdated(uint256 oldFee, uint256 newFee);\r
event FeeReceived(address indexed payer, uint256 amount);\r
\r
// --- Constructor ---\r
constructor() {\r
_initialized = true; // lock the implementation\r
}\r
\r
// --- Initializers ---\r
/// @notice Initialize with explicit fee value (token units)\r
function initialize(address _treasury, address _feeToken, uint256 _fee) external {\r
_initialize(_treasury, _feeToken, _fee);\r
}\r
\r
/// @notice Initialize with default fee of "10 tokens", respecting token decimals (fallback = 18)\r
function initializeDefault(address _treasury, address _feeToken) external {\r
uint256 fee;\r
// Best-effort decimals read; default to 18 on failure\r
try IERC20(_feeToken).decimals() returns (uint8 dec) {\r
fee = 10 * (10 ** dec);\r
} catch {\r
fee = 10 * 1e18;\r
}\r
_initialize(_treasury, _feeToken, fee);\r
}\r
\r
function _initialize(address _treasury, address _feeToken, uint256 _fee) internal {\r
require(!_initialized, "already initialized");\r
require(_treasury != address(0), "treasury=0");\r
require(_feeToken != address(0), "token=0");\r
treasury = _treasury;\r
feeToken = IERC20(_feeToken);\r
registerFee = _fee;\r
_entered = 0;\r
_initialized = true;\r
}\r
\r
// --- Core: create a claim by paying token fee ---\r
/// @param encryptedSafe Encrypted safe identifier (required)\r
/// @param attestor Designated attestor (required)\r
/// @param encryptedPhone Opaque encrypted blob\r
/// @param amount Amount of tokens to attempt to pay (must result in received >= registerFee)\r
function createClaim(\r
string calldata encryptedSafe,\r
address attestor,\r
string calldata encryptedPhone,\r
uint256 amount\r
) external whenInitialized nonReentrant returns (uint256 id) {\r
require(amount > 0, "amount=0");\r
//require(bytes(encryptedSafe).length > 0, "encryptedSafe=empty");\r
require(attestor != address(0), "attestor=0");\r
\r
// Pull tokens; verify actual amount received against registerFee (handles fee-on-transfer)\r
uint256 received = _pullReceived(feeToken, msg.sender, amount);\r
require(received >= registerFee, "fee too low");\r
\r
unchecked { id = ++nextId; }\r
Claim storage c = claims[id];\r
c.claimer = msg.sender;\r
c.encryptedSafe = encryptedSafe; \r
c.attestor = attestor;\r
c.encryptedPhone = encryptedPhone;\r
c.createdAt = uint64(block.timestamp);\r
c.status = Status.Pending;\r
\r
ids.push(id);\r
\r
emit ClaimCreated(id, msg.sender, encryptedSafe, attestor, encryptedPhone, c.createdAt);\r
emit FeeReceived(msg.sender, received);\r
}\r
\r
// --- Admin (treasury) can change status ---\r
function setStatus(uint256 id, Status newStatus) external onlyTreasury {\r
Claim storage c = _mustGet(id);\r
Status old = c.status;\r
if (old != newStatus) {\r
c.status = newStatus;\r
emit StatusChanged(id, old, newStatus);\r
}\r
}\r
\r
/// @notice Change the treasury address. Only current treasury may call.\r
function setTreasury(address newTreasury) external onlyTreasury {\r
require(newTreasury != address(0), "treasury=0");\r
address old = treasury;\r
treasury = newTreasury;\r
emit TreasuryChanged(old, newTreasury);\r
}\r
\r
/// @notice Update the register fee amount (token units). Only treasury may call.\r
function setRegisterFee(uint256 newFee) external onlyTreasury {\r
uint256 old = registerFee;\r
registerFee = newFee;\r
emit RegisterFeeUpdated(old, newFee);\r
}\r
\r
// --- Claimer can update encrypted phone & attestor while Pending ---\r
function updateEncryptedPhone(uint256 id, string calldata newEncryptedPhone) external whenInitialized {\r
Claim storage c = _mustGet(id);\r
require(msg.sender == c.claimer, "not claimer");\r
require(c.status == Status.Pending, "locked");\r
string memory old = c.encryptedPhone;\r
c.encryptedPhone = newEncryptedPhone;\r
emit PhoneUpdated(id, old, newEncryptedPhone);\r
}\r
\r
function updateAttestor(uint256 id, address newAttestor) external whenInitialized {\r
require(newAttestor != address(0), "attestor=0");\r
Claim storage c = _mustGet(id);\r
require(msg.sender == c.claimer, "not claimer");\r
require(c.status == Status.Pending, "locked");\r
address old = c.attestor;\r
c.attestor = newAttestor;\r
emit AttestorUpdated(id, old, newAttestor);\r
}\r
\r
// --- Views ---\r
\r
function getClaimsByAttester(address _attestor)\r
external\r
view\r
whenInitialized\r
returns (\r
uint256[] memory outIds,\r
address[] memory claimers,\r
string[] memory encryptedSafes,\r
address[] memory attestors,\r
string[] memory encryptedPhones,\r
uint64[] memory createdAts,\r
Status[] memory statuses\r
)\r
{\r
// First pass: count matches\r
uint256 count = 0;\r
for (uint256 i = 0; i < ids.length; i++) {\r
if (claims[ids[i]].attestor == _attestor) {\r
unchecked { count++; }\r
}\r
}\r
\r
// Allocate outputs\r
outIds = new uint256[](count);\r
claimers = new address[](count);\r
encryptedSafes = new string[](count);\r
attestors = new address[](count);\r
encryptedPhones = new string[](count);\r
createdAts = new uint64[](count);\r
statuses = new Status[](count);\r
\r
if (count == 0) {\r
return (outIds, claimers, encryptedSafes, attestors, encryptedPhones, createdAts, statuses);\r
}\r
\r
// Second pass: fill arrays (newest first)\r
uint256 idx = 0;\r
for (uint256 i = ids.length; i > 0; i--) {\r
uint256 id = ids[i - 1];\r
Claim storage c = claims[id];\r
if (c.attestor == _attestor) {\r
outIds[idx] = id;\r
claimers[idx] = c.claimer;\r
encryptedSafes[idx] = c.encryptedSafe;\r
attestors[idx] = c.attestor;\r
encryptedPhones[idx] = c.encryptedPhone;\r
createdAts[idx] = c.createdAt;\r
statuses[idx] = c.status;\r
unchecked { idx++; }\r
if (idx == count) break;\r
}\r
}\r
\r
return (outIds, claimers, encryptedSafes, attestors, encryptedPhones, createdAts, statuses);\r
}\r
\r
function idsLength() external view whenInitialized returns (uint256) {\r
return ids.length;\r
}\r
\r
// --- Treasury-only withdrawals of collected fee tokens ---\r
function withdrawTokens(uint256 amount) external onlyTreasury nonReentrant {\r
require(amount > 0, "amount=0");\r
require(feeToken.balanceOf(address(this)) >= amount, "insufficient balance");\r
require(_safeTransfer(feeToken, treasury, amount), "withdraw failed");\r
emit Withdrawn(treasury, amount);\r
}\r
\r
function withdrawAllTokens() external onlyTreasury nonReentrant {\r
uint256 bal = feeToken.balanceOf(address(this));\r
if (bal == 0) return;\r
require(_safeTransfer(feeToken, treasury, bal), "withdraw failed");\r
emit Withdrawn(treasury, bal);\r
}\r
\r
// --- internal ---\r
function _authorizeUpgrade(address) internal override onlyTreasury {}\r
\r
function _mustGet(uint256 id) internal view returns (Claim storage c) {\r
c = claims[id];\r
require(c.createdAt != 0, "claim not found");\r
}\r
\r
// --- Internal helpers ---\r
// Accept both no-return and bool-return ERC-20s; bubble up reverts.\r
function _safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal returns (bool) {\r
(bool ok, bytes memory data) = address(token).call(\r
abi.encodeWithSelector(IERC20.transferFrom.selector, from, to, value)\r
);\r
require(ok, "transferFrom revert");\r
return (data.length == 0) || abi.decode(data, (bool));\r
}\r
\r
function _safeTransfer(IERC20 token, address to, uint256 value) internal returns (bool) {\r
(bool ok, bytes memory data) = address(token).call(\r
abi.encodeWithSelector(IERC20.transfer.selector, to, value)\r
);\r
require(ok, "transfer revert");\r
return (data.length == 0) || abi.decode(data, (bool));\r
}\r
\r
/// @notice Pull tokens and return the actual amount received (handles fee-on-transfer).\r
function _pullReceived(IERC20 token, address from, uint256 requested) internal returns (uint256 received) {\r
uint256 beforeBal = token.balanceOf(address(this));\r
require(_safeTransferFrom(token, from, address(this), requested), "transferFrom failed");\r
unchecked {\r
received = token.balanceOf(address(this)) - beforeBal;\r
}\r
require(received > 0, "nothing received");\r
}\r
}\r
"
}
},
"settings": {
"optimizer": {
"enabled": true,
"runs": 200
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"remappings": []
}
}}
Submitted on: 2025-10-30 12:50:34
Comments
Log in to comment.
No comments yet.