Sol.College

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 directorySource files
programs63
onchain53
scripts32
tests31
legacy9
oracle5
(root)3
sdk2

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.

AntonGrid/ENRG · 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>,
  1. Line 316 let clock = Clock::get()?;: Reads on-chain time from the Clock sysvar.
  2. Line 317 emit!(DeviceProvisioned {: Emits an Anchor event that clients can parse from the logs.
  3. Line 322 msg!("Device provisioned: {}", producer.device_id);: Logs to the program log, which is visible in explorers and the test output.
  4. Line 328 pub authority: Signer<'info>,: Requires this account to have signed the transaction.
  5. 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.
  6. Line 332 bump,: Uses the bump seed, the extra byte that pushes the derived address off the ed25519 curve.
  7. Line 333 has_one = authority @ ErrorCode::Unauthorized,: Checks that a stored pubkey field matches the passed account.
AntonGrid/ENRG · 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.
  1. Line 38 pub authority: Signer<'info>,: Requires this account to have signed the transaction.
  2. Line 45 seeds = [b"manifest-registry"],: Declares the PDA seeds. Anchor re-derives the address and rejects the account if it doesn't match.
  3. Line 46 bump,: Uses the bump seed, the extra byte that pushes the derived address off the ed25519 curve.
  4. Line 47 has_one = authority: Checks that a stored pubkey field matches the passed account.
  5. Line 56 let clock = Clock::get()?;: Reads on-chain time from the Clock sysvar.
  6. 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.
AntonGrid/ENRG · 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,
  1. Line 181 pub oracle_signer: Signer<'info>,: Requires this account to have signed the transaction.
  2. Line 187 require!(lamports >= 1_000_000, ErrorCode::InsufficientStake); // ≥ 0.001 SOL: An explicit validation that aborts the instruction with an error.
  3. Line 190 let stake = &mut ctx.accounts.oracle_stake;: Touches staking state: the amount locked and when it was locked.
  4. Line 193 stake.joined_at = Clock::get()?.unix_timestamp;: Reads on-chain time from the Clock sysvar.
  5. Line 197 .checked_add(lamports): Uses checked arithmetic so overflow fails instead of wrapping.
  6. Line 201 CpiContext::new(: Makes a cross-program invocation into another program.
AntonGrid/ENRG · 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>,}
  1. 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.
  2. 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.
  3. Line 26 seeds = [b"founder-vesting"],: Declares the PDA seeds. Anchor re-derives the address and rejects the account if it doesn't match.
  4. Line 27 bump,: Uses the bump seed, the extra byte that pushes the derived address off the ed25519 curve.
  5. 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.

DirectoryCommandRuns
.npm run startnode server.js
.npm run testmocha "tests/*.test.js"
oraclenpm run startnode server.js
oracle/registrynpm run startnode server.js
oracle/registrynpm run testmocha test/**/*.js
.anchor buildcompile programs and generate IDLs
.anchor testlocal validator + test suite
.anchor run testyarn run ts-mocha -p ./tsconfig.json -t 1000000 'tests/**/*.ts'
.cargo buildRust workspace build
.cargo testRust tests

What you could build from it

Used in lessons