File size: 11,669 Bytes
9bf0b92
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

/**
 * @title FORGE Token (FRG)
 * @author CIPHER (Cryptographic Agent) β€” co-architecting with NOVA
 * @notice FORGE is the native inter-agent protocol token of the SnapKitty
 *         Stochastic Autonomous Compute Mesh (SACM). It is a UTILITY TOKEN only.
 *         It is NOT a security, investment contract, or financial instrument.
 *         No public sale has occurred. All distributions are subject to legal
 *         review by Jessica Lee Westerhoff, CPA, and applicable counsel before
 *         any public offering. This token confers no ownership, equity, dividends,
 *         or profit-sharing rights in SnapKitty Collective LLC or any affiliated
 *         entity.
 *
 * @dev ERC-20 implementation built on OpenZeppelin 5.x base contracts.
 *      Key mechanics:
 *        - Hard cap: 21,000,000 FRG (twenty-one million, 18 decimals) β€” VAULT ruling 2026-05-21
 *        - Mint gated to WORM-verified work events via the treasury multisig
 *        - Burn mechanic for SEALFORGE tier upgrades
 *        - Governance weight: 1 FRG = 1 vote, capped at 1% of supply per address
 *        - Emergency pause controlled by the Architect role
 *
 * @custom:security-contact legal@snapkitty.io
 * @custom:deployed-by SnapKitty Collective LLC β€” EIN 41-5105572
 * @custom:holding-entity Bel Esprit D'Accord Trust β€” EIN 41-6630640
 */

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Pausable.sol";
import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";

contract FORGE is ERC20, ERC20Burnable, ERC20Pausable, AccessControl, ReentrancyGuard {

    // =========================================================================
    //  ROLES
    // =========================================================================

    /// @notice ARCHITECT_ROLE: highest privilege β€” pause/unpause, role management
    bytes32 public constant ARCHITECT_ROLE = keccak256("ARCHITECT_ROLE");

    /// @notice MINTER_ROLE: granted exclusively to the SnapKitty Treasury multisig
    ///         Minting is triggered only after a WORM-chain entry is verified off-chain
    ///         and submitted by the treasury. No permissionless minting exists.
    bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");

    /// @notice BURNER_ROLE: granted to the SEALFORGE upgrade contract
    ///         Allows programmatic burns on tier-upgrade events
    bytes32 public constant BURNER_ROLE = keccak256("BURNER_ROLE");

    // =========================================================================
    //  SUPPLY CONSTANTS
    // =========================================================================

    /// @notice Absolute hard cap β€” 21,000,000 FRG (VAULT supply ruling 2026-05-21)
    uint256 public constant MAX_SUPPLY = 21_000_000 * 10 ** 18;

    /// @notice Governance vote cap per address: 1% of MAX_SUPPLY
    uint256 public constant GOVERNANCE_CAP = MAX_SUPPLY / 100;

    // =========================================================================
    //  STATE
    // =========================================================================

    /// @notice Total FRG burned to date (informational, tracked across all burn paths)
    uint256 public totalBurned;

    /// @notice WORM entry hash => minted flag β€” prevents replay of the same
    ///         WORM-sealed work event being used to mint twice
    mapping(bytes32 => bool) public wormEntryMinted;

    // =========================================================================
    //  EVENTS
    // =========================================================================

    /// @notice Emitted on every WORM-gated mint
    /// @param recipient Address receiving minted tokens
    /// @param amount Amount minted (18-decimal units)
    /// @param wormEntryHash HMAC-SHA256 hash of the sealed WORM ledger entry
    event WORMMint(
        address indexed recipient,
        uint256 amount,
        bytes32 indexed wormEntryHash
    );

    /// @notice Emitted when SEALFORGE upgrade burns tokens
    /// @param burner Address whose tokens were burned
    /// @param amount Amount burned
    /// @param tier SEALFORGE tier purchased ("basic", "pro", "architect")
    event SealforgeBurn(
        address indexed burner,
        uint256 amount,
        string tier
    );

    /// @notice Emitted on emergency pause
    event EmergencyPause(address indexed architect, string reason);

    /// @notice Emitted on unpause
    event EmergencyUnpause(address indexed architect);

    // =========================================================================
    //  CONSTRUCTOR
    // =========================================================================

    /**
     * @notice Deploy MAGMA token.
     * @param architect Address of the Architect (multisig recommended β€” Gnosis Safe)
     * @param treasury Address of the SnapKitty Treasury multisig (receives MINTER_ROLE)
     */
    constructor(address architect, address treasury) ERC20("FORGE", "FRG") {
        require(architect != address(0), "FORGE: zero architect address");
        require(treasury != address(0), "FORGE: zero treasury address");

        // Grant roles
        _grantRole(DEFAULT_ADMIN_ROLE, architect);
        _grantRole(ARCHITECT_ROLE, architect);
        _grantRole(MINTER_ROLE, treasury);

        // No tokens minted at construction β€” all distribution is WORM-event driven
        // Team / ecosystem allocations are minted by the treasury against
        // pre-scheduled WORM entries at genesis.
    }

    // =========================================================================
    //  MINT β€” WORM-GATED
    // =========================================================================

    /**
     * @notice Mint MGM tokens to `recipient` for a verified WORM work event.
     * @dev Only callable by an address holding MINTER_ROLE (treasury multisig).
     *      The `wormEntryHash` is the HMAC-SHA256 of the sealed WORM ledger entry
     *      and acts as a nonce β€” each entry can only be redeemed once.
     *      The function reverts if minting would exceed MAX_SUPPLY.
     *
     * @param recipient Address to receive minted tokens
     * @param amount Amount to mint (18-decimal units)
     * @param wormEntryHash HMAC-SHA256 bytes32 digest of the WORM ledger entry
     */
    function wormMint(
        address recipient,
        uint256 amount,
        bytes32 wormEntryHash
    )
        external
        nonReentrant
        whenNotPaused
        onlyRole(MINTER_ROLE)
    {
        require(recipient != address(0), "FORGE: mint to zero address");
        require(amount > 0, "FORGE: zero mint amount");
        require(!wormEntryMinted[wormEntryHash], "FORGE: WORM entry already redeemed");
        require(totalSupply() + amount <= MAX_SUPPLY, "FORGE: hard cap exceeded");

        wormEntryMinted[wormEntryHash] = true;
        _mint(recipient, amount);

        emit WORMMint(recipient, amount, wormEntryHash);
    }

    // =========================================================================
    //  BURN β€” SEALFORGE TIER UPGRADES
    // =========================================================================

    /**
     * @notice Burn MGM from `burner` for a SEALFORGE tier upgrade.
     * @dev Callable by BURNER_ROLE (SEALFORGE upgrade contract) OR by the
     *      token holder themselves (standard ERC20Burnable.burn also works).
     *      This route records the tier string in the event for analytics.
     *
     * @param burner Address whose tokens are burned
     * @param amount Amount to burn (18-decimal units)
     * @param tier Human-readable SEALFORGE tier label
     */
    function sealforgeBurn(
        address burner,
        uint256 amount,
        string calldata tier
    )
        external
        nonReentrant
        whenNotPaused
        onlyRole(BURNER_ROLE)
    {
        require(burner != address(0), "FORGE: burn from zero address");
        require(amount > 0, "FORGE: zero burn amount");
        require(balanceOf(burner) >= amount, "FORGE: insufficient balance");

        _burn(burner, amount);

        emit SealforgeBurn(burner, amount, tier);
    }

    // =========================================================================
    //  GOVERNANCE WEIGHT
    // =========================================================================

    /**
     * @notice Returns governance voting weight for `account`.
     * @dev Weight equals token balance, capped at 1% of MAX_SUPPLY (GOVERNANCE_CAP).
     *      This prevents whale capture of governance decisions.
     *      Integrate this function in any on-chain governance module (Governor.sol).
     *
     * @param account Address to query
     * @return weight Effective governance votes (capped)
     */
    function governanceWeight(address account) external view returns (uint256 weight) {
        uint256 balance = balanceOf(account);
        weight = balance > GOVERNANCE_CAP ? GOVERNANCE_CAP : balance;
    }

    // =========================================================================
    //  EMERGENCY PAUSE β€” ARCHITECT ONLY
    // =========================================================================

    /**
     * @notice Pause all token transfers, mints, and burns.
     * @dev Only the Architect role may pause. Emits reason for transparency.
     * @param reason Human-readable reason for the pause (stored in event log)
     */
    function emergencyPause(string calldata reason)
        external
        onlyRole(ARCHITECT_ROLE)
    {
        _pause();
        emit EmergencyPause(msg.sender, reason);
    }

    /**
     * @notice Unpause the contract after an emergency.
     * @dev Only the Architect role may unpause.
     */
    function emergencyUnpause()
        external
        onlyRole(ARCHITECT_ROLE)
    {
        _unpause();
        emit EmergencyUnpause(msg.sender);
    }

    // =========================================================================
    //  SUPPLY INSPECTION
    // =========================================================================

    /**
     * @notice Returns the remaining mintable supply.
     * @return Tokens that can still be minted before hitting MAX_SUPPLY
     */
    function remainingMintable() external view returns (uint256) {
        return MAX_SUPPLY - totalSupply();
    }

    // =========================================================================
    //  OVERRIDES REQUIRED BY SOLIDITY
    // =========================================================================

    /// @dev Resolves diamond inheritance conflict. Tracks totalBurned for all
    ///      burn paths β€” burns are _update calls where `to == address(0)`.
    function _update(
        address from,
        address to,
        uint256 value
    )
        internal
        override(ERC20, ERC20Pausable)
    {
        if (to == address(0)) {
            totalBurned += value;
        }
        super._update(from, to, value);
    }

    // =========================================================================
    //  INTERFACE SUPPORT
    // =========================================================================

    /**
     * @dev Returns true for ERC-20, AccessControl, and ERC-165 interfaces.
     */
    function supportsInterface(bytes4 interfaceId)
        public
        view
        override(AccessControl)
        returns (bool)
    {
        return super.supportsInterface(interfaceId);
    }
}