Some checks failed
Deploy / deploy (push) Has been cancelled
Private Ethereum chain (Clique PoA, chain ID 8888) with ERC-20 token (0 decimals, 1 AFC = 1 diamond) bridging casino balances on-chain so players can view tokens in MetaMask. - Geth v1.13.15 node with 5s block time, zero gas cost - AfterCoin ERC-20 contract with owner-gated mint/burn/bridgeTransfer - Bridge API (Express + ethers.js + SQLite) with register, deposit, withdraw, balance, and wallet endpoints - Nonce queue for serial transaction safety - Auto-deploys contract on first boot - Updated mainframe Lua with diff-based on-chain sync (pcall fallback) - Updated card generator Lua with wallet info display Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
184 lines
7.1 KiB
Solidity
184 lines
7.1 KiB
Solidity
// SPDX-License-Identifier: MIT
|
|
pragma solidity ^0.8.20;
|
|
|
|
/**
|
|
* @title AfterCoin (AFC)
|
|
* @notice ERC-20 token for the Afterlife Project game preservation platform.
|
|
* 1 AFC = 1 diamond. Zero decimals — integer-only balances.
|
|
* @dev Self-contained implementation (no OpenZeppelin). Owner-gated mint,
|
|
* burn-from, and bridge-transfer helpers for the off-chain bridge service.
|
|
*/
|
|
contract AfterCoin {
|
|
|
|
// ──────────────────────────── ERC-20 metadata ────────────────────────────
|
|
|
|
string private constant _name = "AfterCoin";
|
|
string private constant _symbol = "AFC";
|
|
uint8 private constant _decimals = 0; // 1 token = 1 diamond
|
|
|
|
// ──────────────────────────── State ──────────────────────────────────────
|
|
|
|
uint256 private _totalSupply;
|
|
|
|
mapping(address => uint256) private _balances;
|
|
mapping(address => mapping(address => uint256)) private _allowances;
|
|
|
|
address public owner;
|
|
|
|
// ──────────────────────────── Events (ERC-20) ────────────────────────────
|
|
|
|
event Transfer(address indexed from, address indexed to, uint256 value);
|
|
event Approval(address indexed owner, address indexed spender, uint256 value);
|
|
|
|
// ──────────────────────────── Errors ─────────────────────────────────────
|
|
|
|
error NotOwner();
|
|
error ZeroAddress();
|
|
error InsufficientBalance(address account, uint256 required, uint256 available);
|
|
error InsufficientAllowance(address spender, uint256 required, uint256 available);
|
|
|
|
// ──────────────────────────── Modifier ───────────────────────────────────
|
|
|
|
modifier onlyOwner() {
|
|
if (msg.sender != owner) revert NotOwner();
|
|
_;
|
|
}
|
|
|
|
// ──────────────────────────── Constructor ────────────────────────────────
|
|
|
|
constructor() {
|
|
owner = msg.sender;
|
|
// Initial supply is 0 — tokens are minted on demand by the bridge.
|
|
}
|
|
|
|
// ──────────────────────────── ERC-20 view functions ──────────────────────
|
|
|
|
function name() external pure returns (string memory) {
|
|
return _name;
|
|
}
|
|
|
|
function symbol() external pure returns (string memory) {
|
|
return _symbol;
|
|
}
|
|
|
|
function decimals() external pure returns (uint8) {
|
|
return _decimals;
|
|
}
|
|
|
|
function totalSupply() external view returns (uint256) {
|
|
return _totalSupply;
|
|
}
|
|
|
|
function balanceOf(address account) external view returns (uint256) {
|
|
return _balances[account];
|
|
}
|
|
|
|
function allowance(address tokenOwner, address spender) external view returns (uint256) {
|
|
return _allowances[tokenOwner][spender];
|
|
}
|
|
|
|
// ──────────────────────────── ERC-20 mutative functions ──────────────────
|
|
|
|
function transfer(address to, uint256 amount) external returns (bool) {
|
|
_transfer(msg.sender, to, amount);
|
|
return true;
|
|
}
|
|
|
|
function approve(address spender, uint256 amount) external returns (bool) {
|
|
_approve(msg.sender, spender, amount);
|
|
return true;
|
|
}
|
|
|
|
function transferFrom(
|
|
address from,
|
|
address to,
|
|
uint256 amount
|
|
) external returns (bool) {
|
|
uint256 currentAllowance = _allowances[from][msg.sender];
|
|
if (currentAllowance != type(uint256).max) {
|
|
if (currentAllowance < amount) {
|
|
revert InsufficientAllowance(msg.sender, amount, currentAllowance);
|
|
}
|
|
unchecked {
|
|
_approve(from, msg.sender, currentAllowance - amount);
|
|
}
|
|
}
|
|
_transfer(from, to, amount);
|
|
return true;
|
|
}
|
|
|
|
// ──────────────────────────── Owner-only functions ───────────────────────
|
|
|
|
/**
|
|
* @notice Mint new tokens to `to`. Only callable by the contract owner.
|
|
* @param to Recipient address.
|
|
* @param amount Number of tokens to create.
|
|
*/
|
|
function mint(address to, uint256 amount) external onlyOwner {
|
|
if (to == address(0)) revert ZeroAddress();
|
|
_totalSupply += amount;
|
|
_balances[to] += amount;
|
|
emit Transfer(address(0), to, amount);
|
|
}
|
|
|
|
/**
|
|
* @notice Burn tokens from `from`. Only callable by the contract owner.
|
|
* Does NOT require an allowance — the owner is the bridge operator.
|
|
* @param from Address whose tokens are burned.
|
|
* @param amount Number of tokens to destroy.
|
|
*/
|
|
function burnFrom(address from, uint256 amount) external onlyOwner {
|
|
if (from == address(0)) revert ZeroAddress();
|
|
uint256 bal = _balances[from];
|
|
if (bal < amount) {
|
|
revert InsufficientBalance(from, amount, bal);
|
|
}
|
|
unchecked {
|
|
_balances[from] = bal - amount;
|
|
}
|
|
_totalSupply -= amount;
|
|
emit Transfer(from, address(0), amount);
|
|
}
|
|
|
|
/**
|
|
* @notice Transfer tokens between two addresses on behalf of the bridge.
|
|
* Only callable by the contract owner.
|
|
* @param from Source address.
|
|
* @param to Destination address.
|
|
* @param amount Number of tokens to move.
|
|
*/
|
|
function bridgeTransfer(
|
|
address from,
|
|
address to,
|
|
uint256 amount
|
|
) external onlyOwner {
|
|
_transfer(from, to, amount);
|
|
}
|
|
|
|
// ──────────────────────────── Internal helpers ───────────────────────────
|
|
|
|
function _transfer(address from, address to, uint256 amount) internal {
|
|
if (from == address(0)) revert ZeroAddress();
|
|
if (to == address(0)) revert ZeroAddress();
|
|
|
|
uint256 fromBal = _balances[from];
|
|
if (fromBal < amount) {
|
|
revert InsufficientBalance(from, amount, fromBal);
|
|
}
|
|
unchecked {
|
|
_balances[from] = fromBal - amount;
|
|
}
|
|
_balances[to] += amount;
|
|
|
|
emit Transfer(from, to, amount);
|
|
}
|
|
|
|
function _approve(address tokenOwner, address spender, uint256 amount) internal {
|
|
if (tokenOwner == address(0)) revert ZeroAddress();
|
|
if (spender == address(0)) revert ZeroAddress();
|
|
|
|
_allowances[tokenOwner][spender] = amount;
|
|
emit Approval(tokenOwner, spender, amount);
|
|
}
|
|
}
|