Deterministic on-chain staking protocol — three-tier singularity pipeline with immutable lifecycle tracking, event-driven state transitions, and real-time Three.js accretion visualization on XRPL EVM Sidechain.
| Dashboard | https://dark-matter-farm.vercel.app/ |
| Contract on Explorer → | 0x5268A28d...185046Af · XRPL EVM Testnet |
Experimental high-convexity yield system with on-chain state-driven emissions, deployed on XRPL EVM Testnet (Chain ID 1449000).
DARK MATTER FARM is a production-grade staking protocol deployed on the XRPL EVM Sidechain. DARK tokens are committed to one of three orbit tiers — Kuiper Belt, Asteroid Belt, or Accretion Disk — each with a deterministic lock period and yield multiplier encoded directly in contract storage at deployment. Yield accrues every second using integer basis-point arithmetic with no floating-point operations, no rounding errors, and no off-chain computation.
No backend. No API. No framework.
Every state transition — stake creation, yield accrual, claim, unstake — is executed as an on-chain write through a six-event lifecycle. The frontend reads directly from contract storage via eth_call and streams live state changes via contract.on() event listeners. There is no server, no database, no intermediary layer between the user's wallet and the contract. The blockchain is the state machine. The event log is the audit trail.
The architecture mirrors real-world financial protocol design: per-position ownership enforcement, checks-effects-interactions pattern on every write, custom errors for gas-efficient reverts, and immutable tier parameters that cannot be altered mid-position without explicit owner action. Yield formula:
yield = amount × elapsed(seconds) × baseYieldRateBps × multiplierBps / 10,000²
ScreenRecorderProject57.mp4
- ✅ Deterministic state machine —
stake → claimYield → unstakelifecycle enforced on-chain - ✅ Three orbit tiers with independent lock periods and basis-point multipliers
- ✅ Per-position ownership model —
msg.sender == batch.ownerenforced on every write - ✅ Custom errors (
NotOwner,ZeroAmount,InvalidTier,StakeAlreadyComplete,ZeroYield) — no string revert waste - ✅ Six indexed events — full on-chain audit trail replayable from genesis
- ✅ Integer-only yield math —
amount × elapsed × baseRate × multiplier / BPS_DENOM² - ✅ Configurable base yield rate and per-tier multipliers via
onlyOwnerfunctions - ✅ Checks-effects-interactions pattern on all state-mutating functions
- ✅
totalYieldPaidaccounting — cumulative protocol yield tracked on-chain - ✅ Deployed on XRPL EVM Testnet with
--legacyflag (no EIP-1559 support)
- ✅ Three.js black hole — pure void event horizon, four-lane neon accretion disk, relativistic jets, Hawking radiation
- ✅ Live stake orbit arcs — each position rendered as a glowing arc sized by amount, racing its tier ring
- ✅ Spiral-in injection animation — neon trail coils inward on stake creation
- ✅ 3,000-star parallax field — mouse-tracked depth simulation
- ✅ LIGO-style gravitational wave monitor — waveform spikes on yield events
- ✅ Per-stake yield ticker — updates every 250ms, live lock progress bar, individual claim buttons
- ✅ Hawking burst FX — full radial flash + radiation bloom on yield claim
- ✅ Full simulation sandbox — runs without wallet, isolated from on-chain state
- ✅ 4-step tx lifecycle — Gas → Broadcast → Mine → Confirm with XRPL explorer links
- ✅ On-chain event feed —
contract.on()streaming all six contract events live - ✅ Hardcoded XRPL EVM overrides —
type: 0,gasLimit: 300000,gasPrice: 100 gweion every write
ScreenRecorderProject55.mp4
DarkMatterFarm.sol · 0x5268A28d3ceEB367756b88e97001ff38185046Af · XRPL EVM Testnet · solc 0.8.24
| ID | Tier | Lock Period | Multiplier | Visual Ring |
|---|---|---|---|---|
0 |
Kuiper Belt | 30 days | 1.0× (100 bps) |
Outer · orange |
1 |
Asteroid Belt | 90 days | 2.5× (250 bps) |
Mid · red |
2 |
Accretion Disk | 180 days | 5.0× (500 bps) |
Inner · white-hot |
// Commits `amount` tokens (18-decimal wei) to `tier` (0–2).
// Initialises Stake struct: id, amount, tier, stakedAt, lockEndsAt, lastClaimed, complete=false.
// Increments stakeCount. Updates tier.totalStaked and tier.stakeCount.
// Reverts: ZeroAmount | InvalidTier | TierNotActive | NotOwner
// Emits: Staked(stakeId, staker, amount, tier, lockEndsAt)
function stake(uint256 amount, uint8 tier) external onlyOwner
// Closes an active position. Calculates pending yield via _calculateYield().
// Sets complete=true (terminal — no re-activation). Decrements tier.totalStaked.
// Yield is recorded in totalYieldPaid but not transferred (no ERC-20 in this version).
// Reverts: InvalidStake | StakeAlreadyComplete | NotOwner
// Emits: Unstaked(stakeId, amount, yieldEarned, timestamp)
function unstake(uint256 stakeId) external onlyOwner
// Harvests all accrued yield since lastClaimed without closing the position.
// Resets lastClaimed = block.timestamp. Accumulation restarts from zero.
// Reverts: InvalidStake | StakeAlreadyComplete | ZeroYield | NotOwner
// Emits: YieldClaimed(stakeId, staker, amount, timestamp)
function claimYield(uint256 stakeId) external onlyOwner
// Updates yield multiplier for a tier in basis points (100 = 1×, 500 = 5×).
// Affects all existing stakes in that tier from next claim forward.
// Reverts: InvalidTier | ZeroAmount | NotOwner
// Emits: TierMultiplierSet(tier, oldBps, newBps)
function setTierMultiplier(uint8 tier, uint256 multiplierBps) external onlyOwner
// Updates the protocol base yield rate (bps per second per unit staked).
// Default: 1 bps/sec ≈ 3.15% APY at 1× multiplier.
// Reverts: ZeroAmount | NotOwner
// Emits: BaseYieldRateSet(oldRate, newRate)
function setBaseYieldRate(uint256 ratePerSecondBps) external onlyOwner
// Transfers the owner role to newOwner. Immediately effective.
// IRREVERSIBLE if wrong address — no recovery mechanism.
// Reverts: ZeroAddress | NotOwner
// Emits: OwnershipTransferred(previousOwner, newOwner)
function transferOwnership(address newOwner) external onlyOwner// Returns full Stake struct for stakeId.
// Reverts with InvalidStake if stakeId >= stakeCount.
function getStake(uint256 stakeId) external view
returns (uint256 id, uint256 amount, uint8 tier, uint256 stakedAt,
uint256 lockEndsAt, uint256 lastClaimed, bool complete)
// Returns accrued yield in wei since lastClaimed.
// Returns 0 for complete stakes. Calculated as:
// amount × (block.timestamp - lastClaimed) × baseYieldRateBps × multiplierBps / 1e8
function getPendingYield(uint256 stakeId) external view returns (uint256)
// Returns full Tier struct: name, lockDays, multiplierBps, totalStaked, stakeCount, active.
function getTier(uint8 tier) external view
returns (string memory name, uint256 lockDays, uint256 multiplierBps,
uint256 totalStaked, uint256 stakeCountInTier, bool active)
// Sums totalStaked across all three tiers. Principal only — excludes yield.
function getTotalStaked() external view returns (uint256 total)
// Returns cumulative yield recorded since deployment via claimYield() and unstake().
function getTotalYieldPaid() external view returns (uint256)
// Total stake positions created (including completed). Next stakeId = stakeCount().
function stakeCount() external view returns (uint256)
// Current contract owner address. Only this address can call onlyOwner functions.
function owner() external view returns (address)| Pattern | Implementation |
|---|---|
| Custom errors | NotOwner · ZeroAmount · InvalidTier · InvalidStake · StakeAlreadyComplete · ZeroYield · TierNotActive · ZeroAddress |
| Indexed events | stakeId indexed + staker indexed on all position events — efficient off-chain filtering |
| Basis points | multiplierBps / BPS_DENOMINATOR (10,000) — all yield math in integer bps, zero floats |
onlyOwner |
Custom modifier — if (msg.sender != owner) revert NotOwner() — no OpenZeppelin dependency |
| Terminal states | complete = true on unstake — no re-activation, append-only position history |
| CEI pattern | Checks-Effects-Interactions on all write functions — state updated before any external logic |
| NatSpec | Full @notice / @param / @dev coverage on all public functions |
| Layer | Technology |
|---|---|
| Blockchain | XRPL EVM Sidechain — Chain ID 1449000 |
| Smart Contract | Solidity 0.8.24 — zero external dependencies |
| Dev & Deploy | Foundry (forge create · forge test · cast) |
| Frontend | Vanilla HTML · CSS · JS — no build step, no framework |
| 3D Visualization | Three.js r128 (CDN) |
| Charts | Chart.js 4.4.1 (CDN) |
| Web3 Client | ethers.js v5.7.2 (UMD CDN) |
| Hosting | Vercel |
| Fonts | Orbitron · Share Tech Mono · Rajdhani |
dark-matter-farm/
├── src/
│ └── DarkMatterFarm.sol ← staking contract — state machine + yield engine
├── test/
│ └── DarkMatterFarm.t.sol ← 35+ unit, integration, and fuzz tests (forge-std)
├── script/
│ └── Deploy.s.sol ← Foundry deploy script (reference only — use forge create)
├── lib/
│ └── forge-std/ ← test harness (git clone, not committed)
├── index.html ← full dashboard — Three.js + ethers.js, no build step
├── foundry.toml ← src=src · solc=0.8.24 · optimizer=true
├── Makefile ← build · test · gas · deploy-xrpl shortcuts
├── .env.example ← PRIVATE_KEY · XRPL_RPC_URL template
├── .gitignore ← excludes .env · out/ · cache/ · lib/
└── README.md
No install required. Open index.html directly in a browser or serve it statically:
# Option 1 — open directly
open index.html
# Option 2 — local server (avoids CORS on some browsers)
npx serve .
# or
python3 -m http.server 8080The simulation sandbox runs fully without a wallet. Connect MetaMask for on-chain interaction.
# Install forge-std (once per project)
git clone https://github.com/foundry-rs/forge-std lib/forge-std
# Compile
forge build
# Run full test suite
forge test -vvvv
# Run a single test
forge test --match-test test_stake_kuiper_succeeds -vvvv
# Gas report
forge test --gas-report
# Fuzz tests (runs 256 fuzz cases per fuzz test)
forge test --match-test testFuzz -vvvv
# Gas snapshot (baseline for regression tracking)
forge snapshotrm -rf out/ cache/ broadcast/export PRIVATE_KEY=your_private_key_hereforge create src/DarkMatterFarm.sol:DarkMatterFarm \
--rpc-url https://rpc.testnet.xrplevm.org \
--private-key $PRIVATE_KEY \
--legacy \
--gas-price 100000000000 \
--gas-limit 3000000Expected output:
Deployer: 0xYOUR_WALLET
Deployed to: 0xCONTRACT_ADDRESS ← copy this
Transaction hash: 0xTX_HASH
Open index.html and update line ~778:
var CONTRACT = "0xYOUR_DEPLOYED_ADDRESS";surge . https://dark-matter-farm.surge.sh| Field | Value |
|---|---|
| Network Name | XRPL EVM Testnet |
| RPC URL | https://rpc.testnet.xrplevm.org |
| Chain ID | 1449000 |
| Currency Symbol | XRP |
| Block Explorer | https://explorer.testnet.xrplevm.org |
| # | Issue | Symptom | Fix |
|---|---|---|---|
| 1 | eth_estimateGas not supported |
opts.fn.estimateGas is not a function — all writes fail |
Remove estimateGas call entirely. Hardcode gasLimit: 300000 in every tx override |
| 2 | eth_gasPrice not supported |
Promise.all with getGasPrice() fails silently — block number stops updating too |
Isolate getGasPrice() in its own try/catch, separate from getBlockNumber(). Show "100 gwei" on failure |
| 3 | No EIP-1559 (type-2 tx) | UNSUPPORTED_TX_TYPE revert |
Always pass type: 0 in tx overrides. Use --legacy on every forge create / cast send |
| 4 | Stale bytecode on redeploy | Old contract logic executes even after redeployment | Always rm -rf out/ cache/ broadcast/ before recompiling and redeploying |
| 5 | JS falsy tier == 0 bug |
Tier 0 (Kuiper Belt) blocked by if (!tier) guard |
Use tier === "" || tier === null check. !0 evaluates true in JavaScript |
| 6 | Insufficient fee | TX rejected with insufficient fee |
Set gasPrice to at minimum 100000000000 (100 gwei). Never rely on provider auto-detection |
export CONTRACT=0x5268A28d3ceEB367756b88e97001ff38185046Af
export RPC=https://rpc.testnet.xrplevm.org
# Read owner
cast call $CONTRACT "owner()" --rpc-url $RPC
# Read total stake count
cast call $CONTRACT "stakeCount()" --rpc-url $RPC
# Read tier info (0=Kuiper, 1=Asteroid, 2=Accretion)
cast call $CONTRACT "getTier(uint8)" 2 --rpc-url $RPC
# Read stake by ID
cast call $CONTRACT "getStake(uint256)" 0 --rpc-url $RPC
# Read pending yield for stake ID 0
cast call $CONTRACT "getPendingYield(uint256)" 0 --rpc-url $RPC
# Read total staked across all tiers
cast call $CONTRACT "getTotalStaked()" --rpc-url $RPC
# Write — stake 1000 DARK into Accretion Disk (tier 2)
cast send $CONTRACT "stake(uint256,uint8)" \
1000000000000000000000 2 \
--rpc-url $RPC \
--private-key $PRIVATE_KEY \
--legacy \
--gas-price 100000000000 \
--gas-limit 300000
# Write — claim yield on stake ID 0
cast send $CONTRACT "claimYield(uint256)" 0 \
--rpc-url $RPC \
--private-key $PRIVATE_KEY \
--legacy \
--gas-price 100000000000 \
--gas-limit 300000
# Write — unstake position ID 0
cast send $CONTRACT "unstake(uint256)" 0 \
--rpc-url $RPC \
--private-key $PRIVATE_KEY \
--legacy \
--gas-price 100000000000 \
--gas-limit 300000
# Write — update base yield rate to 5 bps/sec
cast send $CONTRACT "setBaseYieldRate(uint256)" 5 \
--rpc-url $RPC \
--private-key $PRIVATE_KEY \
--legacy \
--gas-price 100000000000 \
--gas-limit 300000| Project | Description | Status |
|---|---|---|
| ZUC Mine Command Center | On-chain uranium mining operations dashboard — real-time reserve tracking, miner registry, and contract interaction via a fully frontend-driven command interface | ✅ Live |
| U235 Fuel Cycle | Nuclear fuel cycle pipeline — uranium ore to enriched fuel rod, deterministic multi-stage processing with full on-chain traceability | ✅ Live |
| ISR Network | Intelligence surveillance reconnaissance system — on-chain asset tracking, mission lifecycle state machine, and role-based operator control | ✅ Live |
| Dark Matter Farm | DeFi yield protocol — experimental high-convexity farming system with custom reward mechanics and on-chain state-driven emissions | ✅ Live |
MIT — see LICENSE


