ERC-1404 Security Token Contract

Deploy to Avalanche C-Chain for on-chain securities

1. Copy Contract

Copy the ERC-1404 compliant smart contract code below

2. Deploy on Avalanche

Use Remix, Hardhat, or Foundry to deploy to Avalanche C-Chain

3. Set Secrets

Configure RPC URL, private key, and contract address in settings

📋 Contract Features

ERC-1404 Compliance

Transfer restriction detection and messaging

KYC/AML Verification

On-chain investor verification status

Lockup Periods

Reg D, Rule 144, Reg A+ restriction support

Corporate Actions

On-chain dividends, splits, and events

Access Control

Role-based permissions for admins and compliance

Emergency Pause

Pause all transfers in case of security issues

🚀 Deployment Instructions

Option 1: Remix IDE (Easiest)

  1. Copy the contract code below
  2. Go to remix.ethereum.org
  3. Create a new file: SecurityToken.sol
  4. Install OpenZeppelin: npm install @openzeppelin/contracts
  5. Compile with Solidity 0.8.19+
  6. Connect MetaMask to Avalanche C-Chain
  7. Deploy with constructor args: ("Chayton Security Token", "CST")
  8. Copy the deployed contract address

Option 2: Hardhat (Advanced)

  1. npx hardhat init
  2. npm install @openzeppelin/contracts
  3. Add contract to contracts/ folder
  4. Configure hardhat.config.js for Avalanche
  5. npx hardhat run scripts/deploy.js --network avalanche

🌐 Avalanche C-Chain Networks:

Mainnet: https://api.avax.network/ext/bc/C/rpc

Fuji Testnet: https://api.avax-test.network/ext/bc/C/rpc

Chain ID: 43114 (Mainnet) | 43113 (Testnet)

SecurityToken.sol

Solidity 0.8.19+
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/security/Pausable.sol";

/**
 * @title SecurityToken (ERC-1404 Compliant)
 * @dev Security token with transfer restrictions for Reg D, 144A, Reg A+ compliance
 */
contract SecurityToken is ERC20, AccessControl, Pausable {
    bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE");
    bytes32 public constant COMPLIANCE_ROLE = keccak256("COMPLIANCE_ROLE");
    
    // Restriction codes (ERC-1404)
    uint8 public constant SUCCESS = 0;
    uint8 public constant TRANSFER_RESTRICTED = 1;
    uint8 public constant SENDER_NOT_VERIFIED = 2;
    uint8 public constant RECIPIENT_NOT_VERIFIED = 3;
    uint8 public constant LOCKUP_PERIOD = 4;
    
    string constant SUCCESS_MESSAGE = "SUCCESS";
    string constant TRANSFER_RESTRICTED_MESSAGE = "Transfer restricted";
    string constant SENDER_NOT_VERIFIED_MESSAGE = "Sender not KYC verified";
    string constant RECIPIENT_NOT_VERIFIED_MESSAGE = "Recipient not KYC verified";
    string constant LOCKUP_PERIOD_MESSAGE = "Token is in lockup period";
    
    struct TokenMetadata {
        string tokenId;
        string certificateNumber;
        uint256 issueDate;
        uint256 restrictionEndDate;
        string restrictionType; // "reg_d", "rule_144", "reg_a_plus", "unrestricted"
        bool isRestricted;
    }
    
    struct InvestorProfile {
        bool isVerified;
        bool isAccredited;
        uint256 verificationDate;
        string investorType; // "individual", "entity", "trust"
    }
    
    // Mappings
    mapping(address => InvestorProfile) public investors;
    mapping(address => TokenMetadata) public tokenMetadata;
    mapping(address => uint256) public lockupEndDate;
    
    // Events
    event TokenMinted(address indexed investor, uint256 amount, string tokenId, string certificateNumber);
    event InvestorVerified(address indexed investor, bool isAccredited);
    event TransferRestrictionUpdated(address indexed investor, uint256 restrictionEndDate);
    event CorporateAction(string actionType, uint256 timestamp, string details);
    
    constructor(
        string memory name,
        string memory symbol
    ) ERC20(name, symbol) {
        _grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
        _grantRole(ADMIN_ROLE, msg.sender);
        _grantRole(COMPLIANCE_ROLE, msg.sender);
    }
    
    /**
     * @dev Mint new security tokens
     */
    function mintToken(
        address investor,
        uint256 amount,
        string memory tokenId,
        string memory certificateNumber,
        string memory restrictionType,
        uint256 restrictionEndDate
    ) external onlyRole(ADMIN_ROLE) {
        require(investors[investor].isVerified, "Investor not verified");
        
        _mint(investor, amount);
        
        tokenMetadata[investor] = TokenMetadata({
            tokenId: tokenId,
            certificateNumber: certificateNumber,
            issueDate: block.timestamp,
            restrictionEndDate: restrictionEndDate,
            restrictionType: restrictionType,
            isRestricted: restrictionEndDate > block.timestamp
        });
        
        if (restrictionEndDate > 0) {
            lockupEndDate[investor] = restrictionEndDate;
        }
        
        emit TokenMinted(investor, amount, tokenId, certificateNumber);
    }
    
    /**
     * @dev Verify investor for KYC/AML compliance
     */
    function verifyInvestor(
        address investor,
        bool isAccredited,
        string memory investorType
    ) external onlyRole(COMPLIANCE_ROLE) {
        investors[investor] = InvestorProfile({
            isVerified: true,
            isAccredited: isAccredited,
            verificationDate: block.timestamp,
            investorType: investorType
        });
        
        emit InvestorVerified(investor, isAccredited);
    }
    
    /**
     * @dev Check if transfer is allowed (ERC-1404)
     */
    function detectTransferRestriction(
        address from,
        address to,
        uint256 amount
    ) public view returns (uint8) {
        if (paused()) return TRANSFER_RESTRICTED;
        if (!investors[from].isVerified) return SENDER_NOT_VERIFIED;
        if (!investors[to].isVerified) return RECIPIENT_NOT_VERIFIED;
        if (lockupEndDate[from] > block.timestamp) return LOCKUP_PERIOD;
        if (balanceOf(from) < amount) return TRANSFER_RESTRICTED;
        
        return SUCCESS;
    }
    
    /**
     * @dev Get message for restriction code (ERC-1404)
     */
    function messageForTransferRestriction(uint8 restrictionCode)
        public
        pure
        returns (string memory)
    {
        if (restrictionCode == SUCCESS) return SUCCESS_MESSAGE;
        if (restrictionCode == SENDER_NOT_VERIFIED) return SENDER_NOT_VERIFIED_MESSAGE;
        if (restrictionCode == RECIPIENT_NOT_VERIFIED) return RECIPIENT_NOT_VERIFIED_MESSAGE;
        if (restrictionCode == LOCKUP_PERIOD) return LOCKUP_PERIOD_MESSAGE;
        return TRANSFER_RESTRICTED_MESSAGE;
    }
    
    /**
     * @dev Override transfer to include compliance checks
     */
    function _beforeTokenTransfer(
        address from,
        address to,
        uint256 amount
    ) internal override {
        super._beforeTokenTransfer(from, to, amount);
        
        if (from != address(0)) { // Skip check for minting
            uint8 restriction = detectTransferRestriction(from, to, amount);
            require(restriction == SUCCESS, messageForTransferRestriction(restriction));
        }
    }
    
    /**
     * @dev Record corporate action on-chain
     */
    function recordCorporateAction(
        string memory actionType,
        string memory details
    ) external onlyRole(ADMIN_ROLE) {
        emit CorporateAction(actionType, block.timestamp, details);
    }
    
    /**
     * @dev Burn tokens (redemption)
     */
    function burn(address account, uint256 amount) external onlyRole(ADMIN_ROLE) {
        _burn(account, amount);
    }
    
    /**
     * @dev Update lockup period
     */
    function updateLockupPeriod(address investor, uint256 newEndDate)
        external
        onlyRole(COMPLIANCE_ROLE)
    {
        lockupEndDate[investor] = newEndDate;
        tokenMetadata[investor].restrictionEndDate = newEndDate;
        tokenMetadata[investor].isRestricted = newEndDate > block.timestamp;
        
        emit TransferRestrictionUpdated(investor, newEndDate);
    }
    
    /**
     * @dev Pause all transfers (emergency)
     */
    function pause() external onlyRole(ADMIN_ROLE) {
        _pause();
    }
    
    /**
     * @dev Unpause transfers
     */
    function unpause() external onlyRole(ADMIN_ROLE) {
        _unpause();
    }
    
    /**
     * @dev Get investor details
     */
    function getInvestorProfile(address investor)
        external
        view
        returns (InvestorProfile memory)
    {
        return investors[investor];
    }
    
    /**
     * @dev Get token metadata
     */
    function getTokenMetadata(address holder)
        external
        view
        returns (TokenMetadata memory)
    {
        return tokenMetadata[holder];
    }
}

✅ After Deployment

  1. Copy your deployed contract address
  2. Go to Admin Settings → Environment Variables
  3. Set AVALANCHE_RPC_URL
  4. Set BLOCKCHAIN_PRIVATE_KEY (admin wallet)
  5. Set SECURITY_TOKEN_CONTRACT_ADDRESS
  6. Grant ADMIN_ROLE and COMPLIANCE_ROLE to your backend wallet address
  7. Return to Securities Registry to start minting tokens on-chain