StockFactoryConveyor

0x483c6cc68f9ef77eb9550e13e93657c0f8b60ea6

Verification
Verified
v0.8.26+commit.8a97fa7a
Type
Contract
11,844 bytes
ABI entries
10
5 read · 2 write
License
none

Contract information

Address
0x483c6cc68f9ef77eb9550e13e93657c0f8b60ea6
Chain
Robinhood Chain (4663)
Compiler
v0.8.26+commit.8a97fa7a
Optimization
Enabled
Creation tx
0x5f42175368…e45fe93d9f

Token

Not a token

This contract does not expose ERC-20 metadata.

Read contract (5)

MAX_SALT_TRIES()uint256
factory()address
findSaltTokenLTQuote(string, string, bytes32, address)bytes32, address
getTokenBytecode(string, string, address)bytes
indexer()address

ABI

[
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "_factory",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "_indexer",
        "type": "address"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "constructor"
  },
  {
    "inputs": [],
    "name": "OnlyFactory",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "SaltSearchFailed",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "MAX_SALT_TRIES",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "string",
        "name": "name",
        "type": "string"
      },
      {
        "internalType": "string",
        "name": "symbol",
        "type": "string"
      },
      {
        "internalType": "bytes32",
        "name": "salt",
        "type": "bytes32"
      },
      {
        "internalType": "address",
        "name": "originalDeployer",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "quoteToken",
        "type": "address"
      }
    ],
    "name": "deployToken",
    "outputs": [
      {
        "internalType": "address",
        "name": "token",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "predicted",
        "type": "address"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "factory",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "string",
        "name": "name",
        "type": "string"
      },
      {
        "internalType": "string",
        "name": "symbol",
        "type": "string"
      },
      {
        "internalType": "bytes32",
        "name": "baseSalt",
        "type": "bytes32"
      },
      {
        "internalType": "address",
        "name": "quoteToken",
        "type": "address"
      }
    ],
    "name": "findSaltTokenLTQuote",
    "outputs": [
      {
        "internalType": "bytes32",
        "name": "saltFound",
        "type": "bytes32"
      },
      {
        "internalType": "address",
        "name": "predicted",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "string",
        "name": "name",
        "type": "string"
      },
      {
        "internalType": "string",
        "name": "symbol",
        "type": "string"
      },
      {
        "internalType": "address",
        "name": "quoteToken",
        "type": "address"
      }
    ],
    "name": "getTokenBytecode",
    "outputs": [
      {
        "internalType": "bytes",
        "name": "",
        "type": "bytes"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "indexer",
    "outputs": [
      {
        "internalType": "contract IIndexer",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "_newIndexer",
        "type": "address"
      }
    ],
    "name": "setIndexer",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  }
]

Source code

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.9;

import "./StockToken.sol";

/// @notice Same CREATE2 salt-mining pattern as FactoryConveyor, generalized
/// so the "must sort above the quote asset" check works against whichever
/// stock token a given launch is paired with, instead of a single address
/// fixed at deploy time. One conveyor instance can back launches against any
/// number of different tokenized stocks.
contract StockFactoryConveyor {
    address public immutable factory;
    uint256 public constant MAX_SALT_TRIES = 1024;

    IIndexer public indexer;

    error OnlyFactory();

    modifier onlyFactory() {
        if (msg.sender != factory) revert OnlyFactory();
        _;
    }

    constructor(address _factory, address _indexer) {
        factory = _factory;
        indexer = IIndexer(_indexer);
    }

    function setIndexer(address _newIndexer) external onlyFactory {
        indexer = IIndexer(_newIndexer);
    }

    function _tokenInitCode(
        string memory name,
        string memory symbol,
        address quoteToken
    ) internal view returns (bytes memory) {
        return
            abi.encodePacked(
                type(StockFunToken).creationCode,
                abi.encode(name, symbol, factory, address(indexer), quoteToken)
            );
    }

    function _predictCreate2(
        bytes32 salt,
        bytes32 initCodeHash
    ) internal view returns (address) {
        bytes32 h = keccak256(
            abi.encodePacked(bytes1(0xff), address(this), salt, initCodeHash)
        );
        return address(uint160(uint256(h)));
    }

    function getTokenBytecode(
        string memory name,
        string memory symbol,
        address quoteToken
    ) external view returns (bytes memory) {
        return _tokenInitCode(name, symbol, quoteToken);
    }

    /// @notice Brute-forces a salt so the predicted token address sorts
    /// above `quoteToken` - the same trick FactoryConveyor uses for WBNB,
    /// generalized to any quote asset so the new token always lands as
    /// token1 and the quote asset as token0, a consistent ordering the
    /// Factory's liquidity/fee logic can rely on.
    function findSaltTokenLTQuote(
        string memory name,
        string memory symbol,
        bytes32 baseSalt,
        address quoteToken
    ) external view onlyFactory returns (bytes32 saltFound, address predicted) {
        bytes memory initCode = _tokenInitCode(name, symbol, quoteToken);
        bytes32 initHash = keccak256(initCode);
        uint160 quoteNum = uint160(quoteToken);

        for (uint256 i; i < MAX_SALT_TRIES; ) {
            bytes32 saltCand = keccak256(abi.encodePacked(baseSalt, i));
            address addr = _predictCreate2(saltCand, initHash);
            if (uint160(addr) > quoteNum) {
                return (saltCand, addr);
            }
            unchecked {
                ++i;
            }
        }
        revert SaltSearchFailed();
    }

    function deployToken(
        string memory name,
        string memory symbol,
        bytes32 salt,
        address originalDeployer,
        address quoteToken
    ) external onlyFactory returns (address token, address predicted) {
        bytes memory initCode = _tokenInitCode(name, symbol, quoteToken);
        bytes32 initHash = keccak256(initCode);

        StockFunToken t = new StockFunToken{salt: salt}(
            name,
            symbol,
            factory,
            address(indexer),
            quoteToken
        );
        token = address(t);

        predicted = _predictCreate2(salt, initHash);
        require(token == predicted, "CREATE2 address mismatch");

        indexer.emitSaltBruteforced(salt, token);
        indexer.emitERC20TokenCreated(token, originalDeployer);
        indexer.authorizeAddress(token);
    }
}
Chain explorer1881msChain node92ms