DAOstack is an open-source software stack for building and operating decentralized autonomous organizations on Ethereum, combining a modular smart-contract framework, a JavaScript developer environment, and a user-facing app historically called Alchemy. Its distinguishing idea is holographic consensus, a mechanism that lets a small group of staked predictors surface proposals for broader voting rather than requiring every member to review every proposal. This article explains the primitives, shows how to query a deployed DAO through a standard JSON-RPC endpoint, and gives a reproducible procedure for verifying governance state. It separates documented protocol behavior from integration recommendations and from measurements you must run yourself against your own deployment.
Scope and Direct Answer
DAOstack is best understood as a governance framework rather than a single contract. It provides reusable primitives — an avatar that holds the organization's assets, a controller that routes proposals through schemes, voting machines that tally support, and a reputation system that weights influence — and lets builders compose those primitives into an organization with whatever decision rules they need. The GEN token was designed as the staking and prediction asset used by holographic consensus, and it is an ERC-20 on Ethereum.
This guide targets developers and operators who need to integrate with, audit, or operate a DAOstack-based organization. It assumes you can run curl or Node.js, that you have access to an Ethereum JSON-RPC endpoint (mainnet or a test network where the relevant contracts are deployed), and that you understand basic ABI encoding. Contract addresses and deployment availability vary by network and by release, so always confirm them against the project's published artifacts rather than copying addresses from third-party pages.
- Documented behavior: the contract roles and the holographic consensus concept as described in DAOstack's public documentation and developer portal.
- Recommendation: treat every address, ABI, and network as configuration you verify, not as a constant.
- Measurement: latency, proposal throughput, and query cost must be measured against your own endpoint and deployment.
Prerequisites and Environment Assumptions
Before writing integration code, pin down four things. First, the network: DAOstack's tooling was built around Ethereum, so your RPC endpoint must serve the chain where your target contracts live. Second, the contract addresses for the controller, avatar, reputation, token, and the schemes you intend to call. Third, the ABI for each contract, ideally taken from the same release tag as the deployed bytecode. Fourth, an account strategy: read-only queries need no key, but any transaction requires a funded signer and careful nonce management.
For endpoint selection, the concrete provider and network choice belongs in a short recommendation rather than a long comparison; the RPC Assistant entry on the best Ethereum RPC API covers that decision. For this article, assume an environment variable RPC_URL points at an Ethereum endpoint you control or have contracted, and that PRIVATE_KEY is set only in environments where you actually intend to send transactions.
export RPC_URL="https://your-ethereum-endpoint.example"
export DAO_CONTROLLER="0xYourControllerAddress"
export DAO_AVATAR="0xYourAvatarAddress"
# Never commit PRIVATE_KEY; load it from a secret manager in real use.
export PRIVATE_KEY="0xYourPrivateKeyPlaceholder"Mental Model: How the Primitives Fit Together
An organization in DAOstack is assembled from a small set of roles. The avatar is the on-chain identity that owns assets and is the address other contracts see as the organization. The controller is the gatekeeper: schemes register with it, and only registered schemes can ask the avatar to execute an action. A scheme is a proposal type — a funding proposal, a generic call, a scheme upgrade — and each scheme defines its own proposal lifecycle. A voting machine decides whether a proposal passes, typically by comparing weighted votes against a threshold and quorum. Reputation is a non-transferable, per-account weight that voting machines consult.
Holographic consensus sits on top of this. Proposals can be boosted by stakers who predict whether the broader membership will support them; if the prediction is right, the proposal is fast-tracked, and if it is wrong, the staker loses stake. The practical consequence for integrators is that a proposal has more states than simply open and closed — it can be queued, pre-boosted, boosted, quiet-ended, or expired — and your UI or indexer must model those states explicitly rather than assuming a binary vote.
- Avatar: asset holder and organizational identity.
- Controller: registration and execution gate for schemes.
- Scheme: proposal type with its own parameters and lifecycle.
- Voting machine: threshold and quorum logic over reputation or token weight.
- GEN: staking and prediction asset used by the consensus mechanism.
Querying Governance State Through JSON-RPC
The safest first integration step is read-only. Use eth_call against the controller to confirm which schemes are registered, and against the reputation contract to read a member's weight. The example below uses curl with a minimal ABI-encoded call; replace the selector and arguments with those from your pinned ABI. The eth_call pattern is identical across Ethereum tooling, so it also works when you later move to a client library.
A useful sanity check is to compare the block number returned by eth_blockNumber with the block at which you believe the contracts were deployed. If the endpoint is lagging or pointed at the wrong network, this mismatch surfaces immediately, before you waste time debugging ABI encoding.
curl -s "$RPC_URL" \
-H 'Content-Type: application/json' \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "eth_call",
"params": [
{
"to": "'"$DAO_CONTROLLER"'",
"data": "0x0000000000000000000000000000000000000000000000000000000000000000"
},
"latest"
]
}'
curl -s "$RPC_URL" \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":2,"method":"eth_blockNumber","params":[]}'Reading Proposals and Reputation in JavaScript
For anything beyond a single call, use a library that handles ABI encoding and decoding. The snippet below uses ethers with a placeholder ABI; substitute the real fragments from your release artifacts. The goal is to demonstrate the shape of the integration — connect, read, decode — not to hardcode any particular deployment.
When reading reputation, remember that weight is per-account and per-reputation-contract. A member can hold reputation in more than one organization, so always scope queries to the specific reputation address associated with the DAO you are inspecting. Cache reads that change rarely, such as scheme registration, and re-read proposal state on every user interaction.
import { JsonRpcProvider, Contract } from "ethers";
const provider = new JsonRpcProvider(process.env.RPC_URL);
const controllerAbi = [
// Replace with the real fragment from your pinned ABI.
"function schemes() view returns (bytes32[])"
];
const reputationAbi = [
"function reputationOf(address) view returns (uint256)"
];
async function inspect(controllerAddress, reputationAddress, member) {
const controller = new Contract(controllerAddress, controllerAbi, provider);
const reputation = new Contract(reputationAddress, reputationAbi, provider);
const [schemes, weight, block] = await Promise.all([
controller.schemes(),
reputation.reputationOf(member),
provider.getBlockNumber()
]);
console.log({ block, schemeCount: schemes.length, memberWeight: weight.toString() });
}
inspect(process.env.DAO_CONTROLLER, process.env.DAO_REPUTATION, process.env.MEMBER)
.catch((err) => { console.error(err); process.exit(1); });Reproducible Verification Procedure
Because DAOstack deployments differ, the only trustworthy verification is one you run against your own addresses. The procedure below produces a results table you can attach to an audit or an incident report. Run each step at least three times and record the values; do not fill in numbers you did not observe.
Record the following columns for each run: endpoint URL (redacted if needed), chain ID from eth_chainId, latest block number, controller scheme count, member reputation weight, and wall-clock latency for the read batch. If any column changes between runs at the same block height, you have a consistency problem worth investigating before trusting the integration.
- Step 1: call
eth_chainIdand confirm it matches the network you intend. - Step 2: call
eth_blockNumberand record the height. - Step 3: read the controller's registered schemes and compare against your expected list.
- Step 4: read reputation for a known member and compare against an independent source.
- Step 5: repeat the batch and record latency and any divergence.
| Run | Chain ID | Block | Schemes | Member Weight | Batch Latency (ms) |
|-----|----------|-------|---------|---------------|--------------------|
| 1 | | | | | |
| 2 | | | | | |
| 3 | | | | | |Sending a Proposal Transaction Safely
Writes are where most integrations break. A proposal transaction must target the correct scheme, encode the scheme's specific parameters, and be signed by an account that meets the scheme's eligibility rules. Before broadcasting, simulate the call with eth_call at the latest block; if the simulation reverts, the transaction will almost certainly revert on-chain and you will pay gas for nothing.
Nonce handling deserves explicit attention. If you send several governance transactions in parallel from one account, fetch the pending nonce once and increment locally, or serialize the sends. A stuck nonce can block every subsequent proposal from that signer, which is a governance outage even though the chain is healthy. For high-volume operators, a dedicated node reduces the chance that a shared endpoint's mempool view causes nonce confusion; see the dedicated node overview for the tradeoffs.
import { JsonRpcProvider, Wallet, Contract } from "ethers";
const provider = new JsonRpcProvider(process.env.RPC_URL);
const signer = new Wallet(process.env.PRIVATE_KEY, provider);
const schemeAbi = [
// Replace with the real fragment for your scheme.
"function propose(bytes32 proposalId, address[] targets, uint256[] values, bytes[] calldatas) returns (bytes32)"
];
async function simulateAndSend(schemeAddress, args) {
const scheme = new Contract(schemeAddress, schemeAbi, signer);
// Simulate first; throws if the call would revert.
await scheme.propose.staticCall(...args);
const tx = await scheme.propose(...args);
console.log("submitted", tx.hash);
const receipt = await tx.wait();
console.log("mined in block", receipt.blockNumber, "status", receipt.status);
}
// simulateAndSend(process.env.SCHEME, [proposalId, targets, values, calldatas]);Troubleshooting Common Integration Failures
Most DAOstack integration problems fall into a few buckets. Reverts on simulation usually mean the caller lacks the required reputation, the scheme is not registered with the controller, or the encoded parameters do not match the scheme's expected types. Stale reads usually mean the endpoint is load-balanced across nodes with different head heights; pin reads to a block number when consistency matters. Missing proposals usually mean your indexer is not tracking the scheme's events, or it is tracking the wrong scheme address after an upgrade.
A subtler failure is state-model mismatch. If your UI treats a proposal as binary, boosted and quiet-ended proposals will render incorrectly, and users may believe a proposal passed when it did not. Model the full state machine from the scheme's ABI and event definitions, and write tests that replay historical events into your reducer.
- Revert on simulate: check reputation, scheme registration, and parameter encoding.
- Inconsistent reads: pin to a block number or use a single-node endpoint.
- Missing proposals: verify event subscriptions and scheme addresses after upgrades.
- Wrong status: implement the full proposal state machine, not a boolean.
Limitations and Tradeoffs
DAOstack's flexibility is also its cost. Composing schemes and voting machines gives you exactly the governance you specify, but it also means there is no single canonical configuration to copy, and audits must cover your composition rather than a reference deployment. Holographic consensus adds a staking layer that improves throughput for large memberships but introduces prediction-market dynamics and stake risk that small organizations may not want.
Operationally, the stack is Ethereum-centric and its tooling reflects the era in which it was built. Expect to pin ABIs, verify addresses per network, and maintain your own indexer for a responsive UI. If your organization needs a simpler model, a token-weighted governor or a multisig may be a better fit; DAOstack earns its complexity when you genuinely need reputation-weighted, multi-scheme governance with fast-tracked proposals.
Next Steps for Operators and Integrators
Start by pinning your deployment: record chain ID, contract addresses, ABI release tag, and the block at which each contract was deployed. Build a read-only dashboard using the query patterns above, then add a simulation-first write path. Once reads and writes are stable, add an event indexer so proposal state is derived from chain events rather than polled ad hoc.
For endpoint planning, review the network directory to confirm which chains you need, compare RPC pricing against your expected read and write volume, and consult the Learn library for related governance and node-operation material. If you are choosing a provider for the first time, the RPC Assistant's guidance on the best Ethereum RPC API is the right starting point, and a multi-chain RPC endpoints guide helps if your DAO later spans more than one network.
- Document your deployment manifest and version it alongside your code.
- Add simulation-before-send to every governance write path.
- Index events rather than polling proposal state.
- Measure endpoint latency and consistency before scaling membership.