├── docs ├── .gitkeep ├── AFRX_Token_White_Paper.pdf ├── Updated_AFRX_White_Paper_v1_4_May2025.pdf ├── AFRX_Dividend_FAQ.md ├── AFRX_White_Paper_Section_7_Escrow.md └── AFRXToken_legacy.sol ├── branding ├── afrx-token.jpg └── README.md ├── archived-contracts ├── README.md └── AFRXToken.sol ├── NOTICE.md ├── .github ├── ISSUE_TEMPLATE │ ├── feature_request.md │ ├── bug_report.md │ └── security-issue-template.md └── workflows │ └── blank.yml ├── SECURITY.md ├── LICENSE ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── CHANGELOG.md ├── README.md └── AFRXTokenV1_18.sol /docs/.gitkeep: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /branding/afrx-token.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afrail-inc/afrx-security-token/HEAD/branding/afrx-token.jpg -------------------------------------------------------------------------------- /docs/AFRX_Token_White_Paper.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afrail-inc/afrx-security-token/HEAD/docs/AFRX_Token_White_Paper.pdf -------------------------------------------------------------------------------- /docs/Updated_AFRX_White_Paper_v1_4_May2025.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afrail-inc/afrx-security-token/HEAD/docs/Updated_AFRX_White_Paper_v1_4_May2025.pdf -------------------------------------------------------------------------------- /archived-contracts/README.md: -------------------------------------------------------------------------------- 1 | # Archived Smart Contracts 2 | 3 | This folder contains previously deployed or deprecated versions of the AFRX Security Token smart contracts for reference and audit history. 4 | -------------------------------------------------------------------------------- /branding/README.md: -------------------------------------------------------------------------------- 1 | # AFRX Token Design 2 | 3 | This folder contains the official design mockup of the AFRX Security Token. 4 | 5 | ## File 6 | 7 | - `AFRX Security Token Icon` – Gold coin visual used for presentation, media, and token listing previews. 8 | 9 | ## Usage 10 | 11 | This mockup may be used in press, social media, investor decks, and listings. Please do not modify the visual elements without prior approval. 12 | 13 | For licensing or reproduction rights, contact: press@afrail.xyz 14 | -------------------------------------------------------------------------------- /NOTICE.md: -------------------------------------------------------------------------------- 1 | # Technical Clarification: ERC-3643 vs ERC-1400 2 | 3 | This repository includes documentation that references ERC‑3643 as being “based on” ERC‑1400. 4 | 5 | ### Clarification: 6 | 7 | - ERC‑3643 adopts many compliance principles originally described in ERC‑1400 — such as document linking, transfer restrictions, and identity-based permissions. 8 | - However, **ERC‑3643 is technically built on ERC‑20**, not on ERC‑1400 contracts. 9 | - This distinction is important for developers, auditors, and protocol reviewers analyzing the AFRX token’s architecture. 10 | 11 | No changes have been made to historical whitepapers or documentation in order to preserve version integrity. 12 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: afrail-inc 7 | 8 | --- 9 | 10 | name: Feature Request 11 | about: Suggest an idea for this project. 12 | title: "[FEATURE]" 13 | labels: enhancement 14 | assignees: '' 15 | 16 | ## Feature Description 17 | Please describe the feature you would like to see implemented. 18 | 19 | ## Motivation 20 | Why is this feature important? How will it benefit the AFRX project and its users? 21 | 22 | ## Proposed Solution 23 | Provide a clear and detailed explanation of how this feature might be implemented. If you have any code or ideas, feel free to share them! 24 | 25 | ## Additional Context 26 | Any additional information (e.g., relevant research, examples of similar features elsewhere, etc.). 27 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: afrail-inc 7 | 8 | --- 9 | 10 | name: Bug Report 11 | about: Create a report to help us improve the AFRX Security Token project. 12 | title: "[BUG]" 13 | labels: bug 14 | assignees: '' 15 | 16 | ## Description 17 | Please provide a clear and concise description of what the bug is. 18 | 19 | ## Steps to Reproduce 20 | 1. Step 1 21 | 2. Step 2 22 | 3. Step 3 23 | 24 | ## Expected Behavior 25 | What did you expect to happen? 26 | 27 | ## Actual Behavior 28 | What actually happened? 29 | 30 | ## Screenshots 31 | If applicable, add screenshots to help explain your problem. 32 | 33 | ## Additional Context 34 | Add any other context about the problem here (e.g., version, environment). 35 | 36 | ## Smart Contract Address (if applicable) 37 | Please provide the contract address if relevant. 38 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/security-issue-template.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Security Issue Template 3 | about: Reporting security issues 4 | title: '' 5 | labels: '' 6 | assignees: afrail-inc 7 | 8 | --- 9 | 10 | name: Security Issue 11 | about: Report a security vulnerability or concern. 12 | title: "[SECURITY]" 13 | labels: security 14 | assignees: '' 15 | 16 | ## Description 17 | Please describe the security issue clearly. 18 | 19 | ## Risk Assessment 20 | What level of risk do you assess this issue to have (low, medium, high)? 21 | 22 | ## Steps to Reproduce 23 | 1. Step 1 24 | 2. Step 2 25 | 26 | ## Potential Impact 27 | How could this vulnerability impact users, the contract, or other parts of the system? 28 | 29 | ## Recommended Mitigation 30 | If you have any recommendations or insights on how to mitigate this issue, please provide them. 31 | 32 | ## Contact Information 33 | Please provide contact information where we can reach you for follow-up. 34 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # AFRX Security Policy 2 | 3 | ## Supported Versions 4 | We currently maintain and monitor the latest version of the AFRX Security Token smart contract. 5 | 6 | If you find a security vulnerability, please report it promptly using the process below. 7 | 8 | ## Reporting a Vulnerability 9 | 10 | If you discover a security issue related to this repository: 11 | 12 | - **Do not** create a public issue. 13 | - Instead, please email us directly at: 14 | 15 | **security@afrail.xyz** 16 | 17 | Provide as much detail as possible, including: 18 | - A clear description of the issue. 19 | - Steps to reproduce (if applicable). 20 | - The potential impact or risk. 21 | 22 | We will respond within **5 business days** and coordinate a resolution as quickly as possible. 23 | 24 | ## Our Commitment 25 | We are committed to protecting the security of AFRX token holders and users. 26 | Responsible disclosure is greatly appreciated and encouraged. 27 | 28 | Thank you for helping to make AFRX safer and more secure! 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Afrail Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /.github/workflows/blank.yml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow to help you get started with GitHub Actions 2 | 3 | name: CI 4 | 5 | # Set strict permissions to improve security 6 | permissions: 7 | contents: read 8 | actions: read 9 | pull-requests: none 10 | statuses: none 11 | 12 | on: 13 | # Triggers the workflow on push or pull request events to the "main" branch 14 | push: 15 | branches: [ "main" ] 16 | pull_request: 17 | branches: [ "main" ] 18 | 19 | # Allows manual trigger from the GitHub Actions tab 20 | workflow_dispatch: 21 | 22 | jobs: 23 | # This workflow contains a single job called "build" 24 | build: 25 | runs-on: ubuntu-latest 26 | 27 | steps: 28 | # Checks out your repository under $GITHUB_WORKSPACE 29 | - uses: actions/checkout@v4 30 | 31 | # Runs a one-line shell command 32 | - name: Run a one-line script 33 | run: echo Hello, world! 34 | 35 | # Runs a multi-line shell command 36 | - name: Run a multi-line script 37 | run: | 38 | echo Add other actions to build, 39 | echo test, and deploy your project. 40 | -------------------------------------------------------------------------------- /docs/AFRX_Dividend_FAQ.md: -------------------------------------------------------------------------------- 1 | AFRX Security Token – Dividend Distribution FAQ 2 | 3 | Who is eligible to receive dividends from AFRX? 4 | 5 | All verified and registered AFRX security token holders are eligible. This excludes wallets that do not meet compliance under ERC-3643 standards. 6 | 7 | Are dividends distributed to all AFRX investors or only specific roles? 8 | 9 | Dividends are distributed proportionally to all eligible token holders, based on holdings. There’s no special role required beyond ERC-3643 compliance. 10 | 11 | How often will dividends be paid out? 12 | 13 | Dividends are paid periodically based on profits generated by Afrail Inc. and its smart mobility subsidiaries. Schedules and amounts will be announced in advance. 14 | 15 | How are dividends distributed to token holders? 16 | 17 | Dividends are paid in USDC (or another approved stablecoin), directly to the verified wallets of compliant holders. 18 | 19 | Do I need to take any action to receive my dividends? 20 | 21 | No — as long as your wallet is verified and compliant, dividends are distributed automatically through the AFRX smart contract’s claim mechanism. 22 | 23 | What is ERC-3643 and why is it important? 24 | 25 | ERC-3643 enforces on-chain identity verification, regulatory compliance, and jurisdictional transfer restrictions — making it ideal for secure, legal dividend payouts. 26 | 27 | Where can I find updates or reports on dividend distributions? 28 | 29 | Official updates will be published on: 30 | 31 | The Afrail Inc. investor relations page 32 | 33 | The AFRX dashboard 34 | 35 | Our GitHub repository and community channels 36 | 37 | More info: https://afrail.xyz 38 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | Code of Conduct 2 | 3 | Our Commitment 4 | 5 | We are committed to fostering a welcoming, safe, and respectful environment for everyone contributing to or using the AFRX Security Token repository. 6 | 7 | We expect all interactions to reflect the professional, compliance-focused nature of the AFRX project, and to comply with applicable legal and regulatory standards. 8 | 9 | Expected Behavior 10 | 11 | Act with integrity, respect, and professionalism at all times. 12 | 13 | Use inclusive and welcoming language. 14 | 15 | Be considerate of differing viewpoints, experiences, and backgrounds. 16 | 17 | Provide constructive feedback. 18 | 19 | Uphold compliance and regulatory standards relevant to financial technology and securities. 20 | 21 | Unacceptable Behavior 22 | 23 | Use of discriminatory, harassing, or abusive language or conduct. 24 | 25 | Any form of harassment, intimidation, or threats. 26 | 27 | Sharing misleading, false, or non-compliant information related to securities. 28 | 29 | Disrespectful or disruptive behavior that undermines project collaboration. 30 | 31 | Unapproved promotion of third-party products or services. 32 | 33 | Responsibilities of Maintainers 34 | 35 | Repository maintainers are responsible for clarifying the standards of acceptable behavior and will take appropriate corrective action in response to any behavior they deem inappropriate, threatening, offensive, or harmful. 36 | 37 | Maintainers have the right to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that violate this Code of Conduct. 38 | 39 | Reporting Issues 40 | 41 | If you experience or witness unacceptable behavior, please report it by emailing: 42 | 43 | conduct@afrail.xyz 44 | 45 | Reports will be handled confidentially. 46 | 47 | Scope 48 | 49 | This Code of Conduct applies within all project spaces, including GitHub issues, pull requests, and discussions, and in any project-related public spaces. 50 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to AFRX Security Token 2 | 3 | Thank you for considering contributing to the AFRX Security Token project! 4 | 5 | We welcome contributions that improve the project, enhance its compliance, security, or overall functionality. 6 | 7 | Please follow the guidelines below to help maintain a high standard of quality. 8 | 9 | ## Code of Conduct 10 | Participation in this project is governed by our [Code of Conduct](./CODE_OF_CONDUCT.md). 11 | Please review it before contributing. 12 | 13 | ## How to Contribute 14 | 15 | ### Reporting Issues 16 | - Use GitHub Issues to report bugs, suggest enhancements, or propose new features. 17 | - Provide clear and concise information to help us reproduce and understand the issue. 18 | 19 | ### Submitting Pull Requests 20 | 1. Fork the repository and create your branch from `main`. 21 | 2. Write clear, well-documented code that aligns with the project's style and standards. 22 | 3. Ensure all existing tests pass and add new tests if applicable. 23 | 4. Describe your changes clearly in the Pull Request (PR) description. 24 | 5. Link the issue your PR addresses, if applicable. 25 | 26 | ### Code Standards 27 | - Solidity Smart Contracts must follow industry best practices (e.g., [OpenZeppelin Guidelines](https://docs.openzeppelin.com/)). 28 | - Maintain clear, professional commit messages. 29 | - Perform security-focused code reviews for smart contracts. 30 | 31 | ### Legal and Compliance 32 | - Contributions must comply with relevant securities laws and regulations. 33 | - Do not submit content that might mislead investors or users. 34 | 35 | ## Communication 36 | For major changes or feature proposals, please first open an issue to discuss what you would like to change. 37 | 38 | Questions, clarifications, or sensitive disclosures should be emailed to: 39 | 40 | **conduct@afrail.xyz** 41 | 42 | # Website 43 | **https://afrail.xyz** 44 | 45 | Thank you for helping us build a secure, compliant, and innovative future with AFRX! 46 | -------------------------------------------------------------------------------- /docs/AFRX_White_Paper_Section_7_Escrow.md: -------------------------------------------------------------------------------- 1 | 2 | # 7. Proceeds Management and Escrow Strategy 3 | 4 | This section outlines how proceeds from the AFRX Security Token sale will be managed, including details on escrow custody, access requirements, and interim financing options. These structures are designed to protect investors, comply with regulatory best practices, and support pre-operational readiness for Afrail Inc. 5 | 6 | ## 7.1 Escrow-Based Custody of AFRX Proceeds 7 | 8 | All cash proceeds from the sale of AFRX Security Tokens will be paid directly into a regulated Escrow Account upon the commencement of token production and sale. This escrow mechanism ensures: 9 | 10 | - Transparency 11 | - Investor protection 12 | - Regulatory and compliance assurance 13 | 14 | Afrail Inc. will not be able to access these funds immediately upon receipt. 15 | 16 | ## 7.2 Access Contingency: Minimum Raise Threshold 17 | 18 | Funds in escrow will remain locked until Afrail Inc. reaches a minimum raise threshold of **10% of the targeted raise amount**. 19 | 20 | - **Target Raise:** $4.039 billion USD 21 | - **10% Threshold:** $403.9 million USD 22 | 23 | Once this threshold is reached and verified, Afrail Inc. may begin drawing down funds per agreed use-of-funds schedules and reporting requirements. 24 | 25 | ## 7.3 Bridge Financing Potential 26 | 27 | Prior to reaching the 10% threshold, Afrail Inc. will explore short-term, low-interest loans backed by the escrowed AFRX funds to support early operations, including: 28 | 29 | - Pre-engineering & infrastructure readiness in South Florida 30 | - Site preparation and engineering surveys in Northern Namibia 31 | 32 | These operations will help maintain momentum and readiness while ensuring financial prudence and accountability. 33 | 34 | ## 7.4 Key Benefits 35 | 36 | - ⚖️ **Investor Assurance**: Investors know their capital is not misused before substantial capital is raised. 37 | - 🔐 **Compliance & Credibility**: Aligns with global standards for real-world asset tokenization and STOs. 38 | - 🚀 **Operational Readiness**: Allows Afrail Inc. to progress with foundational work through secured interim financing. 39 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # CHANGELOG 2 | 3 | All notable changes to the AFRX Security Token smart contract repository will be documented in this file. 4 | 5 | ---------- 6 | 7 | ## [v1.18.4] – 2025-07-17 8 | 9 | ### Updated 10 | 11 | - `README.md`: Added prominent link at the top to `NOTICE.md` to clarify that ERC‑3643 is built on ERC‑20, not ERC‑1400. 12 | 13 | ---------- 14 | 15 | ## [v1.18.3] – 2025-07-17 16 | 17 | ### Added 18 | 19 | - `NOTICE.md` with technical clarification that ERC‑3643 is built on ERC‑20, not ERC‑1400. 20 | 21 | ### Updated 22 | 23 | - `README.md`: Updated all references from ERC‑1400 to ERC‑20 to reflect accurate token standard architecture. 24 | 25 | ---------- 26 | 27 | ## [v1.18.2] – 2025-06-24 28 | 29 | ### Changed 30 | 31 | - Replaced outdated README.md with updated full version including token design breakdown and corrected anchor links. 32 | - Linked the official AFRX token image (`branding/afrx-token.jpg`) in both image display and internal documentation. 33 | - Verified internal markdown anchors to ensure all Project Links route correctly to their sections. 34 | 35 | ---------- 36 | 37 | ## [v1.18.1] – 2025-06-22 38 | 39 | ### Changed 40 | 41 | - Updated token name in `AFRXTokenV1_18.sol` from "AfrailX Security Token" to "Afrail Security Token" (line 57). 42 | - No changes to contract logic or functionality. 43 | - Aligned token metadata with official issuer name: Afrail Inc. 44 | 45 | ---------- 46 | 47 | ## [v1.18] – 2025-06-22 48 | 49 | ### Added 50 | 51 | - Full audit-style revision of smart contract using CertiK-style methodology. 52 | - `AFRXTokenV1_18.sol` introduced with final production-grade compliance features. 53 | - Improved error handling and input validations across all major functions. 54 | - `dividendInfo[roundId].reclaimedAmount` to track unclaimed dividend recoveries. 55 | - `snapshotTimestamps` mapping for reclaim timing logic enforcement. 56 | - Events for `DividendsReclaimed`, `TestModeToggled`, `Upgraded` added. 57 | - Role-guarded upgradeability with UUPS implementation and `UPGRADER_ROLE`. 58 | 59 | ### Changed 60 | 61 | - Migrated prior production contract `AFRXToken.sol` to `contracts-archive` folder. 62 | - Updated jurisdiction enforcement to use normalized lowercase hashed keys. 63 | - Whitelisting logic now includes on-chain jurisdiction verification. 64 | - Token issuance now includes lockup timing enforcement (`LOCKUP_PERIOD`). 65 | - Enhanced double-claim protection in `claimDividends()` with snapshot balance validation. 66 | 67 | ### Fixed 68 | 69 | - Redundant logic and poor modularization from previous versions. 70 | - Snapshot imbalance issue when treasury or contract address skewed supply. 71 | - Compatibility issues with OpenZeppelin `ERC20Capped` and `Snapshot`. 72 | 73 | ### Deprecated 74 | 75 | - `AFRXToken.sol` (moved to archive) 76 | - `AFRXToken_legacy.sol` (retained for historical reference) 77 | 78 | ---------- 79 | 80 | ## [v1.17] – 2025-06-21 81 | 82 | Initial final-stage candidate before CertiK audit pass. Introduced: 83 | 84 | - Structured investor lockup and jurisdiction enforcement. 85 | - Basic dividend distribution via snapshots. 86 | - Full OpenZeppelin upgradeable framework. 87 | 88 | ---------- 89 | 90 | ## [v1.0.0] – 2025-05-26 91 | 92 | ### Added 93 | 94 | - Initial implementation of AFRX Security Token contract (ERC-3643 based) 95 | - KYC/AML whitelisting framework via Tokeny and ONCHAINID integration 96 | - Basic compliance modules: document linking, transfer validation, forced transfers 97 | - Terms of Service, Tokenomics, Audit, and Legal documentation files 98 | - Investor onboarding guides and full technical overview in `/docs` 99 | - Initial `README.md` and licensing files 100 | -------------------------------------------------------------------------------- /docs/AFRXToken_legacy.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // AFRXToken_legacy.sol 3 | // Archived legacy version of the AFRX Security Token smart contract. 4 | // Note: Superseded by AFRXToken.sol v1.1 due to enhancements in compliance and dividend distribution mechanisms. 5 | 6 | pragma solidity ^0.8.0; 7 | 8 | // SPDX-License-Identifier: MIT pragma solidity ^0.8.20; 9 | 10 | import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Capped.sol"; import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol"; import "@openzeppelin/contracts/security/Pausable.sol"; import "@openzeppelin/contracts/access/AccessControl.sol"; import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Snapshot.sol"; import "@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; 11 | 12 | contract AFRXToken is Initializable, ERC20Capped, ERC20Burnable, Pausable, AccessControl, ReentrancyGuard, ERC20Snapshot, UUPSUpgradeable { bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE"); bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE"); bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE"); bytes32 public constant SNAPSHOT_ROLE = keccak256("SNAPSHOT_ROLE"); bytes32 public constant UPGRADER_ROLE = keccak256("UPGRADER_ROLE"); 13 | 14 | uint256 public constant LOCKUP_PERIOD = 365 days; 15 | uint8 public constant TOKEN_DECIMALS = 18; 16 | string private constant TOKEN_NAME = "AfrailX Security Token"; 17 | string private constant TOKEN_SYMBOL = "AFRX"; 18 | 19 | struct InvestorInfo { 20 | uint256 releaseTime; 21 | bool isWhitelisted; 22 | string jurisdiction; 23 | } 24 | 25 | mapping(address => InvestorInfo) private _investors; 26 | mapping(bytes32 => bool) private allowedJurisdictions; 27 | 28 | event TokensIssued(address indexed investor, uint256 amount, string jurisdiction); 29 | event TokenIssueRejected(address indexed investor, string reason); 30 | event InvestorWhitelisted(address indexed investor, string jurisdiction); 31 | event InvestorRemovedFromWhitelist(address indexed investor); 32 | event JurisdictionAdded(string jurisdiction); 33 | event JurisdictionRemoved(string jurisdiction); 34 | event ContractPaused(string reason); 35 | event ContractUnpaused(); 36 | event DividendsDistributed(uint256 totalAmount); 37 | event BuybackExecuted(uint256 amount); 38 | 39 | modifier onlyWhitelisted(address account) { 40 | require(_investors[account].isWhitelisted, "Investor not whitelisted"); 41 | _; 42 | } 43 | 44 | constructor() ERC20(TOKEN_NAME, TOKEN_SYMBOL) ERC20Capped(5_770_000_000 * (10 ** TOKEN_DECIMALS)) {} 45 | 46 | function initialize(address admin) public initializer { 47 | _grantRole(DEFAULT_ADMIN_ROLE, admin); 48 | _grantRole(ADMIN_ROLE, admin); 49 | _grantRole(MINTER_ROLE, admin); 50 | _grantRole(PAUSER_ROLE, admin); 51 | _grantRole(SNAPSHOT_ROLE, admin); 52 | _grantRole(UPGRADER_ROLE, admin); 53 | } 54 | 55 | function issueTokens(address investor, uint256 amount, string memory jurisdiction) external onlyRole(MINTER_ROLE) whenNotPaused { 56 | require(totalSupply() + amount <= cap(), "Cap exceeded"); 57 | require(_isJurisdictionAllowed(jurisdiction), "Jurisdiction not allowed"); 58 | 59 | _mint(investor, amount); 60 | 61 | if (_investors[investor].releaseTime == 0) { 62 | _investors[investor] = InvestorInfo({ 63 | releaseTime: block.timestamp + LOCKUP_PERIOD, 64 | isWhitelisted: true, 65 | jurisdiction: _normalizeString(jurisdiction) 66 | }); 67 | } 68 | 69 | emit TokensIssued(investor, amount, jurisdiction); 70 | } 71 | 72 | function batchIssueTokens(address[] calldata investors, uint256[] calldata amounts, string[] calldata jurisdictions) external onlyRole(MINTER_ROLE) whenNotPaused { 73 | require(investors.length == amounts.length && amounts.length == jurisdictions.length, "Array lengths mismatch"); 74 | for (uint256 i = 0; i < investors.length; ++i) { 75 | issueTokens(investors[i], amounts[i], jurisdictions[i]); 76 | } 77 | } 78 | 79 | function _beforeTokenTransfer(address from, address to, uint256 amount) internal override(ERC20, ERC20Snapshot) whenNotPaused { 80 | if (from != address(0) && to != address(0)) { 81 | require(_investors[from].isWhitelisted, "Sender not whitelisted"); 82 | require(_investors[to].isWhitelisted, "Recipient not whitelisted"); 83 | require(block.timestamp >= _investors[from].releaseTime, "Lock-up not over"); 84 | require(_isJurisdictionAllowed(_investors[to].jurisdiction), "Recipient jurisdiction not allowed"); 85 | } 86 | super._beforeTokenTransfer(from, to, amount); 87 | } 88 | 89 | function whitelistInvestor(address investor, string memory jurisdiction) external onlyRole(ADMIN_ROLE) { 90 | require(_isJurisdictionAllowed(jurisdiction), "Jurisdiction not allowed"); 91 | _investors[investor].isWhitelisted = true; 92 | _investors[investor].jurisdiction = _normalizeString(jurisdiction); 93 | emit InvestorWhitelisted(investor, jurisdiction); 94 | } 95 | 96 | function removeFromWhitelist(address investor) external onlyRole(ADMIN_ROLE) { 97 | _investors[investor].isWhitelisted = false; 98 | emit InvestorRemovedFromWhitelist(investor); 99 | } 100 | 101 | function addJurisdiction(string memory jurisdiction) external onlyRole(ADMIN_ROLE) { 102 | bytes32 j = keccak256(abi.encodePacked(_normalizeString(jurisdiction))); 103 | allowedJurisdictions[j] = true; 104 | emit JurisdictionAdded(jurisdiction); 105 | } 106 | 107 | function removeJurisdiction(string memory jurisdiction) external onlyRole(ADMIN_ROLE) { 108 | bytes32 j = keccak256(abi.encodePacked(_normalizeString(jurisdiction))); 109 | allowedJurisdictions[j] = false; 110 | emit JurisdictionRemoved(jurisdiction); 111 | } 112 | 113 | function distributeDividends(uint256 totalAmount) external onlyRole(ADMIN_ROLE) nonReentrant whenNotPaused { 114 | require(totalSupply() > 0, "No tokens in circulation"); 115 | _mint(address(this), totalAmount); 116 | for (uint256 i = 0; i < getRoleMemberCount(DEFAULT_ADMIN_ROLE); i++) { 117 | address investor = getRoleMember(DEFAULT_ADMIN_ROLE, i); 118 | if (balanceOf(investor) > 0) { 119 | uint256 share = (balanceOf(investor) * totalAmount) / totalSupply(); 120 | _transfer(address(this), investor, share); 121 | } 122 | } 123 | emit DividendsDistributed(totalAmount); 124 | } 125 | 126 | function executeBuyback(uint256 amount) external onlyRole(ADMIN_ROLE) whenNotPaused { 127 | require(balanceOf(msg.sender) >= amount, "Insufficient tokens"); 128 | _burn(msg.sender, amount); 129 | emit BuybackExecuted(amount); 130 | } 131 | 132 | function _normalizeString(string memory str) internal pure returns (string memory) { 133 | bytes memory bStr = bytes(str); 134 | for (uint256 i = 0; i < bStr.length; i++) { 135 | if ((uint8(bStr[i]) >= 65) && (uint8(bStr[i]) <= 90)) { 136 | bStr[i] = bytes1(uint8(bStr[i]) + 32); 137 | } 138 | } 139 | return string(bStr); 140 | } 141 | 142 | function pause(string memory reason) external onlyRole(PAUSER_ROLE) { 143 | _pause(); 144 | emit ContractPaused(reason); 145 | } 146 | 147 | function unpause() external onlyRole(PAUSER_ROLE) { 148 | _unpause(); 149 | emit ContractUnpaused(); 150 | } 151 | 152 | function snapshot() external onlyRole(SNAPSHOT_ROLE) { 153 | _snapshot(); 154 | } 155 | 156 | function _authorizeUpgrade(address newImplementation) internal override onlyRole(UPGRADER_ROLE) {} 157 | 158 | // Required overrides 159 | function decimals() public pure override returns (uint8) { 160 | return TOKEN_DECIMALS; 161 | } 162 | 163 | function supportsInterface(bytes4 interfaceId) public view override(AccessControl) returns (bool) { 164 | return super.supportsInterface(interfaceId); 165 | } 166 | 167 | } 168 | 169 | -------------------------------------------------------------------------------- /archived-contracts/AFRXToken.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // AFRXToken.sol 3 | // Version: 1.1 4 | // Production version of the AFRX Security Token smart contract. 5 | // Features: Snapshot-based dividends, 365-day lockup, KYC/AML compliance, buyback and burn, UUPS upgradeability. 6 | // Compliant with SEC Regulation D Rule 506(c) and Regulation S. 7 | 8 | pragma solidity ^0.8.20; 9 | 10 | // SPDX-License-Identifier: MIT pragma solidity ^0.8.20; 11 | 12 | import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Capped.sol"; import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol"; import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Snapshot.sol"; import "@openzeppelin/contracts/security/Pausable.sol"; import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; import "@openzeppelin/contracts/access/AccessControl.sol"; import "@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol"; 13 | 14 | contract AFRXToken is ERC20Capped, ERC20Burnable, ERC20Snapshot, Pausable, AccessControl, ReentrancyGuard, UUPSUpgradeable { bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE"); bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE"); bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE"); bytes32 public constant SNAPSHOT_ROLE = keccak256("SNAPSHOT_ROLE"); bytes32 public constant UPGRADER_ROLE = keccak256("UPGRADER_ROLE"); 15 | 16 | uint256 public constant LOCKUP_PERIOD = 365 days; 17 | uint8 public constant TOKEN_DECIMALS = 18; 18 | 19 | struct InvestorInfo { 20 | uint256 releaseTime; 21 | bool isWhitelisted; 22 | string jurisdiction; 23 | } 24 | 25 | struct DividendInfo { 26 | uint256 totalAmount; 27 | uint256 snapshotId; 28 | uint256 totalSupplyAtSnapshot; 29 | } 30 | 31 | mapping(address => InvestorInfo) private _investors; 32 | mapping(bytes32 => bool) private allowedJurisdictions; 33 | 34 | mapping(uint256 => DividendInfo) public dividendInfo; 35 | mapping(uint256 => mapping(address => bool)) public dividendsClaimed; 36 | uint256 public currentDividendRound; 37 | 38 | event TokensIssued(address indexed investor, uint256 amount, string jurisdiction); 39 | event TokenIssueRejected(address indexed investor, string reason); 40 | event InvestorWhitelisted(address indexed investor, string jurisdiction); 41 | event InvestorRemovedFromWhitelist(address indexed investor); 42 | event JurisdictionAdded(string jurisdiction); 43 | event JurisdictionRemoved(string jurisdiction); 44 | event ContractPaused(string reason); 45 | event ContractUnpaused(); 46 | event DividendsAvailable(uint256 indexed roundId, uint256 totalAmount); 47 | event DividendsClaimed(uint256 indexed roundId, address indexed investor, uint256 amount); 48 | 49 | modifier onlyWhitelisted(address account) { 50 | require(_investors[account].isWhitelisted, "Investor not whitelisted"); 51 | _; 52 | } 53 | 54 | constructor() ERC20("AfrailX Security Token", "AFRX") ERC20Capped(5_770_000_000 * (10 ** TOKEN_DECIMALS)) {} 55 | 56 | function initialize(address admin) public initializer { 57 | _grantRole(DEFAULT_ADMIN_ROLE, admin); 58 | _grantRole(ADMIN_ROLE, admin); 59 | _grantRole(MINTER_ROLE, admin); 60 | _grantRole(PAUSER_ROLE, admin); 61 | _grantRole(SNAPSHOT_ROLE, admin); 62 | _grantRole(UPGRADER_ROLE, admin); 63 | } 64 | 65 | function issueTokens(address investor, uint256 amount, string memory jurisdiction) external onlyRole(MINTER_ROLE) whenNotPaused { 66 | require(totalSupply() + amount <= cap(), "Cap exceeded"); 67 | require(_isJurisdictionAllowed(jurisdiction), "Jurisdiction not allowed"); 68 | 69 | _mint(investor, amount); 70 | 71 | if (_investors[investor].releaseTime == 0) { 72 | _investors[investor] = InvestorInfo({ 73 | releaseTime: block.timestamp + LOCKUP_PERIOD, 74 | isWhitelisted: true, 75 | jurisdiction: _normalizeString(jurisdiction) 76 | }); 77 | } 78 | 79 | emit TokensIssued(investor, amount, jurisdiction); 80 | } 81 | 82 | function batchIssueTokens(address[] calldata investors, uint256[] calldata amounts, string[] calldata jurisdictions) external onlyRole(MINTER_ROLE) whenNotPaused { 83 | require(investors.length == amounts.length && amounts.length == jurisdictions.length, "Array lengths mismatch"); 84 | for (uint256 i = 0; i < investors.length; ++i) { 85 | issueTokens(investors[i], amounts[i], jurisdictions[i]); 86 | } 87 | } 88 | 89 | function distributeDividends(uint256 totalAmount) external onlyRole(ADMIN_ROLE) nonReentrant whenNotPaused { 90 | require(totalSupply() > 0, "No tokens in circulation"); 91 | 92 | uint256 snapshotId = _snapshot(); 93 | uint256 snapshotSupply = totalSupply(); 94 | 95 | _mint(address(this), totalAmount); 96 | 97 | dividendInfo[currentDividendRound] = DividendInfo({ 98 | totalAmount: totalAmount, 99 | snapshotId: snapshotId, 100 | totalSupplyAtSnapshot: snapshotSupply 101 | }); 102 | 103 | emit DividendsAvailable(currentDividendRound, totalAmount); 104 | currentDividendRound++; 105 | } 106 | 107 | function claimDividends(uint256 roundId) external nonReentrant whenNotPaused onlyWhitelisted(msg.sender) { 108 | require(!dividendsClaimed[roundId][msg.sender], "Already claimed"); 109 | 110 | DividendInfo storage dividend = dividendInfo[roundId]; 111 | uint256 balance = balanceOfAt(msg.sender, dividend.snapshotId); 112 | require(balance > 0, "No tokens at snapshot"); 113 | 114 | uint256 share = (balance * dividend.totalAmount) / dividend.totalSupplyAtSnapshot; 115 | require(share > 0, "No dividends to claim"); 116 | 117 | dividendsClaimed[roundId][msg.sender] = true; 118 | _transfer(address(this), msg.sender, share); 119 | 120 | emit DividendsClaimed(roundId, msg.sender, share); 121 | } 122 | 123 | function _beforeTokenTransfer(address from, address to, uint256 amount) internal override(ERC20, ERC20Snapshot) whenNotPaused { 124 | if (from != address(0) && to != address(0)) { 125 | require(_investors[from].isWhitelisted, "Sender not whitelisted"); 126 | require(_investors[to].isWhitelisted, "Recipient not whitelisted"); 127 | require(block.timestamp >= _investors[from].releaseTime, "Lock-up not over"); 128 | require(_isJurisdictionAllowed(_investors[to].jurisdiction), "Recipient jurisdiction not allowed"); 129 | } 130 | super._beforeTokenTransfer(from, to, amount); 131 | } 132 | 133 | function whitelistInvestor(address investor, string memory jurisdiction) external onlyRole(ADMIN_ROLE) { 134 | require(_isJurisdictionAllowed(jurisdiction), "Jurisdiction not allowed"); 135 | _investors[investor].isWhitelisted = true; 136 | _investors[investor].jurisdiction = _normalizeString(jurisdiction); 137 | emit InvestorWhitelisted(investor, jurisdiction); 138 | } 139 | 140 | function removeFromWhitelist(address investor) external onlyRole(ADMIN_ROLE) { 141 | _investors[investor].isWhitelisted = false; 142 | emit InvestorRemovedFromWhitelist(investor); 143 | } 144 | 145 | function addJurisdiction(string memory jurisdiction) external onlyRole(ADMIN_ROLE) { 146 | bytes32 j = keccak256(abi.encodePacked(_normalizeString(jurisdiction))); 147 | allowedJurisdictions[j] = true; 148 | emit JurisdictionAdded(jurisdiction); 149 | } 150 | 151 | function removeJurisdiction(string memory jurisdiction) external onlyRole(ADMIN_ROLE) { 152 | bytes32 j = keccak256(abi.encodePacked(_normalizeString(jurisdiction))); 153 | allowedJurisdictions[j] = false; 154 | emit JurisdictionRemoved(jurisdiction); 155 | } 156 | 157 | function pause(string memory reason) external onlyRole(PAUSER_ROLE) { 158 | _pause(); 159 | emit ContractPaused(reason); 160 | } 161 | 162 | function unpause() external onlyRole(PAUSER_ROLE) { 163 | _unpause(); 164 | emit ContractUnpaused(); 165 | } 166 | 167 | function snapshot() external onlyRole(SNAPSHOT_ROLE) { 168 | _snapshot(); 169 | } 170 | 171 | function _authorizeUpgrade(address newImplementation) internal override onlyRole(UPGRADER_ROLE) {} 172 | 173 | function _isJurisdictionAllowed(string memory jurisdiction) internal view returns (bool) { 174 | bytes32 j = keccak256(abi.encodePacked(_normalizeString(jurisdiction))); 175 | return allowedJurisdictions[j]; 176 | } 177 | 178 | function _normalizeString(string memory str) internal pure returns (string memory) { 179 | bytes memory bStr = bytes(str); 180 | for (uint256 i = 0; i < bStr.length; i++) { 181 | if ((uint8(bStr[i]) >= 65) && (uint8(bStr[i]) <= 90)) { 182 | bStr[i] = bytes1(uint8(bStr[i]) + 32); 183 | } 184 | } 185 | return string(bStr); 186 | } 187 | 188 | function decimals() public pure override returns (uint8) { 189 | return TOKEN_DECIMALS; 190 | } 191 | 192 | function supportsInterface(bytes4 interfaceId) public view override(AccessControl) returns (bool) { 193 | return super.supportsInterface(interfaceId); 194 | } 195 | 196 | } 197 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AFRX Security Token 2 | 3 |  4 |  5 |  6 | 7 |
8 |
9 |