BTBAccountFactory

0x96e3933677b31f253153ef6fc5d77c321825cacc

Verification
Verified
v0.8.35+commit.47b9dedd
Type
Contract
1,158 bytes
ABI entries
11
4 read · 1 write
License
none

Contract information

Address
0x96e3933677b31f253153ef6fc5d77c321825cacc
Chain
Robinhood Chain (4663)
Compiler
v0.8.35+commit.47b9dedd
Optimization
Enabled
Creation tx
0x1b56835acc…d1d6269d59

Token

Not a token

This contract does not expose ERC-20 metadata.

Read contract (4)

accountOf(address)address
implementation()address
isAccount(address)bool
predictAccount(address)address

Events (1)

AccountCreated

ABI

[
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "implementation_",
        "type": "address"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "constructor"
  },
  {
    "inputs": [],
    "name": "FailedDeployment",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "balance",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "needed",
        "type": "uint256"
      }
    ],
    "name": "InsufficientBalance",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "InvalidImplementation",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "InvalidOwner",
    "type": "error"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "owner",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "account",
        "type": "address"
      }
    ],
    "name": "AccountCreated",
    "type": "event"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "owner",
        "type": "address"
      }
    ],
    "name": "accountOf",
    "outputs": [
      {
        "internalType": "address",
        "name": "account",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "owner",
        "type": "address"
      }
    ],
    "name": "createAccount",
    "outputs": [
      {
        "internalType": "address",
        "name": "account",
        "type": "address"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "implementation",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "account",
        "type": "address"
      }
    ],
    "name": "isAccount",
    "outputs": [
      {
        "internalType": "bool",
        "name": "valid",
        "type": "bool"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "owner",
        "type": "address"
      }
    ],
    "name": "predictAccount",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  }
]

Source code

// SPDX-License-Identifier: MIT
pragma solidity 0.8.35;

import { Clones } from "@openzeppelin/contracts/proxy/Clones.sol";

interface IBTBAccountInitializer {
    function initialize(address owner) external;
}

/// @notice Deterministically deploys one non-upgradeable minimal-clone account per owner.
contract BTBAccountFactory {
    error InvalidImplementation();
    error InvalidOwner();

    address public immutable implementation;
    mapping(address owner => address account) public accountOf;
    mapping(address account => bool valid) public isAccount;

    event AccountCreated(address indexed owner, address indexed account);

    constructor(address implementation_) {
        if (implementation_ == address(0) || implementation_.code.length == 0) revert InvalidImplementation();
        implementation = implementation_;
    }

    function createAccount(address owner) external returns (address account) {
        if (owner == address(0)) revert InvalidOwner();
        account = accountOf[owner];
        if (account != address(0)) return account;

        bytes32 salt = keccak256(abi.encode(block.chainid, owner));
        account = Clones.cloneDeterministic(implementation, salt);
        IBTBAccountInitializer(account).initialize(owner);
        accountOf[owner] = account;
        isAccount[account] = true;
        emit AccountCreated(owner, account);
    }

    function predictAccount(address owner) external view returns (address) {
        address existing = accountOf[owner];
        if (existing != address(0)) return existing;
        return Clones.predictDeterministicAddress(
            implementation, keccak256(abi.encode(block.chainid, owner)), address(this)
        );
    }
}
Chain explorer4256msChain node69ms