AML

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"
        ]
      }
    }
  }
}}

Tags:
Multisig, Multi-Signature, Factory, Oracle|addr:0xb532012fe682dbe2e73e773043668978da2c6a5e|verified:true|block:23703022|tx:0x3580e79ed086310e8921a0d5b4059ac4a96de1ef242129ac5d95d093702b312d|first_check:1761997440

Submitted on: 2025-11-01 12:44:01

Comments

Log in to comment.

No comments yet.