BLSModel.sol [Report Model + IESR ⇒ EthereumScammerRegistry]
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import {TokenType} from "./TokenTypes.sol";
abstract contract ReportModel {
event ScamTransactionReported(
address indexed _scammerAddress,
bytes32 _transactionHash
);
event PublicReportUpdated(ReportModel.ScammerAddressRecord[] publicReport);
error InvalidInput(string message);
error NoReportsFound(Set[] reportedAddresses);
/// @notice UserReport is a struct that contains the addresses and transactions of a scammer.
/// @dev scammers is the list of addresses that are part of the scam.
/// @dev transactions is the list of transactions that are part of the scam.
struct UserReport {
uint256 chainId;
address[] scammers; // msg.sender -> 1 -> 2 -> 3 -> 4
bytes32[] transactions; // 1 -> 2 -> 3
}
/// @notice TransactionDetails is a struct that contains the transaction hash and chain id of a transaction.
/// @dev transactionHash is the hash of the transaction.
/// @dev chainId is the chain id of the transaction.
struct TransactionDetails {
bytes32 transactionHash;
uint256 chainId;
}
/// @notice ScammerReported is a struct that contains the reported status, scammer address, and transaction details of a scammer.
/// @dev reported is the reported status of the scammer.
/// @dev scammerAddress is the address of the scammer.
/// @dev transaction is the list of transaction details of the scammer.
struct ScammerReported {
bool reported;
address scammerAddress;
TransactionDetails[] transaction;
}
/// @notice ScammerAddressRecord is a struct that contains the stage, address, transaction hash, and timestamp of a scammer.
/// @dev stage is the stage of the scammer.
/// @dev to is the address of the scammer.
/// @dev txIn is the transaction hash of the scammer.
/// @dev timestamp is the timestamp of the transaction.
struct ScammerAddressRecord {
uint8 stage;
address to;
bytes32 txIn;
uint256 timestamp;
}
/// @notice Set is a struct that contains the index and address of a scammer.
/// @dev index is the index of the scammer.
/// @dev addr is the address of the scammer.
struct Set {
uint256 index;
address addr;
}
}
interface IESR {
// function reportAddress(address _addr, bytes32 _transactionHash) external;
function reportAddress(ReportModel.UserReport calldata report) external;
function isAddressReported(address addr) external view returns (bool);
function getAllAddressReports(
address addr
) external view returns (ReportModel.ScammerAddressRecord[] memory);
function getAllAddressTransactions(
address addr
) external view returns (ReportModel.TransactionDetails[] calldata);
}
abstract contract EthereumScammerRegistry is IESR, ReportModel {
mapping(address => ScammerReported) public scammerMap;
mapping(address => ScammerAddressRecord[]) public publicReports;
mapping(address => Set[]) private userReportSet;
function _addScammerReport(
bool _reported,
address _scammerAddress,
bytes32 _transactionHash,
uint256 _chainId
) internal {
if (_scammerAddress == address(0) || _scammerAddress == msg.sender) {
revert InvalidInput("address zero or self");
}
if (_transactionHash == bytes32(0)) revert InvalidInput("tx zero");
ScammerReported storage scammer = scammerMap[_scammerAddress];
if (!scammer.reported) {
scammer.reported = _reported;
scammer.scammerAddress = _scammerAddress;
scammer.transaction = new TransactionDetails[](0);
}
scammer.transaction.push(
TransactionDetails(_transactionHash, _chainId)
);
emit ScamTransactionReported(_scammerAddress, _transactionHash);
}
function _newScammerAddressRecord(
uint8 _stage,
address _to,
bytes32 _txId,
uint256 _timestamp
) internal pure returns (ScammerAddressRecord memory) {
return ScammerAddressRecord(_stage, _to, _txId, _timestamp);
}
function _addScammerAddressRecord(
uint8 _stage,
address _to,
bytes32 _txId,
uint256 _timestamp
) internal {
if (_to == address(0)) revert InvalidInput("address zero");
if (_txId == bytes32(0)) revert InvalidInput("tx zero");
if (_timestamp > block.timestamp) {
revert InvalidInput("timestamp invalid");
}
ScammerAddressRecord memory scammer = _newScammerAddressRecord(
_stage,
_to,
_txId,
_timestamp
);
userReportSet[msg.sender].push(Set(publicReports[_to].length, _to));
publicReports[_to].push(scammer);
emit PublicReportUpdated(publicReports[_to]);
}
// WIP
function getAllMyReports()
external
view
returns (ScammerAddressRecord[] memory)
{
if (userReportSet[msg.sender].length <= 0) {
revert NoReportsFound(userReportSet[msg.sender]);
}
ScammerAddressRecord[] memory reports = new ScammerAddressRecord[](
userReportSet[msg.sender].length
);
for (uint256 i = 0; i < userReportSet[msg.sender].length; i++) {
reports[i] = publicReports[userReportSet[msg.sender][i].addr][
userReportSet[msg.sender][i].index
];
}
return reports;
}
function getAllAddressTransactions(
address addr
) external view returns (TransactionDetails[] memory) {
TransactionDetails[] memory returnTransactions = scammerMap[addr]
.transaction;
return returnTransactions;
}
function isAddressReported(address addr) external view returns (bool) {
return scammerMap[addr].reported;
}
function getAllAddressReports(
address addr
) external view override returns (ScammerAddressRecord[] memory) {
return publicReports[addr];
}
}
Blacklist.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
// import {ReportModel} from "./src/BLSModels.sol";
import {EthereumScammerRegistry} from "./utils/BLSModels.sol";
contract Blacklist is Ownable, EthereumScammerRegistry {
constructor() Ownable(msg.sender) {}
function reportAddress(UserReport calldata report) external {
if (report.scammers.length <= 0 || report.transactions.length <= 0)
revert InvalidInput();
if (report.scammers.length != report.transactions.length)
revert InvalidInput();
for (uint8 i = 0; i < report.scammers.length; i++) {
_addScammerReport(true, report.scammers[i], report.transactions[i]);
_addScammerAddressRecord(
i,
report.scammers[i],
report.transactions[i],
block.timestamp
);
}
}
}