Description:
Multi-signature wallet contract requiring multiple confirmations for transaction execution.
Blockchain: Ethereum
Source Code: View Code On The Blockchain
Solidity Source Code:
{{
"language": "Solidity",
"sources": {
"@openzeppelin/contracts/access/Ownable.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)
pragma solidity ^0.8.20;
import {Context} from "../utils/Context.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* The initial owner is set to the address provided by the deployer. This can
* later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract Ownable is Context {
address private _owner;
/**
* @dev The caller account is not authorized to perform an operation.
*/
error OwnableUnauthorizedAccount(address account);
/**
* @dev The owner is not a valid owner account. (eg. `address(0)`)
*/
error OwnableInvalidOwner(address owner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the address provided by the deployer as the initial owner.
*/
constructor(address initialOwner) {
if (initialOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
if (newOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
"
},
"@openzeppelin/contracts/utils/Context.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}
"
},
"contracts/AML.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import "@openzeppelin/contracts/access/Ownable.sol";
import "./interfaces/ITransferHook.sol";
/**
* @title AML (Anti-Money Laundering)
* @author Your Name
* @notice A transfer hook contract that implements AML compliance controls
* @dev Implements blacklist and whitelist functionality to prevent
* suspicious transactions and ensure regulatory compliance.
* Allows blacklisted addresses to transfer to whitelisted addresses only.
*/
contract AML is Ownable, ITransferHook {
/// @notice Mapping of addresses to their blacklist status
mapping(address => bool) public blacklist;
/// @notice Mapping of addresses to their whitelist status
mapping(address => bool) public whitelist;
/// @notice Mapping of addresses to their oracle status
mapping(address => bool) public oracles;
/// @notice Maximum number of addresses that can be processed in a batch operation
uint256 public constant MAX_BATCH_SIZE = 100;
/// @notice Error thrown when a blacklisted address attempts unauthorized transfer
error Blacklisted(address account);
/// @notice Error thrown when a non-oracle address attempts to call an oracle function
error NotOracle();
/// @notice Error thrown when transfer is not allowed by AML rules
error TransferHookNotAllowed(address from, address to, uint256 amount);
/// @notice Error thrown when trying to set zero address
error InvalidAddress();
/// @notice Error thrown when trying to blacklist the owner
error CannotBlacklistOwner();
/// @notice Error thrown when batch operation exceeds maximum size
error BatchSizeExceeded();
/// @notice Error thrown when owner tries to remove themselves as oracle
error CannotRemoveOwnerAsOracle();
/// @notice Emitted when an address is added to or removed from blacklist
/// @param account The address that was updated
/// @param isBlacklisted True if blacklisted, false if removed from blacklist
event BlacklistUpdated(address indexed account, bool isBlacklisted);
/// @notice Emitted when an address is added to or removed from whitelist
/// @param account The address that was updated
/// @param isWhitelisted True if whitelisted, false if removed from whitelist
event WhitelistUpdated(address indexed account, bool isWhitelisted);
/// @notice Emitted when an address is added to or removed from oracle
/// @param account The address that was updated
/// @param isOracle True if oracle, false if removed from oracle
event OracleUpdated(address indexed account, bool isOracle);
/**
* @notice Constructor sets the owner of the contract
* @param _owner The address that will own this contract
*/
constructor(address _owner) Ownable(_owner) {}
/**
* @notice Modifier to check if the caller is an oracle
* @dev Only the owner or an oracle can call this function
*/
modifier onlyOracle() {
if (!oracles[msg.sender] && msg.sender != owner()) {
revert NotOracle();
}
_;
}
/**
* @notice Transfer hook function called by the token contract before transfers
* @dev Implements AML logic:
* - Blacklisted addresses can only transfer to whitelisted addresses
* - Blacklisted addresses cannot transfer to non-whitelisted addresses
* - Non-blacklisted addresses can transfer freely
* @param from The address tokens are transferred from
* @param to The address tokens are transferred to
* @param _amount The amount of tokens being transferred
* @return success True if transfer is allowed, false otherwise
*/
function onTransfer(address from, address to, uint256 _amount) public view override returns (bool) {
// If sender is blacklisted
if (blacklist[from]) {
// Allow transfer only if recipient is whitelisted
if (whitelist[to]) {
return true;
}
// Block transfer to non-whitelisted addresses
return false;
}
// If recipient is blacklisted (but sender is not)
if (blacklist[to]) {
// Block transfer to blacklisted addresses
return false;
}
// Allow all other transfers
return true;
}
/**
* @notice Sets the blacklist status of an address
* @dev Only the owner or oracle can call this function (oracles are admins)
* @param account The address to update
* @param _isBlacklisted True to blacklist, false to remove from blacklist
*/
function setBlacklist(address account, bool _isBlacklisted) external onlyOracle {
if (account == address(0)) {
revert InvalidAddress();
}
// Prevent oracles from blacklisting the owner
if (_isBlacklisted && account == owner()) {
revert CannotBlacklistOwner();
}
blacklist[account] = _isBlacklisted;
emit BlacklistUpdated(account, _isBlacklisted);
}
/**
* @notice Sets the whitelist status of an address
* @dev Only the owner or oracle can call this function
* @param account The address to update
* @param _isWhitelisted True to whitelist, false to remove from whitelist
*/
function setWhitelist(address account, bool _isWhitelisted) external onlyOracle {
if (account == address(0)) {
revert InvalidAddress();
}
whitelist[account] = _isWhitelisted;
emit WhitelistUpdated(account, _isWhitelisted);
}
/**
* @notice Batch update multiple addresses to blacklist
* @dev Only the owner or oracle can call this function (oracles are admins)
* @param accounts Array of addresses to update
* @param _isBlacklisted True to blacklist all, false to remove all from blacklist
*/
function batchSetBlacklist(address[] calldata accounts, bool _isBlacklisted) external onlyOracle {
if (accounts.length > MAX_BATCH_SIZE) {
revert BatchSizeExceeded();
}
for (uint256 i = 0; i < accounts.length; i++) {
if (accounts[i] == address(0)) {
revert InvalidAddress();
}
// Prevent oracles from blacklisting the owner
if (_isBlacklisted && accounts[i] == owner()) {
revert CannotBlacklistOwner();
}
blacklist[accounts[i]] = _isBlacklisted;
emit BlacklistUpdated(accounts[i], _isBlacklisted);
}
}
/**
* @notice Batch update multiple addresses to whitelist
* @dev Only the owner or oracle can call this function (oracles are admins)
* @param accounts Array of addresses to update
* @param _isWhitelisted True to whitelist all, false to remove all from whitelist
*/
function batchSetWhitelist(address[] calldata accounts, bool _isWhitelisted) external onlyOracle {
if (accounts.length > MAX_BATCH_SIZE) {
revert BatchSizeExceeded();
}
for (uint256 i = 0; i < accounts.length; i++) {
if (accounts[i] == address(0)) {
revert InvalidAddress();
}
whitelist[accounts[i]] = _isWhitelisted;
emit WhitelistUpdated(accounts[i], _isWhitelisted);
}
}
/**
* @notice Sets the oracle status of an address
* @dev Only the owner can call this function
* @param account The address to update
* @param _isOracle True to set as oracle, false to remove as oracle
*/
function setOracle(address account, bool _isOracle) external onlyOwner {
if (account == address(0)) {
revert InvalidAddress();
}
// Prevent owner from removing themselves as oracle (if they are one)
if (!_isOracle && account == owner() && oracles[account]) {
revert CannotRemoveOwnerAsOracle();
}
oracles[account] = _isOracle;
emit OracleUpdated(account, _isOracle);
}
/**
* @notice Check if an address is blacklisted
* @param account The address to check
* @return True if the address is blacklisted
*/
function isBlacklisted(address account) external view returns (bool) {
return blacklist[account];
}
/**
* @notice Check if an address is whitelisted
* @param account The address to check
* @return True if the address is whitelisted
*/
function isWhitelisted(address account) external view returns (bool) {
return whitelist[account];
}
/**
* @notice Check if an address is an oracle
* @param account The address to check
* @return True if the address is an oracle
*/
function isOracle(address account) external view returns (bool) {
return oracles[account];
}
/**
* @notice Check if a transfer would be allowed between two addresses
* @param from The sender address
* @param to The recipient address
* @return True if the transfer would be allowed
*/
function isTransferAllowed(address from, address to) external view returns (bool) {
// If sender is blacklisted
if (blacklist[from]) {
// Allow only if recipient is whitelisted
return whitelist[to];
}
// If recipient is blacklisted (but sender is not)
if (blacklist[to]) {
return false;
}
// Allow all other transfers
return true;
}
}"
},
"contracts/interfaces/ITransferHook.sol": {
"content": "// SPDX-License-Identifier: MIT\r
pragma solidity ^0.8.20;\r
\r
interface ITransferHook {\r
function onTransfer(address from, address to, uint256 amount) external view returns (bool);\r
}"
}
},
"settings": {
"optimizer": {
"enabled": true,
"runs": 200
},
"viaIR": true,
"evmVersion": "paris",
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
}
}
}}
Submitted on: 2025-11-01 12:44:01
Comments
Log in to comment.
No comments yet.