# Abstract

Traditional blockchain systems face a fundamental trade-off: decentralization and security often come at the cost of scalability, flexibility, and accessibility for mainstream developers. **PWR Chain** redefines this paradigm by decoupling consensus from execution, creating a modular architecture where decentralized applications inherit blockchain’s trust without its constraints.

At its core, PWR Chain operates as a **decentralized data layer**, securing transactions with NIST-standardized post-quantum cryptography while enabling **Verifiable Immutable Data Applications (VIDAs)**—self-contained programs that process data off-chain using familiar Web2 tools (Python, Java, SQL). Unlike smart contracts or L1/L2 chains, VIDAs are not bound by blockchain gas fees, proprietary languages, or monolithic execution. Instead, they leverage PWR Chain’s immutable ledger as a universal source of truth, updating their states independently while remaining verifiable by anyone.

Key innovations include:

* **Horizontal Scalability**: Add unlimited VIDAs without congesting the blockchain.
* **Conduit Nodes**: Trustless relays for cross-VIDA communication, governed by app-specific rules.
* **Enterprise-Ready Design**: Integrate legacy systems (ERP, CRM) via APIs while anchoring critical actions to blockchain.
* **Quantum Resistance**: Post-quantum future-proof validator consensus.

For developers, PWR Chain eliminates the complexity of blockchain-native programming. For enterprises, it delivers auditability and cost efficiency without infrastructure overhauls. For users, it ensures transparency—every action is recorded on-chain, and every result can be independently verified.

By bridging Web2 agility with Web3 trust, PWR Chain empowers a new era of decentralized and immutable applications: scalable enough for global enterprises, flexible enough for indie developers, and secure enough for mission-critical use cases. This is not just a blockchain—it’s a foundation for the next evolution of decentralized software.


# Architecture

PWR Chain’s architecture is designed to empower **Verifiable Immutable Data Applications (VIDAs)**—applications that combine the flexibility of traditional software with blockchain’s trust and transparency. Below, we explain how the system works *and why it’s built this way*.

***

### Overview: Separation of Concerns

PWR Chain separates two critical functions:

1. **The Blockchain aka PWR Chain (Consensus + Data)**: Securely records *what happened* (transactions).
2. **Verifiable Immutable Data Applications (VIDA)**: Determines *how to respond* (processing logic).

**Why?**\
By isolating consensus from execution, PWR Chain avoids bottlenecks (like Ethereum’s gas limits) while VIDAs operate independently. This lets developers build scalable apps without congesting the blockchain.

<figure><img src="/files/nyn5MIFagM5NOE0XIb2U" alt=""><figcaption></figcaption></figure>

***

### Core Components

#### 1. PWR Chain

* **Role**: A decentralized, immutable ledger for recording action requests and data.
* **Key Features**:
  * **NIST-standardized Post-Quantum Consensus:** Uses Falcon signatures (a NIST-standardized lattice-based scheme) to secure against future attacks.
  * **Immutable Records**: Transactions are timestamped and cannot be altered.

**Why This Design?**

* **Efficiency**: PWR Chain focuses solely on ordering and securing transactions—not executing complex logic. This specialization allows it to process thousands of transactions per second.
* **Future-Proofing**: Resistance to quantum computing threats, protecting long-term data integrity.

***

#### 2. Verifiable Immutable Data Applications

* **Role**: VIDAs are programs that read transactions from PWR Chain and process them off-chain.
* **Key Features**:
  * **Off-Chain Execution**: VIDAs run on any machine (laptop, cloud server) using familiar languages like Python or Java.
  * **State Independence**: Each VIDA manages its own data (e.g., user balances, game scores).
  * **Open Verification**: Anyone can rerun the VIDA to validate results against PWR Chain’s ledger.

**Why This Design?**

* **Scalability**: VIDAs process data *outside* the blockchain, avoiding network congestion.
* **Flexibility**: Developers aren’t limited by blockchain-specific languages (e.g., Solidity) or gas fees.
* **Cost Efficiency**: Enterprises avoid expensive blockchain infrastructure; VIDAs can run on existing servers.

**Example: Supply Chain VIDA**

1. Factories write `shipment_start` transactions to PWR Chain.
2. The VIDA reads these transactions and calculates delivery timelines.
3. Retailers verify results by rerunning the VIDA.

***

#### 3. Networking & Communication

* **PWR Chain Nodes**: Decentralized and immutable network that propagates transactions globally.
* **Conduit Nodes**: Specialized nodes that relay messages between VIDAs via PWR Chain (learn more in Conduit Nodes).

**Why This Design?**

* **Decentralization**: No single point of failure—transactions are broadcast peer-to-peer.
* **Trustless Interoperability**: Conduit Nodes use PWR Chain as a neutral message bus, ensuring VIDA interactions are tamper-proof.

***

#### 4. Storage

* **PWR Chain**: Stores all transaction requests permanently (immutable ledger).
* **VIDAs**: Store application states externally (e.g., local databases, cloud storage).

**Why This Design?**

* **Cost Reduction**: Storing large datasets (e.g., user profiles) on-chain is expensive. VIDAs handle this off-chain.
* **Performance**: Databases optimized for specific use cases (e.g., SQL for analytics) outperform blockchain storage.

***

### How Transactions Flow

#### Rule: All State-Changing Actions Start on PWR Chain

Every action that alters a VIDA's state (e.g., updating a balance) begins as a transaction on PWR Chain.

**Why?**

* **Auditability**: Immutable records allow anyone to verify *what* happened and *when*.
* **Decentralization**: No central authority controls action requests—they’re validated by the network.

***

#### Step-by-Step Workflow

1. **Submit a Request**:
   * A user sends a transaction with:
     * **`VIDA_ID`**: An 8-byte identifier (e.g., `0x1a3f7e2d`) to target the VIDA.
     * **`Action`**: The request payload (e.g., `{"type": "redeem_points", "user": "Alice"}`).
   * **Why 8 Bytes?** Minimizes on-chain storage while allowing 18 quintillion unique VIDA IDs.
2. **Consensus Finalization**:
   * Validators order transactions into blocks using fee-based prioritization.
3. **VIDA Processing**:

   * The VIDA filters transactions by its `VIDA_ID` and processes them.

   ```python
   # Example: Process loyalty points
   transactions = pwr_chain.get_transactions(vida_id="0x1a3f7e2d")
   for tx in transactions:
       if tx["action"] == "redeem_points":
           deduct_points(tx["user"], tx["points"])
   ```

   * **Why Off-Chain?** Complex logic (e.g., AI, real-time analytics) would overload the blockchain.
4. **Verification**:
   * Anyone can rerun the VIDA with the same transactions to confirm results.

***

### Cross-VIDA Communication

VIDAs interact by sending transactions through PWR Chain:

1. **Request Relay**: VIDA A sends a transaction (e.g., `{"type": "payment_complete"}`) to VIDA B’s `VIDA_ID`.
2. **Processing**: VIDA B reads the transaction and updates its state.

**Why Use PWR Chain as a Middleman?**

* **Trustlessness**: Transactions are validated by the network, eliminating reliance on third-party APIs.
* **Immutability**: Requests can’t be altered or censored.

***

### Security

#### PWR Chain

* **Post-Quantum Signatures**: Secures validator votes and transactions.
* **Staking Slashing**: Validators lose staked PWR Coin for malicious acts.

**Why These Choices?**

* **Quantum Resistance**: Protects against future attacks that could break classical cryptography.
* **Incentive Alignment**: Validators are financially motivated to act honestly.

#### Verifiable Immutable Data Applications (VIDAs)

* **Replayability**: Since all inputs come from PWR Chain, anyone can reproduce a VIDA’s state.
* **Optional Safeguards**:
  * **Cross-Validation**: Nodes running the same VIDA compare results to detect fraud.
  * **Open-Source Rules**: Most VIDAs publish code for community audits.

**Why Replayability Matters?**

* **Trust Minimization**: Users don’t need to rely on the results from a third party—they can independently run the VIDA themselves to extract data from the blockchain and compute the results.

***

### Scalability

#### Horizontal Scaling

* **Unlimited VIDAs**: Add apps without impacting PWR Chain’s performance.
  * **Why?** VIDAs process data off-chain; their workloads don’t compete for resources.
* **Parallel Execution**: Each VIDA runs independently (no shared resources).

#### Enterprise-Grade Performance

* **Burst Capacity**: Handle traffic spikes (e.g., holiday sales) by scaling VIDA servers.
  * **Why?** Execution is decoupled from consensus—scale servers without blockchain upgrades.
* **Legacy Integration**: Connect VIDAs to ERP/CRM systems via APIs.

***

### Why This Architecture Works

#### For Developers

* **No Blockchain Expertise**: Build VIDAs with existing tools (Python, Java).
* **No Gas Fees**: Off-chain execution avoids Ethereum-style congestion.

#### For Enterprises

* **Auditability**: Every action is timestamped and immutable (simplifies compliance).
* **Cost Savings**: Avoid maintaining custom blockchain nodes.

#### For Users

* **Transparency**: Verify results by rerunning the VIDA.

***

### Traditional Systems vs. PWR Chain

| **Aspect**           | **Traditional Apps**         | **PWR Chain + VIDAs**                      |
| -------------------- | ---------------------------- | ------------------------------------------ |
| **Data Integrity**   | Trusted third-party audits.  | Immutable ledger + replayable VIDAs.       |
| **Scalability**      | Limited by server capacity.  | Scale infinitely with parallel VIDAs.      |
| **Development Cost** | High (custom infra, audits). | Low (leverage PWR Chain’s Infrastructure). |

***

This architecture ensures PWR Chain remains lightweight and secure, while VIDAs handle complex logic off-chain. By explaining the "why" behind each design choice, we empower developers and enterprises to build trustless, scalable solutions.


# VIDA

Verifiable Immutable Data Applications

A **Verifiable Immutable Data Application (VIDA)** is a self-contained software program that leverages PWR Chain’s immutable transaction ledger to operate transparently, securely, and at scale. Think of it as a traditional application—like a database-driven tool or enterprise software—but with a critical difference: **it derives its data and trust from an immutable blockchain** instead of a centralized server.

Whether you’re building a financial platform, a supply chain tracker, or a gaming ecosystem, VIDAs enable developers and organizations to create solutions that are:

* **Tamper-Proof**: Data is anchored to PWR Chain, making it auditable and permanent.
* **Decentralized**: No single entity controls the application’s logic or state.
* **Flexible**: Built using familiar programming languages (Python, Java, C#, etc.) and frameworks.

### How VIDAs Work: A Web2 Analogy

Imagine you’re building a voting app for a corporate board. In a Web2 world, votes are stored in a centralized database controlled by your IT team. With a VIDA:

1. **Votes** are submitted as transactions to PWR Chain (like writing to a shared Google Sheet everyone can see).
2. **Your VIDA** (the voting app) reads these transactions from PWR Chain (like opening the Google Sheet).
3. **Results** are calculated locally by anyone running the VIDA, ensuring transparency (like stakeholders verifying the tally themselves).

No need for a central server, proprietary APIs, or trust in a third party.

<figure><img src="/files/YBqZyTETHNeq0yO34CCG" alt=""><figcaption></figcaption></figure>

A machine running the VIDA is called an **VIDA Execution Instance**.

***

#### Key Features

| **For Web2 Developers**             | **For Web3 Builders**                     |
| ----------------------------------- | ----------------------------------------- |
| Use familiar tools/languages.       | No need for smart contract platforms.     |
| Integrate blockchain data via APIs. | Inherit PWR Chain’s security.             |
| Run privately or publicly.          | Build open-source, community-driven apps. |

1. **Blockchain as a Data Layer**
   * VIDAs treat PWR Chain as a **global database**. Transactions are like rows in a spreadsheet, tagged with an `VIDA ID` to specify which application they belong to.
   * **Example**: A logistics VIDA tracks shipments by reading transactions labeled “Shipment Update” from PWR Chain.
2. **Execution Independence**
   * VIDAs process data **off-chain**, freeing them from blockchain speed limits.
   * **Example**: A retail VIDA could analyze customer purchase histories in real-time without waiting for block confirmations.
3. **State Management**
   * Each VIDA maintains its own state (e.g., user balances, game scores) externally.
   * States can be stored locally, and recreated at any time.
4. **Verifiable Consistency**
   * Anyone can run the VIDA to reproduce its state, ensuring results match the transactions on PWR Chain.
   * Optional **cross-validation**: Nodes running the same VIDA can compare states (e.g., “Does my supply chain log match yours?”).

***

### Why VIDAs?

#### For Corporates & Enterprises

* **Audit Trails**: Financial audits become effortless—every transaction is timestamped and immutable.
* **Supply Chain Integrity**: Track goods from factory to consumer without relying on third-party systems.
* **Cost Efficiency**: Avoid building custom blockchain infrastructure; use PWR Chain as your backbone.

#### For Developers

* **No Blockchain Expertise Required**: Build VIDAs in Python, Java, or even Excel (via plugins).
* **Scalability**: Run complex analytics or AI models off-chain without congesting the blockchain.
* **Interoperability**: Connect VIDAs to legacy systems (ERP, CRM) via standard APIs.

***

### Example Use Cases

1. **Corporate Voting Systems**
   * Shareholders submit votes as PWR Chain transactions.
   * A VIDA tallies results and publishes them to a dashboard.
2. **Loyalty Programs**
   * Customers earn points (recorded on PWR Chain) across partner brands.
   * A VIDA calculates rewards and redeems them at checkout.
3. **IoT Device Management**
   * Sensors send data to PWR Chain.
   * A VIDA monitors conditions and triggers alerts (e.g., factory machinery overheating).

***

### VIDAs vs. Traditional Systems

| **Traditional Web2 App**             | VIDA                                             |
| ------------------------------------ | ------------------------------------------------ |
| Relies on centralized databases.     | Uses PWR Chain as an immutable ledger.           |
| Requires trust in a single provider. | Trustless; results are independently verifiable. |
| Limited transparency.                | Full auditability via blockchain.                |

**Decentralization & Security of VIDAs: Anchored in Blockchain Principles**\
VIDAs (Verifiable Immutable Data Applications) inherit the foundational security and decentralization of blockchain technology, akin to Bitcoin, while introducing novel mechanisms to address modern challenges. Below, we explain how VIDAs achieve trustlessness at scale, using Bitcoin’s proven principles as a reference framework.

***

#### **Foundations Inspired by Bitcoin**

1. **Immutable, Decentralized Ledger**
   * Like Bitcoin’s blockchain, PWR Chain provides a tamper-proof record of all transactions. Every action (e.g., user requests, cross-VIDA messages) is cryptographically signed, timestamped, and permanently stored.
   * *Why it matters*: Just as Bitcoin nodes reject invalid transactions, PWR Chain validators enforce consensus rules, ensuring only valid data enters the ledger.
2. **Deterministic Validation**
   * Bitcoin nodes independently verify transactions using fixed rules (e.g., script validity, UTXO checks). Similarly, all VIDA Execution Instances process transactions using the same logic, guaranteeing consistent outcomes.
   * *Example*: A supply chain VIDA calculating delivery times will produce identical results across instances if given identical on-chain inputs.
3. **No Single Point of Control**
   * Bitcoin’s decentralization relies on globally distributed miners/nodes. In VIDAs:
     * **PWR Chain Validators**: Secure the ledger via a decentralized, stake-weighted network.
     * **VIDA Execution Instances**: Operated by independent parties (developers, enterprises, users).
   * *Result*: No central authority can alter transaction history or manipulate VIDA logic.

***

#### **Enhanced Security & Decentralization**

VIDAs extend Bitcoin’s principles with innovations tailored for scalability and modern threats:

**1. Cross-Instance State Validation**

* *Challenge*: Bitcoin validates transactions but does not verify application-layer states.
* *VIDA Solution*:
  * Stateful VIDAs generate **root hashes** (e.g., Merkle proofs) of their state after processing each block.
  * Execution Instances cross-validate these hashes, flagging discrepancies.
  * *Example*: A payment VIDA’s balance sheet must match across all instances; mismatches trigger audits.

**2. Quantum-Resistant Cryptography**

* *Bitcoin’s Limitation*: ECDSA signatures are vulnerable to quantum attacks.
* *VIDA Advantage*:
  * PWR Chain uses **Falcon signatures** (NIST-standardized post-quantum cryptography) for transactions and validator consensus.
  * Ensures long-term security against quantum decryption.

**3. Trustless Interoperability**

* *Bitcoin’s Scope*: Limited to on-chain transactions.
* *VIDA Flexibility*:
  * **Conduit Nodes**: Decentralized relays governed by VIDA-specific rules (e.g., staking, geographic distribution) enable secure communication between VIDAs and external systems (e.g., IoT devices, legacy APIs).
  * *Use Case*: A logistics VIDA can autonomously trigger shipping updates in another VIDA without relying on centralized middleware.

**4. Replayability for Universal Auditability**

* *Bitcoin’s "Don’t Trust, Verify" Ethos*: Users can independently validate the blockchain.
* *VIDA Parallel*:
  * Any user can rerun a VIDA using PWR Chain’s transaction history to reproduce its state.
  * *Example*: Auditors verify a financial VIDA’s balance calculations by replaying its logic against on-chain records.

**5. Modular Consensus-Execution Split**

* *Bitcoin’s Trade-off*: Scalability limited by on-chain execution.
* *VIDA Architecture*:
  * **PWR Chain**: Handles consensus and data ordering (like Bitcoin’s base layer).
  * **VIDAs**: Process complex logic off-chain (e.g., AI, real-time analytics) while anchoring critical actions on-chain.
  * *Result*: Horizontal scaling (unlimited parallel VIDAs) without congesting the blockchain.

***

#### **Why This Matters for Enterprises & Developers**

* **Security**: Combines Bitcoin’s battle-tested immutability with quantum-resistant safeguards.
* **Decentralization**: Eliminates single points of failure in both ledger maintenance (validators) and application logic (Execution Instances).
* **Scalability**: Supports enterprise-grade throughput (600k+ TPS) while retaining auditability.

***

By building on Bitcoin’s core strengths and addressing its limitations, VIDAs offer a robust framework for decentralized applications—secure enough for financial systems, scalable enough for global supply chains, and flexible enough to integrate with legacy infrastructure.

***

By decoupling execution from consensus, VIDAs empower developers and enterprises to innovate without the complexity of traditional blockchain systems. Whether you’re modernizing legacy software or launching an immutable or decentralized service, VIDAs offer the flexibility of Web2 with the trust of Web3.


# VIDA Security & Trustlessness

VIDAs (Verifiable Immutable Data Applications) inherit the foundational security and decentralization of blockchain technology, akin to Bitcoin, while introducing novel mechanisms to address modern challenges. Below, we explain how VIDAs achieve trustlessness at scale, using Bitcoin’s proven principles as a reference framework.

***

#### **Foundations Inspired by Bitcoin**

1. **Immutable, Decentralized Ledger**
   * Like Bitcoin’s blockchain, PWR Chain provides a tamper-proof record of all transactions. Every action (e.g., user requests, cross-VIDA messages) is cryptographically signed, timestamped, and permanently stored.
   * *Why it matters*: Just as Bitcoin nodes prevent malicious actors from tampering with transactions, PWR Chain’s design ensures historical records cannot be altered or deleted, creating a universal source of truth.
2. **Deterministic Validation**
   * Bitcoin nodes independently verify transactions using fixed rules (e.g., script validity, UTXO checks). Similarly, all VIDA Execution Instances process transactions using fixed rules (i.e same code), guaranteeing consistent outcomes.
   * *Example*: A supply chain VIDA calculating delivery times will produce identical results across all instances if given identical on-chain inputs.
3. **No Single Point of Control**
   * Bitcoin’s decentralization relies on globally distributed miners/nodes. In VIDAs:
     * **PWR Chain Validators**: Secure the ledger via a decentralized, stake-weighted network.
     * **VIDA Execution Instances**: Operated by independent parties (developers, enterprises, users).
   * *Result*: No central authority can alter transaction history or manipulate VIDA logic.

***

#### **Enhanced Security & Decentralization**

VIDAs extend Bitcoin’s principles with innovations tailored for scalability and modern threats:

**1. Cross-Instance State Validation**

* Bitcoin nodes process transactions but does not verify the outcome across different nodes.
* *VIDA*:
  * VIDA instances process transactions according to the rules of the VIDA.
  * Stateful VIDA instances generate **root hashes** (e.g., Merkle proofs) of their state after processing each block.
  * Execution Instances cross-validate these hashes, flagging discrepancies.
  * *Example*: A payment VIDA’s balance sheet must match across all instances; mismatches trigger audits.

**2. Quantum-Resistant Cryptography**

* *Bitcoin’s Limitation*: ECDSA signatures are vulnerable to quantum attacks.
* *VIDA Advantage*:
  * PWR Chain uses **Falcon signatures** (NIST-standardized post-quantum cryptography) for transactions and validator consensus.
  * Ensures long-term security against quantum threats.

**3. Trustless Interoperability**

* *Bitcoin’s Scope*: Limited to on-chain transactions.
* *VIDA Flexibility*:
  * **Conduit Nodes**: Decentralized relays governed by VIDA-specific rules (e.g., staking, geographic distribution) enable secure communication between VIDAs and external systems (e.g., IoT devices, legacy APIs).
  * *Use Case*: A logistics VIDA can autonomously trigger shipping updates in another VIDA without relying on centralized middleware.

**4. Replayability for Universal Auditability**

* *Bitcoin’s "Don’t Trust, Verify" Ethos*: Users can independently validate the blockchain.
* *VIDA Parallel*:
  * Any user can rerun a VIDA using PWR Chain’s transaction history to reproduce its state.
  * *Example*: Auditors verify a financial VIDA’s balance calculations by replaying its logic against on-chain records.

***

#### **Why This Matters for Enterprises & Developers**

* **Security**: Combines battle-tested blockchain immutability with quantum-resistant safeguards.
* **Decentralization**: Eliminates single points of failure in both ledger maintenance (validators) and application logic (Execution Instances).
* **Scalability**: Supports enterprise-grade throughput (600k+ TPS) while retaining auditability.

***

#### **FAQ**

**What happens if someone runs malicious VIDA instances with altered code?**

Malicious VIDA instances can only deceive themselves. Even if someone operates 1,000 modified instances, they cannot alter or manipulate the results of honest instances.

For example, if a financial VIDA issues a coin, major exchanges supporting that coin would run their own VIDA instances to verify balances. If someone creates a fraudulent VIDA instance that falsely credits them with 1 million coins and attempts to transfer them to an exchange or another wallet, the transaction would be automatically rejected. Exchanges and wallets would recognize it as invalid because it does not match the verified state maintained by legitimate VIDA instances.

***

By building on battle-tested strengths and addressing its limitations, VIDAs offer a robust framework for decentralized applications—secure enough for financial systems, scalable enough for global supply chains, and flexible enough to integrate with legacy infrastructure.


# EVMs on PWR Chain

Ethereum Virtual Machines (EVMs) operate seamlessly on PWR Chain as **Verifiable Immutable Data Applications (VIDAs)**, combining the familiarity of Ethereum with PWR Chain’s scalable, decentralized infrastructure. EVM VIDAs inherit PWR Chain’s security and decentralization while maintaining full compatibility with Ethereum tooling, wallets, and tokens. Crucially, **end users interact with the EVM as if it were a standalone Layer 1 (L1) chain**—they use the EVM’s native coin (e.g., ETH, USDC) and never need to interact directly with PWR Chain or its native token ($PWR). Below, we detail how EVMs function within the PWR ecosystem and their unique advantages.

***

### Transaction Lifecycle & Flow

#### User Perspective:

To end users, the EVM on PWR Chain feels indistinguishable from a traditional L1:

1. **Initiate Action**: A user sends a transaction (e.g., token swap) via MetaMask or another EVM wallet.
2. **Pay Fees**: Fees are deducted in the EVM’s native coin (e.g., ETH), *not $PWR*.
3. **Instant Finality**: Transactions are finalized in seconds, visible on EVM block explorers.

#### Behind the Scenes:

1. **User Action**: A user initiates a transaction (e.g., token swap) via an EVM-compatible wallet (e.g., MetaMask).
2. **Wrapper Node**: The transaction is routed to a **wrapper node**, which acts as an intermediary.
   * Wraps the transaction into a VIDA Data Transaction format.
   * Pays the fee in $PWR to include the transaction on the PWR Chain, then receives 50% of the user’s EVM fee as a reward, automatically converting EVM-native fees into $PWR and effectively shielding end users from direct interaction with the PWR Chain.
3. **PWR Base Layer**: The transaction is recorded on PWR Chain with instant finality.
4. **EVM VIDA Processing**: The EVM VIDA reads the transaction, executes the smart contract logic off-chain, and updates its state.
5. **Verification**: Anyone can rerun the EVM VIDA to validate results against PWR Chain’s immutable ledger.

<figure><img src="/files/Ieimww9bjAc4ZlQOG44M" alt=""><figcaption></figcaption></figure>

***

### VIDA Execution Instance Roles: RPC & Wrapper Nodes

EVM VIDA Execution Instances can serve dual roles to enhance usability:

* **RPC Nodes**: Provide endpoints for users to query blockchain data (e.g., balances, contract states).
* **Wrapper Nodes**: Facilitate seamless interaction between EVM wallets and PWR Chain.
  * Enable users to pay fees in **any cryptocurrency** (e.g., ETH, USDC).
  * Automatically convert fees to $PWR and reimburse themselves from the EVM or user.

**Why Wrapper Nodes Matter**:

* Eliminate the need for users to hold $PWR or interact with the PWR Chain directly.
* Simplify onboarding for Ethereum developers and users and maintain the same user experience.

***

### Unique Features of EVMs on PWR Chain

#### 1. High Scalability

* **Average TPS**: 4,000
* **Max TPS**: 10,000

#### 2. Automated Smart Contracts

* Contracts can trigger actions autonomously (e.g., recurring payments, liquidity rebalancing) based on predefined conditions (time, events).
* Reduces reliance on centralized keepers or manual interventions.

#### 3. Fee Sponsorship

* Projects or third parties can sponsor transaction fees for users.
* Enables gasless interactions (e.g., free NFT mints, subsidized DeFi trades).

***

### FAQ

**Why do wrappers receive only 50% of the EVM fee?**\
Wrappers receive only 50% of the EVM Fee in order to protect the EVM from attacks. If the wrapper received 100% of the EVM Fee, then they can spam the EVM with high gas transactions for no cost.

**Who receives the other 50%?**\
This is customizable. It can be sent to a foundation address, charity, or burned. &#x20;


# Conduits

Decentralized verification for external communication

## Conduit Nodes

Conduit Nodes are specialized network participants that enable trustless communication between Verifiable Immutable Data Applications (VIDAs), and also between VIDAs and external systems (e.g. IBC).

1. **VIDA-to-VIDA Communication:** They facilitate secure transaction forwarding between Verifiable Immutable Data Applications (VIDAs) within the PWR Chain, ensuring seamless interoperability without compromising decentralization.
2. **VIDA-to-External System Communication:** They act as trustless bridges, allowing VIDAs to interact with external networks and protocols (e.g., IBC), expanding their reach beyond the PWR Chain.

This dual functionality ensures that VIDAs can communicate with each other **and** with external ecosystems while maintaining decentralization and security. Below, we explain their role, selection process, and how they secure cross-VIDA and cross-system interactions.

***

### When Are Conduit Nodes Needed?

#### **Optional by Design**

* **Not All VIDAs Need Them**: Conduit Nodes are **only required** if a VIDA needs to send data or requests (transactions) to another VIDA or external system.
* **Example**:
  * A *Payment VIDA* might need Conduit Nodes to notify a *Shipping VIDA* when an order is paid.
  * A DeFi VIDA might need Conduit Node to bridge funds using Cosmos IBC
  * A standalone *Voting VIDA* with no external dependencies doesn’t need Conduit Nodes.

***

### How Conduit Nodes Work

#### **1. Primary Role: Internal VIDA Communication**

* **Transaction Relaying**: Facilitate VIDA-to-VIDA messaging within PWR Chain.
* **Voting Mechanism**: Conduit Nodes vote to approve requests, ensuring only valid transactions are sent.

#### **2. Secondary Role: Cross-Chain Support**

* **Protocol Flexibility**: For VIDAs needing external interoperability, Conduit Nodes translate requests into formats like **IBC packets**.

#### **3. Selection Process**

* **VIDA-Specific Rules**:\
  Each VIDA defines its own criteria for selecting Conduit Nodes, such as:
  * **Staking Requirements**: Nodes must lock PWR Coin as collateral.
  * **Reputation**: Nodes with a history of reliable service.
  * **Geographic Distribution**: Minimize single points of failure.
  * **Custom Parameters**: Such as requiring the Conduit to stake the projects coin, or undergo KYC.&#x20;

#### **4. Transaction Lifecycle**

1. **Request Detection**:
   * Conduit Nodes monitor their VIDA for outgoing requests (e.g., "Notify Shipping VIDA: Payment Complete").
2. **Voting**:
   * Conduit Nodes vote on whether to approve the request.
   * Example: A VIDA might require **2/3 of Conduit Nodes** to agree.
3. **Transaction Creation**:
   * If approved, the Conduit Nodes jointly sign a PWR Chain transaction addressed to the target VIDA.
4. **Validation**:
   * PWR Chain validators verify the Conduit Nodes’ signatures and finalize the transaction.

When using external protocols such as IBC, Conduit nodes can be customized to handle requests in a format compatible with the external protocol.&#x20;

<figure><img src="/files/KBGcHXWTuyB3XiqZtSP6" alt=""><figcaption></figcaption></figure>

***

### Security & Decentralization

#### **1. Anti-Collusion Safeguards**

* **Staking Slashing**: Conduit Nodes risk losing staked PWR Coin for malicious voting.
* **Transparency**: All votes and transactions are recorded on PWR Chain for audits.

#### **2. Customizable Consensus**

* **Threshold Flexibility**:\
  VIDAs choose their voting thresholds (e.g., 51% majority, 80% supermajority).
* **Example**:
  * A high-value financial VIDA might require 90% consensus among Conduit Nodes.
  * A gaming VIDA might use 51% for faster interactions.

***

### Example Workflow: Cross-VIDA Payment

1. **Request**: A user pays via a *Payment VIDA*, which needs to notify a *Shipping VIDA*.
2. **Detection**: The Payment VIDA's Conduit Nodes detect the outgoing request.
3. **Voting**: 8/10 Conduit Nodes approve the request.
4. **Transaction**: The approved message is sent to PWR Chain as a transaction tagged with the Shipping VIDA's ID.
5. **Processing**: The Shipping VIDA reads the transaction and initiates delivery.

***

### Why Use Conduit Nodes?

| **Traditional Systems**                 | **Conduit Nodes**                |
| --------------------------------------- | -------------------------------- |
| Rely on centralized APIs.               | Trustless, decentralized relays. |
| Vulnerable to single points of failure. | Redundant nodes ensure uptime.   |

***

### Key Takeaways

* **Optional Infrastructure**: Only VIDAs requiring cross-app communication need Conduit Nodes.
* **VIDA Autonomy**: Each VIDA selects and governs its Conduit Nodes based on its own rules.
* **Trustless Design**: PWR Chain enforces transparency, while Conduit Nodes prevent spam and fraud.

***

For developers building interoperable VIDAs, Conduit Nodes provide a flexible, secure way to bridge applications without sacrificing decentralization.


# Consensus

PWR Chain builds upon the Tendermint consensus protocol while introducing specific enhancements for quantum security and faster block creation. Compared to standard Tendermint, the key differences are:

* **Post-Quantum Signatures:**\
  PWR Chain replaces classical signature algorithms (e.g., Ed25519) with **Falcon**, a post-quantum secure scheme. This strengthens the chain’s resistance against potential quantum-computing attacks.
* **UDP-Based Gossip:**\
  Instead of using TCP for consensus messages, PWR Chain leverages **UDP** to broadcast and gossip validator votes and signatures. This aims to reduce overhead, decrease latency, and speed up block finalization.
* **Equal Chances:**\
  Instead of giving validators chances proportional to their stake, all active validators on PWR Chain have equal chances to submit blocks. &#x20;

For additional details on the foundational protocol, refer to the [Tendermint paper](https://tendermint.com/static/docs/tendermint.pdf).

***

## Why is Equal Chances Important for Decentralization?

In conventional Proof-of-Stake (PoS) systems, validators receive block creation opportunities in proportion to their stake. Many blockchains have adopted this approach, and we’ve seen it often lead to centralization. Why does this happen?

When block creation chances are tied to stake size, large validators naturally produce most of the blocks. This results in a stable return on investment (ROI) for their delegators, which in turn attracts even more delegators. As a result, these large validators grow larger and more influential over time.

On the other hand, if all validators receive an equal opportunity to create blocks, they each earn roughly the same total reward. However, a validator with a large stake must distribute that reward across a bigger pool of delegators, lowering its ROI compared to smaller validators. This dynamic incentivizes users to delegate to smaller validators, which helps maintain a more decentralized network.

***

## The Math in an Equal Chances Model

1. **Validators and Stakes**
   * **V1** has a stake of 1,000,000 coins.
   * **V2** has a stake of 500,000 coins.
2. **Equal Yearly Reward**
   * Both V1 and V2 receive the same total yearly reward, **R**.
     * V1 earns R per year (across its 1,000,000 staked coins).
     * V2 earns R per year (across its 500,000 staked coins).
3. **Yearly Yield per Coin**

   * For V1: yield per coin = R / 1,000,000
   * For V2: yield per coin = R / 500,000

   Observing the numbers:

   * R / 500,000 is equal to 2 × (R / 1,000,000).
   * Therefore, each coin staked with V2 earns twice as much as each coin staked with V1, because V2 has half the stake of V1 but still gets the same block reward R.
4. **Why This Encourages Decentralization**
   * Large validators (with large stake) have a lower yield per coin than smaller validators.
   * Delegators seeking higher returns are incentivized to stake with smaller validators, which helps distribute stake more evenly over time and fosters decentralization.


# Scalability

Traditional blockchain architectures face two primary bottlenecks that limit scalability:

1. **Smart Contract Virtual Machines (VMs)**: Complex on-chain execution environments like Ethereum’s EVM impose computational overhead, restricting throughput.
2. **Transaction Signature Verification**: Cryptographic signature checks, especially under high load, consume significant time and resources.

PWR Chain eliminates the first bottleneck entirely by **decoupling execution from consensus**. Unlike monolithic chains, PWR Chain does not process smart contracts or host a VM on itself. Instead, Verifiable Immutable Data Applications (VIDAs) handle logic off-chain, freeing the blockchain to focus solely on ordering and securing transactions.

The remaining challenge—signature verification—is addressed through two innovations:

#### 1. **Post-Quantum Signature Efficiency**

PWR Chain uses **Falcon**, a post-quantum secure signature scheme optimized for speed. A single CPU core can verify **20,000–40,000 Falcon signatures per second** (depending on hardware), making it orders of magnitude faster than classical algorithms like ECDSA.

#### 2. **Parallelized Processing**

PWR Chain is a **multi-threaded blockchain**, enabling validators to verify signatures and process transactions in parallel across multiple CPU cores. This design ensures linear scalability:

* **1 core**: 20k–40k transactions per second (TPS).
* **16-core server (32 threads)**: Over **600k TPS** (e.g., 32 threads × 20k TPS).

Validators can dynamically scale server capacity to meet demand. For example:

* **Low-cost deployment**: A $6/month server (4 cores/8 threads) handles 160**k TPS**.
* **Enterprise-grade throughput**: A $140/month server (16 cores/32 threads) achieves **600k+ TPS**.

#### Cost Efficiency

By avoiding smart contract execution and leveraging lightweight cryptography, PWR Chain reduces operational costs to a fraction of traditional chains. Validators incur minimal expenses while supporting global-scale throughput, making PWR Chain:

* **Accessible**: Start with consumer-grade hardware.
* **Future-proof**: Scale seamlessly via horizontal server upgrades.

#### Comparison to Traditional Chains

| **Metric**         | **Ethereum** | **Solana**     | **PWR Chain**            |
| ------------------ | ------------ | -------------- | ------------------------ |
| On-chain execution | Yes (EVM)    | Yes (Sealevel) | **No** (VIDAs off-chain) |
| Signature Scheme   | ECDSA        | Ed25519        | **Falcon**               |
| Max TPS            | \~30         | \~65k          | **600k+**                |

This architecture ensures PWR Chain scales not just through VIDA parallelism but also via blockchain optimizations, making it one of the most efficient and cost-effective blockchains for high-throughput use cases.


# Security

PWR Chain is engineered to provide robust security across its consensus mechanism, transaction validation, and decentralized application layer. By integrating NIST-standardized Post-Quantum cryptography, enhancing Byzantine Fault Tolerance (BFT), and enforcing strict economic incentives, PWR Chain ensures resilience against both current and future threats.

***

#### **1. Consensus Security: Quantum-Resistant Tendermint**

PWR Chain modifies the Tendermint BFT protocol to address quantum computing risks and centralization pressures:

* **NIST-standardized Post-Quantum Validator Signatures**:\
  Replaces classical Ed25519 with **Falcon**, a NIST-standardized post-quantum secure signature scheme. This ensures validator votes and block proposals remain secure against quantum attacks.
* **Instant Finality**:\
  Inherits Tendermint’s deterministic finality—blocks are irreversibly finalized in seconds, eliminating reorg risks.
* **Decentralized Block Production**:\
  Implements an **equal chances model** for validators, where block creation opportunities are distributed equally among active validators, regardless of stake size. This prevents stake concentration and promotes long-term decentralization.

***

#### **2. Wallet & Address Security**

PWR Chain prioritizes both security and usability in its wallet and address system. Here are the key features:

1. **Ethereum-Compatible Addresses**: PWR Chain uses the standard Ethereum address format, ensuring easy integration with existing exchanges, wallets, and infrastructure.
2. **Quantum Resistance**: Despite their familiar format, PWR Chain addresses are quantum-secure. The use of Falcon for signatures protects against quantum threats.
3. **Collision Resistance**: Despite the short address format of only 20 bytes, every address in PWR Chain is linked to one and only one public key once it's used. Providing protection against address collision attacks.&#x20;
4. **Easy Integration**: The use of standard Ethereum-format addresses simplifies the process of listing PWR Coin on exchanges and integrating with self-custody wallets, enhancing accessibility for users.

By combining quantum security with user-friendly design, PWR Chain offers a robust and accessible address system that's ready for both current and future cryptographic challenges.

#### **3. Transaction Security: Quantum-Resistant Signatures**

Every transaction on PWR Chain is secured by **Falcon signatures**, ensuring:

* **Future-Proofing**: Resistant to Shor’s algorithm and other quantum decryption methods.
* **Efficiency**: Single CPU core verifies 20k–40k signatures/second, balancing security with scalability.
* **Tamper-Proof Records**: All transactions are immutably timestamped on-chain, creating an auditable history resistant to alteration.

***

#### **4. Additional Safeguards**

**Economic Security**

* **Staking Slashing**: Validators and Conduit Nodes risk losing staked PWR Coin for malicious actions (e.g., double-signing).
* **Inflation-Driven Incentives**: A 2% annual inflation rate rewards honest staking, dynamically adjusting yields to maintain network participation.

**VIDA Security**

* **VIDA Replayability**: Anyone can independently verify VIDA outputs by reprocessing on-chain transaction logs, eliminating reliance on trusted intermediaries.
* **Cross-Validation**: Optional consensus among VIDA Execution Instances to detect and flag discrepancies in state transitions.

**Conduit Node Governance**

* **Anti-Collusion Rules**: Customizable staking requirements, geographic distribution, and reputation systems for Conduit Nodes.
* **Transparent Voting**: All cross-VIDA communication votes are recorded on-chain for public audit.

***

#### **Why PWR Chain’s Security Model Stands Out**

| **Aspect**             | **Traditional Chains**    | **PWR Chain**                |
| ---------------------- | ------------------------- | ---------------------------- |
| **Quantum Resistance** | Relies on ECDSA/Ed25519   | **Falcon** (NIST-standard)   |
| **Finality**           | Probabilistic (e.g., PoW) | **Instant** (BFT-based)      |
| **Decentralization**   | Stake-weighted validation | **Equal chances** + slashing |

***

By unifying quantum-resistant cryptography, decentralized consensus, and trustless verification, PWR Chain delivers enterprise-grade security without compromising scalability or accessibility. Its layered defenses ensure resilience against evolving threats while maintaining the agility required for mainstream adoption.


# Transaction Fee Model

Since PWR Chain is not a smart contract platform, transaction fees are primarily determined by the size of the transaction and the signature scheme used. The total transaction fee is calculated as follows:

*Transaction Fee = (Fee Per Byte × Transaction Size) + Signature Verification Fee*

1. **Fee Per Byte**
   * Reflects the cost per byte of data within a transaction.
   * The exact base value is determined before PWR Chain’s mainnet launch.
   * In case of congestion, users can choose to pay a higher rate to prioritize their transactions.
2. **Signature Verification Fee**
   * Represents the computational effort required to verify a signature.
   * Because PWR Chain supports multiple signature schemes—each varying in complexity—the fee differs accordingly.
   * Each fee amount is predefined based on how much processing power is required to verify a given signature type.

This structure allows PWR Chain to accurately attribute costs to the resources consumed, ensuring that network participants pay fees commensurate with the complexity and size of their transactions.


# PWR Coin Utility

* **Network Security (Validator Nodes & Delegators)**
  * Validators and delegators stake PWR Coin to secure the network.
  * Staking contributes to network integrity and earns rewards.
  * Encourages long-term commitment and aligns incentives with the chain’s health.
* **Decentralized On-chain Governance**
  * Token holders use PWR Coin to vote on proposals affecting the network.
  * Ensures a transparent and democratic process for protocol upgrades, fee adjustments, and other governance matters.
* **Transaction Fees**
  * PWR Coin is used to pay transaction fees for network operations.
  * Covers fees for deploying decentralized applications, managing wallet security features, storing data, and enabling cross-chain interoperability.
  * Ensures costs are distributed fairly among users and resources are allocated efficiently.
* **Additional Fees & Network Services**
  * **Proposal Creation & Governance Actions**
    * Incur fees when creating or modifying proposals within the governance framework.
    * Ensures that only well-considered changes are introduced to the network.
  * **Early Withdrawal**
    * Delegator withdrawals undergo a 7-day lock period. Acknowledging that exceptional circumstances may arise, the network allows participants to withdraw staked assets early (4, 5, or 6 days) by accepting a penalty on staked funds, balancing individual liquidity needs with the network’s sustainability.
  * **VIDA Claiming & ID Management**
    * VIDAs on the PWR Chain require an ID that must be purchased to secure ownership and prevent spam claims.
* **Staking by Conduit Nodes**
  * Conduit nodes, essential for relaying transactions between VIDAs and the PWR Chain, must also stake PWR Coin.
  * Reinforces network reliability by ensuring nodes are invested in the chain’s security and performance.
* **Conduit Fee Payments**
  * PWR Coin is used to pay transaction fees when conduits send votes and VIDA requests to PWR Chain.
  * VIDA's reward their conduit nodes with PWR Coin.
  * VIDA's can chose additional forms of assets to reward their conduits (Coins and NFTs), but PWR Coin is the base for rewards.


# Costs, Inflation & Deflation

PWR Chain is engineered for exceptional cost efficiency, minimizing barriers to network participation. Validator nodes operate on modest hardware requirements: **1 vCPU**, **1GB RAM**, a **100GB NVME SSD** for state management, and **HDD storage** for block history. These specifications make PWR Chain one of the most affordable blockchains to run, democratizing access to network validation.

To ensure long-term network sustainability and validator incentives, PWR Chain implements a **2% annual inflation rate**. However, this rate is applied only to the **initial supply of 1 billion PWR Coins**, meaning that the absolute number of new coins issued remains constant each year. As the total supply grows over time, the **relative inflation rate decreases**, ensuring a deflationary effect.

Key properties of the inflation model:

* **Fixed at 20 million PWR per year** (2% of 1 billion).
* **Not subject to change by validators**—modifying the inflation rate would require a hard fork.
* **Encourages staking participation** while maintaining predictable supply dynamics.

This structure ensures a balanced approach where validators are incentivized, but inflationary pressure decreases as adoption increases.

This inflation is dynamically distributed to delegators, with yields inversely proportional to the percentage of circulating supply staked:

* **10% Staked**: Delegators earn up to **20% APY** (2% inflation ÷ 10% staked x 100).
* **20% Staked**: Yields drop to **10% APY** (2% ÷ 20% x 100).
* **50% Staked**: Yields reach **4% APY** (2% ÷ 50% x 100).
* **100% Staked**: Yields stabilizes at &#x32;**% APY** (2% ÷ 100% x 100).

This mechanism achieves two critical goals:

1. **Security Incentives**: When staking rates are low, high APY attracts delegators, strengthening network decentralization.
2. **Economic Flexibility**: As staking participation grows, lower yields encourage holders to allocate coins elsewhere—such as DeFi protocols, governance, or VIDA operations—optimizing capital utilization without compromising security.

**Deflationary Fee Burning**

In addition to the fixed inflation model, PWR Chain incorporates a **deflationary mechanism** through **fee burning**.

* **10% of all transaction fees are permanently burned**, reducing the circulating supply over time.
* **Validators can vote to adjust the burn percentage**, allowing governance to fine-tune network economics as needed.

By balancing **fixed inflation with variable fee burning**, PWR Chain creates a dynamic yet controlled monetary policy designed for long-term sustainability.


# How To Build a VIDA

*Prerequisite:*&#x20;

* *Reading and understanding the white paper*
* *Basic coding knowledge*

{% content-ref url="/pages/dOw6HILmhpAwsYvmHz1T" %}
[Basics of Blockchain Development](/how-to/how-to-build-a-vida/basics-of-blockchain-development)
{% endcontent-ref %}

{% content-ref url="/pages/GEEf0dWsmWD24wfU3KZT" %}
[Stateless vs. Stateful VIDAs](/how-to/how-to-build-a-vida/stateless-vs.-stateful-vidas)
{% endcontent-ref %}

{% content-ref url="/pages/DkhTc6amaDlcvEqTqV2l" %}
[Building a Stateless VIDA](/how-to/how-to-build-a-vida/building-a-stateless-vida)
{% endcontent-ref %}

{% content-ref url="/pages/KYVOQegUPvp6JiXwxmUq" %}
[Building a Stateful VIDA](/how-to/how-to-build-a-vida/building-a-stateful-vida)
{% endcontent-ref %}


# Basics of Blockchain Development

Before diving into building an Verifiable Immutable Data Application (VIDA), it’s crucial to understand some fundamental blockchain concepts. These concepts form the backbone of decentralized systems and are essential for creating and managing VIDAs effectively.

***

#### **1. Addresses: Your Blockchain Identity**

* **What is an Address?**\
  An address is a unique alphanumeric string that represents a user or entity on the blockchain. Think of it as your public username or account number.\
  Example: 0x552E02E5B031AF314736AE5F947fAB338992aD04
* **Why is it Important?**\
  Addresses allow users to send and receive data or digital assets securely without revealing personal information. Every transaction on the blockchain is tied to an address, ensuring transparency and traceability.
* **How It Works:**\
  Addresses are derived from public keys, which are part of a cryptographic pair (public and private keys). This ensures that only the owner of the corresponding private key can authorize actions associated with the address.

***

#### **2. Wallets: Your Digital Safe**

* **What is a Wallet?**\
  A wallet is a tool that manages your private keys, enabling you to interact with the blockchain securely. It’s like your digital safe, storing the credentials needed to access and use your blockchain address.
* **Types of Wallets:**
  * **Hot Wallets:** Connected to the internet (e.g., browser extensions, mobile apps). Convenient but less secure.
  * **Cold Wallets:** Offline storage (e.g., hardware wallets). Highly secure but less accessible.
* **Why Do You Need a Wallet?**
  * To sign transactions (prove ownership of an address).
  * To store digital assets like PWR Coins (used for transaction fees).
  * To interact with VIDAs by submitting or verifying data on PWR Chain.
* **How It Works:**\
  When you create a wallet, it generates a private-public key pair:
  * The *private key* is secret and used to sign transactions.
  * The *public key* is shared openly and used to derive your address.

***

#### **3. Transactions: Recording Actions**

* **What is a Transaction?**\
  A transaction represents an action or request submitted to the blockchain. For example:
  * Sending PWR Coins from one address to another.
  * Submitting data to an VIDA for processing.
* **Why Are Transactions Important?**\
  Transactions are immutable records stored on the blockchain. They ensure transparency, accountability, and trust in decentralized systems.
* **Key Components of a Transaction:**
  * *Sender Address*: The initiator of the action.
  * *Receiver Address*: The target of the action (optional).
  * *Payload*: Data or instructions tied to the action (e.g., "redeem\_points").
  * *Signature*: Cryptographic proof that the sender authorized the transaction.

***

#### **4. PWR Chain: The Backbone of VIDAs**

* **What is PWR Chain?**\
  PWR Chain is a decentralized ledger that securely records all transactions related to VIDAs. Unlike traditional blockchains, it focuses solely on storing data, while VIDAs handle execution off-chain.
* **Why Use PWR Chain?**
  * Ensures data integrity with immutable records.
  * Provides transparency for all actions performed by VIDAs.
  * Avoids traditional blockchain bottlenecks by separating execution from consensus.
  * Allows deveopment in any coding language.

***

#### **5. Transaction Fees: The Cost of Doing Business**

* **What Are Transaction Fees?**\
  Transaction fees are small payments required to process transactions on the blockchain. They compensate validators for securing the network and recording transactions.
* **Why Are Transaction Fees Necessary?**\
  They prevent spam by attaching a cost to every action and ensure that validators are incentivized to maintain network integrity.

***

#### **Why Understanding These Concepts Matters**

If you want to build a decentralized VIDA, you must grasp these basics because:

1. Your app will rely on wallets for user authentication and interaction.
2. All actions in your app will be recorded as transactions on PWR Chain.
3. Decentralization ensures your app remains trustless, transparent, and tamper-proof.

Even when building a centralized VIDA, understanding these fundamentals is essential to help you build an VIDA.

By mastering these foundational ideas, you’ll be ready to create powerful applications that leverage blockchain’s unique strengths while remaining accessible to users worldwide.


# Stateless vs. Stateful VIDAs

When building an Verifiable Immutable Data Application (VIDA), it’s important to understand the difference between stateless and stateful VIDAs. These two types of VIDAs serve different purposes and are designed for different use cases, depending on the criticality of the data and the level of consistency required.

***

#### **Stateless VIDAs**

Stateless VIDAs are applications that do not need to validate or manage historical data or maintain a consistent state across its Execution Instances. They focus on simplicity, speed, and ease of development, making them ideal for non-critical use cases.

* **Use Cases:**
  * Global chat rooms
  * Simple games
  * Non-critical applications where speed and flexibility are prioritized
* **Key Characteristics:**
  * Do not track or validate past transactions or states.
  * Operate independently of historical data.
  * Prioritize fast execution and low overhead.

Stateless VIDAs are perfect for scenarios where consistency between execution instances is not a concern, and the focus is on rapid development and user interaction.

***

#### **Stateful VIDAs**

Stateful VIDAs, on the other hand, are designed to manage critical data and ensure consistency across all execution instances. They validate both past and present data to guarantee the correctness of execution and maintain a synchronized state.

* **Use Cases:**
  * Financial systems (e.g., payment processing)
  * Supply chain management
  * Internal company operations (e.g., HR or ERP systems)
* **Key Characteristics:**
  * Validate historical data to ensure correctness.
  * Use mechanisms like cross-instance root hash validation or conduit root hash validation on PWR Chain to maintain consistency.
  * Focus on reliability, auditability, and trustworthiness for mission-critical applications.

Stateful VIDAs are essential for applications where data accuracy, consistency, and integrity are non-negotiable.

***

#### **Why This Distinction Matters**

Understanding whether your VIDA should be stateless or stateful is critical because it impacts how you design, develop, and operate your application. Stateless VIDAs offer simplicity but may lack reliability for critical use cases, while stateful VIDAs provide robust guarantees at the cost of additional complexity. Choosing the right type ensures your VIDA aligns with your application's requirements and goals.


# Building a Stateless VIDA

Stateless VIDAs are lightweight, fast, and simple applications that do not require validating or maintaining historical data or consistent state across its execution instances. They are ideal for non-critical use cases such as chat rooms, simple games, or other applications where speed and ease of development are prioritized over strict consistency.

***

#### **Steps to Build a Stateless VIDA**

#### **1. Select an ID for Your VIDA**

Every VIDA requires a unique identifier, which is an 8-byte variable. This ID ensures the PWR Chain knows which transactions belong to your application.

* **Why 8 bytes?**\
  It minimizes storage requirements while allowing for 18 quintillion unique IDs.

{% tabs %}
{% tab title="Java" %}

```java
//generate a random long value
long vidaId = new SecureRandom().nextLong();
System.out.println(vidaId);
        
//Save the vidaId
```

{% endtab %}

{% tab title="JavaScript" %}

```javascript
const crypto = require('crypto');

// Generate a random 64-bit integer
const vidaId = BigInt('0x' + crypto.randomBytes(8).toString('hex'));

console.log(vidaId.toString());
```

{% endtab %}

{% tab title="Python" %}

```python
# Generate a random 64-bit signed integer
vida_id = secrets.randbits(64) - (1 << 63)

print(vida_id)
```

{% endtab %}

{% tab title="Rust" %}

```rust
use rand::Rng;

fn main() {
    let mut rng = rand::thread_rng();
    let veda_id: i64 = rng.gen();
    
    println!("{}", veda_id);
}
```

{% endtab %}

{% tab title="Go" %}

```go
package main

import (
    "crypto/rand"
    "encoding/binary"
    "fmt"
)

func main() {
    var vedaId int64
    err := binary.Read(rand.Reader, binary.LittleEndian, &vedaId)
    if err != nil {
	fmt.Println("Error generating random number:", err)
	return
    }

    fmt.Println(vedaId)
}
```

{% endtab %}
{% endtabs %}

***

#### **2. Import the PWR SDK**

The PWR SDK is your toolkit for interacting with the PWR Chain. It allows you to create wallets, send transactions, and read data from the blockchain.

{% tabs %}
{% tab title="Java (Maven)" %}

```xml
<repositories>
    <repository>
        <id>jitpack.io</id>
        <url>https://jitpack.io</url>
    </repository>
</repositories>

<dependencies>
    <dependency>
        <groupId>com.github.pwrlabs</groupId>
        <artifactId>pwrj</artifactId>
        <version>11.4.1</version>
    </dependency>
</dependencies>
```

Make sure to use the latest version. Check <https://github.com/pwrlabs/pwrj> for the latest release

```java
import com.github.pwrlabs.pwrj.protocol.PWRJ;
```

{% endtab %}

{% tab title="Java (Gradle)" %}

```gradle
dependencyResolutionManagement {
	repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
	repositories {
		mavenCentral()
		maven { url 'https://jitpack.io' }
	}
}

dependencies {
	implementation 'com.github.pwrlabs:pwrj:8.7.0'
}
```

Make sure to use the latest version. Check <https://github.com/pwrlabs/pwrj> for the latest release

```java
import com.github.pwrlabs.pwrj.protocol.PWRJ;
```

{% endtab %}

{% tab title="JavaScript" %}

```javascript
import { PWRJS, PWRWallet } from '@pwrjs/core';
// or
const { PWRJS, PWRWallet } = require('@pwrjs/core');
```

{% endtab %}

{% tab title="Python" %}

```python
from pwrpy.pwrapisdk import PWRPY
from pwrpy.pwrwallet import PWRWallet
```

{% endtab %}

{% tab title="Rust" %}

```rust
use pwr_rs::{ Wallet, RPC };
```

{% endtab %}

{% tab title="Go" %}

```go
import (
    "github.com/pwrlabs/pwrgo/wallet"
    "github.com/pwrlabs/pwrgo/rpc"
)
```

{% endtab %}
{% endtabs %}

***

#### **3. Initializing PWR with an RPC Endpoint**

To interact with the PWR Chain, initialize a PWR object (e.g., `PWRJ` for Java, `PWRPY` for Python). This object serves as your gateway to the blockchain.

**What is an RPC Node?**

An RPC (Remote Procedure Call) node processes blockchain requests, such as transactions and data queries. You can use a public node (e.g., `https://pwrrpc.pwrlabs.io`) or run your own for better control and security.

{% tabs %}
{% tab title="Java" %}

```java
PWRJ pwrj = new PWRJ("https://pwrrpc.pwrlabs.io");
```

{% endtab %}

{% tab title="JavaScript" %}

```javascript
const pwrjs = new PWRJS("https://pwrrpc.pwrlabs.io/");
```

{% endtab %}

{% tab title="Python" %}

```python
pwrpy = PWRPY()
```

{% endtab %}

{% tab title="Rust" %}

```rust
let pwrrs = RPC::new("https://pwrrpc.pwrlabs.io/").await.unwrap();
```

{% endtab %}
{% endtabs %}

This setup enables seamless interaction with the PWR Chain for your VIDA.

#### **4. Create and Fund a Wallet**

A wallet is essential for signing transactions and paying minimal fees on the PWR Chain.

* **Steps:**
  1. Create a new wallet or load an existing one.
  2. Save the wallet securely in an encrypted file.
  3. Fund it using the [PWR Chain faucet](http://faucet.pwrlabs.io/) (for test coins). You can check your PWR coins balance on the[ PWR Chain Explorer](https://explorer.pwrlabs.io/) by putting your address in the search bar.&#x20;

{% tabs %}
{% tab title="Java" %}

```java
    private static PWRFalconWallet createWallet(PWRJ pwrj, String password) {
        try {
            //Try to load an existing wallet
            PWRFalconWallet wallet = PWRFalconWallet.loadWallet(pwrj, "wallet", password);
            
            // If the wallet is null, it means it was not found or could not be loaded
            // Create new wallet
            if(wallet == null) {
                wallet = new PWRFalconWallet(12, pwrj);
                wallet.storeWallet("wallet", password);
            }
            
            return wallet;
        } catch (Exception e) {
            System.err.println("Failed to create or load wallet: " + e.getMessage());
            e.printStackTrace();
            return null;
        }
    }
```

{% endtab %}

{% tab title="JavaScript" %}

```javascript
const { PWRWallet, PWRJS} = require('@pwrjs/core');

const pwrjs = new PWRJS("https://pwrrpc.pwrlabs.io/");

// generate and save wallet
const wallet = new PWRWallet();
console.log("Address: " + wallet.getAddress());
wallet.storeWallet("wallet.dat", "password");

//load wallet
const loadedWallet = PWRWallet.loadWallet("wallet.dat", "password", pwrjs);
console.log("Address: " + loadedWallet.getAddress());
```

{% endtab %}

{% tab title="Python" %}

```python
from pwrpy.pwrwallet import PWRWallet

# generate and save wallet
wallet = PWRWallet()
print(f"Address: {wallet.get_address()}")
PWRWallet(private_key).store_wallet("wallet.dat", "password")

# load wallet
loaded_wallet = PWRWallet.load_wallet("wallet.dat", "password")
print(f"Address: {loaded_wallet.get_address()}")
```

{% endtab %}

{% tab title="Rust" %}

```rust
use pwr_rs::Wallet;

fn main() {
    // generate and save wallet
    let wallet = Wallet::random();
    println!("Address: {:?}", wallet.get_address());
    wallet.store_wallet("wallet.dat", "password")
        .expect("Failed to store wallet");

    // load wallet
    let loaded_wallet = Wallet::load_wallet("wallet.dat", "password")
        .expect("Failed to load wallet");
    println!("Address: {:?}", loaded_wallet.get_address());
}
```

{% endtab %}

{% tab title="Go" %}

```go
import (
    "fmt"
    "github.com/pwrlabs/pwrgo/wallet"
)

func main() {
    // generate and save wallet
    var new_wallet = wallet.NewWallet()
    fmt.Printf("Address: %s\n", new_wallet.GetAddress())
    new_wallet.StoreWallet("wallet.dat", "password")

    // load wallet
    var loaded_wallet, _ = wallet.LoadWallet("wallet.dat", "password")
    fmt.Printf("Address: %s\n", loaded_wallet.GetAddress())
}
```

{% endtab %}
{% endtabs %}

***

#### **5. Define Transaction Data Structure**

While PWR Chain stores all transaction data as raw byte arrays, VIDAs can encode this data into structured formats like JSON. Defining a **schema for your transactions** ensures consistency, simplifies development, and enables collaboration across teams.

#### **Why Define a Schema?**

* **Consistency**: Ensures all transactions follow a predictable format.
* **Documentation**: Serves as a reference for developers interacting with your VIDA.
* **Validation**: Helps catch malformed data early.

Example:&#x20;

```json
[
    {
        "action": "send-message-v1",
        "message": "Hello World!"
    },
    
    {
        "action": "add-reaction-v1",
        "message-hash": "0x54ef...",
        "reaction": "thumbs-up"
    }
]
```

***

#### **6. Send Data to PWR Chain**

After defining your transaction's data structure, you can start sending transactions to PWR Chain. Submit transactions to the PWR Chain to record user actions or data.

{% tabs %}
{% tab title="Java" %}

```java
//Write transaction data
JSONObject jsonObject = new JSONObject();
jsonObject.put("action", "send-message-v1");
jsonObject.put("message", "Hello World!");
byte[] data = jsonObject.toString().getBytes(StandardCharsets.UTF_8);

//Send transaction
Response response = wallet.sendVmDataTransaction(vidaId, data, loadedWallet.getNonce());
if(response.isSuccess()) {
    System.out.println("Transaction sent successfully!");
    System.out.println("Transaction hash: " + response.getTransactionHash());
}
else System.out.println("Transaction failed: " + response.getError());
```

{% endtab %}

{% tab title="Javascript" %}

```javascript
// Write transaction data
const obj = {
    action: 'send-message-v1',
    message: 'Hello World!',
};

const data = new TextEncoder().encode(obj);

//Send transaction
const response = wallet.sendVMDataTxn(vidaId, data);

if(response.sucuccess) {
    console.log("Transaction sent successfully!");
    console.log("Transaction hash: " + response.transactionHash);
}
else console.log("Transaction failed: " + response.message);
```

{% endtab %}

{% tab title="Python" %}

```python
# Write transaction data
json_object = {
    "action": "send-message-v1",
    "message": "Hello World!"
}
data = json.dumps(json_object).encode("utf-8")

# Send transaction
response = wallet.send_vm_data_transaction(vidaId, data)
if response.success:
    print("Transaction sent successfully!")
    print(f"Transaction hash: 0x{response.data.hex()}")
else:
    print(f"Transaction failed: {response.message}")
```

{% endtab %}

{% tab title="Rust" %}

```rust
// Write transaction data
let json_object = json!({
    "action": "send-message-v1",
    "message": "Hello World!"
});
let data: Vec<u8> = serde_json::to_string(&json_object).unwrap().into_bytes();

// Send transaction
let response_tx = wallet.send_vm_data(vidaId, data).await;
if response_tx.success {
    println!("Transaction sent successfully!");
    println!("Transaction hash: {:?}", response_tx.data.unwrap());
} else {
    println!("Transaction failed: {:?}", response_tx.message);
}
```

{% endtab %}

{% tab title="Go" %}

<pre class="language-go"><code class="lang-go">// Write transaction data
<strong>jsonObject := map[string]string{
</strong>    "action":  "send-message-v1",
    "message": "Hello World!",
}
data, _ := json.Marshal(jsonObject)

// Send transaction
response := wallet.SendVMData(vidaId, data)
if response.Success {
   fmt.Println("Transaction sent successfully!")
   fmt.Printf("Transaction hash: %s\n", response.TxHash)
} else {
   fmt.Printf("Transaction failed: %s\n", response.Error)
}
</code></pre>

{% endtab %}
{% endtabs %}

***

#### **7. Read Data from PWR Chain & Handle it.**

The PWR SDK provides functions to easily read and handle data from PWR Chain.

{% tabs %}
{% tab title="Java" %}

```java
PWRJ pwrj = new PWRJ("https://pwrrpc.pwrlabs.io/");

long vidaId = 1; // Replace with your VIDA's ID  

/*Since our VIDA is global chat room and we don't care about historical messages,
we will start reading transactions startng from the latest PWR Chain block*/
long startingBlock = pwrj.getBlockNumber();

VidaTransactionSubscription vidaTransactionSubscription = pwrj.subscribeToVidaTransactions(pwrj, vidaId, startingBlock, (transaction) -> {
    VmDataTransaction vmDataTransaction = transaction;

    //Get the address of the transaction sender
    String sender = vmDataTransaction.getSender();

    //Get the data sent in the transaction (In Hex Format)
    String data = vmDataTransaction.getData();

    try {
        //Decode the data from Hex to byte array
        if (data.startsWith("0x")) data = data.substring(2);
        byte[] dataBytes = Hex.decode(data);

        //Convert the byte array to a JSON Object
        JSONObject jsonObject = new JSONObject(new String(dataBytes));
        String action = jsonObject.optString("action", "no-action");

        //Check the action and execute the necessary code
        if (action.equalsIgnoreCase("send-message-v1")) {
            String message = jsonObject.getString("message");
            System.out.println("Message from " + sender + ": " + message);
        } else {
            //ignore
        }
    } catch (Exception e) {
        e.printStackTrace();

        //This most likely indicates Malformed data from the sender
    }
});

//To pause, resume, and stop the subscription
vidaTransactionSubscription.pause();
vidaTransactionSubscription.resume();
vidaTransactionSubscription.stop();
vidaTransactionSubscription.start();

//To get the block number of the latest checked PWR Chain block
vidaTransactionSubscription.getLatestCheckedBlock();
```

{% endtab %}

{% tab title="Javascript" %}

```javascript
const pwrj = new PWRJS("https://pwrrpc.pwrlabs.io/");

const vidaId = 1n; // Replace with your VIDA's ID

// Since our VIDA is global chat room and we don't care about historical messages, we will start reading transactions startng from the latest PWR Chain block/ long startingBlock = pwrj.getBlockNumber();
function handler(transaction: VmDataTransaction){
    
    //Get the address of the transaction sender
    const sender = VmDataTransaction.sender;
    
    //Get the data sent in the transaction (In Hex Format)
    let data = VmDataTransaction.data;
    
    try {
    
      // convert data string to bytes 
      if (data.startsWith("0x")) data = data.substring(2);
      const bytes = hexToBytes(data);
      const dataStr = new TextDecoder().decode(bytes);
      const dataJson = JSON.parse(dataStr);
      
      //Check the action and execute the necessary cod
      if (dataJson.action === "send-message-v1") {
          const message = data.message;
          console.log("Message from " + sender + ": " + message);
      }
    } catch (e) {
        console.error(e)
    }
}


const subscription = pwrjs.subscribeToVidaTransactions(
    pwrjs, 
    vidaId, 
    startingBlock, 
    {handler}
);
//To pause, resume, and stop the subscription vidaTransactionSubscription.pause(); vidaTransactionSubscription.resume(); vidaTransactionSubscription.stop(); vidaTransactionSubscription.start();
//To get the block number of the latest checked PWR Chain block vidaTransactionSubscription.getLatestCheckedBlock();
```

{% endtab %}

{% tab title="Python" %}

```python
from pwrpy.pwrsdk import PWRPY
from pwrpy.models.Transaction import VmDataTransaction
import json
import time

rpc = PWRPY()
vida_id = 1

# Since our VIDA is global chat room and we don't care about historical messages,
# we will start reading transactions startng from the latest PWR Chain block
starting_block = rpc.get_latest_block_number()

def handle_transaction(txn: VmDataTransaction):
    try:
        sender = txn.sender
        data_hex = txn.data
        data_bytes = bytes.fromhex(data_hex[2:])
        obj = json.loads(data_bytes.decode('utf-8'))

        if obj["action"] == "send-message-v1":
            print(f"Message from {sender}: {obj['message']}")

    except Exception as e:
        print(f"Error processing transaction: {e}")

rpc.subscribe_to_vida_transactions(vida_id, starting_block, handler=handle_transaction)

while True:
    time.sleep(1)
```

{% endtab %}

{% tab title="Rust" %}

```rust
use pwr_rs::{
    RPC,
    transaction::types::VMDataTransaction,
    rpc::tx_subscription::VidaTransactionHandler
};
use std::sync::Arc;
use serde_json::Value;

#[tokio::main]
async fn main() {
    let rpc = Arc::new(RPC::new("https://pwrrpc.pwrlabs.io/").await.unwrap());

    let vida_id = 1;
    let starting_block = rpc.get_latest_block_number().await.unwrap();

    struct Handler(Box<dyn Fn(VMDataTransaction) + Send + Sync>);
    impl VidaTransactionHandler for Handler {
        fn process_vida_transactions(&self, tx: VMDataTransaction) {
            (self.0)(tx)
        }
    }

    let handler = Arc::new(Handler(Box::new(|txn: VMDataTransaction| {
        let sender = txn.sender;
        let data = txn.data;
        let data_str = String::from_utf8(data).unwrap();
        let object: Value = serde_json::from_str(&data_str).unwrap();
        let obj_map = object.as_object().unwrap();

        if obj_map.get("action").and_then(|val| val.as_str()) == Some("send-message-v1")
        {
            if let Some(message_str) = obj_map
                .get("message")
                .and_then(|val| val.as_str())
            {
                println!("Message from {}: {}", sender, message_str);
            }
        }
    })));

    rpc.subscribe_to_vida_transactions(vida_id, starting_block, handler, None);

    loop {
        tokio::time::sleep(std::time::Duration::from_millis(1)).await;
    }
}
```

{% endtab %}

{% tab title="Go" %}

<pre class="language-go"><code class="lang-go"><strong>import (
</strong>    "fmt"
    "encoding/json"
    "encoding/hex"

    "github.com/pwrlabs/pwrgo/rpc"
)

type MyHandler struct{}

func (h *MyHandler) ProcessVidaTransactions(tx rpc.VMDataTransaction) {
    sender := tx.Sender
    data := tx.Data
    
    dataBytes, _ := hex.DecodeString(data[2:])
    var obj map[string]interface{}

    if err := json.Unmarshal(dataBytes, &#x26;obj); err != nil {
        fmt.Println("Error parsing JSON:", err)
    }

    if action, _ := obj["action"].(string); action == "send-message-v1" {
        message, _ := obj["message"].(string)
        fmt.Printf("Message from %s: %s\n", sender, message)
    }
}

func main() {
    vidaId := 1
    startingBlock := rpc.GetLatestBlockNumber()

    handler := &#x26;MyHandler{}

    rpc.SubscribeToVidaTransactions(
        vidaId,
        startingBlock,
        handler,
    )

    select {}
}
</code></pre>

{% endtab %}
{% endtabs %}

***

#### **8. Make Your App Public**

Once your VIDA is ready, share it with others by publishing it:

* **Option 1:** Open-source your code on GitHub with clear instructions.
* **Option 2:** Publish it on the PWR Chain registry for decentralized discovery. *(Coming Soon)*

***

#### **Key Considerations for Stateless VIDAs**

* **No State Management:** Stateless VIDAs do not track or validate past transactions, making them fast but unsuitable for critical use cases.
* **Ideal Use Cases:** Applications prioritizing speed and simplicity over consistency (e.g., chat apps, simple games).

By following these steps, you can build a lightweight and efficient Stateless VIDA that leverages the power of PWR Chain while keeping development simple!


# Building a Stateful VIDA

Stateful Verifiable Immutable Data Applications (VIDAs) are designed for scenarios where data integrity, consistency, and auditability are paramount. Unlike stateless VIDAs, which prioritize simplicity and speed, stateful VIDAs maintain a synchronized state across all execution instances by validating historical transactions and leveraging cryptographic proofs. This makes them ideal for mission-critical applications such as financial systems, supply chain management, or enterprise resource planning.

While the foundational steps for building a stateful VIDA mirror those of a stateless VIDA—such as defining a VIDA ID, integrating the PWR SDK, and interacting with the PWR Chain—stateful VIDAs introduce additional mechanisms to ensure reliable execution and verifiable consistency. Below, we outline the core principles that differentiate stateful VIDAs.

***

### Key Characteristics of Stateful VIDAs

#### 1. **State Initialization & Progress Tracking**

Stateful VIDAs begin processing transactions from a **specific block height** (e.g., the genesis block or a predefined checkpoint). This ensures the VIDA captures every relevant transaction from the start of its operational timeline. As the VIDA processes transactions, it continuously saves its progress (e.g., the latest block processed) and application state (e.g., user balances, inventory levels) to local storage.

* **Why This Matters:**
  * Enables the VIDA to resume seamlessly after interruptions.
  * Ensures all execution instances process the same transaction history, eliminating gaps or inconsistencies.

#### 2. **Local State Management**

Stateful VIDAs store their application state locally on the machines of all the execution instances and conduit nodes. This state is derived entirely from on-chain transactions and can be recreated at any time by replaying the VIDA's logic against the PWR Chain ledger.

* **Why This Matters:**
  * Avoids reliance on centralized servers or trusted third parties.
  * Allows execution instances to independently verify results by reprocessing transactions from scratch.

#### 3. **Root Hashes for Cross-Validation**

To ensure consistency across execution instances, stateful VIDAs generate **root hashes** (e.g., Merkle roots) of their stored data after processing transactions after every block. These hashes are agreed on and published to the PWR Chain by Conduit Nodes, or cross-validated across execution instances, creating a tamper-proof record of the VIDA's state after every block.

* **Why This Matters:**
  * **Consistency Checks:** VIDA Execution Instances and Conduit Nodes compare their root hashes to detect discrepancies.
  * **Auditability:** Anyone can verify that an VIDA's state matches the transactions recorded on PWR Chain.

***

### Ensuring Trustless Consistency

Stateful VIDAs implement two critical safeguards to maintain integrity:

1. **Cross-Instance Validation:**\
   Multiple Execution Instances of the same VIDA share and validate their root hashes after every block (If the block held transactions relevant to the VIDA). If discrepancies arise, the majority state (as proven by on-chain root hashes) is accepted as valid.
2. **Conduit Node Oversight:**\
   For critical workflows, Conduit Nodes can act as decentralized auditors. They validate root hashes, publish consensus-approved states to PWR Chain.

***

### Use Cases for Stateful VIDAs

* **Financial Systems:** Track balances, process payments, and enforce compliance with immutable audit trails.
* **Supply Chain Management:** Monitor goods from production to delivery, with verifiable timestamps and state transitions.
* **Enterprise Operations:** Manage HR records, inventory, or customer data while retaining full auditability.

***

### Upgrade Management

Stateful VIDAs require careful version control to preserve backward compatibility. Upgrades must either:

* Maintain support for processing historical transactions using legacy logic.
* Include a migration mechanism that updates the state while retaining its verifiable link to past data.

Conduit Nodes often govern upgrades through on-chain voting, ensuring decentralized consensus before changes take effect.

***

### Next Steps

While this guide focuses on conceptual foundations, future resources will include:

* **Video Tutorials:** Step-by-step walkthroughs for designing and deploying stateful VIDAs.
* **Code Examples:** Templates for root hash generation, cross-validation, and Conduit Node integration.

By combining PWR Chain’s immutable ledger with robust state management, stateful VIDAs empower developers to build decentralized applications that are as reliable as traditional enterprise software—but with unmatched transparency and security.


# Building a Lite Stateful VIDA

A learning-focused implementation of stateful applications

A **Lite Stateful VIDA** is a learning-focused version of a stateful VIDA that demonstrates core concepts without production complexity.

Key Characteristics:

**Stateful** = Remembers data between transactions

```java
// Each transaction builds on previous state
User A: 1000 tokens → Transfer 100 to User B → User A: 900 tokens
User B: 500 tokens → Receives 100 from User A → User B: 600 tokens
```

**Lite** = Simplified for learning

* ✅ **In-memory storage** (HashMap) instead of databases
* ✅ **Single instance** instead of distributed validation
* ✅ **Simple logging** instead of production monitoring
* ✅ **Basic error handling** instead of complex recovery

#### What Makes It "Stateful"?

Unlike stateless VIDAs that process each transaction independently, stateful VIDAs:

1. **Remember Previous Transactions**: Each new transaction can depend on what happened before
2. **Maintain Application State**: User balances, game scores, inventory levels persist
3. **Process Sequentially**: Transactions must be handled in blockchain order
4. **Provide Consistency**: All instances of the VIDA reach the same state

#### Lite vs Production Comparison:

| Feature              | Lite VIDA            | Production VIDA           |
| -------------------- | -------------------- | ------------------------- |
| **Storage**          | HashMap (memory)     | Merkle Trees              |
| **Validation**       | Single instance      | Multi-instance consensus  |
| **Recovery**         | Restart from scratch | Crash recovery + rollback |
| **APIs**             | None                 | HTTP REST endpoints       |
| **Learning Focus**   | ⭐⭐⭐⭐⭐                | ⭐⭐                        |
| **Production Ready** | ❌                    | ✅                         |

***

### Prerequisites to Building a Lite Stateful VIDA

* Good knowledge in the coding language you want to use
* Completed [Building a Stateless VIDA](/how-to/how-to-build-a-vida/building-a-stateless-vida) tutorial

***

### Building a Lite Stateful VIDA

In this tutorial we will build a token transfer system.

**1.** [**Select an ID for Your VIDA**](https://whitepaperv2.pwrlabs.io/how-to/how-to-build-a-vida/building-a-stateful-vida/pages/DkhTc6amaDlcvEqTqV2l#id-1.-select-an-id-for-your-vida)

**2.** [**Import the PWR SDK**](https://whitepaperv2.pwrlabs.io/how-to/how-to-build-a-vida/building-a-stateful-vida/pages/DkhTc6amaDlcvEqTqV2l#id-2.-import-the-pwr-sdk)

**3.** [**Initializing PWR with an RPC Endpoint**](https://whitepaperv2.pwrlabs.io/how-to/how-to-build-a-vida/building-a-stateful-vida/pages/DkhTc6amaDlcvEqTqV2l#id-3.-initializing-pwr-with-an-rpc-endpoint)

**4.** [**Create and Fund a Wallet**](https://whitepaperv2.pwrlabs.io/how-to/how-to-build-a-vida/building-a-stateful-vida/pages/DkhTc6amaDlcvEqTqV2l#id-4.-create-and-fund-a-wallet)

#### **5. Define Transaction Data Structure**

While PWR Chain stores all transaction data as raw byte arrays, VIDAs can encode this data into structured formats like JSON. Defining a **schema for your transactions** ensures consistency, simplifies development, and enables collaboration across teams.

#### **Why Define a Schema?**

* **Consistency**: Ensures all transactions follow a predictable format.
* **Documentation**: Serves as a reference for developers interacting with your VIDA.
* **Validation**: Helps catch malformed data early.

Example:&#x20;

```json
[
    {
        "action": "send-tokens-v1",
        "receiver": "0xC767EA1D613EEFE0CE1610B18CB047881BAFB829",
        "amount": 1000000
    }
]
```

***

#### 6. Setup Hashmap and Transfer Function

The Hash Map will be used to store all balances.&#x20;

{% tabs %}
{% tab title="Java" %}

```java
    private static Map<String /*Address*/, Long /*Balance*/> userTokenBalances = new HashMap<>();
    
    private static boolean transferTokens(String from, String to, long amount) {
        if (from == null || to == null || amount <= 0) {
            System.err.println("Invalid transfer parameters: from=" + from + ", to=" + to + ", amount=" + amount);
            return false;
        }
        
        // Normalize addresses to lowercase for consistency
        from = from.toLowerCase();
        to = to.toLowerCase();

        Long fromBalance = userTokenBalances.get(from);
        if (fromBalance == null || fromBalance < amount) {
            System.err.println("Insufficient balance for transfer: " + from + " has " + fromBalance + ", trying to transfer " + amount);
            return false;
        }

        userTokenBalances.put(from, fromBalance - amount);
        userTokenBalances.put(to, userTokenBalances.getOrDefault(to, 0L) + amount);
        System.out.println("Transfer successful: " + amount + " tokens from " + from + " to " + to);

        return true;
    }
```

{% endtab %}

{% tab title="JavaScript" %}

```javascript
import PWRJS from '@pwrjs/core';

const VIDA_ID = YOUR_VIDA_ID;
const START_BLOCK = 350000;
const RPC_ENDPOINT = "https://pwrrpc.pwrlabs.io/";

const userTokenBalances = new Map();

function getBalance(address) {
    address = address.startsWith("0x") ? address : "0x" + address;
    return userTokenBalances.get(address.toLowerCase()) || 0n;
}

function setBalance(address, balance) {
    address = address.startsWith("0x") ? address : "0x" + address;
    userTokenBalances.set(address.toLowerCase(), balance);
}

function transferTokens(from, to, amount) {
    if (!from || !to || amount <= 0n) {
        console.error(`Invalid transfer parameters: from=${from}, to=${to}, amount=${amount}`);
        return false;
    }

    // Normalize addresses for consistency
    from = from.startsWith("0x") ? from : "0x" + from;
    to = to.startsWith("0x") ? to : "0x" + to;

    const fromBalance = getBalance(from);
    if (fromBalance < amount) {
        console.error(`Insufficient balance for transfer: ${from.toLowerCase()} has ${fromBalance}, trying to transfer ${amount}`);
        return false;
    }

    // Perform the transfer
    setBalance(from, fromBalance - amount);
    const toBalance = getBalance(to);
    setBalance(to, toBalance + amount);
    
    console.log(`New balances - ${from.toLowerCase()}: ${getBalance(from)}, ${to.toLowerCase()}: ${getBalance(to)}`);
    
    return true;
}
```

{% endtab %}

{% tab title="Python" %}

```python
from pwrpy.pwrsdk import PWRPY
import json
import sys

VIDA_ID = YOUR_VIDA_ID
START_BLOCK = 350000
RPC_ENDPOINT = "https://pwrrpc.pwrlabs.io/"

user_token_balances = {}

def get_balance(address):
    address = address if address.startswith("0x") else "0x" + address
    return user_token_balances.get(address.lower(), 0)

def set_balance(address, balance):
    address = address if address.startswith("0x") else "0x" + address
    user_token_balances[address.lower()] = balance

def transfer_tokens(from_addr, to_addr, amount):
    if not from_addr or not to_addr or amount <= 0:
        print(f"Invalid transfer parameters: from={from_addr}, to={to_addr}, amount={amount}")
        return False
    
    # Normalize addresses for consistency
    from_addr = from_addr if from_addr.startswith("0x") else "0x" + from_addr
    to_addr = to_addr if to_addr.startswith("0x") else "0x" + to_addr
    
    from_balance = get_balance(from_addr)
    if from_balance < amount:
        print(f"Insufficient balance for transfer: {from_addr.lower()} has {from_balance}, trying to transfer {amount}")
        return False
    
    # Perform the transfer
    set_balance(from_addr, from_balance - amount)
    to_balance = get_balance(to_addr)
    set_balance(to_addr, to_balance + amount)
    
    print(f"New balances - {from_addr.lower()}: {get_balance(from_addr)}, {to_addr.lower()}: {get_balance(to_addr)}")
    
    return True
```

{% endtab %}

{% tab title="Rust" %}

```rust
use pwr_rs::RPC;
use serde_json;
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use std::sync::OnceLock;

const VIDA_ID: u64 = YOUR_VIDA_ID;
const START_BLOCK: u64 = 350000;
const RPC_ENDPOINT: &str = "https://pwrrpc.pwrlabs.io/";

static USER_TOKEN_BALANCES: OnceLock<Mutex<HashMap<String, u64>>> = OnceLock::new();

fn get_balance(address: &str) -> u64 {
    let address = if address.starts_with("0x") {
        address.to_string()
    } else {
        format!("0x{}", address)
    };

    let balances = USER_TOKEN_BALANCES.get_or_init(|| Mutex::new(HashMap::new()));
    let balances = balances.lock().unwrap();
    *balances.get(&address.to_lowercase()).unwrap_or(&0)
}

fn set_balance(address: &str, balance: u64) {
    let address = if address.starts_with("0x") {
        address.to_string()
    } else {
        format!("0x{}", address)
    };

    let balances = USER_TOKEN_BALANCES.get_or_init(|| Mutex::new(HashMap::new()));
    let mut balances = balances.lock().unwrap();
    balances.insert(address.to_lowercase(), balance);
}

fn transfer_tokens(from_addr: &str, to_addr: &str, amount: u64) -> bool {
    if from_addr.is_empty() || to_addr.is_empty() || amount == 0 {
        println!("Invalid transfer parameters: from={}, to={}, amount={}", from_addr, to_addr, amount);
        return false;
    }

    let from_addr = if from_addr.starts_with("0x") {
        from_addr.to_string()
    } else {
        format!("0x{}", from_addr)
    };

    let to_addr = if to_addr.starts_with("0x") {
        to_addr.to_string()
    } else {
        format!("0x{}", to_addr)
    };

    let from_balance = get_balance(&from_addr);
    if from_balance < amount {
        println!("Insufficient balance for transfer: {} has {}, trying to transfer {}", from_addr.to_lowercase(), from_balance, amount);
        return false;
    }

    // Perform the transfer
    set_balance(&from_addr, from_balance - amount);
    let to_balance = get_balance(&to_addr);
    set_balance(&to_addr, to_balance + amount);

    println!("New balances - {}: {}, {}: {}", 
        from_addr.to_lowercase(), get_balance(&from_addr),
        to_addr.to_lowercase(), get_balance(&to_addr)
    );

    true
}
```

{% endtab %}

{% tab title="Go" %}

```go
package main

import (
    "encoding/hex"
    "encoding/json"
    "fmt"
    "os"
    "os/signal"
    "strconv"
    "strings"
    "sync"
    "syscall"

    "github.com/pwrlabs/pwrgo/rpc"
)

const (
    VIDA_ID      = 123
    START_BLOCK  = 350000
    RPC_ENDPOINT = "https://pwrrpc.pwrlabs.io/"
)

// Global balance storage like in JavaScript and Python
var userTokenBalances = make(map[string]int64)
var balancesMutex sync.RWMutex

func getBalance(address string) int64 {
    if !strings.HasPrefix(address, "0x") {
        address = "0x" + address
    }
    address = strings.ToLower(address)

    balancesMutex.RLock()
    defer balancesMutex.RUnlock()

    balance, exists := userTokenBalances[address]
    if !exists {
        return 0
    }
    return balance
}

func setBalance(address string, balance int64) {
    if !strings.HasPrefix(address, "0x") {
        address = "0x" + address
    }
    address = strings.ToLower(address)

    balancesMutex.Lock()
    defer balancesMutex.Unlock()

    userTokenBalances[address] = balance
}

func transferTokens(fromAddr, toAddr string, amount int64) bool {
    if fromAddr == "" || toAddr == "" || amount <= 0 {
        fmt.Printf("Invalid transfer parameters: from=%s, to=%s, amount=%d\n", fromAddr, toAddr, amount)
        return false
    }

    if !strings.HasPrefix(fromAddr, "0x") {
        fromAddr = "0x" + fromAddr
    }
    if !strings.HasPrefix(toAddr, "0x") {
        toAddr = "0x" + toAddr
    }

    fromBalance := getBalance(fromAddr)
    if fromBalance < amount {
        fmt.Printf("Insufficient balance for transfer: %s has %d, trying to transfer %d\n",
            strings.ToLower(fromAddr), fromBalance, amount)
        return false
    }

    // Perform the transfer
    setBalance(fromAddr, fromBalance-amount)
    toBalance := getBalance(toAddr)
    setBalance(toAddr, toBalance+amount)

    fmt.Printf("New balances - %s: %d, %s: %d\n",
        strings.ToLower(fromAddr), getBalance(fromAddr),
        strings.ToLower(toAddr), getBalance(toAddr))

    return true
}
```

{% endtab %}

{% tab title="C#" %}

```csharp
using System;
using System.Collections.Generic;
using System.Numerics;
using System.Text;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using PWR;
using PWR.Models;

namespace LiteStatefulVida;

class Index
{
    // Constants
    private const ulong VIDA_ID = 123;
    private const ulong START_BLOCK = 350000;
    private const string RPC_ENDPOINT = "https://pwrrpc.pwrlabs.io/";

    // Global balance storage like in JavaScript and Python
    private static readonly Dictionary<string, BigInteger> userTokenBalances = new();
    private static readonly object balancesLock = new();

    private static BigInteger GetBalance(string address)
    {
        if (!address.StartsWith("0x"))
            address = "0x" + address;
        address = address.ToLower();

        lock (balancesLock)
        {
            return userTokenBalances.TryGetValue(address, out var balance) ? balance : 0;
        }
    }

    private static void SetBalance(string address, BigInteger balance)
    {
        if (!address.StartsWith("0x"))
            address = "0x" + address;
        address = address.ToLower();

        lock (balancesLock)
        {
            userTokenBalances[address] = balance;
        }
    }

    private static bool TransferTokens(string fromAddr, string toAddr, BigInteger amount)
    {
        if (string.IsNullOrEmpty(fromAddr) || string.IsNullOrEmpty(toAddr) || amount <= 0)
        {
            Console.WriteLine($"Invalid transfer parameters: from={fromAddr}, to={toAddr}, amount={amount}");
            return false;
        }

        if (!fromAddr.StartsWith("0x"))
            fromAddr = "0x" + fromAddr;
        if (!toAddr.StartsWith("0x"))
            toAddr = "0x" + toAddr;

        var fromBalance = GetBalance(fromAddr);
        if (fromBalance < amount)
        {
            Console.WriteLine($"Insufficient balance for transfer: {fromAddr.ToLower()} has {fromBalance}, trying to transfer {amount}");
            return false;
        }

        // Perform the transfer
        SetBalance(fromAddr, fromBalance - amount);
        var toBalance = GetBalance(toAddr);
        SetBalance(toAddr, toBalance + amount);

        Console.WriteLine($"New balances - {fromAddr.ToLower()}: {GetBalance(fromAddr)}, {toAddr.ToLower()}: {GetBalance(toAddr)}");

        return true;
    }
}
```

{% endtab %}
{% endtabs %}

***

#### 7. Define a Starting Block

**Stateful VIDAs** must define a starting block because they need to build up their state by processing every relevant transaction in order. Without knowing where to start, they can't guarantee their state is correct.

**Best Practice:** Set your starting block to the **latest PWR Chain block at the time of your VIDA's development or launch**, since previous blocks won't contain any transactions for your VIDA (it didn't exist yet).

You can find the current latest block at: [**https://explorer.pwrlabs.io/**](https://explorer.pwrlabs.io/)

{% tabs %}
{% tab title="Java" %}

```java
private static final long START_BLOCK = 22338;
```

{% endtab %}

{% tab title="JavaScript" %}

```javascript
const START_BLOCK = 350000;
```

{% endtab %}

{% tab title="Python" %}

```python
START_BLOCK = 350000
```

{% endtab %}

{% tab title="Rust" %}

```rust
const START_BLOCK: u64 = 350000;
```

{% endtab %}

{% tab title="Go" %}

```go
const START_BLOCK = 350000
```

{% endtab %}

{% tab title="C#" %}

```csharp
private const ulong START_BLOCK = 350000;
```

{% endtab %}
{% endtabs %}

***

#### 8. Set Initial Balances

Since we're creating a token VIDA, some addresses must have tokens when the VIDA launches. Without initial balances, no one would have tokens to transfer, making the system unusable.

{% tabs %}
{% tab title="Java" %}

```java
userTokenBalances.put("0xc767ea1d613eefe0ce1610b18cb047881bafb829".toLowerCase(), 1_000_000_000_000L); //Replace the address with your address or any desired address
userTokenBalances.put("0x3b4412f57828d1ceb0dbf0d460f7eb1f21fed8b4".toLowerCase(), 1_000_000_000_000L);
```

{% endtab %}

{% tab title="JavaScript" %}

```javascript
function setupInitialBalances() {
    setBalance("0xc767ea1d613eefe0ce1610b18cb047881bafb829", 1_000_000_000_000n);
    setBalance("0x3b4412f57828d1ceb0dbf0d460f7eb1f21fed8b4", 1_000_000_000_000n);
}
```

{% endtab %}

{% tab title="Python" %}

```python
def setup_initial_balances():
    set_balance("0xc767ea1d613eefe0ce1610b18cb047881bafb829", 1_000_000_000_000)
    set_balance("0x3b4412f57828d1ceb0dbf0d460f7eb1f21fed8b4", 1_000_000_000_000)
```

{% endtab %}

{% tab title="Rust" %}

```rust
fn setup_initial_balances() {
    set_balance("0xc767ea1d613eefe0ce1610b18cb047881bafb829", 1_000_000_000_000);
    set_balance("0x3b4412f57828d1ceb0dbf0d460f7eb1f21fed8b4", 1_000_000_000_000);
}
```

{% endtab %}

{% tab title="Go" %}

```go
func setupInitialBalances() {
    setBalance("0xc767ea1d613eefe0ce1610b18cb047881bafb829", 1_000_000_000_000)
    setBalance("0x3b4412f57828d1ceb0dbf0d460f7eb1f21fed8b4", 1_000_000_000_000)
}
```

{% endtab %}

{% tab title="C#" %}

```csharp
    private static void SetupInitialBalances()
    {
        SetBalance("0xc767ea1d613eefe0ce1610b18cb047881bafb829", new BigInteger(1_000_000_000_000));
        SetBalance("0x3b4412f57828d1ceb0dbf0d460f7eb1f21fed8b4", new BigInteger(1_000_000_000_000));
    }
```

{% endtab %}
{% endtabs %}

***

#### 9. Read Data from PWR Chain & Handle it

**Stateful VIDAs** need to read data from PWR Chain to update their state. This is done by subscribing to the VIDA's transactions and handling them accordingly.

{% tabs %}
{% tab title="Java" %}

```java
private static void readAndHandleData(PWRJ pwrj, long vidaId, long startingBlock) throws IOException {
    pwrj.subscribeToVidaTransactions(pwrj, vidaId, startingBlock, null, (transaction) -> {
        String from = transaction.getSender();
        String to = transaction.getReceiver();
        byte[] data = transaction.getData();
        
        // Normalize addresses to ensure they start with "0x" because the RPC might return them without it
        if(!from.startsWith("0x")) from = "0x" + from;
        if(!to.startsWith("0x")) to = "0x" + to;

        JSONObject jsonData = new JSONObject(new String(data));
        String action = jsonData.optString("action", "");
        
        if(action.equalsIgnoreCase("transfer")) {
            long amount = jsonData.optLong("amount", 0);
            
            if (transferTokens(from, to, amount)) {
                System.out.println("Transaction processed: " + from + " transferred " + amount + " tokens to " + to);
            } else {
                System.err.println("Failed to process transaction: " + from + " -> " + to + " for amount: " + amount);
            }
        }
    });
}
```

{% endtab %}

{% tab title="JavaScript" %}

```javascript
function processTransaction(transaction) {
    try {
        const from = (transaction.sender).startsWith("0x") ? transaction.sender : "0x" + transaction.sender;
        const data = transaction.data;

        // Parse transaction data as JSON
        let jsonData;
        try {
            // Convert hex data to string and parse JSON
            const dataBytes = Buffer.from(data, 'hex');
            const dataString = dataBytes.toString('utf8');
            jsonData = JSON.parse(dataString);
        } catch (parseError) {
            console.error(`Failed to parse transaction data: ${parseError.message}`);
            return;
        }

        const action = jsonData.action || "";
        
        if (action === "send-tokens-v1") {
            const amount = BigInt(jsonData.amount || 0);
            const receiver = jsonData.receiver.startsWith("0x") ? jsonData.receiver : "0x" + jsonData.receiver;
            
            console.log(`Transfer request: ${amount} tokens from ${from.toLowerCase()} to ${receiver.toLowerCase()}`);
            
            if (transferTokens(from, receiver, amount)) {
                console.log(`✅ Transaction processed successfully`);
            } else {
                console.error(`❌ Failed to process transaction`);
            }
        } else {
            console.log(`Unknown action: ${action}`);
        }
        
    } catch (error) {
        console.error(`Error processing transaction: ${error.message}`);
    }
}

async function main() {
    console.log("🚀 Starting PWR Chain Lite Stateful VIDA - Token Transfer System");

    try {
        // Initialize PWR Chain connection
        const pwrjs = new PWRJS(RPC_ENDPOINT);

        setupInitialBalances();
        
        const subscription = pwrjs.subscribeToVidaTransactions(
            BigInt(VIDA_ID),
            BigInt(START_BLOCK),
            processTransaction
        );

        console.log("\n⏳ Application running... Press Ctrl+C to stop");
        
        process.on('SIGINT', () => {
            console.log('\n🛑 Shutting down gracefully...');
            subscription.stop();
            console.log('✅ Subscription stopped');
            process.exit(0);
        });
    } catch (error) {
        console.error(`❌ Application error: ${error.message}`);
        console.error(error.stack);
        process.exit(1);
    }
}
```

{% endtab %}

{% tab title="Python" %}

```python
def process_transaction(transaction):
    try:
        from_addr = transaction.sender if transaction.sender.startswith("0x") else "0x" + transaction.sender
        data = transaction.data
        
        # Parse transaction data as JSON
        try:
            # Convert hex data to string and parse JSON
            data_bytes = bytes.fromhex(data)
            data_string = data_bytes.decode('utf-8')
            json_data = json.loads(data_string)
        except Exception as parse_error:
            print(f"Failed to parse transaction data: {parse_error}")
            return
        
        action = json_data.get("action", "")
        
        if action == "send-tokens-v1":
            amount = int(json_data.get("amount", 0))
            receiver = json_data["receiver"] if json_data["receiver"].startswith("0x") else "0x" + json_data["receiver"]
            
            print(f"Transfer request: {amount} tokens from {from_addr.lower()} to {receiver.lower()}")
            
            if transfer_tokens(from_addr, receiver, amount):
                print("✅ Transaction processed successfully")
            else:
                print("❌ Failed to process transaction")
        else:
            print(f"Unknown action: {action}")
            
    except Exception as error:
        print(f"Error processing transaction: {error}")

def main():
    print("🚀 Starting PWR Chain Lite Stateful VIDA - Token Transfer System")
    
    try:
        # Initialize PWR Chain connection
        pwrpy = PWRPY(RPC_ENDPOINT)
        
        setup_initial_balances()
        
        pwrpy.subscribe_to_vida_transactions(VIDA_ID, START_BLOCK, process_transaction)
        
        print("\n⏳ Application running... Press Ctrl+C to stop")
            
    except Exception as error:
        print(f"❌ Application error: {error}")
        sys.exit(1)
```

{% endtab %}

{% tab title="Rust" %}

```rust
fn process_transaction(transaction: pwr_rs::transaction::types::VidaDataTransaction) {
    let from_addr = if transaction.sender.starts_with("0x") {
        transaction.sender.clone()
    } else {
        format!("0x{}", transaction.sender)
    };

    let data = transaction.data;

    // Parse transaction data as JSON
    let data_string = match String::from_utf8(data) {
        Ok(s) => s,
        Err(e) => {
            println!("Failed to parse transaction data: {}", e);
            return;
        }
    };

    let json_data: serde_json::Value = match serde_json::from_str(&data_string) {
        Ok(data) => data,
        Err(e) => {
            println!("Failed to parse transaction data: {}", e);
            return;
        }
    };
    
    let action = json_data.get("action").and_then(|v| v.as_str()).unwrap_or("");
    
    if action == "send-tokens-v1" {
        let amount = json_data.get("amount")
            .and_then(|v| v.as_str())
            .and_then(|s| s.parse::<u64>().ok())
            .unwrap_or(0);
            
        let receiver = json_data.get("receiver")
            .and_then(|v| v.as_str())
            .unwrap_or("");
            
        let receiver = if receiver.starts_with("0x") {
            receiver.to_string()
        } else {
            format!("0x{}", receiver)
        };


        println!("Transfer request: {} tokens from {} to {}", amount, from_addr.to_lowercase(), receiver.to_lowercase());

        if transfer_tokens(&from_addr, &receiver, amount) {
            println!("✅ Transaction processed successfully");
        } else {
            println!("❌ Failed to process transaction");
        }
    } else {
        println!("Unknown action: {}", action);
    }
}

pub async fn main() -> Result<(), Box<dyn std::error::Error>> {
    println!("🚀 Starting PWR Chain Lite Stateful VIDA - Token Transfer System");

    let rpc = RPC::new(RPC_ENDPOINT).await.unwrap();
    let rpc = Arc::new(rpc);

    setup_initial_balances();

    rpc.subscribe_to_vida_transactions(VIDA_ID, START_BLOCK, process_transaction, None);

    println!("\n⏳ Application running... Press Ctrl+C to stop");

    // To exit the program ctrl+c
    tokio::signal::ctrl_c().await?;
    println!("\n🛑 Shutting down gracefully...");
    println!("✅ Subscription stopped");

    Ok(())
}
```

{% endtab %}

{% tab title="Go" %}

```go
func processTransaction(transaction rpc.VidaDataTransaction) {
    fromAddr := transaction.Sender
    if !strings.HasPrefix(fromAddr, "0x") {
        fromAddr = "0x" + fromAddr
    }

    data := transaction.Data

    // Parse transaction data as JSON
    dataBytes, err := hex.DecodeString(data)
    if err != nil {
        fmt.Printf("Failed to decode hex data: %v\n", err)
        return
    }

    dataString := string(dataBytes)
    var jsonData map[string]interface{}
    err = json.Unmarshal([]byte(dataString), &jsonData)
    if err != nil {
        fmt.Printf("Failed to parse transaction data: %v\n", err)
        return
    }

    action, exists := jsonData["action"].(string)
    if !exists {
        action = ""
    }

    if action == "send-tokens-v1" {
        amountStr, exists := jsonData["amount"].(string)
        if !exists {
            fmt.Println("Amount not found in transaction data")
            return
        }

        amount, err := strconv.ParseInt(amountStr, 10, 64)
        if err != nil {
            fmt.Printf("Failed to parse amount: %v\n", err)
            return
        }

        receiver, exists := jsonData["receiver"].(string)
        if !exists {
            fmt.Println("Receiver not found in transaction data")
            return
        }

        if !strings.HasPrefix(receiver, "0x") {
            receiver = "0x" + receiver
        }

        fmt.Printf("Transfer request: %d tokens from %s to %s\n",
            amount, strings.ToLower(fromAddr), strings.ToLower(receiver))

        if transferTokens(fromAddr, receiver, amount) {
            fmt.Println("✅ Transaction processed successfully")
        } else {
            fmt.Println("❌ Failed to process transaction")
        }
    } else {
        fmt.Printf("Unknown action: %s\n", action)
    }
}

func main() {
    fmt.Println("🚀 Starting PWR Chain Lite Stateful VIDA - Token Transfer System")

    // Initialize PWR Chain connection
    rpcClient := rpc.SetRpcNodeUrl(RPC_ENDPOINT)

    setupInitialBalances()

    // Subscribe to VIDA transactions
    subscription := rpcClient.SubscribeToVidaTransactions(VIDA_ID, START_BLOCK, processTransaction)

    fmt.Println("\n⏳ Application running... Press Ctrl+C to stop")

    // Set up signal handling for graceful shutdown
    c := make(chan os.Signal, 1)
    signal.Notify(c, os.Interrupt, syscall.SIGTERM)
    <-c
    fmt.Println("\n🛑 Shutting down gracefully...")
    if subscription != nil {
        fmt.Println("✅ Subscription stopped")
    }
}
```

{% endtab %}

{% tab title="C#" %}

```csharp
    private static void ProcessTransaction(VidaDataTransaction transaction)
    {
        try
        {
            var fromAddr = transaction.Sender.StartsWith("0x") ? transaction.Sender : "0x" + transaction.Sender;
            var data = transaction.Data;

            // Parse transaction data as JSON
            string dataString;
            try
            {
                // Convert hex data to string and parse JSON
                var dataBytes = Convert.FromHexString(data);
                dataString = Encoding.UTF8.GetString(dataBytes);
            }
            catch (Exception parseError)
            {
                Console.WriteLine($"Failed to parse transaction data: {parseError.Message}");
                return;
            }

            JsonDocument jsonData;
            try
            {
                jsonData = JsonDocument.Parse(dataString);
            }
            catch (Exception jsonError)
            {
                Console.WriteLine($"Failed to parse JSON data: {jsonError.Message}");
                return;
            }

            using (jsonData)
            {
                var action = jsonData.RootElement.TryGetProperty("action", out var actionElement) 
                    ? actionElement.GetString() ?? "" 
                    : "";

                if (action == "send-tokens-v1")
                {
                    var amountStr = jsonData.RootElement.TryGetProperty("amount", out var amountElement) 
                        ? amountElement.GetString() ?? "0" 
                        : "0";

                    if (!BigInteger.TryParse(amountStr, out var amount))
                    {
                        Console.WriteLine($"Failed to parse amount: {amountStr}");
                        return;
                    }

                    var receiver = jsonData.RootElement.TryGetProperty("receiver", out var receiverElement) 
                        ? receiverElement.GetString() ?? "" 
                        : "";

                    if (!receiver.StartsWith("0x"))
                        receiver = "0x" + receiver;

                    Console.WriteLine($"Transfer request: {amount} tokens from {fromAddr.ToLower()} to {receiver.ToLower()}");

                    if (TransferTokens(fromAddr, receiver, amount))
                    {
                        Console.WriteLine("✅ Transaction processed successfully");
                    }
                    else
                    {
                        Console.WriteLine("❌ Failed to process transaction");
                    }
                }
                else
                {
                    Console.WriteLine($"Unknown action: {action}");
                }
            }
        }
        catch (Exception error)
        {
            Console.WriteLine($"Error processing transaction: {error.Message}");
        }
    }

    public static async Task Main()
    {
        Console.WriteLine("🚀 Starting PWR Chain Lite Stateful VIDA - Token Transfer System");

        try
        {
            // Initialize PWR Chain connection
            var pwr = new RPC(RPC_ENDPOINT);

            SetupInitialBalances();

            // Subscribe to VIDA transactions
            var subscription = pwr.SubscribeToVidaTransactions(VIDA_ID, START_BLOCK, ProcessTransaction);

            Console.WriteLine("\n⏳ Application running... Press Ctrl+C to stop");

            // Set up cancellation token for graceful shutdown
            using var cts = new CancellationTokenSource();
            Console.CancelKeyPress += (sender, e) =>
            {
                e.Cancel = true;
                cts.Cancel();
            };

            try
            {
                await Task.Delay(Timeout.Infinite, cts.Token);
            }
            catch (OperationCanceledException)
            {
                Console.WriteLine("\n🛑 Shutting down gracefully...");
                subscription?.Stop();
                Console.WriteLine("✅ Subscription stopped");
            }
        }
        catch (Exception error)
        {
            Console.WriteLine($"❌ Application error: {error.Message}");
            Environment.Exit(1);
        }
    }
```

{% endtab %}
{% endtabs %}

***

#### 10. Send Transactions

{% tabs %}
{% tab title="Java" %}

```java
private static boolean transferTokens(PWRFalconWallet wallet, String receiver, long amount) throws IOException {
    String senderAddress = wallet.getAddress().toLowerCase();
    long senderBalance = getBalance(senderAddress);

    if (senderBalance < amount) {
        System.err.println("Insufficient balance for transfer: " + senderAddress + " has " + senderBalance + ", trying to transfer " + amount);
        return false;
    }

    // Normalize receiver address to ensure it starts with "0x"
    if(!receiver.startsWith("0x")) {
        receiver = "0x" + receiver; // Ensure the address starts with "0x"
    }

    JSONObject transferData = new JSONObject();
    transferData.put("action", "send-tokens-v1");
    transferData.put("receiver", receiver);
    transferData.put("amount", amount);

    byte[] data = transferData.toString().getBytes();

    Response response = wallet.submitPayableVidaData(VIDA_ID, data, 0, pwrj.getFeePerByte());
    if (!response.isSuccess()) {
        System.err.println("Failed to transfer tokens: " + response.getError());
        return false;
    }

    return true;
}
```

{% endtab %}

{% tab title="JavaScript" %}

```javascript
function processTransaction(transaction) {
    try {
        const from = (transaction.sender).startsWith("0x") ? transaction.sender : "0x" + transaction.sender;
        const data = transaction.data;

        // Parse transaction data as JSON
        let jsonData;
        try {
            // Convert hex data to string and parse JSON
            const dataBytes = Buffer.from(data, 'hex');
            const dataString = dataBytes.toString('utf8');
            jsonData = JSON.parse(dataString);
        } catch (parseError) {
            console.error(`Failed to parse transaction data: ${parseError.message}`);
            return;
        }

        const action = jsonData.action || "";
        
        if (action === "send-tokens-v1") {
            const amount = BigInt(jsonData.amount || 0);
            const receiver = jsonData.receiver.startsWith("0x") ? jsonData.receiver : "0x" + jsonData.receiver;
            
            console.log(`Transfer request: ${amount} tokens from ${from.toLowerCase()} to ${receiver.toLowerCase()}`);
            
            if (transferTokens(from, receiver, amount)) {
                console.log(`✅ Transaction processed successfully`);
            } else {
                console.error(`❌ Failed to process transaction`);
            }
        } else {
            console.log(`Unknown action: ${action}`);
        }
        
    } catch (error) {
        console.error(`Error processing transaction: ${error.message}`);
    }
}

async function main() {
    console.log("🚀 Starting PWR Chain Lite Stateful VIDA - Token Transfer System");

    try {
        // Initialize PWR Chain connection
        const pwrjs = new PWRJS(RPC_ENDPOINT);

        setupInitialBalances();
        
        const subscription = pwrjs.subscribeToVidaTransactions(
            BigInt(VIDA_ID),
            BigInt(START_BLOCK),
            processTransaction
        );

        console.log("\n⏳ Application running... Press Ctrl+C to stop");
        
        process.on('SIGINT', () => {
            console.log('\n🛑 Shutting down gracefully...');
            subscription.stop();
            console.log('✅ Subscription stopped');
            process.exit(0);
        });
    } catch (error) {
        console.error(`❌ Application error: ${error.message}`);
        console.error(error.stack);
        process.exit(1);
    }
}
```

{% endtab %}

{% tab title="Python" %}

```python
def process_transaction(transaction):
    try:
        from_addr = transaction.sender if transaction.sender.startswith("0x") else "0x" + transaction.sender
        data = transaction.data
        
        # Parse transaction data as JSON
        try:
            # Convert hex data to string and parse JSON
            data_bytes = bytes.fromhex(data)
            data_string = data_bytes.decode('utf-8')
            json_data = json.loads(data_string)
        except Exception as parse_error:
            print(f"Failed to parse transaction data: {parse_error}")
            return
        
        action = json_data.get("action", "")
        
        if action == "send-tokens-v1":
            amount = int(json_data.get("amount", 0))
            receiver = json_data["receiver"] if json_data["receiver"].startswith("0x") else "0x" + json_data["receiver"]
            
            print(f"Transfer request: {amount} tokens from {from_addr.lower()} to {receiver.lower()}")
            
            if transfer_tokens(from_addr, receiver, amount):
                print("✅ Transaction processed successfully")
            else:
                print("❌ Failed to process transaction")
        else:
            print(f"Unknown action: {action}")
            
    except Exception as error:
        print(f"Error processing transaction: {error}")

def main():
    print("🚀 Starting PWR Chain Lite Stateful VIDA - Token Transfer System")
    
    try:
        # Initialize PWR Chain connection
        pwrpy = PWRPY(RPC_ENDPOINT)
        
        setup_initial_balances()
        
        pwrpy.subscribe_to_vida_transactions(VIDA_ID, START_BLOCK, process_transaction)
        
        print("\n⏳ Application running... Press Ctrl+C to stop")
            
    except Exception as error:
        print(f"❌ Application error: {error}")
        sys.exit(1)
```

{% endtab %}

{% tab title="Rust" %}

```rust
fn process_transaction(transaction: pwr_rs::transaction::types::VidaDataTransaction) {
    let from_addr = if transaction.sender.starts_with("0x") {
        transaction.sender.clone()
    } else {
        format!("0x{}", transaction.sender)
    };

    let data = transaction.data;

    // Parse transaction data as JSON
    let data_string = match String::from_utf8(data) {
        Ok(s) => s,
        Err(e) => {
            println!("Failed to parse transaction data: {}", e);
            return;
        }
    };

    let json_data: serde_json::Value = match serde_json::from_str(&data_string) {
        Ok(data) => data,
        Err(e) => {
            println!("Failed to parse transaction data: {}", e);
            return;
        }
    };
    
    let action = json_data.get("action").and_then(|v| v.as_str()).unwrap_or("");
    
    if action == "send-tokens-v1" {
        let amount = json_data.get("amount")
            .and_then(|v| v.as_str())
            .and_then(|s| s.parse::<u64>().ok())
            .unwrap_or(0);
            
        let receiver = json_data.get("receiver")
            .and_then(|v| v.as_str())
            .unwrap_or("");
            
        let receiver = if receiver.starts_with("0x") {
            receiver.to_string()
        } else {
            format!("0x{}", receiver)
        };


        println!("Transfer request: {} tokens from {} to {}", amount, from_addr.to_lowercase(), receiver.to_lowercase());

        if transfer_tokens(&from_addr, &receiver, amount) {
            println!("✅ Transaction processed successfully");
        } else {
            println!("❌ Failed to process transaction");
        }
    } else {
        println!("Unknown action: {}", action);
    }
}

pub async fn main() -> Result<(), Box<dyn std::error::Error>> {
    println!("🚀 Starting PWR Chain Lite Stateful VIDA - Token Transfer System");

    let rpc = RPC::new(RPC_ENDPOINT).await.unwrap();
    let rpc = Arc::new(rpc);

    setup_initial_balances();

    rpc.subscribe_to_vida_transactions(VIDA_ID, START_BLOCK, process_transaction, None);

    println!("\n⏳ Application running... Press Ctrl+C to stop");

    // To exit the program ctrl+c
    tokio::signal::ctrl_c().await?;
    println!("\n🛑 Shutting down gracefully...");
    println!("✅ Subscription stopped");

    Ok(())
}
```

{% endtab %}

{% tab title="Go" %}

```go
func SendTransfer(wallet *wallet.PWRWallet, receiver string, amount int64) bool {
    normalizedReceiver := strings.ToLower(receiver)

    transferData := map[string]interface{}{
        "action":   "send-tokens-v1",
        "receiver": normalizedReceiver,
        "amount":   fmt.Sprintf("%d", amount),
    }

    // Convert to bytes
    dataBytes, err := json.Marshal(transferData)
    if err != nil {
        fmt.Printf("Error marshaling transfer data: %v\n", err)
        return false
    }
    feePerByte := 1000

    response := wallet.SendPayableVidaData(123, dataBytes, 0, feePerByte)

    if response.Success {
        fmt.Println("✅ Transaction sent successfully!")
        fmt.Printf("Transaction hash: %s\n", response.Hash)
        return true
    } else {
        fmt.Printf("❌ Failed to send transaction: %v\n", response.Error)
        return false
    }
}
```

{% endtab %}

{% tab title="C#" %}

```csharp
public static async Task<bool> SendTransfer(Wallet wallet, string receiver, BigInteger amount)
{
    try
    {
        var normalizedReceiver = receiver.ToLower();

        var transferData = new
        {
            action = "send-tokens-v1",
            receiver = normalizedReceiver,
            amount = amount.ToString()
        };

        // Convert to bytes
        var dataString = JsonSerializer.Serialize(transferData);
        var dataBytes = Encoding.UTF8.GetBytes(dataString);

        // Get fee per byte
        var feePerByte = await wallet.GetRpc().GetFeePerByte();

        // Send transaction to PWR Chain
        var response = await wallet.SendPayableVidaData(123, dataBytes, 0, feePerByte);

        if (response.Success)
        {
            Console.WriteLine("✅ Transaction sent successfully!");
            Console.WriteLine($"Transaction hash: {response.Hash}");
            return true;
        }
        else
        {
            Console.WriteLine($"❌ Failed to send transaction: {response.Error}");
            return false;
        }
    }
    catch (Exception error)
    {
        Console.WriteLine($"Error sending transfer: {error.Message}");
        return false;
    }
}
```

{% endtab %}
{% endtabs %}

#### Final Notes & Best Practices

When building a Lite Stateful VIDA, your primary goal is to maintain the benefits of a stateful design—verifiable consistency, auditability, and resilience—while minimizing complexity and overhead. By storing only essential state, validating transactions from a known checkpoint, and leveraging PWR Chain’s immutable ledger, you can achieve strong guarantees without excessive resource usage.


# Building a Stateful VIDA - Part 1

Stateful VIDAs are robust, consistent, and reliable applications that maintain and validate persistent state across execution instances. They are essential for critical use cases such as financial applications, token systems, voting mechanisms, or any application where data integrity and consistency are paramount.

Unlike stateless VIDAs, stateful VIDAs require:

* **Persistent storage** for maintaining state between transactions
* **State validation** to ensure data consistency across network participants
* **Consensus mechanisms** for distributed state agreement
* **Recovery systems** for handling errors and network partitions

### What Makes a Production Stateful VIDA Different?

While Lite Stateful VIDAs demonstrate core concepts with simplified implementations, production Stateful VIDAs include:

| Feature                | Lite Stateful VIDA   | Production Stateful VIDA         |
| ---------------------- | -------------------- | -------------------------------- |
| **Storage**            | HashMap (memory)     | MerkleTree (persistent database) |
| **State Verification** | None                 | Cryptographic root hashes        |
| **Consensus**          | Single instance      | Multi-peer validation            |
| **Recovery**           | Restart from scratch | Checkpoint-based recovery        |
| **Production Ready**   | ❌                    | ✅                                |

### **Architecture Overview**

This tutorial builds a **token transfer system** that demonstrates all key concepts of stateful VIDAs:

* **Database Layer**: RocksDB with MerkleTree for cryptographic state verification
* **Transaction Processing**: Handles token transfers with balance validation
* **Peer Consensus**: Validates state consistency across network nodes
* **API Layer**: HTTP endpoints for inter-node communication
* **Error Recovery**: Robust handling of consensus failures and data inconsistencies

### Prerequisites

* Completed "Building a Lite Stateful VIDA" tutorial
* Basic knowledge of REST APIs

### Steps to Build a Stateful VIDA <a href="#steps-to-build-a-stateful-vida" id="steps-to-build-a-stateful-vida"></a>

#### 1. Project Setup and Dependencies <a href="#id-1-project-setup-and-dependencies" id="id-1-project-setup-and-dependencies"></a>

Create a new Maven project with the required dependencies for PWR SDK, database storage, and HTTP server.

{% tabs %}
{% tab title="JavaScript" %}

```bash
mkdir pwr-stateful-vida && cd pwr-stateful-vida
npm init -y
npm install @pwrjs/core express
```

{% endtab %}

{% tab title="Python" %}

```bash
mkdir pwr_stateful_vida && cd pwr_stateful_vida
python3 -m venv venv
source venv/bin/activate 
pip install pwrpy flask
```

{% endtab %}

{% tab title="Rust" %}

```bash
cargo new pwr_stateful_vida && cd pwr_stateful_vida
cargo add pwr-rs serde_json serde num-bigint hex warp reqwest
cargo add tokio@1.0 --features full
```

{% endtab %}

{% tab title="Go" %}

```bash
mkdir pwr-stateful-vida && cd pwr-stateful-vida
go mod init pwr-stateful-vida
```

{% endtab %}

{% tab title="C#" %}

```csharp
dotnet new console -n pwr-stateful-vida && cd pwr-stateful-vida
dotnet add package PWR
```

{% endtab %}

{% tab title="Java" %}

```java
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>PWR-Stateful-Vida</artifactId>
    <version>1.0-SNAPSHOT</version>

    <repositories>
        <repository>
            <id>jitpack.io</id>
            <url>https://jitpack.io</url>
        </repository>
    </repositories>

    <dependencies>
        <!-- PWR SDK for blockchain interaction -->
        <dependency>
            <groupId>com.github.pwrlabs</groupId>
            <artifactId>pwrj</artifactId>
            <version>11.5.3</version>
        </dependency>

        <!-- Spark Java for HTTP API -->
        <dependency>
            <groupId>com.sparkjava</groupId>
            <artifactId>spark-core</artifactId>
            <version>2.9.3</version>
        </dependency>
    </dependencies>

    <properties>
        <maven.compiler.source>21</maven.compiler.source>
        <maven.compiler.target>21</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
</project>
```

{% endtab %}
{% endtabs %}

#### 2. Database Layer Implementation <a href="#id-2-database-layer-implementation" id="id-2-database-layer-implementation"></a>

Create a singleton service for managing persistent state using MerkleTree for cryptographic verification.

{% tabs %}
{% tab title="JavaScript" %}

```javascript
// databaseService.js
import { MerkleTree } from "@pwrjs/core/services"

class DatabaseService {
    static #tree = null;
    static #initialized = false;
    static #LAST_CHECKED_BLOCK_KEY = Buffer.from('lastCheckedBlock');
    static #BLOCK_ROOT_PREFIX = 'blockRootHash_';

    static async initialize() {
        if (this.#initialized) {
            throw new Error('DatabaseService already initialized');
        }
        
        try {
            this.#tree = new MerkleTree('database');
            await this.#tree.ensureInitialized();
            this.#initialized = true;
            
            process.on('SIGINT', () => this.#cleanup());
            process.on('SIGTERM', () => this.#cleanup());
            process.on('exit', () => this.#cleanup());
            process.on('uncaughtException', () => this.#cleanup());
            process.on('unhandledRejection', () => this.#cleanup());
            
        } catch (error) {
            throw new Error(`Failed to initialize DatabaseService: ${error.message}`);
        }
    }

    static #getTree() {
        if (!this.#initialized || !this.#tree) {
            throw new Error('DatabaseService not initialized. Call initialize() first.');
        }
        return this.#tree;
    }

    static async #cleanup() {
        if (this.#tree && !this.#tree.closed) {
            try {
                await this.#tree.close();
            } catch (error) {
                console.error('Error closing MerkleTree:', error);
            }
        }
    }

    static async getRootHash() {
        const tree = this.#getTree();
        return await tree.getRootHash();
    }

    static async flush() {
        const tree = this.#getTree();
        return await tree.flushToDisk();
    }

    static async revertUnsavedChanges() {
        const tree = this.#getTree();
        return await tree.revertUnsavedChanges();
    }

    static async getBalance(address) {
        if (!address || address.length === 0) {
            throw new Error('Address must not be empty');
        }

        const tree = this.#getTree();
        const data = await tree.getData(Buffer.from(address));
        
        if (!data || data.length === 0) {
            return 0n;
        }
        
        return this.#bytesToBigInt(data);
    }

    static async setBalance(address, balance) {
        if (!address || address.length === 0) {
            throw new Error('Address must not be empty');
        }

        const tree = this.#getTree();
        const balanceBytes = this.#bigIntToBytes(balance);
        return await tree.addOrUpdateData(Buffer.from(address), balanceBytes);
    }

    static async transfer(sender, receiver, amount) {
        if (!sender || sender.length === 0) {
            throw new Error('Sender address must not be empty');
        }
        if (!receiver || receiver.length === 0) {
            throw new Error('Receiver address must not be empty');
        }

        const senderBalance = await this.getBalance(sender);
        
        if (senderBalance < amount) {
            return false;
        }

        const newSenderBalance = senderBalance - amount;
        const receiverBalance = await this.getBalance(receiver);
        const newReceiverBalance = receiverBalance + amount;

        await this.setBalance(sender, newSenderBalance);
        await this.setBalance(receiver, newReceiverBalance);

        return true;
    }

    static async getLastCheckedBlock() {
        const tree = this.#getTree();
        const data = await tree.getData(this.#LAST_CHECKED_BLOCK_KEY);
        
        if (!data || data.length < 8) {
            return 0;
        }

        const buffer = Buffer.from(data.slice(0, 8));
        return Number(buffer.readBigUInt64BE(0));
    }

    static async setLastCheckedBlock(blockNumber) {
        const tree = this.#getTree();
        const buffer = Buffer.allocUnsafe(8);
        buffer.writeBigUInt64BE(BigInt(blockNumber), 0);
        return await tree.addOrUpdateData(this.#LAST_CHECKED_BLOCK_KEY, buffer);
    }

    static async setBlockRootHash(blockNumber, rootHash) {
        if (!rootHash || rootHash.length === 0) {
            throw new Error('Root hash must not be empty');
        }

        const tree = this.#getTree();
        const key = Buffer.from(`${this.#BLOCK_ROOT_PREFIX}${blockNumber}`);
        return await tree.addOrUpdateData(key, Buffer.from(rootHash));
    }

    static async getBlockRootHash(blockNumber) {
        const tree = this.#getTree();
        const key = Buffer.from(`${this.#BLOCK_ROOT_PREFIX}${blockNumber}`);
        return await tree.getData(key);
    }

    static async close() {
        if (this.#tree && !this.#tree.closed) {
            await this.#tree.close();
        }
        this.#initialized = false;
        this.#tree = null;
    }

    static #bytesToBigInt(bytes) {
        if (bytes.length === 0) return 0n;
        let result = 0n;
        for (let i = 0; i < bytes.length; i++) {
            result = (result << 8n) | BigInt(bytes[i]);
        }
        return result;
    }

    static #bigIntToBytes(bigint) {
        if (bigint === 0n) return Buffer.from([0]);
        
        const bytes = [];
        let value = bigint;
        while (value > 0n) {
            bytes.unshift(Number(value & 0xFFn));
            value = value >> 8n;
        }
        return Buffer.from(bytes);
    }
}

export default DatabaseService;
```

{% endtab %}

{% tab title="Python" %}

```python
# database_service.py
import atexit
import struct
import threading
from typing import Optional
from pwrpy.models.MerkleTree import MerkleTree

class DatabaseServiceError(Exception):
    """Custom exception for DatabaseService operations"""
    pass

# Global instance and lock
_tree = None
_lock = threading.Lock()

# Constants
LAST_CHECKED_BLOCK_KEY = b"lastCheckedBlock"
BLOCK_ROOT_PREFIX = "blockRootHash_"

def _get_tree():
    """Get or create the global MerkleTree instance"""
    global _tree
    if _tree is None:
        with _lock:
            if _tree is None:
                try:
                    _tree = MerkleTree("database")
                    atexit.register(_shutdown_hook)
                except Exception as e:
                    raise DatabaseServiceError(f"Failed to initialize MerkleTree: {str(e)}")
    return _tree

def _shutdown_hook():
    """Cleanup method called on application shutdown"""
    global _tree
    try:
        if _tree is not None:
            _tree.close()
    except Exception as e:
        print(f"Error during DatabaseService shutdown: {e}")

class DatabaseService:
    """Database service class providing methods for Merkle tree operations"""
    
    @staticmethod
    def get_root_hash() -> Optional[bytes]:
        """Get current Merkle root hash"""
        try:
            tree = _get_tree()
            return tree.get_root_hash()
        except Exception as e:
            raise DatabaseServiceError(f"Error getting root hash: {str(e)}")

    @staticmethod
    def flush():
        """Flush pending writes to disk"""
        try:
            tree = _get_tree()
            tree.flush_to_disk()
        except Exception as e:
            raise DatabaseServiceError(f"Error flushing to disk: {str(e)}")

    @staticmethod
    def revert_unsaved_changes():
        """Reverts all unsaved changes to the Merkle tree"""
        try:
            tree = _get_tree()
            tree.revert_unsaved_changes()
        except Exception as e:
            raise DatabaseServiceError(f"Error reverting changes: {str(e)}")

    @staticmethod
    def get_balance(address: bytes) -> int:
        """Retrieves the balance stored at the given address"""
        if address is None:
            raise ValueError("Address must not be null")
        
        try:
            tree = _get_tree()
            data = tree.get_data(address)
            
            if data is None or len(data) == 0:
                return 0
            
            return int.from_bytes(data, byteorder='big', signed=False)
            
        except Exception as e:
            raise DatabaseServiceError(f"Error getting balance: {str(e)}")

    @staticmethod
    def set_balance(address: bytes, balance: int):
        """Sets the balance for the given address"""
        if address is None:
            raise ValueError("Address must not be null")
        if balance is None:
            raise ValueError("Balance must not be null")
        if balance < 0:
            raise ValueError("Balance must be non-negative")
        
        try:
            tree = _get_tree()
            balance_bytes = balance.to_bytes((balance.bit_length() + 7) // 8, byteorder='big', signed=False)
            if balance == 0:
                balance_bytes = b'\x00'
            
            tree.add_or_update_data(address, balance_bytes)
            
        except Exception as e:
            raise DatabaseServiceError(f"Error setting balance: {str(e)}")

    @staticmethod
    def transfer(sender: bytes, receiver: bytes, amount: int) -> bool:
        """Transfers amount from sender to receiver"""
        if sender is None:
            raise ValueError("Sender must not be null")
        if receiver is None:
            raise ValueError("Receiver must not be null")
        if amount is None:
            raise ValueError("Amount must not be null")
        
        try:
            sender_balance = DatabaseService.get_balance(sender)
            if sender_balance < amount:
                return False
            
            DatabaseService.set_balance(sender, sender_balance - amount)
            receiver_balance = DatabaseService.get_balance(receiver)
            DatabaseService.set_balance(receiver, receiver_balance + amount)
            
            return True
            
        except Exception as e:
            raise DatabaseServiceError(f"Error during transfer: {str(e)}")

    @staticmethod
    def get_last_checked_block() -> int:
        """Get the last checked block number"""
        try:
            tree = _get_tree()
            data = tree.get_data(LAST_CHECKED_BLOCK_KEY)
            
            if data is None or len(data) < 8:
                return 0
            
            return struct.unpack('>Q', data)[0]
            
        except Exception as e:
            raise DatabaseServiceError(f"Error getting last checked block: {str(e)}")

    @staticmethod
    def set_last_checked_block(block_number: int):
        """Updates the last checked block number"""
        if block_number < 0:
            raise ValueError("Block number must be non-negative")
        
        try:
            tree = _get_tree()
            data = struct.pack('>Q', block_number)
            tree.add_or_update_data(LAST_CHECKED_BLOCK_KEY, data)
            
        except Exception as e:
            raise DatabaseServiceError(f"Error setting last checked block: {str(e)}")

    @staticmethod
    def set_block_root_hash(block_number: int, root_hash: bytes):
        """Records the Merkle root hash for a specific block"""
        if root_hash is None:
            raise ValueError("Root hash must not be null")
        
        try:
            tree = _get_tree()
            key = f"{BLOCK_ROOT_PREFIX}{block_number}"
            tree.add_or_update_data(key.encode('utf-8'), root_hash)
            
        except Exception as e:
            raise DatabaseServiceError(f"Error setting block root hash: {str(e)}")

    @staticmethod
    def get_block_root_hash(block_number: int) -> Optional[bytes]:
        """Retrieves the Merkle root hash for a specific block"""
        try:
            tree = _get_tree()
            key = f"{BLOCK_ROOT_PREFIX}{block_number}"
            return tree.get_data(key.encode('utf-8'))
            
        except Exception as e:
            raise DatabaseServiceError(f"Error getting block root hash: {str(e)}")
```

{% endtab %}

{% tab title="Rust" %}

```rust
// src/database_service.rs
use std::sync::{Arc, OnceLock};
use pwr_rs::merkle_tree::{MerkleTree, MerkleTreeError};
use num_bigint::BigUint;
use std::convert::TryInto;

/// Singleton service for interacting with the underlying RocksDB-backed MerkleTree.
/// Provides methods for managing account balances, transfers, block tracking, and
/// Merkle root hash operations.
pub struct DatabaseService;

// Global static instance of the MerkleTree
static TREE: OnceLock<Arc<MerkleTree>> = OnceLock::new();

// Constants
const LAST_CHECKED_BLOCK_KEY: &[u8] = b"lastCheckedBlock";
const BLOCK_ROOT_PREFIX: &str = "blockRootHash_";

impl DatabaseService {
    /// Initialize the DatabaseService. Must be called once before using any other methods.
    pub fn initialize() -> Result<(), MerkleTreeError> {
        let tree = MerkleTree::new("database".to_string())?;
        TREE.set(tree).map_err(|_| {
            MerkleTreeError::IllegalState("DatabaseService already initialized".to_string())
        })?;
        Ok(())
    }
    
    /// Get the global tree instance
    fn get_tree() -> Result<&'static Arc<MerkleTree>, MerkleTreeError> {
        TREE.get().ok_or_else(|| {
            MerkleTreeError::IllegalState("DatabaseService not initialized. Call initialize() first.".to_string())
        })
    }
    
    /// Get current Merkle root hash
    pub fn get_root_hash() -> Result<Option<Vec<u8>>, MerkleTreeError> {
        let tree = Self::get_tree()?;
        tree.get_root_hash()
    }
    
    /// Flush pending writes to disk
    pub fn flush() -> Result<(), MerkleTreeError> {
        let tree = Self::get_tree()?;
        tree.flush_to_disk()
    }
    
    /// Reverts all unsaved changes to the Merkle tree
    pub fn revert_unsaved_changes() -> Result<(), MerkleTreeError> {
        let tree = Self::get_tree()?;
        tree.revert_unsaved_changes()
    }
    
    /// Retrieves the balance stored at the given address
    pub fn get_balance(address: &[u8]) -> Result<BigUint, MerkleTreeError> {
        if address.is_empty() {
            return Err(MerkleTreeError::InvalidArgument("Address must not be empty".to_string()));
        }
        
        let tree = Self::get_tree()?;
        let data = tree.get_data(address)?;
        
        match data {
            Some(bytes) if !bytes.is_empty() => {
                Ok(BigUint::from_bytes_be(&bytes))
            }
            _ => Ok(BigUint::from(0u32))
        }
    }
    
    /// Sets the balance for the given address
    pub fn set_balance(address: &[u8], balance: &BigUint) -> Result<(), MerkleTreeError> {
        if address.is_empty() {
            return Err(MerkleTreeError::InvalidArgument("Address must not be empty".to_string()));
        }
        
        let tree = Self::get_tree()?;
        let balance_bytes = balance.to_bytes_be();
        tree.add_or_update_data(address, &balance_bytes)
    }
    
    /// Transfers amount from sender to receiver
    pub fn transfer(sender: &[u8], receiver: &[u8], amount: &BigUint) -> Result<bool, MerkleTreeError> {
        if sender.is_empty() {
            return Err(MerkleTreeError::InvalidArgument("Sender address must not be empty".to_string()));
        }
        if receiver.is_empty() {
            return Err(MerkleTreeError::InvalidArgument("Receiver address must not be empty".to_string()));
        }
        
        let sender_balance = Self::get_balance(sender)?;
        
        if sender_balance < *amount {
            return Ok(false);
        }
        
        let new_sender_balance = &sender_balance - amount;
        let receiver_balance = Self::get_balance(receiver)?;
        let new_receiver_balance = &receiver_balance + amount;
        
        Self::set_balance(sender, &new_sender_balance)?;
        Self::set_balance(receiver, &new_receiver_balance)?;
        
        Ok(true)
    }
    
    /// Get the last checked block number
    pub fn get_last_checked_block() -> Result<u64, MerkleTreeError> {
        let tree = Self::get_tree()?;
        let data = tree.get_data(LAST_CHECKED_BLOCK_KEY)?;
        
        match data {
            Some(bytes) if bytes.len() >= 8 => {
                let block_bytes: [u8; 8] = bytes[..8].try_into()
                    .map_err(|_| MerkleTreeError::InvalidArgument("Invalid block number format".to_string()))?;
                Ok(u64::from_be_bytes(block_bytes))
            }
            _ => Ok(0)
        }
    }
    
    /// Updates the last checked block number
    pub fn set_last_checked_block(block_number: u64) -> Result<(), MerkleTreeError> {
        let tree = Self::get_tree()?;
        let block_bytes = block_number.to_be_bytes();
        tree.add_or_update_data(LAST_CHECKED_BLOCK_KEY, &block_bytes)
    }
    
    /// Records the Merkle root hash for a specific block
    pub fn set_block_root_hash(block_number: u64, root_hash: &[u8]) -> Result<(), MerkleTreeError> {
        if root_hash.is_empty() {
            return Err(MerkleTreeError::InvalidArgument("Root hash must not be empty".to_string()));
        }
        
        let tree = Self::get_tree()?;
        let key = format!("{}{}", BLOCK_ROOT_PREFIX, block_number);
        tree.add_or_update_data(key.as_bytes(), root_hash)
    }
    
    /// Retrieves the Merkle root hash for a specific block
    pub fn get_block_root_hash(block_number: u64) -> Result<Option<Vec<u8>>, MerkleTreeError> {
        let tree = Self::get_tree()?;
        let key = format!("{}{}", BLOCK_ROOT_PREFIX, block_number);
        tree.get_data(key.as_bytes())
    }
}
```

{% endtab %}

{% tab title="Go" %}

```go
// dbservice/main.go
package dbservice

import (
    "encoding/binary"
    "math/big"
    "sync"

    "github.com/pwrlabs/pwrgo/config/merkletree"
)

var (
    tree                *merkletree.MerkleTree
    initOnce            sync.Once
    lastCheckedBlockKey = []byte("lastCheckedBlock")
    blockRootPrefix     = "blockRootHash_"
)

// initialize sets up the singleton MerkleTree instance
func initialize() {
    initOnce.Do(func() {
        tree, _ = merkletree.NewMerkleTree("database")
    })
}

// GetRootHash returns the current Merkle root hash
func GetRootHash() ([]byte, error) {
    initialize()
    return tree.GetRootHash()
}

// Flush pending writes to disk
func Flush() error {
    initialize()
    return tree.FlushToDisk()
}

// RevertUnsavedChanges reverts all unsaved changes
func RevertUnsavedChanges() error {
    initialize()
    return tree.RevertUnsavedChanges()
}

// GetBalance retrieves the balance stored at the given address
func GetBalance(address []byte) (*big.Int, error) {
    initialize()
    if address == nil {
        return big.NewInt(0), nil
    }

    data, err := tree.GetData(address)
    if err != nil {
        return nil, err
    }

    if data == nil || len(data) == 0 {
        return big.NewInt(0), nil
    }

    balance := new(big.Int)
    balance.SetBytes(data)
    return balance, nil
}

// SetBalance sets the balance for the given address
func SetBalance(address []byte, balance *big.Int) error {
    initialize()
    if address == nil || balance == nil {
        return nil
    }

    return tree.AddOrUpdateData(address, balance.Bytes())
}

// Transfer transfers amount from sender to receiver
func Transfer(sender, receiver []byte, amount *big.Int) (bool, error) {
    initialize()
    if sender == nil || receiver == nil || amount == nil {
        return false, nil
    }

    senderBalance, err := GetBalance(sender)
    if err != nil {
        return false, err
    }

    if senderBalance.Cmp(amount) < 0 {
        return false, nil // Insufficient funds
    }

    newSenderBalance := new(big.Int).Sub(senderBalance, amount)
    if err := SetBalance(sender, newSenderBalance); err != nil {
        return false, err
    }

    receiverBalance, _ := GetBalance(receiver)
    newReceiverBalance := new(big.Int).Add(receiverBalance, amount)
    if err := SetBalance(receiver, newReceiverBalance); err != nil {
        return false, err
    }

    return true, nil
}

// GetLastCheckedBlock returns the last checked block number
func GetLastCheckedBlock() (int64, error) {
    initialize()
    data, err := tree.GetData(lastCheckedBlockKey)
    if err != nil {
        return 0, err
    }

    if data == nil || len(data) < 8 {
        return 0, nil
    }

    return int64(binary.BigEndian.Uint64(data)), nil
}

// SetLastCheckedBlock updates the last checked block number
func SetLastCheckedBlock(blockNumber int64) error {
    initialize()
    blockBytes := make([]byte, 8)
    binary.BigEndian.PutUint64(blockBytes, uint64(blockNumber))
    return tree.AddOrUpdateData(lastCheckedBlockKey, blockBytes)
}

// SetBlockRootHash records the Merkle root hash for a specific block
func SetBlockRootHash(blockNumber int64, rootHash []byte) error {
    initialize()
    if rootHash == nil {
        return nil
    }

    key := []byte(blockRootPrefix + string(rune(blockNumber)))
    return tree.AddOrUpdateData(key, rootHash)
}

// GetBlockRootHash retrieves the Merkle root hash for a specific block
func GetBlockRootHash(blockNumber int64) ([]byte, error) {
    initialize()
    key := []byte(blockRootPrefix + string(rune(blockNumber)))
    return tree.GetData(key)
}

// Close explicitly closes the DatabaseService
func Close() error {
    if tree != nil {
        return tree.Close()
    }
    return nil
}
```

{% endtab %}

{% tab title="C#" %}

```csharp
// DatabaseService.cs
using System;
using System.Numerics;
using System.Text;
using PWR.Utils.MerkleTree;

namespace PwrStatefulVIDA;

public static class DatabaseService
{
    private static MerkleTree? _tree = null;
    private static readonly object _lock = new object();
    private static readonly byte[] LAST_CHECKED_BLOCK_KEY = Encoding.UTF8.GetBytes("lastCheckedBlock");
    private const string BLOCK_ROOT_PREFIX = "blockRootHash_";

    public static void Initialize()
    {
        lock (_lock)
        {
            if (_tree != null)
            {
                throw new InvalidOperationException("DatabaseService already initialized");
            }

            _tree = new MerkleTree("database");
            
            AppDomain.CurrentDomain.ProcessExit += (sender, e) => 
            {
                try
                {
                    Close();
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"Error during shutdown: {ex.Message}");
                }
            };
        }
    }

    private static MerkleTree GetTree()
    {
        if (_tree == null)
        {
            throw new InvalidOperationException("DatabaseService not initialized. Call Initialize() first.");
        }
        return _tree;
    }

    public static byte[]? GetRootHash()
    {
        var tree = GetTree();
        return tree.GetRootHash();
    }

    public static void Flush()
    {
        var tree = GetTree();
        tree.FlushToDisk();
    }

    public static void RevertUnsavedChanges()
    {
        var tree = GetTree();
        tree.RevertUnsavedChanges();
    }

    public static BigInteger GetBalance(byte[] address)
    {
        if (address == null || address.Length == 0)
        {
            throw new ArgumentException("Address must not be empty");
        }

        var tree = GetTree();
        var data = tree.GetData(address);

        if (data != null && data.Length > 0)
        {
            return new BigInteger(data, isUnsigned: true, isBigEndian: true);
        }

        return BigInteger.Zero;
    }

    public static void SetBalance(byte[] address, BigInteger balance)
    {
        if (address == null || address.Length == 0)
        {
            throw new ArgumentException("Address must not be empty");
        }

        var tree = GetTree();
        var balanceBytes = balance.ToByteArray(isUnsigned: true, isBigEndian: true);
        tree.AddOrUpdateData(address, balanceBytes);
    }

    public static bool Transfer(byte[] sender, byte[] receiver, BigInteger amount)
    {
        if (sender == null || sender.Length == 0)
        {
            throw new ArgumentException("Sender address must not be empty");
        }
        if (receiver == null || receiver.Length == 0)
        {
            throw new ArgumentException("Receiver address must not be empty");
        }

        var senderBalance = GetBalance(sender);

        if (senderBalance < amount)
        {
            return false;
        }

        var newSenderBalance = senderBalance - amount;
        var receiverBalance = GetBalance(receiver);
        var newReceiverBalance = receiverBalance + amount;

        SetBalance(sender, newSenderBalance);
        SetBalance(receiver, newReceiverBalance);

        return true;
    }

    public static ulong GetLastCheckedBlock()
    {
        var tree = GetTree();
        var data = tree.GetData(LAST_CHECKED_BLOCK_KEY);

        if (data != null && data.Length >= 8)
        {
            try
            {
                if (BitConverter.IsLittleEndian)
                {
                    Array.Reverse(data, 0, 8);
                }
                return BitConverter.ToUInt64(data, 0);
            }
            catch (Exception)
            {
                throw new ArgumentException("Invalid block number format");
            }
        }

        return 0;
    }

    public static void SetLastCheckedBlock(ulong blockNumber)
    {
        var tree = GetTree();
        var blockBytes = BitConverter.GetBytes(blockNumber);
        
        if (BitConverter.IsLittleEndian)
        {
            Array.Reverse(blockBytes);
        }
        
        tree.AddOrUpdateData(LAST_CHECKED_BLOCK_KEY, blockBytes);
    }

    public static void SetBlockRootHash(ulong blockNumber, byte[] rootHash)
    {
        if (rootHash == null || rootHash.Length == 0)
        {
            throw new ArgumentException("Root hash must not be empty");
        }

        var tree = GetTree();
        var key = Encoding.UTF8.GetBytes($"{BLOCK_ROOT_PREFIX}{blockNumber}");
        tree.AddOrUpdateData(key, rootHash);
    }

    public static byte[]? GetBlockRootHash(ulong blockNumber)
    {
        var tree = GetTree();
        var key = Encoding.UTF8.GetBytes($"{BLOCK_ROOT_PREFIX}{blockNumber}");
        return tree.GetData(key);
    }

    public static void Close()
    {
        lock (_lock)
        {
            if (_tree != null)
            {
                _tree.Close();
                _tree = null;
            }
        }
    }
}
```

{% endtab %}

{% tab title="Java" %}

```java
// DatabaseService.java
package main;

import io.pwrlabs.database.rocksdb.MerkleTree;
import org.rocksdb.RocksDBException;

import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.Objects;

/**
* Singleton service for interacting with the underlying RocksDB-backed MerkleTree.
* Provides methods for managing account balances, transfers, block tracking, and
* Merkle root hash operations. All operations may throw RocksDBException.
*
* <p>This service maintains:
* <ul>
*   <li>Account balances stored in the Merkle tree</li>
*   <li>Last checked block number for synchronization</li>
*   <li>Historical block root hashes for validation</li>
* </ul>
*
* <p>The underlying MerkleTree is automatically closed on JVM shutdown.
*/
public final class DatabaseService {
    private static final MerkleTree TREE;
    private static final byte[] LAST_CHECKED_BLOCK_KEY = "lastCheckedBlock".getBytes(StandardCharsets.UTF_8);
    private static final String BLOCK_ROOT_PREFIX = "blockRootHash_";

    static {
        try {
            TREE = new MerkleTree("database");
        } catch (RocksDBException e) {
            throw new ExceptionInInitializerError("Failed to initialize MerkleTree: " + e.getMessage());
        }
        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            try {
                TREE.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }));
    }

    /**
    * @return current Merkle root hash
    * @throws RocksDBException on RocksDB errors
    */
    public static byte[] getRootHash() throws RocksDBException {
        return TREE.getRootHash();
    }

    /**
    * Flush pending writes to disk.
    * @throws RocksDBException on RocksDB errors
    */
    public static void flush() throws RocksDBException {
        TREE.flushToDisk();
    }

    /**
    * Reverts all unsaved changes to the Merkle tree, restoring it to the last
    * flushed state. This is useful for rolling back invalid transactions or
    * when consensus validation fails.
    */
    public static void revertUnsavedChanges() {
            TREE.revertUnsavedChanges();
    }

    /**
    * Retrieves the balance stored at the given address.
    *
    * @param address 20-byte account address
    * @return non-negative balance, zero if absent
    * @throws RocksDBException on RocksDB errors
    */
    public static BigInteger getBalance(byte[] address) throws RocksDBException {
        Objects.requireNonNull(address, "Address must not be null");
        byte[] data = TREE.getData(address);
        if (data == null || data.length == 0) {
            return BigInteger.ZERO;
        }
        return new BigInteger(1, data);
    }

    /**
    * Sets the balance for the given address.
    *
    * @param address 20-byte account address
    * @param balance non-negative balance
    * @throws RocksDBException on RocksDB errors
    */
    public static void setBalance(byte[] address, BigInteger balance) throws RocksDBException {
        Objects.requireNonNull(address, "Address must not be null");
        Objects.requireNonNull(balance, "Balance must not be null");
        TREE.addOrUpdateData(address, balance.toByteArray());
    }

    /**
    * Transfers amount from sender to receiver.
    *
    * @param sender   sender address
    * @param receiver receiver address
    * @param amount   amount to transfer
    * @return true if transfer succeeded, false on insufficient funds
    * @throws RocksDBException on RocksDB errors
    */
    public static boolean transfer(byte[] sender, byte[] receiver, BigInteger amount) throws RocksDBException {
        Objects.requireNonNull(sender);
        Objects.requireNonNull(receiver);
        Objects.requireNonNull(amount);
        BigInteger senderBal = getBalance(sender);
        if (senderBal.compareTo(amount) < 0) {
            return false;
        }
        setBalance(sender, senderBal.subtract(amount));
        setBalance(receiver, getBalance(receiver).add(amount));
        return true;
    }

    /**
    * @return the last checked block number, or zero if unset
    * @throws RocksDBException on RocksDB errors
    */
    public static long getLastCheckedBlock() throws RocksDBException {
        byte[] bytes = TREE.getData(LAST_CHECKED_BLOCK_KEY);
        return (bytes == null || bytes.length < Long.BYTES)
                ? 0L
                : ByteBuffer.wrap(bytes).getLong();
    }

    /**
    * Updates the last checked block number.
    *
    * @param blockNumber non-negative block number
    * @throws RocksDBException on RocksDB errors
    */
    public static void setLastCheckedBlock(long blockNumber) throws RocksDBException {
        ByteBuffer buf = ByteBuffer.allocate(Long.BYTES).putLong(blockNumber);
        TREE.addOrUpdateData(LAST_CHECKED_BLOCK_KEY, buf.array());
    }

    /**
    * Records the Merkle root hash for a specific block.
    *
    * @param blockNumber block height
    * @param rootHash    32-byte Merkle root
    * @throws RocksDBException on RocksDB errors
    */
    public static void setBlockRootHash(long blockNumber, byte[] rootHash) throws RocksDBException {
        Objects.requireNonNull(rootHash, "Root hash must not be null");
        String key = BLOCK_ROOT_PREFIX + blockNumber;
        TREE.addOrUpdateData(key.getBytes(StandardCharsets.UTF_8), rootHash);
    }

    /**
    * Retrieves the Merkle root hash for a specific block.
    *
    * @param blockNumber block height
    * @return 32-byte root hash, or null if absent
    * @throws RocksDBException on RocksDB errors
    */
    public static byte[] getBlockRootHash(long blockNumber) throws RocksDBException {
        String key = BLOCK_ROOT_PREFIX + blockNumber;
        return TREE.getData(key.getBytes(StandardCharsets.UTF_8));
    }
}
```

{% endtab %}
{% endtabs %}

**Key Features:**

* **MerkleTree Integration**: Provides cryptographic state verification through root hashes
* **Atomic Transfers**: Ensures balance consistency during token transfers
* **Block State Management**: Tracks validated states for specific block heights
* **Error Recovery**: Can revert changes when consensus validation fails

#### 3. HTTP API Implementation <a href="#id-3-http-api-implementation" id="id-3-http-api-implementation"></a>

Create HTTP endpoints for peer communication and state queries.

{% tabs %}
{% tab title="JavaScript" %}

```javascript
// api/get.js
import DatabaseService from '../databaseService.js';

export class GET {
    static run(app) {
        app.get('/rootHash', async (req, res) => {
            try {
                const response = await this.handleRootHash(req.query);
                
                if (response === '') {
                    return res.status(500).send('');
                }
                
                if (response.startsWith('Block root hash not found') || 
                    response === 'Invalid block number') {
                    return res.status(400).send(response);
                }
                
                return res.send(response);
                
            } catch (error) {
                return res.status(500).send('');
            }
        });
    }

    static async handleRootHash(params) {
        const blockNumberStr = params.blockNumber;
        if (!blockNumberStr) {
            throw new Error('Missing blockNumber parameter');
        }
        
        const blockNumber = parseInt(blockNumberStr, 10);
        if (isNaN(blockNumber)) {
            throw new Error('Invalid block number format');
        }
        
        const lastCheckedBlock = await DatabaseService.getLastCheckedBlock();
        
        if (blockNumber === lastCheckedBlock) {
            const rootHash = await DatabaseService.getRootHash();
            if (rootHash) {
                return rootHash.toString('hex');
            } else {
                return '';
            }
        }
        else if (blockNumber < lastCheckedBlock && blockNumber > 1) {
            const blockRootHash = await DatabaseService.getBlockRootHash(blockNumber);
            
            if (blockRootHash !== null) {
                return blockRootHash.toString('hex');
            } else {
                return `Block root hash not found for block number: ${blockNumber}`;
            }
        } else {
            return 'Invalid block number';
        }
    }
}

export default { GET };
```

{% endtab %}

{% tab title="Python" %}

```python
# api/get.py
from flask import Flask, request
from typing import Optional
from database_service import DatabaseService, DatabaseServiceError

# Flask app instance
app = Flask(__name__)

def bytes_to_hex_string(data: Optional[bytes]) -> Optional[str]:
    """Convert bytes to hex string"""
    if data is None:
        return None
    return data.hex()

@app.route('/rootHash', methods=['GET'])
def root_hash_endpoint():
    """GET /rootHash endpoint"""
    try:
        # Parse blockNumber query parameter
        block_number_str = request.args.get('blockNumber')
        if block_number_str is None:
            return "Missing blockNumber parameter", 400
        
        try:
            block_number = int(block_number_str)
        except ValueError:
            return "Invalid block number format", 400
        
        # Get last checked block for validation
        last_checked_block = DatabaseService.get_last_checked_block()
        
        if block_number == last_checked_block:
            # Return current root hash
            root_hash = DatabaseService.get_root_hash()
            if root_hash is not None:
                return bytes_to_hex_string(root_hash)
            else:
                return "Root hash not available", 400
                
        elif block_number < last_checked_block and block_number > 1:
            # Return historical root hash
            block_root_hash = DatabaseService.get_block_root_hash(block_number)
            if block_root_hash is not None:
                return bytes_to_hex_string(block_root_hash)
            else:
                return f"Block root hash not found for block number: {block_number}", 400
        else:
            # Invalid block number
            return "Invalid block number", 400
            
    except DatabaseServiceError:
        return "Database error", 500
    except Exception:
        return "", 500

def run():
    """Initializes and registers all GET endpoint handlers with Flask"""
    # The route is already registered via the @app.route decorator
    pass

if __name__ == "__main__":
    app.run(debug=True)
```

{% endtab %}

{% tab title="Rust" %}

```rust
// src/api/mod.rs
use warp::Filter;
use std::collections::HashMap;
use crate::database_service::DatabaseService;

pub struct GET;

impl GET {
    /// Initializes and registers all GET endpoint handlers with the Warp framework.
    /// Currently registers the /rootHash endpoint for retrieving Merkle root hashes
    /// for specific block numbers.
    pub fn run() -> impl Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone {
        warp::path("rootHash")
            .and(warp::get())
            .and(warp::query::<HashMap<String, String>>())
            .map(|params: HashMap<String, String>| {
                match Self::handle_root_hash(params) {
                    Ok(response) => response,
                    Err(_) => String::new()
                }
            })
    }
    
    fn handle_root_hash(params: HashMap<String, String>) -> Result<String, String> {
        let block_number_str = params.get("blockNumber")
            .ok_or("Missing blockNumber parameter")?;
        let block_number: u64 = block_number_str.parse()
            .map_err(|_| "Invalid block number format")?;
        
        let last_checked_block = DatabaseService::get_last_checked_block()
            .map_err(|_| "Database error")?;
        
        if block_number == last_checked_block {
            let root_hash = DatabaseService::get_root_hash()
                .map_err(|_| "Database error")?;
            match root_hash {
                Some(hash) => Ok(hex::encode(hash)),
                None => Ok(String::new())
            }
        } else if block_number < last_checked_block && block_number > 1 {
            let block_root_hash = DatabaseService::get_block_root_hash(block_number)
                .map_err(|_| "Database error")?;
            
            match block_root_hash {
                Some(hash) => Ok(hex::encode(hash)),
                None => Ok(format!("Block root hash not found for block number: {}", block_number))
            }
        } else {
            Ok("Invalid block number".to_string())
        }
    }
}
```

{% endtab %}

{% tab title="Go" %}

```go
// api/get.go
package api

import (
    "encoding/hex"
    "net/http"
    "strconv"

    "github.com/gin-gonic/gin"
    "pwr-stateful-vida/dbservice"
)

func RegisterRoutes(router *gin.Engine) {
    router.GET("/rootHash", func(c *gin.Context) {
        blockNumber, _ := strconv.ParseInt(c.Query("blockNumber"), 10, 64)
        lastCheckedBlock, _ := dbservice.GetLastCheckedBlock()

        if blockNumber == lastCheckedBlock {
            if rootHash, _ := dbservice.GetRootHash(); rootHash != nil {
                c.String(http.StatusOK, hex.EncodeToString(rootHash))
                return
            }
        } else if blockNumber < lastCheckedBlock && blockNumber > 1 {
            if blockRootHash, _ := dbservice.GetBlockRootHash(blockNumber); blockRootHash != nil {
                c.String(http.StatusOK, hex.EncodeToString(blockRootHash))
                return
            }
            c.String(http.StatusBadRequest, "Block root hash not found for block number: "+c.Query("blockNumber"))
            return
        }

        c.String(http.StatusBadRequest, "Invalid block number")
    })
}
```

{% endtab %}

{% tab title="C#" %}

```csharp
// api/Get.cs
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Builder;

namespace PwrStatefulVIDA;

public static class GET
{
    public static void Run(WebApplication app)
    {
        app.MapGet("/rootHash", async (HttpContext context) =>
        {
            try
            {
                var response = HandleRootHash(context.Request.Query);
                await context.Response.WriteAsync(response);
            }
            catch (Exception)
            {
                await context.Response.WriteAsync("");
            }
        });
    }

    private static string HandleRootHash(IQueryCollection queryParams)
    {
        if (!queryParams.TryGetValue("blockNumber", out var blockNumberStr) || 
            !ulong.TryParse(blockNumberStr, out var blockNumber))
        {
            return "Missing or invalid blockNumber parameter";
        }

        var lastCheckedBlock = DatabaseService.GetLastCheckedBlock();

        if (blockNumber == lastCheckedBlock)
        {
            var rootHash = DatabaseService.GetRootHash();
            return rootHash != null ? Convert.ToHexString(rootHash).ToLowerInvariant() : "";
        }
        else if (blockNumber < lastCheckedBlock && blockNumber > 1)
        {
            var blockRootHash = DatabaseService.GetBlockRootHash(blockNumber);
            
            return blockRootHash != null 
                ? Convert.ToHexString(blockRootHash).ToLowerInvariant()
                : $"Block root hash not found for block number: {blockNumber}";
        }
        else
        {
            return "Invalid block number";
        }
    }
}
```

{% endtab %}

{% tab title="Java" %}

```java
// api/GET.java
package api;

import io.pwrlabs.util.encoders.Hex;
import main.DatabaseService;

import static spark.Spark.get;

public class GET {
    /**
    * Initializes and registers all GET endpoint handlers with the Spark framework.
    * Currently registers the /rootHash endpoint for retrieving Merkle root hashes
    * for specific block numbers.
    */
    public static void run() {
        get("/rootHash", (request, response) -> {
            try {
                long blockNumber = Long.parseLong(request.queryParams("blockNumber"));

                if(blockNumber == DatabaseService.getLastCheckedBlock()) return Hex.toHexString(DatabaseService.getRootHash());
                else if(blockNumber < DatabaseService.getLastCheckedBlock() && blockNumber > 1) {
                    byte[] blockRootHash = DatabaseService.getBlockRootHash(blockNumber);
                    if (blockRootHash != null) {
                        return Hex.toHexString(blockRootHash);
                    } else {
                        response.status(400);
                        return "Block root hash not found for block number: " + blockNumber;
                    }
                } else {
                    response.status(400);
                    return "Invalid block number";
                }
            } catch (Exception e) {
                e.printStackTrace();
                return "";
            }
        });
    }
}
```

{% endtab %}
{% endtabs %}

### Summary of Part 1 <a href="#summary-of-part-1" id="summary-of-part-1"></a>

In Part 1, we covered the foundational components of a stateful VIDA:

1. Project Setup: Configured dependencies for PWR SDK, HTTP server, and database integration
2. Database Layer: Implemented a MerkleTree-backed service for cryptographic state verification and persistent storage
3. HTTP API: Created endpoints for peer communication and root hash queries

These components provide the infrastructure needed for maintaining and validating persistent state across network participants. In Part 2, we'll implement the transaction processing logic, main application orchestration, and demonstrate how to run the complete stateful VIDA system.

The database layer ensures data integrity through cryptographic verification, while the API layer enables peer consensus validation. Together, they form the foundation for building robust, production-ready stateful applications on the PWR Chain.


# Building a Stateful VIDA - Part 2

In Part 1, we established the foundational components of our stateful VIDA: project setup, database layer with MerkleTree integration, and HTTP API for peer communication. Now we'll implement the core transaction processing logic, orchestrate the main application, and demonstrate how to run the complete system.

### Continuing the Build Process

#### 4. Transaction Processing <a href="#id-4-transaction-processing" id="id-4-transaction-processing"></a>

Implement the core application that subscribes to VIDA transactions and processes them while maintaining state consistency.

{% tabs %}
{% tab title="JavaScript" %}

```javascript
// handler.js
import PWRJS from "@pwrjs/core";
import fetch from 'node-fetch';
import DatabaseService from './databaseService.js';

const VIDA_ID = 73746238;
const RPC_URL = "https://pwrrpc.pwrlabs.io/";
const REQUEST_TIMEOUT = 10000;

export let peersToCheckRootHashWith = [];
let pwrjsClient = null;
let subscription = null;

// Fetches the root hash from a peer node for the specified block number
async function fetchPeerRootHash(peer, blockNumber) {
    const url = `http://${peer}/rootHash?blockNumber=${blockNumber}`;
    
    try {
        const response = await fetch(url, {
            method: 'GET',
            timeout: REQUEST_TIMEOUT,
            headers: {
                'Accept': 'text/plain'
            }
        });
        
        if (response.ok) {
            const hexString = await response.text();
            const trimmed = hexString.trim();
            
            if (!trimmed) {
                console.log(`Peer ${peer} returned empty root hash for block ${blockNumber}`);
                return { success: false, rootHash: null };
            }
            
            try {
                const rootHash = Buffer.from(trimmed, 'hex');
                console.log(`Successfully fetched root hash from peer ${peer} for block ${blockNumber}`);
                return { success: true, rootHash };
            } catch (error) {
                console.log(`Invalid hex response from peer ${peer} for block ${blockNumber}`);
                return { success: false, rootHash: null };
            }
        } else {
            console.log(`Peer ${peer} returned HTTP ${response.status} for block ${blockNumber}`);
            return { success: true, rootHash: null };
        }
    } catch (error) {
        console.log(`Failed to fetch root hash from peer ${peer} for block ${blockNumber}`);
        return { success: false, rootHash: null };
    }
}

// Validates the local Merkle root against peers and persists it if a quorum of peers agree
async function checkRootHashValidityAndSave(blockNumber) {
    const localRoot = await DatabaseService.getRootHash();
    
    if (!localRoot) {
        console.log(`No local root hash available for block ${blockNumber}`);
        return;
    }
    
    let peersCount = peersToCheckRootHashWith.length;
    let quorum = Math.floor((peersCount * 2) / 3) + 1;
    let matches = 0;
    
    for (const peer of peersToCheckRootHashWith) {
        const { success, rootHash } = await fetchPeerRootHash(peer, blockNumber);
        
        if (success && rootHash) {
            if (localRoot.equals(rootHash)) {
                matches++;
            }
        } else {
            if (peersCount > 0) {
                peersCount--;
                quorum = Math.floor((peersCount * 2) / 3) + 1;
            }
        }
        
        if (matches >= quorum) {
            await DatabaseService.setBlockRootHash(blockNumber, localRoot);
            console.log(`Root hash validated and saved for block ${blockNumber}`);
            return;
        }
    }
    
    console.log(`Root hash mismatch: only ${matches}/${peersToCheckRootHashWith.length} peers agreed`);
    await DatabaseService.revertUnsavedChanges();
    subscription.setLatestCheckedBlock(BigInt(await DatabaseService.getLastCheckedBlock()));
}

// Executes a token transfer described by the given JSON payload
async function handleTransfer(jsonData, senderHex) {
    const amount = BigInt(jsonData.amount || 0);
    const receiverHex = jsonData.receiver || "";
    
    if (amount <= 0 || !receiverHex) {
        console.log("Skipping invalid transfer:", jsonData);
        return;
    }

    const senderAddress = senderHex.startsWith("0x") ? senderHex.slice(2) : senderHex;
    const receiverAddress = receiverHex.startsWith("0x") ? receiverHex.slice(2) : receiverHex;
    
    const sender = Buffer.from(senderAddress, 'hex');
    const receiver = Buffer.from(receiverAddress, 'hex');
    
    const success = await DatabaseService.transfer(sender, receiver, amount);
    
    if (success) {
        console.log(`Transfer succeeded: ${amount} from ${senderHex} to ${receiverHex}`);
    } else {
        console.log(`Transfer failed (insufficient funds): ${amount} from ${senderHex} to ${receiverHex}`);
    }
}

// Processes a single VIDA transaction
function processTransaction(txn) {    
    try {
        const dataBytes = Buffer.from(txn.data, 'hex');
    
        const dataStr = dataBytes.toString('utf8');
        const jsonData = JSON.parse(dataStr);
        
        const action = jsonData.action || "";
        
        if (action.toLowerCase() === "transfer") {
            handleTransfer(jsonData, txn.sender);
        }
    } catch (error) {
        console.error("Error processing transaction:", txn.hash, error);
    }
}

// Callback invoked as blocks are processed
async function onChainProgress(blockNumber) {
    try {
        await DatabaseService.setLastCheckedBlock(blockNumber);
        await checkRootHashValidityAndSave(blockNumber);
        console.log(`Checkpoint updated to block ${blockNumber}`);
        await DatabaseService.flush();
    } catch (error) {
        console.error("Failed to update last checked block:", blockNumber, error);
    } finally {
        return null;
    }
}

// Subscribes to VIDA transactions starting from the given block
export async function subscribeAndSync(fromBlock) {
    console.log(`Starting VIDA transaction subscription from block ${fromBlock}`);

    // Initialize RPC client
    pwrjsClient = new PWRJS(RPC_URL);
    
    // Subscribe to VIDA transactions
    subscription = pwrjsClient.subscribeToVidaTransactions(
        VIDA_ID,
        BigInt(fromBlock),
        processTransaction,
        onChainProgress
    );
    console.log(`Successfully subscribed to VIDA ${VIDA_ID} transactions`);
}
```

{% endtab %}

{% tab title="Python" %}

```python
# handler.py
import json
import requests
from pwrpy.pwrsdk import PWRPY
from database_service import DatabaseService

VIDA_ID = 73_746_238
RPC_URL = "https://pwrrpc.pwrlabs.io/"
REQUEST_TIMEOUT = 10

pwrpy_client = None
subscription = None
peers_to_check_root_hash_with = []

# Fetches the root hash from a peer node for the specified block number
def fetch_peer_root_hash(peer, block_number):
    url = f"http://{peer}/rootHash?blockNumber={block_number}"
        
    try:
        response = requests.get(url, timeout=REQUEST_TIMEOUT, headers={'Accept': 'text/plain'})
        
        if response.status_code == 200:
            hex_string = response.text.strip()
            
            if not hex_string:
                print(f"Peer {peer} returned empty root hash for block {block_number}")
                return False, None
            
            try:
                root_hash = bytes.fromhex(hex_string)
                print(f"Successfully fetched root hash from peer {peer} for block {block_number}")
                return True, root_hash
            except ValueError:
                print(f"Invalid hex response from peer {peer} for block {block_number}")
                return False, None
        else:
            print(f"Peer {peer} returned HTTP {response.status_code} for block {block_number}")
            return True, None
            
    except Exception:
        print(f"Failed to fetch root hash from peer {peer} for block {block_number}")
        return False, None

# Validates the local Merkle root against peers and persists it if a quorum of peers agree
def check_root_hash_validity_and_save(block_number):
    local_root = DatabaseService.get_root_hash()
    
    if not local_root:
        print(f"No local root hash available for block {block_number}")
        return
    
    peers_count = len(peers_to_check_root_hash_with)
    quorum = (peers_count * 2) // 3 + 1
    matches = 0
    
    for peer in peers_to_check_root_hash_with:
        success, root_hash = fetch_peer_root_hash(peer, block_number)
        
        if success and root_hash:
            if root_hash == local_root:
                matches += 1
        else:
            if peers_count > 0:
                peers_count -= 1
                quorum = (peers_count * 2) // 3 + 1
        
        if matches >= quorum:
            DatabaseService.set_block_root_hash(block_number, local_root)
            print(f"Root hash validated and saved for block {block_number}")
            return
    
    print(f"Root hash mismatch: only {matches}/{len(peers_to_check_root_hash_with)} peers agreed")
    DatabaseService.revert_unsaved_changes()
    subscription.set_latest_checked_block(DatabaseService.get_last_checked_block())

# Executes a token transfer described by the given JSON payload
def handle_transfer(json_data, sender_hex):
    try:
        amount = int(json_data.get('amount', 0))
        receiver_hex = json_data.get('receiver', '')
        
        if amount <= 0 or not receiver_hex:
            print(f"Invalid transfer data: {json_data}")
            return

        sender_address = sender_hex[2:] if sender_hex.startswith('0x') else sender_hex
        receiver_address = receiver_hex[2:] if receiver_hex.startswith('0x') else receiver_hex

        sender = bytes.fromhex(sender_address)
        receiver = bytes.fromhex(receiver_address)
        
        success = DatabaseService.transfer(sender, receiver, amount)
        
        if success:
            print(f"Transfer succeeded: {amount} from {sender_hex} to {receiver_hex}")
        else:
            print(f"Transfer failed (insufficient funds): {amount} from {sender_hex} to {receiver_hex}")
            
    except Exception as e:
        print(f"Error handling transfer: {e}")

# Processes a single VIDA transaction
def process_transaction(txn):
    try:
        data_hex = txn.data
        data_bytes = bytes.fromhex(data_hex)
        
        data_str = data_bytes.decode('utf-8')
        json_data = json.loads(data_str)
        
        action = json_data.get('action', '')
        
        if action.lower() == 'transfer':
            handle_transfer(json_data, txn.sender)
            
    except Exception as e:
        print(f"Error processing transaction: {e}")

# Callback invoked as blocks are processed
def on_chain_progress(block_number):
    DatabaseService.set_last_checked_block(block_number)
    check_root_hash_validity_and_save(block_number)
    print(f"Checkpoint updated to block {block_number}")
    DatabaseService.flush()

# Subscribes to VIDA transactions starting from the given block
def subscribe_and_sync(from_block):
    global pwrpy_client, subscription
    
    print(f"Starting VIDA transaction subscription from block {from_block}")
    
    pwrpy_client = PWRPY(RPC_URL)
    
    subscription = pwrpy_client.subscribe_to_vida_transactions(
        VIDA_ID,
        from_block,
        process_transaction,
        on_chain_progress
    )
    
    print(f"Successfully subscribed to VIDA {VIDA_ID} transactions")
```

{% endtab %}

{% tab title="Rust" %}

```rust
// src/handler.rs
use pwr_rs::{
    RPC,
    transaction::types::VidaDataTransaction,
    rpc::types::{VidaTransactionSubscription, block_saver},
};
use std::sync::Arc;
use std::time::Duration;
use hex;
use serde_json::{Value, Map};
use num_bigint::BigUint;

use crate::database_service::DatabaseService;

// Constants
const VIDA_ID: u64 = 73_746_238;
const RPC_URL: &str = "https://pwrrpc.pwrlabs.io/";

// Global state
#[allow(non_upper_case_globals)]
static mut subscription: Option<VidaTransactionSubscription> = None;

pub static mut PEERS_TO_CHECK_ROOT_HASH_WITH: Vec<String> = Vec::new();

// Fetches the root hash from a peer node for the specified block number
async fn fetch_peer_root_hash(
    client: &reqwest::Client,
    peer: &str, 
    block_number: u64
) -> (bool, Option<Vec<u8>>) {
    let url = format!("http://{}/rootHash?blockNumber={}", peer, block_number);
    
    match client.get(&url)
        .header("Accept", "text/plain")
        .send()
        .await
    {
        Ok(response) => {
            if response.status().is_success() {
                match response.text().await {
                    Ok(hex_string) => {
                        let trimmed = hex_string.trim();
                        if trimmed.is_empty() {
                            println!("Peer {} returned empty root hash for block {}", peer, block_number);
                            (false, None)
                        } else {
                            match hex::decode(trimmed) {
                                Ok(root_hash) => {
                                    println!("Successfully fetched root hash from peer {} for block {}", peer, block_number);
                                    (true, Some(root_hash))
                                }
                                Err(_) => {
                                    println!("Invalid hex response from peer {} for block {}", peer, block_number);
                                    (false, None)
                                }
                            }
                        }
                    }
                    Err(_) => {
                        println!("Failed to read response from peer {} for block {}", peer, block_number);
                        (false, None)
                    }
                }
            } else {
                println!("Peer {} returned HTTP {} for block {}", peer, response.status(), block_number);
                (true, None)
            }
        }
        Err(_) => {
            println!("Failed to fetch root hash from peer {} for block {}", peer, block_number);
            (false, None)
        }
    }
}

// Validates the local Merkle root against peers and persists it if a quorum of peers agree
async fn check_root_hash_validity_and_save(block_number: u64) {
    let local_root = match DatabaseService::get_root_hash() {
        Ok(Some(root)) => root,
        _ => {
            println!("No local root hash available for block {}", block_number);
            return;
        }
    };
    
    let peers = unsafe { &PEERS_TO_CHECK_ROOT_HASH_WITH };
    let mut peers_count = peers.len();
    let mut quorum = (peers_count * 2) / 3 + 1;
    let mut matches = 0;
    
    // Create HTTP client
    let client = reqwest::Client::builder()
        .timeout(Duration::from_secs(10))
        .build()
        .unwrap();
    
    for peer in peers {
        let (success, peer_root) = fetch_peer_root_hash(&client, peer, block_number).await;
        
        if success && peer_root.is_some() {
            if peer_root.unwrap() == local_root {
                matches += 1;
            }
        } else {
            if peers_count > 0 {
                peers_count -= 1;
                quorum = (peers_count * 2) / 3 + 1;
            }
        }
        
        if matches >= quorum {
            DatabaseService::set_block_root_hash(block_number, &local_root).unwrap();
            println!("Root hash validated and saved for block {}", block_number);
            return;
        }
    }
    
    println!("Root hash mismatch: only {}/{} peers agreed", matches, peers.len());
    
    // Revert changes and reset block to reprocess the data
    DatabaseService::revert_unsaved_changes().unwrap();
    unsafe {
        subscription.as_ref().unwrap().set_latest_checked_block(
            DatabaseService::get_last_checked_block().unwrap() as u64
        );
    }
}

// Executes a token transfer described by the given JSON payload
fn handle_transfer(json_data: &Map<String, Value>, sender_hex: &str) {
    // Extract amount and receiver from JSON
    let amount = match json_data.get("amount")
        .and_then(|val| {
            if let Some(s) = val.as_str() {
                s.parse::<BigUint>().ok()
            } else if let Some(n) = val.as_u64() {
                Some(BigUint::from(n))
            } else {
                None
            }
        }) {
        Some(amt) => amt,
        None => {
            println!("Invalid or missing amount");
            return;
        }
    };
    
    let receiver_hex = match json_data.get("receiver")
        .and_then(|val| val.as_str()) {
        Some(r) => r,
        None => {
            println!("Missing receiver");
            return;
        }
    };
    
    // Decode hex addresses
    let sender_address = if sender_hex.starts_with("0x") { &sender_hex[2..] } else { sender_hex };
    let receiver_address = if receiver_hex.starts_with("0x") { &receiver_hex[2..] } else { receiver_hex };

    let sender = hex::decode(sender_address).unwrap_or_default();
    let receiver = hex::decode(receiver_address).unwrap_or_default();
    
    // Execute transfer
    match DatabaseService::transfer(&sender, &receiver, &amount) {
        Ok(true) => {
            println!("Transfer succeeded: {} from {} to {}", amount, sender_hex, receiver_hex);
        }
        Ok(false) => {
            println!("Transfer failed (insufficient funds): {} from {} to {}", amount, sender_hex, receiver_hex);
        }
        Err(_) => {
            println!("Transfer operation failed");
        }
    }
}

// Processes a single VIDA transaction
fn process_transaction(txn: VidaDataTransaction) {
    let data_bytes = txn.data;
    
    // Parse JSON data
    let data_str = match String::from_utf8(data_bytes) {
        Ok(s) => s,
        Err(_) => {
            println!("Error decoding transaction data");
            return;
        }
    };
    
    let json_data: Value = match serde_json::from_str(&data_str) {
        Ok(json) => json,
        Err(_) => {
            println!("Error parsing transaction JSON");
            return;
        }
    };
    
    if let Some(obj_map) = json_data.as_object() {
        let action = obj_map.get("action")
            .and_then(|val| val.as_str())
            .unwrap_or("");
        
        if action.to_lowercase() == "transfer" {
            handle_transfer(obj_map, &txn.sender);
        }
    }
}

// Callback invoked as blocks are processed
async fn on_chain_progress(block_number: u64) {
    DatabaseService::set_last_checked_block(block_number).unwrap();
    check_root_hash_validity_and_save(block_number).await;
    println!("Checkpoint updated to block {}", block_number);
    DatabaseService::flush().map_err(|e| format!("Failed to flush database: {:?}", e)).unwrap();
}

// Subscribes to VIDA transactions starting from the given block
pub async fn subscribe_and_sync(from_block: u64) -> Result<(), Box<dyn std::error::Error>> {
    println!("Starting VIDA transaction subscription from block {}", from_block);
    
    // Initialize RPC client
    let rpc = RPC::new(RPC_URL).await.map_err(|e| format!("Failed to create RPC client: {:?}", e))?;
    let rpc = Arc::new(rpc);
    
    let block_saver = block_saver::from_async(on_chain_progress);
    // Subscribe to VIDA transactions
    unsafe {
        subscription = Some(rpc.subscribe_to_vida_transactions(
            VIDA_ID,
            from_block,
            process_transaction,
            Some(block_saver)
        ));
    }
    
    println!("Successfully subscribed to VIDA {} transactions", VIDA_ID);
    Ok(())
}
```

{% endtab %}

{% tab title="Go" %}

```go
// handler.go
package main

import (
    "encoding/hex"
    "encoding/json"
    "fmt"
    "io"
    "math/big"
    "net/http"
    "strings"
    "time"

    "pwr-stateful-vida/dbservice"
    "github.com/pwrlabs/pwrgo/rpc"
)

var subscription *rpc.VidaTransactionSubscription
var peersToCheckRootHashWith []string

// fetchPeerRootHash fetches the root hash from a peer node for the specified block number
func fetchPeerRootHash(peer string, blockNumber int) (bool, []byte) {
    url := fmt.Sprintf("http://%s/rootHash?blockNumber=%d", peer, blockNumber)

    client := &http.Client{Timeout: 10 * time.Second}
    resp, err := client.Get(url)
    if err != nil {
        fmt.Printf("Failed to fetch root hash from peer %s for block %d\n", peer, blockNumber)
        return false, nil
    }
    defer resp.Body.Close()

    if resp.StatusCode == 200 {
        body, _ := io.ReadAll(resp.Body)
        hexString := strings.TrimSpace(string(body))

        if hexString == "" {
            fmt.Printf("Peer %s returned empty root hash for block %d\n", peer, blockNumber)
            return false, nil
        }

        rootHash, err := hex.DecodeString(hexString)
        if err != nil {
            fmt.Printf("Invalid hex response from peer %s for block %d\n", peer, blockNumber)
            return false, nil
        }

        fmt.Printf("Successfully fetched root hash from peer %s for block %d\n", peer, blockNumber)
        return true, rootHash
    } else {
        fmt.Printf("Peer %s returned HTTP %d for block %d\n", peer, resp.StatusCode, blockNumber)
        return true, nil
    }
}

// checkRootHashValidityAndSave validates the local Merkle root against peers and persists it if a quorum of peers agree
func checkRootHashValidityAndSave(blockNumber int) {
    localRoot, _ := dbservice.GetRootHash()
    if localRoot == nil {
        fmt.Printf("No local root hash available for block %d\n", blockNumber)
        return
    }

    peersCount := len(peersToCheckRootHashWith)
    quorum := (peersCount*2)/3 + 1
    matches := 0

    for _, peer := range peersToCheckRootHashWith {
        success, peerRoot := fetchPeerRootHash(peer, blockNumber)

        if success && peerRoot != nil {
            if string(peerRoot) == string(localRoot) {
                matches++
            }
        } else {
            peersCount--
            quorum = (peersCount*2)/3 + 1
        }

        if matches >= quorum {
            dbservice.SetBlockRootHash(blockNumber, localRoot)
            fmt.Printf("Root hash validated and saved for block %d\n", blockNumber)
            return
        }
    }

    fmt.Printf("Root hash mismatch: only %d/%d peers agreed\n", matches, len(peersToCheckRootHashWith))

    // Revert changes and reset block to reprocess the data
    dbservice.RevertUnsavedChanges()
    lastCheckedBlock, _ := dbservice.GetLastCheckedBlock()
    subscription.SetLatestCheckedBlock(int(lastCheckedBlock))
}

// handleTransfer executes a token transfer described by the given JSON payload
func handleTransfer(jsonData map[string]interface{}, senderHex string) {
    // Extract amount and receiver from JSON
    amountRaw := jsonData["amount"]
    receiverHex, _ := jsonData["receiver"].(string)

    if amountRaw == nil || receiverHex == "" {
        fmt.Printf("Skipping invalid transfer: %v\n", jsonData)
        return
    }

    // Convert amount to big.Int
    var amount *big.Int
    switch v := amountRaw.(type) {
    case string:
        amount, _ = new(big.Int).SetString(v, 10)
    case float64:
        amount = big.NewInt(int64(v))
    default:
        fmt.Printf("Invalid amount type: %v\n", jsonData)
        return
    }

    // Decode hex addresses
    senderAddress := strings.TrimPrefix(senderHex, "0x")
    receiverAddress := strings.TrimPrefix(receiverHex, "0x")

    sender, _ := hex.DecodeString(senderAddress)
    receiver, _ := hex.DecodeString(receiverAddress)

    // Execute transfer
    success, _ := dbservice.Transfer(sender, receiver, amount)

    if success {
        fmt.Printf("Transfer succeeded: %s from %s to %s\n", amount, senderHex, receiverHex)
    } else {
        fmt.Printf("Transfer failed (insufficient funds): %s from %s to %s\n", amount, senderHex, receiverHex)
    }
}

// processTransaction processes a single VIDA transaction
func processTransaction(transaction rpc.VidaDataTransaction) {
    // Get transaction data and convert from hex to bytes
    dataBytes, _ := hex.DecodeString(transaction.Data)

    // Parse JSON data
    var jsonData map[string]interface{}
    json.Unmarshal(dataBytes, &jsonData)

    // Get action from JSON
    action, _ := jsonData["action"].(string)

    if strings.ToLower(action) == "transfer" {
        handleTransfer(jsonData, transaction.Sender)
    }
}

// onChainProgress callback invoked as blocks are processed
func onChainProgress(blockNumber int) error {
    dbservice.SetLastCheckedBlock(blockNumber)
    checkRootHashValidityAndSave(blockNumber)
    fmt.Printf("Checkpoint updated to block %d\n", blockNumber)
    dbservice.Flush()

    return nil
}

// subscribeAndSync subscribes to VIDA transactions starting from the given block
func subscribeAndSync(fromBlock int) {
    fmt.Printf("Starting VIDA transaction subscription from block %d\n", fromBlock)

    // Initialize RPC client
    rpcClient := rpc.SetRpcNodeUrl(RPC_URL)

    subscription = rpcClient.SubscribeToVidaTransactions(
        VIDA_ID,
        fromBlock,
        processTransaction,
        onChainProgress,
    )

    fmt.Printf("Successfully subscribed to VIDA %d transactions\n", VIDA_ID)
}
```

{% endtab %}

{% tab title="C#" %}

```csharp
// Handler.cs
using System;
using System.Numerics;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json.Linq;
using PWR;
using PWR.Models;
using PWR.Utils;

namespace PwrStatefulVIDA;

public class Handler
{
    // Constants
    private const ulong VIDA_ID = 73746238;
    private const string RPC_URL = "https://pwrrpc.pwrlabs.io/";

    private static RPC? pwrClient;
    private static VidaTransactionSubscription? subscription;
    // Global state
    public static List<string> peersToCheckRootHashWith = new();

    private static async Task<(bool success, byte[]? rootHash)> FetchPeerRootHash(
        HttpClient client, string peer, ulong blockNumber)
    {
        var url = $"http://{peer}/rootHash?blockNumber={blockNumber}";

        try
        {
            var response = await client.GetAsync(url);

            if (response.IsSuccessStatusCode)
            {
                var hexString = await response.Content.ReadAsStringAsync();
                var trimmed = hexString.Trim();

                if (string.IsNullOrEmpty(trimmed))
                {
                    Console.WriteLine($"Peer {peer} returned empty root hash for block {blockNumber}");
                    return (false, null);
                }

                try
                {
                    var rootHash = PWR.Utils.Extensions.HexStringToByteArray(trimmed);
                    Console.WriteLine($"Successfully fetched root hash from peer {peer} for block {blockNumber}");
                    return (true, rootHash);
                }
                catch (Exception)
                {
                    Console.WriteLine($"Invalid hex response from peer {peer} for block {blockNumber}");
                    return (false, null);
                }
            }
            else
            {
                Console.WriteLine($"Peer {peer} returned HTTP {response.StatusCode} for block {blockNumber}");
                return (true, null);
            }
        }
        catch (Exception)
        {
            Console.WriteLine($"Failed to fetch root hash from peer {peer} for block {blockNumber}");
            return (false, null);
        }
    }

    private static async Task CheckRootHashValidityAndSave(ulong blockNumber)
    {
        var localRoot = DatabaseService.GetRootHash();

        if (localRoot == null)
        {
            Console.WriteLine($"No local root hash available for block {blockNumber}");
            return;
        }

        int peersCount = peersToCheckRootHashWith.Count;
        int quorum = (peersCount * 2) / 3 + 1;
        int matches = 0;

        using var httpClient = new HttpClient { Timeout = TimeSpan.FromSeconds(10) };

        foreach (var peer in peersToCheckRootHashWith)
        {
            var (success, peerRoot) = await FetchPeerRootHash(httpClient, peer, blockNumber);

            if (success && peerRoot != null)
            {
                if (localRoot.SequenceEqual(peerRoot))
                {
                    matches++;
                }
            }
            else
            {
                if (peersCount > 0)
                {
                    peersCount--;
                    quorum = (peersCount * 2) / 3 + 1;
                }
            }

            if (matches >= quorum)
            {
                DatabaseService.SetBlockRootHash(blockNumber, localRoot);
                Console.WriteLine($"Root hash validated and saved for block {blockNumber}");
                return;
            }
        }

        Console.WriteLine($"Root hash mismatch: only {matches}/{peersToCheckRootHashWith.Count} peers agreed");
        DatabaseService.RevertUnsavedChanges();
        subscription.SetLatestCheckedBlock(DatabaseService.GetLastCheckedBlock());
    }

    private static void HandleTransfer(JObject jsonData, string senderHex)
    {
        var amountToken = jsonData["amount"];
        BigInteger amount;

        if (amountToken?.Type == JTokenType.String)
        {
            amount = BigInteger.Parse(amountToken.ToString());
        }
        else if (amountToken?.Type == JTokenType.Integer)
        {
            amount = new BigInteger((long)amountToken);
        }
        else
        {
            Console.WriteLine("Invalid or missing amount");
            return;
        }

        var receiverHex = jsonData["receiver"]?.ToString();
        if (string.IsNullOrEmpty(receiverHex))
        {
            Console.WriteLine("Missing receiver");
            return;
        }

        var senderAddress = senderHex.StartsWith("0x") ? senderHex[2..] : senderHex;
        var receiverAddress = receiverHex.StartsWith("0x") ? receiverHex[2..] : receiverHex;

        var sender = PWR.Utils.Extensions.HexStringToByteArray(senderAddress);
        var receiver = PWR.Utils.Extensions.HexStringToByteArray(receiverAddress);

        var success = DatabaseService.Transfer(sender, receiver, amount);

        if (success)
        {
            Console.WriteLine($"Transfer succeeded: {amount} from {senderHex} to {receiverHex}");
        }
        else
        {
            Console.WriteLine($"Transfer failed (insufficient funds): {amount} from {senderHex} to {receiverHex}");
        }
    }

    private static void ProcessTransaction(VidaDataTransaction txn)
    {
        try
        {
            var dataBytes = PWR.Utils.Extensions.HexStringToByteArray(txn.Data);
            var dataStr = Encoding.UTF8.GetString(dataBytes);
            var jsonData = JObject.Parse(dataStr);

            var action = jsonData["action"]?.ToString() ?? "";

            if (action.Equals("transfer", StringComparison.OrdinalIgnoreCase))
            {
                HandleTransfer(jsonData, txn.Sender);
            }
        }
        catch (Exception error)
        {
            Console.WriteLine($"Error processing transaction: {error.Message}");
        }
    }

    private static async Task OnChainProgress(ulong blockNumber)
    {
        DatabaseService.SetLastCheckedBlock(blockNumber);
        await CheckRootHashValidityAndSave(blockNumber);
        Console.WriteLine($"Checkpoint updated to block {blockNumber}");
        DatabaseService.Flush();
    }

    public static async Task SubscribeAndSync(ulong fromBlock)
    {
        Console.WriteLine($"Starting VIDA transaction subscription from block {fromBlock}");

        pwrClient = new RPC(RPC_URL);

        subscription = pwrClient.SubscribeToVidaTransactions(
            VIDA_ID,
            fromBlock,
            ProcessTransaction,
            OnChainProgress
        );
        Console.WriteLine($"Successfully subscribed to VIDA {VIDA_ID} transactions");
    }

}
```

{% endtab %}

{% tab title="Java" %}

```java
// Handler.java
package main;

import com.github.pwrlabs.pwrj.entities.FalconTransaction;
import com.github.pwrlabs.pwrj.protocol.PWRJ;
import com.github.pwrlabs.pwrj.protocol.VidaTransactionSubscription;
import io.pwrlabs.util.encoders.BiResult;
import org.bouncycastle.util.encoders.Hex;
import org.json.JSONObject;
import org.rocksdb.RocksDBException;

import java.io.IOException;
import java.math.BigInteger;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.Arrays;
import java.util.logging.Level;
import java.util.logging.Logger;


public class Handler {
    private static final Logger LOGGER = Logger.getLogger(Main.class.getName());
    private static final long VIDA_ID = 73_746_238L;
    private static final PWRJ PWRJ_CLIENT = new PWRJ("https://pwrrpc.pwrlabs.io/");
    private static VidaTransactionSubscription subscription;
    private static final HttpClient HTTP_CLIENT = HttpClient.newHttpClient();
    private static final Duration REQUEST_TIMEOUT = Duration.ofSeconds(10);

    /**
    * Fetches the root hash from a peer node for the specified block number.
    *
    * @param peer the peer hostname/address
    * @param blockNumber the block number to query
    * @return BiResult where first element indicates successful connection, second element contains the root hash bytes
    */
    private static BiResult<Boolean /*Replied*/, byte[]> fetchPeerRootHash(String peer, long blockNumber) {
        try {
            // Build the URL for the peer's rootHash endpoint
            String url = "http://" + peer + "/rootHash?blockNumber=" + blockNumber;

            // Create the HTTP request
            HttpRequest request = HttpRequest.newBuilder()
                    .uri(URI.create(url))
                    .GET()
                    .timeout(REQUEST_TIMEOUT)
                    .header("Accept", "text/plain")
                    .build();

            // Send the request and get response
            HttpResponse<String> response = HTTP_CLIENT.send(request, HttpResponse.BodyHandlers.ofString());

            // Check if the response was successful
            if (response.statusCode() == 200) {
                String hexString = response.body().trim();

                // Validate that we received a non-empty hex string
                if (hexString.isEmpty()) {
                    LOGGER.warning("Peer " + peer + " returned empty root hash for block " + blockNumber);
                    return new BiResult<>(false, new byte[0]);
                }

                // Decode the hex string to bytes
                byte[] rootHash = Hex.decode(hexString);

                LOGGER.fine("Successfully fetched root hash from peer " + peer + " for block " + blockNumber);
                return new BiResult<>(true, rootHash);

            } else {
                LOGGER.warning("Peer " + peer + " returned HTTP " + response.statusCode() +
                        " for block " + blockNumber + ": " + response.body());
                return new BiResult<>(true, new byte[0]);
            }

        } catch (IllegalArgumentException e) {
            LOGGER.warning("Invalid hex response from peer " + peer + " for block " + blockNumber + ": " + e.getMessage());
            return new BiResult<>(false, new byte[0]);
        } catch (Exception e) {
            LOGGER.log(Level.WARNING, "Failed to fetch root hash from peer " + peer + " for block " + blockNumber, e);
            return new BiResult<>(false, new byte[0]);
        }
    }

    /**
    * Validates the local Merkle root against peers and persists it if a quorum
    * of peers agree.
    *
    * @param blockNumber block height being validated
    */
    private static void checkRootHashValidityAndSave(long blockNumber) {
        try {
            byte[] localRoot = DatabaseService.getRootHash();
            int peersCount = Main.peersToCheckRootHashWith.size();
            long quorum = (peersCount * 2) / 3 + 1;
            int matches = 0;
            for (String peer : Main.peersToCheckRootHashWith) {
                // TODO: fetch peer root via RPC and compare
                BiResult<Boolean /**/, byte[]> response = fetchPeerRootHash(peer, blockNumber);
                if(response.getFirst()) {
                    if(Arrays.equals(response.getSecond(), localRoot)) {
                        matches++;
                    }
                } else {
                    --peersCount;
                    quorum = (peersCount * 2) / 3 + 1;
                }

                if (matches >= quorum) {
                    DatabaseService.setBlockRootHash(blockNumber, localRoot);
                    LOGGER.info("Root hash validated and saved for block " + blockNumber);
                    return;
                }
            }

            LOGGER.severe("Root hash mismatch: only " + matches + "/" + Main.peersToCheckRootHashWith.size());
            //Revert changes and reset block to reprocess the data
            DatabaseService.revertUnsavedChanges();
            subscription.setLatestCheckedBlock(DatabaseService.getLastCheckedBlock());
        } catch (Exception e) {
            LOGGER.log(Level.SEVERE, "Error verifying root hash at block " + blockNumber, e);
        }
    }

    /**
    * Executes a token transfer described by the given JSON payload.
    *
    * @param json       transfer description
    * @param senderHex  hexadecimal sender address
    * @throws RocksDBException if balance updates fail
    */
    private static void handleTransfer(JSONObject json, String senderHex) throws RocksDBException {
        BigInteger amount = json.optBigInteger("amount", null);
        String receiverHex = json.optString("receiver", null);
        if (amount == null || receiverHex == null) {
            LOGGER.warning("Skipping invalid transfer: " + json);
            return;
        }

        String senderAddress = senderHex.startsWith("0x") ? senderHex.substring(2) : senderHex;
        String receiverAddress = receiverHex.startsWith("0x") ? receiverHex.substring(2) : receiverHex;

        byte[] sender = Hex.decode(senderAddress);
        byte[] receiver = Hex.decode(receiverAddress);

        boolean success = DatabaseService.transfer(sender, receiver, amount);
        if (success) {
            LOGGER.info("Transfer succeeded: " + json);
        } else {
            LOGGER.warning("Transfer failed (insufficient funds): " + json);
        }
    }

    /**
    * Processes a single VIDA transaction.
    *
    * @param txn the transaction to handle
    */
    private static void processTransaction(FalconTransaction.PayableVidaDataTxn txn) {
        try {
            JSONObject json = new JSONObject(new String(txn.getData(), StandardCharsets.UTF_8));
            String action = json.optString("action", "");
            if ("transfer".equalsIgnoreCase(action)) {
                handleTransfer(json, txn.getSender());
            }
        } catch (Exception e) {
            LOGGER.log(Level.SEVERE, "Error processing transaction: " + txn.getTransactionHash(), e);
        }
    }

    /**
    * Callback invoked as blocks are processed.
    *
    * @param blockNumber block height that was just processed
    * @return always {@code null}
    */
    private static Void onChainProgress(long blockNumber) {
        try {
            DatabaseService.setLastCheckedBlock(blockNumber);
            checkRootHashValidityAndSave(blockNumber);
            LOGGER.info("Checkpoint updated to block " + blockNumber);
        } catch (RocksDBException e) {
            LOGGER.log(Level.WARNING, "Failed to update last checked block: " + blockNumber, e);
        } finally {
            return null;
        }
    }

    /**
    * Subscribes to VIDA transactions starting from the given block.
    *
    * @param fromBlock block height to begin synchronization from
    * @throws IOException if network communication fails
    * @throws RocksDBException if persisting data fails
    */
    public static void subscribeAndSync(long fromBlock) throws IOException, RocksDBException {
        //The subscription to VIDA transactions has a built in shutdwown hook
        subscription =
                PWRJ_CLIENT.subscribeToVidaTransactions(
                        PWRJ_CLIENT,
                        VIDA_ID,
                        fromBlock,
                        Handler::onChainProgress,
                        Handler::processTransaction
                );
    }
}
```

{% endtab %}
{% endtabs %}

#### 5. Main Application Logic <a href="#id-5-main-application-logic" id="id-5-main-application-logic"></a>

Implement the main application logic for your stateful VIDA. This includes initializing the database, setting up the API server, configuring peers, and starting the transaction synchronization:

{% tabs %}
{% tab title="JavaScript" %}

```javascript
// main.js
import express from 'express';
import { GET } from './api/get.js';
import DatabaseService from './databaseService.js';
import { subscribeAndSync, peersToCheckRootHashWith } from './handler.js';

const START_BLOCK = 1;
const PORT = 8080;

const INITIAL_BALANCES = new Map([
    [Buffer.from("c767ea1d613eefe0ce1610b18cb047881bafb829", 'hex'), 1000000000000n],
    [Buffer.from("3b4412f57828d1ceb0dbf0d460f7eb1f21fed8b4", 'hex'), 1000000000000n],
    [Buffer.from("9282d39ca205806473f4fde5bac48ca6dfb9d300", 'hex'), 1000000000000n],
    [Buffer.from("e68191b7913e72e6f1759531fbfaa089ff02308a", 'hex'), 1000000000000n],
]);

let app = null;

// Initializes peer list from arguments or defaults
function initializePeers() {
    const args = process.argv.slice(2);
    
    if (args.length > 0) {
        peersToCheckRootHashWith.length = 0;
        peersToCheckRootHashWith.push(...args);
        console.log("Using peers from args:", peersToCheckRootHashWith);
    } else {
        peersToCheckRootHashWith.length = 0;
        peersToCheckRootHashWith.push("localhost:8080");
        console.log("Using default peers:", peersToCheckRootHashWith);
    }
}

// Sets up the initial account balances when starting from a fresh database
async function initInitialBalances() {
    const lastCheckedBlock = await DatabaseService.getLastCheckedBlock();
    
    if (lastCheckedBlock === 0) {
        console.log("Setting up initial balances for fresh database");
        
        for (const [address, balance] of INITIAL_BALANCES) {
            await DatabaseService.setBalance(address, balance);
            console.log(`Set initial balance for ${address.toString('hex')}: ${balance}`);
        }
        console.log("Initial balances setup completed");
    }
}

// Start the API server in a background task
async function startApiServer() {
    app = express();
    
    GET.run(app);
    
    return new Promise((resolve, reject) => {
        const server = app.listen(PORT, '0.0.0.0', (err) => {
            if (err) {
                reject(err);
            } else {
                console.log(`Starting API server on port ${PORT}`);
                setTimeout(() => {
                    console.log(`API server started on http://0.0.0.0:${PORT}`);
                    resolve(server);
                }, 2000);
            }
        });
    });
}

// Sets up shutdown handlers for graceful shutdown
function setupShutdownHandlers() {
    const gracefulShutdown = async (signal) => {
        console.log(`Received ${signal}, shutting down gracefully...`);
        process.exit(0);
    };
    
    process.on('SIGINT', gracefulShutdown);
    process.on('SIGTERM', gracefulShutdown);
}

// Application entry point for synchronizing VIDA transactions
// with the local Merkle-backed database.
async function main() {
    console.log("Starting PWR VIDA Transaction Synchronizer...");

    initializePeers();
    await DatabaseService.initialize();
    await startApiServer();
    await initInitialBalances();

    const lastBlock = await DatabaseService.getLastCheckedBlock();
    const fromBlock = lastBlock > 0 ? lastBlock : START_BLOCK;

    console.log(`Starting synchronization from block ${fromBlock}`);

    await subscribeAndSync(fromBlock);

    // Keep the main thread alive
    console.log("Application started successfully. Press Ctrl+C to exit.");
    // Graceful shutdown
    setupShutdownHandlers();
}

main().catch(console.error);
```

{% endtab %}

{% tab title="Python" %}

```python
# main.py
import sys
import threading
import time
from database_service import DatabaseService
from api.get import app as api_app
from handler import subscribe_and_sync, peers_to_check_root_hash_with

START_BLOCK = 1
PORT = 8080

INITIAL_BALANCES = {
    bytes.fromhex("c767ea1d613eefe0ce1610b18cb047881bafb829"): 1_000_000_000_000,
    bytes.fromhex("3b4412f57828d1ceb0dbf0d460f7eb1f21fed8b4"): 1_000_000_000_000,
    bytes.fromhex("9282d39ca205806473f4fde5bac48ca6dfb9d300"): 1_000_000_000_000,
    bytes.fromhex("e68191b7913e72e6f1759531fbfaa089ff02308a"): 1_000_000_000_000,
}

flask_thread = None

# Initializes peer list from arguments or defaults
def initialize_peers():
    if len(sys.argv) > 1:
        peers_to_check_root_hash_with.clear()
        peers_to_check_root_hash_with.extend(sys.argv[1:])
        print(f"Using peers from args: {peers_to_check_root_hash_with}")
    else:
        peers_to_check_root_hash_with.clear()
        peers_to_check_root_hash_with.extend([
            "localhost:8080"
        ])
        print(f"Using default peers: {peers_to_check_root_hash_with}")

# Sets up the initial account balances when starting from a fresh database
def init_initial_balances():
    if DatabaseService.get_last_checked_block() == 0:
        print("Setting up initial balances for fresh database")
        
        for address, balance in INITIAL_BALANCES.items():
            DatabaseService.set_balance(address, balance)
            print(f"Set initial balance for {address.hex()}: {balance}")
        print("Initial balances setup completed")

# Start the API server in a background task
def start_api_server():
    global flask_thread
    
    def run_flask():
        try:
            print(f"Starting Flask API server on port {PORT}")
            
            # Disable Flask request logging
            import logging
            logging.getLogger('werkzeug').setLevel(logging.ERROR)
            
            api_app.run(host='0.0.0.0', port=PORT, debug=False, use_reloader=False)
        except Exception as e:
            print(f"Flask server error: {e}")
    
    flask_thread = threading.Thread(target=run_flask, daemon=True)
    flask_thread.start()
    
    time.sleep(2)
    print(f"Flask API server started on http://0.0.0.0:{PORT}")

# Application entry point for synchronizing VIDA transactions
# with the local Merkle-backed database.
def main():
    print("Starting PWR VIDA Transaction Synchronizer...")
    
    initialize_peers()
    start_api_server()
    init_initial_balances()
    
    last_block = DatabaseService.get_last_checked_block()
    from_block = last_block if last_block > 0 else START_BLOCK
    
    print(f"Starting synchronization from block {from_block}")
    
    subscribe_and_sync(from_block)

if __name__ == "__main__":
    main()
```

{% endtab %}

{% tab title="Rust" %}

```rust
// src/main.rs
mod database_service;
mod api;
mod handler;

use std::env;
use std::time::Duration;
use hex;
use num_bigint::BigUint;
use tokio::time::sleep;

use crate::database_service::DatabaseService;
use crate::api::GET;
use crate::handler::{subscribe_and_sync, PEERS_TO_CHECK_ROOT_HASH_WITH};

// Constants
const START_BLOCK: u64 = 1;
const PORT: u16 = 8080;

// Initializes peer list from arguments or defaults
fn initialize_peers() {
    let args: Vec<String> = env::args().collect();
    
    unsafe {
        if args.len() > 1 {
            PEERS_TO_CHECK_ROOT_HASH_WITH = args[1..].to_vec();
            println!("Using peers from args: {:?}", PEERS_TO_CHECK_ROOT_HASH_WITH);
        } else {
            PEERS_TO_CHECK_ROOT_HASH_WITH = vec![
                "localhost:8080".to_string(),
            ];
            println!("Using default peers: {:?}", PEERS_TO_CHECK_ROOT_HASH_WITH);
        }
    }
}

// Sets up the initial account balances when starting from a fresh database
async fn init_initial_balances() -> Result<(), Box<dyn std::error::Error>> {
    if DatabaseService::get_last_checked_block().map_err(|e| format!("Failed to get last checked block: {:?}", e))? == 0 {
        println!("Setting up initial balances for fresh database");
        
        let initial_balances = vec![
            (hex::decode("c767ea1d613eefe0ce1610b18cb047881bafb829").unwrap(), BigUint::from(1_000_000_000_000u64)),
            (hex::decode("3b4412f57828d1ceb0dbf0d460f7eb1f21fed8b4").unwrap(), BigUint::from(1_000_000_000_000u64)),
            (hex::decode("9282d39ca205806473f4fde5bac48ca6dfb9d300").unwrap(), BigUint::from(1_000_000_000_000u64)),
            (hex::decode("e68191b7913e72e6f1759531fbfaa089ff02308a").unwrap(), BigUint::from(1_000_000_000_000u64)),
        ];
        
        for (address, balance) in initial_balances {
            DatabaseService::set_balance(&address, &balance).map_err(|e| format!("Failed to set balance: {:?}", e))?;
            println!("Set initial balance for {}: {}", hex::encode(&address), balance);
        }
        println!("Initial balances setup completed");
    }
    
    Ok(())
}

/// Start the API server in a background task
async fn start_api_server() {
    let routes = GET::run();
    
    tokio::spawn(async move {
        println!("Starting API server on port {}", PORT);
        warp::serve(routes)
            .run(([0, 0, 0, 0], PORT))
            .await;
    });
    
    // Give server time to start
    sleep(Duration::from_millis(2000)).await;
    println!("API server started on http://0.0.0.0:{}", PORT);
}

/// Application entry point for synchronizing VIDA transactions
/// with the local Merkle-backed database.
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    println!("Starting PWR VIDA Transaction Synchronizer...");

    initialize_peers();
    DatabaseService::initialize().map_err(|e| format!("Database initialization failed: {:?}", e))?;

    start_api_server().await;
    init_initial_balances().await?;

    let last_block = DatabaseService::get_last_checked_block().map_err(|e| format!("Failed to get last checked block: {:?}", e))?;
    let from_block = if last_block > 0 { last_block } else { START_BLOCK };

    println!("Starting synchronization from block {}", from_block);

    subscribe_and_sync(from_block).await?;

    // Keep the main thread alive
    println!("Application started successfully. Press Ctrl+C to exit.");
    tokio::signal::ctrl_c().await?;

    Ok(())
}
```

{% endtab %}

{% tab title="Go" %}

```go
// main.go
package main

import (
    "encoding/hex"
    "fmt"
    "math/big"
    "os"
    "os/signal"
    "syscall"

    "pwr-stateful-vida/api"
    "pwr-stateful-vida/dbservice"
    "github.com/gin-gonic/gin"
)

// Constants
const (
    VIDA_ID     = 73746238
    START_BLOCK = 1
    PORT        = 8080
    RPC_URL     = "https://pwrrpc.pwrlabs.io"
)

// initializePeers initializes peer list from arguments or defaults
func initializePeers() {
    if len(os.Args) > 1 {
        peersToCheckRootHashWith = os.Args[1:]
        fmt.Printf("Using peers from args: %v\n", peersToCheckRootHashWith)
    } else {
        peersToCheckRootHashWith = []string{"localhost:8080"}
        fmt.Printf("Using default peers: %v\n", peersToCheckRootHashWith)
    }
}

// initInitialBalances sets up the initial account balances when starting from a fresh database
func initInitialBalances() {
    lastBlock, _ := dbservice.GetLastCheckedBlock()
    if lastBlock == 0 {
        fmt.Println("Setting up initial balances for fresh database")

        initialBalances := map[string]*big.Int{
            "c767ea1d613eefe0ce1610b18cb047881bafb829": big.NewInt(1000000000000),
            "3b4412f57828d1ceb0dbf0d460f7eb1f21fed8b4": big.NewInt(1000000000000),
            "9282d39ca205806473f4fde5bac48ca6dfb9d300": big.NewInt(1000000000000),
            "e68191b7913e72e6f1759531fbfaa089ff02308a": big.NewInt(1000000000000),
        }

        for addressHex, balance := range initialBalances {
            address, _ := hex.DecodeString(addressHex)
            dbservice.SetBalance(address, balance)
        }
        fmt.Println("Initial balances setup completed")
    }
}

// startAPIServer initializes and starts the HTTP API server
func startAPIServer() {
    gin.SetMode(gin.ReleaseMode)
    router := gin.New()
    api.RegisterRoutes(router)

    fmt.Printf("Starting HTTP server on port %d\n", PORT)
    router.Run(fmt.Sprintf(":%d", PORT))
}

// main is the application entry point for synchronizing VIDA transactions
func main() {
    fmt.Println("Starting PWR VIDA Transaction Synchronizer...")

    // Initialize peers from command line arguments
    initializePeers()

    // Set up HTTP API server
    go startAPIServer()

    // Initialize database with initial balances if needed
    initInitialBalances()

    // Get starting block number
    lastBlock, _ := dbservice.GetLastCheckedBlock()
    fromBlock := START_BLOCK
    if lastBlock > 0 {
        fromBlock = int(lastBlock)
    }

    fmt.Printf("Starting synchronization from block %d\n", fromBlock)

    // Subscribe to VIDA transactions
    subscribeAndSync(fromBlock)

    // Keep the main thread alive
    fmt.Println("Application started successfully. Press Ctrl+C to exit.")
    c := make(chan os.Signal, 1)
    signal.Notify(c, os.Interrupt, syscall.SIGTERM)
    <-c
}
```

{% endtab %}

{% tab title="C#" %}

```csharp
// Program.cs
using System;
using System.Numerics;

namespace PwrStatefulVIDA;

public class Program
{
    // Constants
    private const ulong START_BLOCK = 1;
    private const int PORT = 8080;
    
    // Initial balances for fresh database
    private static readonly Dictionary<byte[], BigInteger> INITIAL_BALANCES = new()
    {
        { Convert.FromHexString("c767ea1d613eefe0ce1610b18cb047881bafb829"), new BigInteger(1000000000000) },
        { Convert.FromHexString("3b4412f57828d1ceb0dbf0d460f7eb1f21fed8b4"), new BigInteger(1000000000000) },
        { Convert.FromHexString("9282d39ca205806473f4fde5bac48ca6dfb9d300"), new BigInteger(1000000000000) },
        { Convert.FromHexString("e68191b7913e72e6f1759531fbfaa089ff02308a"), new BigInteger(1000000000000) },
    };

    private static void InitializePeers(string[] args)
    {
        if (args.Length > 0)
        {
            Handler.peersToCheckRootHashWith = args.ToList();
            Console.WriteLine($"Using peers from args: [{string.Join(", ", Handler.peersToCheckRootHashWith)}]");
        }
        else
        {
            Handler.peersToCheckRootHashWith = new List<string> { "localhost:8080" };
            Console.WriteLine($"Using default peers: [{string.Join(", ", Handler.peersToCheckRootHashWith)}]");
        }
    }

    private static void InitInitialBalances()
    {
        var lastCheckedBlock = DatabaseService.GetLastCheckedBlock();

        if (lastCheckedBlock == 0)
        {
            Console.WriteLine("Setting up initial balances for fresh database");

            foreach (var (address, balance) in INITIAL_BALANCES)
            {
                DatabaseService.SetBalance(address, balance);
                Console.WriteLine($"Set initial balance for {Convert.ToHexString(address).ToLowerInvariant()}: {balance}");
            }
            Console.WriteLine("Initial balances setup completed");
        }
    }

    private static async Task StartApiServer()
    {
        var builder = WebApplication.CreateBuilder();
        builder.WebHost.UseUrls($"http://0.0.0.0:{PORT}");
        
        // Disable ASP.NET Core request logging
        builder.Logging.ClearProviders();
        builder.Logging.SetMinimumLevel(LogLevel.Warning);
        
        var app = builder.Build();
        GET.Run(app);

        _ = Task.Run(async () =>
        {
            Console.WriteLine($"Starting API server on port {PORT}");
            await app.RunAsync();
        });

        await Task.Delay(2000);
        Console.WriteLine($"API server started on http://0.0.0.0:{PORT}");
    }

    public static async Task Main(string[] args)
    {
        Console.WriteLine("Starting PWR VIDA Transaction Synchronizer...");
        
        InitializePeers(args);
        DatabaseService.Initialize();
        await StartApiServer();
        InitInitialBalances();
        
        var lastBlock = DatabaseService.GetLastCheckedBlock();
        var fromBlock = lastBlock > 0 ? lastBlock : START_BLOCK;
        
        Console.WriteLine($"Starting synchronization from block {fromBlock}");
        
        await Handler.SubscribeAndSync(fromBlock);
        
        // Keep the main thread alive
        Console.WriteLine("Application started successfully. Press Ctrl+C to exit.");
        Console.CancelKeyPress += (sender, e) =>
        {
            e.Cancel = true;
            Environment.Exit(0);
        };
        await Task.Delay(Timeout.Infinite);
    }
}
```

{% endtab %}

{% tab title="Java" %}

```java
// Main.java
package main;

import api.GET;
import org.bouncycastle.util.encoders.Hex;
import org.rocksdb.RocksDBException;

import java.io.IOException;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import static spark.Spark.port;

/**
* Entry point for synchronizing VIDA transactions with the local Merkle-backed database.
*/
public final class Main {
    private static final Logger LOGGER = Logger.getLogger(Main.class.getName());
    private static final long START_BLOCK = 1L;
    private static final int PORT = 8080;
    public static List<String> peersToCheckRootHashWith;

    /**
    * Initializes peer list from arguments or defaults.
    * @param args command-line arguments; if present, each arg is a peer hostname
    */
    private static void initializePeers(String[] args) {
        if (args != null && args.length > 0) {
            peersToCheckRootHashWith = Arrays.asList(args);
            LOGGER.info("Using peers from args: " + peersToCheckRootHashWith);
        } else {
            peersToCheckRootHashWith = List.of(
                    "localhost:8080"
            );
            LOGGER.info("Using default peers: " + peersToCheckRootHashWith);
        }
    }

    /**
    * Sets up the initial account balances when starting from a fresh database.
    *
    * @throws RocksDBException if persisting the balances fails
    */
    private static void initInitialBalances() throws RocksDBException {
        if(DatabaseService.getLastCheckedBlock() == 0) {
            DatabaseService.setBalance(Hex.decode("c767ea1d613eefe0ce1610b18cb047881bafb829"), BigInteger.valueOf(1_0000_000_000_000L));
            DatabaseService.setBalance(Hex.decode("3b4412f57828d1ceb0dbf0d460f7eb1f21fed8b4"), BigInteger.valueOf(1_0000_000_000_000L));
            DatabaseService.setBalance(Hex.decode("9282d39ca205806473f4fde5bac48ca6dfb9d300"), BigInteger.valueOf(1_0000_000_000_000L));
            DatabaseService.setBalance(Hex.decode("e68191b7913e72e6f1759531fbfaa089ff02308a"), BigInteger.valueOf(1_0000_000_000_000L));
        }
    }

    /**
    * Application entry point.
    *
    * @param args optional list of peer hosts to query for root hash
    */
    public static void main(String[] args) {
        try {
            port(PORT);
            GET.run();
            initInitialBalances();
            initializePeers(args);
            long lastBlock = DatabaseService.getLastCheckedBlock();
            long fromBlock = (lastBlock > 0) ? lastBlock : START_BLOCK;
            Handler.subscribeAndSync(fromBlock);
        } catch (IOException | RocksDBException e) {
            LOGGER.log(Level.SEVERE, "Initialization failed", e);
        }
    }
}
```

{% endtab %}
{% endtabs %}

#### 6. Run the project <a href="#id-6-run-the-project" id="id-6-run-the-project"></a>

To run the PWR Stateful VIDA project, add the following command:

{% tabs %}
{% tab title="JavaScript" %}

```bash
node main.js
```

{% endtab %}

{% tab title="Python" %}

```bash
python main.py
```

{% endtab %}

{% tab title="Rust" %}

```bash
cargo build --release
cargo run
```

{% endtab %}

{% tab title="Go" %}

```bash
go mod tidy
go run .
```

{% endtab %}

{% tab title="C#" %}

```bash
dotnet run
```

{% endtab %}

{% tab title="Java" %}

```bash
// Some code
```

{% endtab %}
{% endtabs %}

### Transaction Data Structure <a href="#transaction-data-structure" id="transaction-data-structure"></a>

Define the JSON schema for your stateful VIDA transactions:

**Transfer Transaction Example:**

```json
{
    "action": "transfer",
    "receiver": "0x3b4412f57828d1ceb0dbf0d460f7eb1f21fed8b4",
    "amount": 1000000000
}
```

### Best Practices for Stateful VIDAs <a href="#best-practices-for-stateful-vidas" id="best-practices-for-stateful-vidas"></a>

1. **Always Validate State**: Never trust local state without peer consensus
2. **Handle Consensus Failures**: Implement robust error recovery mechanisms
3. **Monitor Performance**: Track consensus time and database performance
4. **Secure Peer Communication**: Use HTTPS in production environments
5. **Backup Strategy**: Regular database backups for disaster recovery
6. **Gradual Rollouts**: Test thoroughly before deploying state changes

### Conclusion <a href="#conclusion" id="conclusion"></a>

Building stateful VIDAs requires careful consideration of data consistency, consensus mechanisms, and error handling. This tutorial provides a foundation for creating robust, production-ready applications that maintain critical state on the PWR Chain.

The example token transfer system demonstrates all key concepts you'll need for more complex stateful applications, including financial systems, voting mechanisms, and other use cases where data integrity is paramount.

Remember: stateful VIDAs trade simplicity for consistency and reliability. Choose this architecture when your application requires strong guarantees about data integrity and state consistency across network participants.

### Next Steps <a href="#next-steps" id="next-steps"></a>

While this guide focuses on conceptual foundations, future resources will include:

* **Video Tutorials**: Step-by-step walkthroughs for designing and deploying stateful VIDAs.
* **Code Examples**: Templates for root hash generation, cross-validation, and Conduit Node integration.

By combining PWR Chain’s immutable ledger with robust state management, stateful VIDAs empower developers to build decentralized applications that are as reliable as traditional enterprise software—but with unmatched transparency and security.


# How does PWR Chain Resolve Inconsistent VIDA Execution Results?

Anyone on PWR Chain can run a VIDA to independently verify its results (if the VIDA is public). However, inconsistent results may arise if VIDA instances are running different versions, encountering non-deterministic behavior, or facing unexpected issues.

VIDAs are responsible for implementing their own execution verification mechanisms. This can be achieved through cross-instance state verification or validation by Conduit Nodes, which confirm the results and publish their state root hash on the PWR Chain.

**Best Practices to Prevent Inconsistencies**:

* VIDAs must adhere to deterministic execution rules (e.g., avoiding floating-point operations or local system time).
* External data sources (e.g., APIs) must be validated and timestamped by Conduit Nodes before being used in VIDA logic.


# Inconsistent VIDA Results from Non-Deterministic Execution?

Non-deterministic execution is not allowed in a VIDA, randomness and other Non-deterministic practices make it impossible to create a consistent execution result across different VIDA Execution Instances.

If a VIDA wants to use randomness, it should derive randomness from on-chain data (e.g., block hashes or timestamps) to ensure reproducibility. Example:

```
python# Seed RNG with block hash of transaction  
import hashlib  
seed = hashlib.sha256(pwr_chain.get_block_header(tx.block_height)).digest()  
random.seed(seed)  
```

Or it should Use external randomness providers (e.g., Chainlink VRF) *only if* their outputs are recorded on PWR Chain first1.

Time-based logic can still be implemented because PWR Chain timestamps all transactions and that can be used to create time-based logic.


# Are there any restrictions on external API calls within VIDAs?

There are no restrictions on external API calls. However, if an API returns data that can change over time or introduce non-determinism, it should be handled by Conduit Nodes instead of being directly queried by the VIDA. The Conduit Nodes will fetch the data, reach consensus through a voting process, and publish the agreed-upon result on PWR Chain with a timestamp. This ensures that future VIDA Execution Instances can reference the same verified data at a specific point in time, preventing state inconsistencies and maintaining execution integrity.


# How are VIDA Upgrades Managed without Breaking Replayability?

A structured approach to upgrades involves assigning a version identifier to the VIDA.&#x20;

If the VIDA is centralized, then the development team can announce upgrade schedules, version numbers, and application hashes.

If it's decentralized, Conduit Nodes can vote on upgrade schedules, version numbers, and application hashes.&#x20;

Once an upgrade is approved, both Conduit Nodes and VIDA Execution Instances must update their software accordingly. If a node fails to update before the new version becomes active at a specified block or timestamp, it will be unable to continue operating and will effectively freeze.


# If an VIDA’s Logic Changes, how does PWR Chain Ensure Past Transactions Remain Verifiable?

VIDA upgrades must either be backward-compatible or preserve the previous logic for processing transactions up to the block where the update was applied.


# Glossary

**A**

**Address**\
A unique alphanumeric identifier representing a user or entity on the blockchain, derived from a cryptographic public key. Used to send/receive transactions and interact with VIDAs.

***

#### **B**

**Blockchain as a Data Layer**\
A design where PWR Chain serves as an immutable ledger for VIDAs, treating transactions like rows in a global, verifiable database.

**Byzantine Fault Tolerance (BFT)**\
A consensus mechanism (enhanced in PWR Chain) that ensures network agreement even if some nodes act maliciously or fail.

***

#### **C**

**Conduit Nodes**\
Specialized nodes that enable trustless communication between VIDAs or between VIDAs and external systems (e.g., cross-chain protocols). They validate and relay transactions, governed by VIDA-specific rules.

**Cross-Validation**\
A security mechanism where multiple Execution Agents of a stateful VIDA compare root hashes to detect discrepancies and ensure consistency.

***

#### **F**

**Falcon Signatures**\
A post-quantum cryptographic signature scheme (NIST-standardized) used by PWR Chain to secure transactions and validator votes.

***

#### **H**

**Horizontal Scalability**\
The ability to add unlimited VIDAs without impacting PWR Chain’s performance, achieved by offloading execution to independent VIDAs.

***

#### **I**

**Immutable Records**\
Transactions permanently recorded on PWR Chain that cannot be altered or deleted.

**Instant Finality**\
A feature of PWR Chain’s consensus where blocks are finalized irreversibly within seconds.

***

#### **O**

**Open Verification**\
The ability for anyone to rerun a VIDA using on-chain transactions to independently verify its state or outputs.

***

#### **P**

**Parallelized Processing**\
A scalability feature of PWR Chain where transactions are verified across multiple CPU cores, enabling high throughput (e.g., 600k+ TPS).

**Post-Quantum Cryptography**\
Cryptographic algorithms (e.g., Falcon) resistant to attacks from quantum computers, used to secure PWR Chain.

**PWR Coin**\
The native cryptocurrency of PWR Chain, used for staking, governance, transaction fees, and incentivizing network participants.

***

#### **R**

**Replayability**\
The ability to recreate an VIDA's state by reprocessing its transaction history from PWR Chain, ensuring transparency and auditability.

**Root Hash**\
A cryptographic summary (e.g., Merkle root) of an VIDA's state, published on-chain for cross-validation and consistency checks.

**RPC Node**\
A Remote Procedure Call node that processes blockchain requests, enabling VIDA's to interact with PWR Chain.

***

#### **S**

**Staking Slashing**\
A penalty mechanism where validators or Conduit Nodes lose staked PWR Coin for malicious actions (e.g., double-signing).

***

#### **T**

**Tendermint Consensus**\
A BFT consensus protocol modified by PWR Chain for quantum resistance and equal validator opportunities.

***

#### V

**VIDA Execution Instance**\
A VIDA actively running on a machine, processing its logic and transactions sent to it through PWR Chain.

***

#### **W**

**Wallet**\
A tool to manage private keys, sign transactions, and interact with PWR Chain. Types include hot (online) and cold (offline) wallets.


