Full lesson
ENRG
Decentralized energy protocol. Proof-of-Production turns any energy (solar, wind, hydro, gas) into liquid tokens backed by real kWh. Energy Vault provides buyback & burn.
What it does
From the project README:
ENRG is the **first application** built on the Axis Protocol — an open standard for cryptographically verifiable trust between physical devices and digital systems.
- Original
- AntonGrid/ENRG
- SuperStack fork
- Blockchains/ENRG @
3fba0f5 - Stars
- 0
- Last push (original)
- 2026-10-30
- Languages
- Rust, TypeScript, JavaScript, C++, Python, Shell
- Detected stack
- Anchorsolana-pyweb3.js
- Categories
- DeFi
- Licence
- MIT
Architecture
203 source files. By extension: .rs ×63, .sol ×51, .ts ×37, .js ×29, .py ×22, .cpp ×1.
| Top-level directory | Source files |
|---|---|
programs | 63 |
onchain | 53 |
scripts | 32 |
tests | 31 |
legacy | 9 |
oracle | 5 |
(root) | 3 |
sdk | 2 |
On-chain programs: programs/enrg-mvp, programs/enrg-profile
Dapp: devnet deploy pending
⏳ Devnet deploy pending — goes live when the devnet wallet is topped up. This fork's own dapp is not deployed yet, so there is no program address or transaction to show. It is queued in dapp-queue.txt; once devnet SOL is available it is deployed, exercised with real devnet transactions, and this section switches to the live dapp at /dapps/enrg/. Until then, the lesson and step-by-step guide below are complete, and you can run it locally with the commands below.
Key code, explained
Excerpts chosen automatically where the code uses the most Solana concepts, pinned to the fork's commit.
programs/enrg-mvp/src/instructions/device_lifecycle.rs L313–336 @ 3fba0f5 · MIT · original ); producer.state = DeviceState::Provisioned; let clock = Clock::get()?; emit!(DeviceProvisioned { device_id: producer.device_id, owner: producer.authority, timestamp: clock.unix_timestamp, }); msg!("Device provisioned: {}", producer.device_id); Ok(())} #[derive(Accounts)]pub struct ActivateDevice<'info> { pub authority: Signer<'info>, #[account( mut, seeds = [b"producer", producer.device_id.as_ref()], bump, has_one = authority @ ErrorCode::Unauthorized, constraint = producer.device_id != Pubkey::default() @ ErrorCode::InvalidParameter )] pub producer: Account<'info, EnergyProducer>,- Line 316
let clock = Clock::get()?;: Reads on-chain time from the Clock sysvar. - Line 317
emit!(DeviceProvisioned {: Emits an Anchor event that clients can parse from the logs. - Line 322
msg!("Device provisioned: {}", producer.device_id);: Logs to the program log, which is visible in explorers and the test output. - Line 328
pub authority: Signer<'info>,: Requires this account to have signed the transaction. - Line 331
seeds = [b"producer", producer.device_id.as_ref()],: Declares the PDA seeds. Anchor re-derives the address and rejects the account if it doesn't match. - Line 332
bump,: Uses the bump seed, the extra byte that pushes the derived address off the ed25519 curve. - Line 333
has_one = authority @ ErrorCode::Unauthorized,: Checks that a stored pubkey field matches the passed account.
programs/enrg-mvp/src/instructions/manifest_registry.rs L37–60 @ 3fba0f5 · MIT · original /// Authority to manage oracle access pub authority: Signer<'info>,} #[derive(Accounts)]pub struct SetOracleAuthority<'info> { #[account( mut, seeds = [b"manifest-registry"], bump, has_one = authority )] pub registry: Account<'info, ManifestRegistry>, pub authority: Signer<'info>,} pub fn initialize_manifest_registry(ctx: Context<InitializeManifestRegistry>) -> Result<()> { let registry = &mut ctx.accounts.registry; let clock = Clock::get()?; // Guard: initialization fields are written ONLY at first creation. // Otherwise any caller could re-run init_if_needed to overwrite // authority and oracle_authority and take over the registry.- Line 38
pub authority: Signer<'info>,: Requires this account to have signed the transaction. - Line 45
seeds = [b"manifest-registry"],: Declares the PDA seeds. Anchor re-derives the address and rejects the account if it doesn't match. - Line 46
bump,: Uses the bump seed, the extra byte that pushes the derived address off the ed25519 curve. - Line 47
has_one = authority: Checks that a stored pubkey field matches the passed account. - Line 56
let clock = Clock::get()?;: Reads on-chain time from the Clock sysvar. - Line 59
// Otherwise any caller could re-run init_if_needed to overwrite: Creates the account only if it doesn't exist. Watch for re-initialisation bugs.
programs/enrg-mvp/src/instructions/oracle_quorum.rs L181–204 @ 3fba0f5 · MIT · original pub oracle_signer: Signer<'info>, pub system_program: Program<'info, System>,} pub fn stake_oracle(ctx: Context<StakeOracle>, lamports: u64) -> Result<()> { require!(lamports >= 1_000_000, ErrorCode::InsufficientStake); // ≥ 0.001 SOL let stake_info = ctx.accounts.oracle_stake.to_account_info(); let signer_info = ctx.accounts.oracle_signer.to_account_info(); let stake = &mut ctx.accounts.oracle_stake; if stake.oracle == Pubkey::default() { stake.oracle = ctx.accounts.oracle_signer.key(); stake.joined_at = Clock::get()?.unix_timestamp; } stake.lamports = stake .lamports .checked_add(lamports) .ok_or(ErrorCode::ArithmeticOverflow)?; anchor_lang::system_program::transfer( CpiContext::new( ctx.accounts.system_program.to_account_info(), anchor_lang::system_program::Transfer { from: signer_info,- Line 181
pub oracle_signer: Signer<'info>,: Requires this account to have signed the transaction. - Line 187
require!(lamports >= 1_000_000, ErrorCode::InsufficientStake); // ≥ 0.001 SOL: An explicit validation that aborts the instruction with an error. - Line 190
let stake = &mut ctx.accounts.oracle_stake;: Touches staking state: the amount locked and when it was locked. - Line 193
stake.joined_at = Clock::get()?.unix_timestamp;: Reads on-chain time from the Clock sysvar. - Line 197
.checked_add(lamports): Uses checked arithmetic so overflow fails instead of wrapping. - Line 201
CpiContext::new(: Makes a cross-program invocation into another program.
programs/enrg-mvp/src/instructions/vesting.rs L13–35 @ 3fba0f5 · MIT · original/// 1. **Genesis/pre-seed** (localnet `solana-test-validator --account`,/// file `tests/genesis/founder-vesting.json`): the account already exists at/// `findProgramAddress([b"founder-vesting"])` and belongs to the program —/// `init_if_needed` skips initialization and the fields are overwritten./// 2. **On-chain bootstrap** (Devnet/mainnet): the account is absent — `init_if_needed`/// creates it via the program with the same seed (payer = founder); the handler/// then fills the fields. No external genesis injection is required.#[derive(Accounts)]pub struct InitializeFounderVesting<'info> { #[account( init_if_needed, payer = authority, space = 8 + FounderVesting::LEN, seeds = [b"founder-vesting"], bump, )] pub vesting: Account<'info, FounderVesting>, #[account(mut)] pub authority: Signer<'info>, pub system_program: Program<'info, System>,}- Line 15
/// `findProgramAddress([b"founder-vesting"])` and belongs to the program —: Derives a PDA from seeds and the program ID and returns the address with its canonical bump. - Line 16
/// `init_if_needed` skips initialization and the fields are overwritten.: Creates the account only if it doesn't exist. Watch for re-initialisation bugs. - Line 26
seeds = [b"founder-vesting"],: Declares the PDA seeds. Anchor re-derives the address and rejects the account if it doesn't match. - Line 27
bump,: Uses the bump seed, the extra byte that pushes the derived address off the ed25519 curve. - Line 32
pub authority: Signer<'info>,: Requires this account to have signed the transaction.
How to run, build and test
These commands come from the repo's own manifests (package.json scripts, Anchor.toml, Cargo, go.mod, Makefile). Check the README for prerequisites and environment variables.
| Directory | Command | Runs |
|---|---|---|
. | npm run start | node server.js |
. | npm run test | mocha "tests/*.test.js" |
oracle | npm run start | node server.js |
oracle/registry | npm run start | node server.js |
oracle/registry | npm run test | mocha test/**/*.js |
. | anchor build | compile programs and generate IDLs |
. | anchor test | local validator + test suite |
. | anchor run test | yarn run ts-mocha -p ./tsconfig.json -t 1000000 'tests/**/*.ts' |
. | cargo build | Rust workspace build |
. | cargo test | Rust tests |
What you could build from it
- Fork the SuperStack copy and extend it within the DeFi gaps listed on the idea page.
- Learn the pattern first: Program Derived Addresses (PDAs)
- Learn the pattern first: Cross-Program Invocations (CPIs)
- Learn the pattern first: SPL Tokens: mints, accounts and transfers