// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;
import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol";
import {IFomoAccessRegistry} from "./interfaces/IFomoAccessRegistry.sol";
/// @notice Expiring wallet attestations for fomo-only protocol paths.
/// @dev The verifier role represents an approved membership source. A wallet signature alone is not membership proof.
contract FomoAccessRegistry is AccessControl, IFomoAccessRegistry {
bytes32 public constant VERIFIER_ROLE = keccak256("VERIFIER_ROLE");
struct Attestation {
uint64 validUntil;
bytes32 evidenceHash;
}
mapping(address wallet => Attestation attestation) public attestations;
event WalletAttested(address indexed wallet, uint64 validUntil, bytes32 indexed evidenceHash, address verifier);
event WalletRevoked(address indexed wallet, address verifier);
error ZeroAddress();
error InvalidExpiry();
error EmptyEvidence();
constructor(address admin) {
if (admin == address(0)) revert ZeroAddress();
_grantRole(DEFAULT_ADMIN_ROLE, admin);
_grantRole(VERIFIER_ROLE, admin);
}
function attest(address wallet, uint64 validUntil, bytes32 evidenceHash) external onlyRole(VERIFIER_ROLE) {
if (wallet == address(0)) revert ZeroAddress();
if (validUntil <= block.timestamp) revert InvalidExpiry();
if (evidenceHash == bytes32(0)) revert EmptyEvidence();
attestations[wallet] = Attestation({validUntil: validUntil, evidenceHash: evidenceHash});
emit WalletAttested(wallet, validUntil, evidenceHash, msg.sender);
}
function revoke(address wallet) external onlyRole(VERIFIER_ROLE) {
if (wallet == address(0)) revert ZeroAddress();
delete attestations[wallet];
emit WalletRevoked(wallet, msg.sender);
}
function isVerified(address wallet) external view returns (bool) {
return attestations[wallet].validUntil >= block.timestamp;
}
}[
{
"type": "constructor",
"inputs": [
{
"name": "admin",
"type": "address",
"internalType": "address"
}
],
"stateMutability": "nonpayable"
},
{
"name": "AccessControlBadConfirmation",
"type": "error",
"inputs": []
},
{
"name": "AccessControlUnauthorizedAccount",
"type": "error",
"inputs": [
{
"name": "account",
"type": "address",
"internalType": "address"
},
{
"name": "neededRole",
"type": "bytes32",
"internalType": "bytes32"
}
]
},
{
"name": "EmptyEvidence",
"type": "error",
"inputs": []
},
{
"name": "InvalidExpiry",
"type": "error",
"inputs": []
},
{
"name": "ZeroAddress",
"type": "error",
"inputs": []
},
{
"name": "RoleAdminChanged",
"type": "event",
"inputs": [
{
"name": "role",
"type": "bytes32",
"indexed": true,
"internalType": "bytes32"
},
{
"name": "previousAdminRole",
"type": "bytes32",
"indexed": true,
"internalType": "bytes32"
},
{
"name": "newAdminRole",
"type": "bytes32",
"indexed": true,
"internalType": "bytes32"
}
],
"anonymous": false
},
{
"name": "RoleGranted",
"type": "event",
"inputs": [
{
"name": "role",
"type": "bytes32",
"indexed": true,
"internalType": "bytes32"
},
{
"name": "account",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "sender",
"type": "address",
"indexed": true,
"internalType": "address"
}
],
"anonymous": false
},
{
"name": "RoleRevoked",
"type": "event",
"inputs": [
{
"name": "role",
"type": "bytes32",
"indexed": true,
"internalType": "bytes32"
},
{
"name": "account",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "sender",
"type": "address",
"indexed": true,
"internalType": "address"
}
],
"anonymous": false
},
{
"name": "WalletAttested",
"type": "event",
"inputs": [
{
"name": "wallet",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "validUntil",
"type": "uint64",
"indexed": false,
"internalType": "uint64"
},
{
"name": "evidenceHash",
"type": "bytes32",
"indexed": true,
"internalType": "bytes32"
},
{
"name": "verifier",
"type": "address",
"indexed": false,
"internalType": "address"
}
],
"anonymous": false
},
{
"name": "WalletRevoked",
"type": "event",
"inputs": [
{
"name": "wallet",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "verifier",
"type": "address",
"indexed": false,
"internalType": "address"
}
],
"anonymous": false
},
{
"name": "DEFAULT_ADMIN_ROLE",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "bytes32",
"internalType": "bytes32"
}
],
"stateMutability": "view"
},
{
"name": "VERIFIER_ROLE",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "bytes32",
"internalType": "bytes32"
}
],
"stateMutability": "view"
},
{
"name": "attest",
"type": "function",
"inputs": [
{
"name": "wallet",
"type": "address",
"internalType": "address"
},
{
"name": "validUntil",
"type": "uint64",
"internalType": "uint64"
},
{
"name": "evidenceHash",
"type": "bytes32",
"internalType": "bytes32"
}
],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"name": "attestations",
"type": "function",
"inputs": [
{
"name": "wallet",
"type": "address",
"internalType": "address"
}
],
"outputs": [
{
"name": "validUntil",
"type": "uint64",
"internalType": "uint64"
},
{
"name": "evidenceHash",
"type": "bytes32",
"internalType": "bytes32"
}
],
"stateMutability": "view"
},
{
"name": "getRoleAdmin",
"type": "function",
"inputs": [
{
"name": "role",
"type": "bytes32",
"internalType": "bytes32"
}
],
"outputs": [
{
"name": "",
"type": "bytes32",
"internalType": "bytes32"
}
],
"stateMutability": "view"
},
{
"name": "grantRole",
"type": "function",
"inputs": [
{
"name": "role",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "account",
"type": "address",
"internalType": "address"
}
],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"name": "hasRole",
"type": "function",
"inputs": [
{
"name": "role",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "account",
"type": "address",
"internalType": "address"
}
],
"outputs": [
{
"name": "",
"type": "bool",
"internalType": "bool"
}
],
"stateMutability": "view"
},
{
"name": "isVerified",
"type": "function",
"inputs": [
{
"name": "wallet",
"type": "address",
"internalType": "address"
}
],
"outputs": [
{
"name": "",
"type": "bool",
"internalType": "bool"
}
],
"stateMutability": "view"
},
{
"name": "renounceRole",
"type": "function",
"inputs": [
{
"name": "role",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "callerConfirmation",
"type": "address",
"internalType": "address"
}
],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"name": "revoke",
"type": "function",
"inputs": [
{
"name": "wallet",
"type": "address",
"internalType": "address"
}
],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"name": "revokeRole",
"type": "function",
"inputs": [
{
"name": "role",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "account",
"type": "address",
"internalType": "address"
}
],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"name": "supportsInterface",
"type": "function",
"inputs": [
{
"name": "interfaceId",
"type": "bytes4",
"internalType": "bytes4"
}
],
"outputs": [
{
"name": "",
"type": "bool",
"internalType": "bool"
}
],
"stateMutability": "view"
}
]0x60803461008757601f6108c338819003918201601f19168301916001600160401b0383118484101761008b5780849260209460405283398101031261008757516001600160a01b038116808203610087571561007857806100626100689261009f565b50610115565b506040516106ba90816101a98239f35b63d92e233d60e01b5f5260045ffd5b5f80fd5b634e487b7160e01b5f52604160045260245ffd5b6001600160a01b0381165f9081525f805160206108a3833981519152602052604090205460ff16610110576001600160a01b03165f8181525f805160206108a383398151915260205260408120805460ff191660011790553391905f805160206108638339815191528180a4600190565b505f90565b6001600160a01b0381165f9081525f80516020610883833981519152602052604090205460ff16610110576001600160a01b03165f8181525f8051602061088383398151915260205260408120805460ff191660011790553391907f0ce23c3e399818cfee81a7ab0880f714e53d7672b08df0fa62f2843416e1ea09905f805160206108638339815191529080a460019056fe6080806040526004361015610012575f80fd5b5f3560e01c90816301ffc9a71461045657508063203570481461040b578063248a9ca3146103d95780632f2ff15d1461039c57806336568abe1461035857806374a8f103146102e857806391c89dbf146101c957806391d1485414610181578063a217fddf14610167578063b9209e3314610122578063d547741f146100de5763e7705db6146100a0575f80fd5b346100da575f3660031901126100da5760206040517f0ce23c3e399818cfee81a7ab0880f714e53d7672b08df0fa62f2843416e1ea098152f35b5f80fd5b346100da5760403660031901126100da576101206004356100fd6104bf565b9061011b610116825f525f602052600160405f20015490565b610544565b610604565b005b346100da5760203660031901126100da576001600160a01b036101436104a9565b165f526001602052602067ffffffffffffffff60405f205416604051904211158152f35b346100da575f3660031901126100da5760206040515f8152f35b346100da5760403660031901126100da5761019a6104bf565b6004355f525f60205260405f209060018060a01b03165f52602052602060ff60405f2054166040519015158152f35b346100da5760603660031901126100da576101e26104a9565b60243567ffffffffffffffff81168091036100da57604435916102036104d5565b6001600160a01b03169081156102d957428111156102cb5782156102bc5760405190604082019082821067ffffffffffffffff8311176102a8577fc02391815aebffb1330ef39e9e8bd3695f939f055b43d507b28540b8a5bb682b926040928352818152600160208201878152865f528160205267ffffffffffffffff855f2093511667ffffffffffffffff19845416178355519101558151908152336020820152a3005b634e487b7160e01b5f52604160045260245ffd5b63cb3a13f760e01b5f5260045ffd5b62d36c8560e81b5f5260045ffd5b63d92e233d60e01b5f5260045ffd5b346100da5760203660031901126100da576103016104a9565b6103096104d5565b6001600160a01b031680156102d957805f5260016020525f60016040822082815501557f9acdbd9054166a56a857b5a2526ae7449bc952f1303dee2ac6dee9b41cd16a4a6020604051338152a2005b346100da5760403660031901126100da576103716104bf565b336001600160a01b0382160361038d5761012090600435610604565b63334bd91960e11b5f5260045ffd5b346100da5760403660031901126100da576101206004356103bb6104bf565b906103d4610116825f525f602052600160405f20015490565b61057c565b346100da5760203660031901126100da5760206104036004355f525f602052600160405f20015490565b604051908152f35b346100da5760203660031901126100da576001600160a01b0361042c6104a9565b165f5260016020526040805f20600167ffffffffffffffff82541691015482519182526020820152f35b346100da5760203660031901126100da576004359063ffffffff60e01b82168092036100da57602091637965db0b60e01b8114908115610498575b5015158152f35b6301ffc9a760e01b14905083610491565b600435906001600160a01b03821682036100da57565b602435906001600160a01b03821682036100da57565b335f9081527f1da0b5176dfe03406af5a6eed66ca01dda9b45ac4da671c3fa379cb93717ef81602052604090205460ff161561050d57565b63e2517d3f60e01b5f52336004527f0ce23c3e399818cfee81a7ab0880f714e53d7672b08df0fa62f2843416e1ea0960245260445ffd5b5f8181526020818152604080832033845290915290205460ff16156105665750565b63e2517d3f60e01b5f523360045260245260445ffd5b5f818152602081815260408083206001600160a01b038616845290915290205460ff166105fe575f818152602081815260408083206001600160a01b0395909516808452949091528120805460ff19166001179055339291907f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d9080a4600190565b50505f90565b5f818152602081815260408083206001600160a01b038616845290915290205460ff16156105fe575f818152602081815260408083206001600160a01b0395909516808452949091528120805460ff19169055339291907ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9080a460019056fea2646970667358221220286d854a0f86a7ddb41ad56f7faec06e8b10502faacae43e51fc0e9b62dedf1264736f6c634300081a00332f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d1da0b5176dfe03406af5a6eed66ca01dda9b45ac4da671c3fa379cb93717ef81ad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5000000000000000000000000d9c3637d540f5d08783ef384df58460239d2e8a7
0x6080806040526004361015610012575f80fd5b5f3560e01c90816301ffc9a71461045657508063203570481461040b578063248a9ca3146103d95780632f2ff15d1461039c57806336568abe1461035857806374a8f103146102e857806391c89dbf146101c957806391d1485414610181578063a217fddf14610167578063b9209e3314610122578063d547741f146100de5763e7705db6146100a0575f80fd5b346100da575f3660031901126100da5760206040517f0ce23c3e399818cfee81a7ab0880f714e53d7672b08df0fa62f2843416e1ea098152f35b5f80fd5b346100da5760403660031901126100da576101206004356100fd6104bf565b9061011b610116825f525f602052600160405f20015490565b610544565b610604565b005b346100da5760203660031901126100da576001600160a01b036101436104a9565b165f526001602052602067ffffffffffffffff60405f205416604051904211158152f35b346100da575f3660031901126100da5760206040515f8152f35b346100da5760403660031901126100da5761019a6104bf565b6004355f525f60205260405f209060018060a01b03165f52602052602060ff60405f2054166040519015158152f35b346100da5760603660031901126100da576101e26104a9565b60243567ffffffffffffffff81168091036100da57604435916102036104d5565b6001600160a01b03169081156102d957428111156102cb5782156102bc5760405190604082019082821067ffffffffffffffff8311176102a8577fc02391815aebffb1330ef39e9e8bd3695f939f055b43d507b28540b8a5bb682b926040928352818152600160208201878152865f528160205267ffffffffffffffff855f2093511667ffffffffffffffff19845416178355519101558151908152336020820152a3005b634e487b7160e01b5f52604160045260245ffd5b63cb3a13f760e01b5f5260045ffd5b62d36c8560e81b5f5260045ffd5b63d92e233d60e01b5f5260045ffd5b346100da5760203660031901126100da576103016104a9565b6103096104d5565b6001600160a01b031680156102d957805f5260016020525f60016040822082815501557f9acdbd9054166a56a857b5a2526ae7449bc952f1303dee2ac6dee9b41cd16a4a6020604051338152a2005b346100da5760403660031901126100da576103716104bf565b336001600160a01b0382160361038d5761012090600435610604565b63334bd91960e11b5f5260045ffd5b346100da5760403660031901126100da576101206004356103bb6104bf565b906103d4610116825f525f602052600160405f20015490565b61057c565b346100da5760203660031901126100da5760206104036004355f525f602052600160405f20015490565b604051908152f35b346100da5760203660031901126100da576001600160a01b0361042c6104a9565b165f5260016020526040805f20600167ffffffffffffffff82541691015482519182526020820152f35b346100da5760203660031901126100da576004359063ffffffff60e01b82168092036100da57602091637965db0b60e01b8114908115610498575b5015158152f35b6301ffc9a760e01b14905083610491565b600435906001600160a01b03821682036100da57565b602435906001600160a01b03821682036100da57565b335f9081527f1da0b5176dfe03406af5a6eed66ca01dda9b45ac4da671c3fa379cb93717ef81602052604090205460ff161561050d57565b63e2517d3f60e01b5f52336004527f0ce23c3e399818cfee81a7ab0880f714e53d7672b08df0fa62f2843416e1ea0960245260445ffd5b5f8181526020818152604080832033845290915290205460ff16156105665750565b63e2517d3f60e01b5f523360045260245260445ffd5b5f818152602081815260408083206001600160a01b038616845290915290205460ff166105fe575f818152602081815260408083206001600160a01b0395909516808452949091528120805460ff19166001179055339291907f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d9080a4600190565b50505f90565b5f818152602081815260408083206001600160a01b038616845290915290205460ff16156105fe575f818152602081815260408083206001600160a01b0395909516808452949091528120805460ff19169055339291907ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9080a460019056fea2646970667358221220286d854a0f86a7ddb41ad56f7faec06e8b10502faacae43e51fc0e9b62dedf1264736f6c634300081a0033
| Token | Symbol | Balance | Price | Value |
|---|---|---|---|---|
| no token holdings | ||||
| Type | Age | Block | Details |
|---|---|---|---|
| no cross-chain L1↔L2 transactions for this address | |||
| Txn Hash | Age | Event | Topics / Data |
|---|---|---|---|
| 0x95dc49…df2f6c | 3 days agoSat, 15 Aug 2026 00:44:01 UTC | 0xf6391f…171b | [0] 0x000000000000…00000000 [1] 0x000000000000…39d2e8a7 [2] 0x000000000000…39d2e8a7 |
| 0x5c902d…d4677e | 3 days agoSat, 15 Aug 2026 00:43:57 UTC | 0xf6391f…171b | [0] 0x0ce23c3e3998…16e1ea09 [1] 0x000000000000…39d2e8a7 [2] 0x000000000000…39d2e8a7 |
| 0x26da3e…0f06e2 | 3 days agoSat, 15 Aug 2026 00:43:54 UTC | 0x2f8788…6f0d | [0] 0x0ce23c3e3998…16e1ea09 [1] 0x000000000000…e2783c19 [2] 0x000000000000…39d2e8a7 |
| 0x70ea34…6e7c82 | 3 days agoSat, 15 Aug 2026 00:43:50 UTC | 0x2f8788…6f0d | [0] 0x000000000000…00000000 [1] 0x000000000000…e2783c19 [2] 0x000000000000…39d2e8a7 |
| 0x1405c3…9ea014 | 3 days agoSat, 15 Aug 2026 00:40:13 UTC | 0x2f8788…6f0d | [0] 0x0ce23c3e3998…16e1ea09 [1] 0x000000000000…39d2e8a7 [2] 0x000000000000…39d2e8a7 |
| 0x1405c3…9ea014 | 3 days agoSat, 15 Aug 2026 00:40:13 UTC | 0x2f8788…6f0d | [0] 0x000000000000…00000000 [1] 0x000000000000…39d2e8a7 [2] 0x000000000000…39d2e8a7 |
| Transaction Hash | Method | Block | Age | From | To | Amount | Token | ||
|---|---|---|---|---|---|---|---|---|---|
| no token transfers for this address yet | |||||||||
| Transaction Hash | Method | Block | Age | From | To | Type | Item | ||
|---|---|---|---|---|---|---|---|---|---|
| no NFT transfers for this address yet | |||||||||
| Block | Age | Parent Transaction Hash | Type | Method | From | To | Value | |
|---|---|---|---|---|---|---|---|---|
| no internal transactions found for this address yet (traced blocks + on-demand) | ||||||||
| Transaction Hash | Method ? | Block | Age | From | To | Amount | Txn Fee ? | ||
|---|---|---|---|---|---|---|---|---|---|
| 0x95dc49…df2f6c | revokeRole | 36,693,340 | 3 days agoSat, 15 Aug 2026 00:44:01 UTC | 0xd9c3…e8a7 | IN | FomoAccessRegistry | $0.000 ETH | 0.00000105 | |
| 0x5c902d…d4677e | revokeRole | 36,693,302 | 3 days agoSat, 15 Aug 2026 00:43:57 UTC | 0xd9c3…e8a7 | IN | FomoAccessRegistry | $0.000 ETH | 0.00000114 | |
| 0x26da3e…0f06e2 | grantRole | 36,693,266 | 3 days agoSat, 15 Aug 2026 00:43:54 UTC | 0xd9c3…e8a7 | IN | FomoAccessRegistry | $0.000 ETH | 0.00000200 | |
| 0x70ea34…6e7c82 | grantRole | 36,693,228 | 3 days agoSat, 15 Aug 2026 00:43:50 UTC | 0xd9c3…e8a7 | IN | FomoAccessRegistry | $0.000 ETH | 0.00000196 |