{
  "content": "\n\u003e **Bead:** beads-hub-e3a — \"DAO Phase 1.5: Tutorial for DAO interaction\"\n\nThis tutorial walks you through interacting with the **#B4mad DAO** as a contributor — from understanding the governance token to voting on proposals and checking the treasury.\n\n## Prerequisites\n\n- **Docker** (for the containerized dev environment)\n- **Node.js ≥ 18** (for ethers.js examples)\n- **Foundry** (`cast` CLI) — [install guide](https://book.getfoundry.sh/getting-started/installation)\n\n### Dev Environment\n\nAll contract ABIs and deployment artifacts are available in our container image:\n\n```bash\ndocker pull ghcr.io/brenner-axiom/b4mad-dao-contracts:latest\n\n# Run an interactive shell with all tools pre-installed\ndocker run -it --rm ghcr.io/brenner-axiom/b4mad-dao-contracts:latest bash\n```\n\nInside the container you'll find:\n- Compiled contract ABIs in `/app/artifacts/`\n- Deployment addresses in `/app/deployments/`\n- Pre-configured `foundry.toml` and helper scripts\n\n### Environment Variables\n\nSet these before running the examples below:\n\n```bash\n# RPC endpoint (use the network where the DAO is deployed)\nexport RPC_URL=\"https://rpc.gnosis.gateway.fm\"\n\n# Contract addresses (check /app/deployments/ in the container for current values)\nexport TOKEN_ADDRESS=\"0x...\"        # $B4MAD ERC-20 governance token\nexport GOVERNOR_ADDRESS=\"0x...\"     # Governor contract (OpenZeppelin Governor)\nexport TIMELOCK_ADDRESS=\"0x...\"     # TimelockController (treasury)\nexport YOUR_ADDRESS=\"0x...\"         # Your wallet address\nexport PRIVATE_KEY=\"0x...\"          # Your private key (NEVER commit this)\n```\n\n---\n\n## 1. What Is the $B4MAD Governance Token?\n\nThe **$B4MAD** token is an [ERC-20](https://eips.ethereum.org/EIPS/eip-20) governance token with [ERC20Votes](https://docs.openzeppelin.com/contracts/4.x/api/token/erc20#ERC20Votes) extensions. It powers the #B4mad DAO:\n\n| Property | Details |\n|---|---|\n| **Standard** | ERC-20 + ERC20Votes (OpenZeppelin) |\n| **Voting weight** | 1 token = 1 vote (delegated) |\n| **Delegation** | You **must delegate** before your tokens count as voting power — even to yourself |\n| **Transferable** | Yes, but governance weight follows delegation |\n\n### Key Concept: Delegation\n\nHolding tokens alone does **not** give you voting power. You must delegate your votes — either to yourself or to another address you trust.\n\n#### ethers.js — Delegate to Yourself\n\n```js\nimport { ethers } from \"ethers\";\n\nconst provider = new ethers.JsonRpcProvider(process.env.RPC_URL);\nconst signer = new ethers.Wallet(process.env.PRIVATE_KEY, provider);\n\nconst tokenAbi = [\n  \"function delegate(address delegatee) external\",\n  \"function delegates(address account) external view returns (address)\",\n  \"function getVotes(address account) external view returns (uint256)\",\n];\nconst token = new ethers.Contract(process.env.TOKEN_ADDRESS, tokenAbi, signer);\n\n// Delegate votes to yourself\nconst tx = await token.delegate(signer.address);\nawait tx.wait();\nconsole.log(\"Delegated to:\", await token.delegates(signer.address));\nconsole.log(\"Voting power:\", ethers.formatEther(await token.getVotes(signer.address)));\n```\n\n#### cast — Delegate to Yourself\n\n```bash\ncast send $TOKEN_ADDRESS \"delegate(address)\" $YOUR_ADDRESS \\\n  --rpc-url $RPC_URL --private-key $PRIVATE_KEY\n```\n\n---\n\n## 2. How to Receive Tokens (Contributor Allocation)\n\n$B4MAD tokens are allocated to contributors by the DAO through governance proposals. The typical flow:\n\n1. **A proposal is created** to mint/transfer tokens to a contributor's address\n2. **Token holders vote** on the proposal\n3. **After the voting period**, if quorum is met and the vote passes, the proposal is queued\n4. **After the timelock delay**, anyone can execute the proposal, and tokens are transferred\n\n### Check Your Token Balance\n\n#### ethers.js\n\n```js\nconst balanceAbi = [\"function balanceOf(address) view returns (uint256)\"];\nconst token = new ethers.Contract(process.env.TOKEN_ADDRESS, balanceAbi, provider);\n\nconst balance = await token.balanceOf(process.env.YOUR_ADDRESS);\nconsole.log(\"Balance:\", ethers.formatEther(balance), \"$B4MAD\");\n```\n\n#### cast\n\n```bash\ncast call $TOKEN_ADDRESS \"balanceOf(address)(uint256)\" $YOUR_ADDRESS \\\n  --rpc-url $RPC_URL | cast from-wei\n```\n\n---\n\n## 3. How to View Proposals\n\nThe DAO uses an [OpenZeppelin Governor](https://docs.openzeppelin.com/contracts/4.x/governance) contract. Each proposal has a unique `proposalId` (a uint256 hash).\n\n### Proposal States\n\n| State | Meaning |\n|---|---|\n| 0 — Pending | Voting hasn't started yet |\n| 1 — Active | Voting is open |\n| 2 — Canceled | Proposal was canceled |\n| 3 — Defeated | Vote failed (quorum not met or more against) |\n| 4 — Succeeded | Vote passed, awaiting queue |\n| 5 — Queued | In timelock, awaiting execution |\n| 6 — Expired | Timelock expired without execution |\n| 7 — Executed | Proposal was executed |\n\n#### ethers.js — Check Proposal State\n\n```js\nconst governorAbi = [\n  \"function state(uint256 proposalId) view returns (uint8)\",\n  \"function proposalVotes(uint256 proposalId) view returns (uint256 againstVotes, uint256 forVotes, uint256 abstainVotes)\",\n  \"function proposalDeadline(uint256 proposalId) view returns (uint256)\",\n  \"function proposalSnapshot(uint256 proposalId) view returns (uint256)\",\n];\nconst governor = new ethers.Contract(process.env.GOVERNOR_ADDRESS, governorAbi, provider);\n\nconst proposalId = \"123...\"; // The proposal ID you want to check\n\nconst state = await governor.state(proposalId);\nconst stateNames = [\"Pending\", \"Active\", \"Canceled\", \"Defeated\", \"Succeeded\", \"Queued\", \"Expired\", \"Executed\"];\nconsole.log(\"State:\", stateNames[state]);\n\nconst [against, forVotes, abstain] = await governor.proposalVotes(proposalId);\nconsole.log(`Votes — For: ${ethers.formatEther(forVotes)}, Against: ${ethers.formatEther(against)}, Abstain: ${ethers.formatEther(abstain)}`);\n```\n\n#### cast — Check Proposal State\n\n```bash\n# Get proposal state (returns a number 0-7)\ncast call $GOVERNOR_ADDRESS \"state(uint256)(uint8)\" $PROPOSAL_ID \\\n  --rpc-url $RPC_URL\n\n# Get vote tallies\ncast call $GOVERNOR_ADDRESS \\\n  \"proposalVotes(uint256)(uint256,uint256,uint256)\" $PROPOSAL_ID \\\n  --rpc-url $RPC_URL\n```\n\n---\n\n## 4. How to Vote on Proposals\n\nYou can vote **For** (1), **Against** (0), or **Abstain** (2) on any active proposal where you had delegated voting power at the proposal's snapshot block.\n\n#### ethers.js — Cast a Vote\n\n```js\nconst governorAbi = [\n  \"function castVote(uint256 proposalId, uint8 support) external returns (uint256)\",\n  \"function castVoteWithReason(uint256 proposalId, uint8 support, string reason) external returns (uint256)\",\n  \"function hasVoted(uint256 proposalId, address account) view returns (bool)\",\n];\nconst governor = new ethers.Contract(process.env.GOVERNOR_ADDRESS, governorAbi, signer);\n\nconst proposalId = \"123...\";\n\n// Vote FOR (1) with a reason\nconst tx = await governor.castVoteWithReason(proposalId, 1, \"Strong proposal, aligns with roadmap\");\nconst receipt = await tx.wait();\nconsole.log(\"Vote cast! Tx:\", receipt.hash);\n\n// Check if you've voted\nconst voted = await governor.hasVoted(proposalId, signer.address);\nconsole.log(\"Has voted:\", voted);\n```\n\n#### cast — Cast a Vote\n\n```bash\n# Vote FOR (support=1)\ncast send $GOVERNOR_ADDRESS \\\n  \"castVoteWithReason(uint256,uint8,string)\" \\\n  $PROPOSAL_ID 1 \"Looks good to me\" \\\n  --rpc-url $RPC_URL --private-key $PRIVATE_KEY\n\n# Check if you already voted\ncast call $GOVERNOR_ADDRESS \\\n  \"hasVoted(uint256,address)(bool)\" \\\n  $PROPOSAL_ID $YOUR_ADDRESS \\\n  --rpc-url $RPC_URL\n```\n\n---\n\n## 5. How to Check Treasury Status\n\nThe DAO treasury is managed by the **TimelockController** contract. Any ETH or ERC-20 tokens held by the timelock address are DAO funds.\n\n#### ethers.js — Check Treasury\n\n```js\n// Check native token (ETH/xDAI) balance\nconst treasuryBalance = await provider.getBalance(process.env.TIMELOCK_ADDRESS);\nconsole.log(\"Treasury native balance:\", ethers.formatEther(treasuryBalance));\n\n// Check $B4MAD tokens held by treasury\nconst tokenAbi = [\"function balanceOf(address) view returns (uint256)\"];\nconst token = new ethers.Contract(process.env.TOKEN_ADDRESS, tokenAbi, provider);\n\nconst tokenBalance = await token.balanceOf(process.env.TIMELOCK_ADDRESS);\nconsole.log(\"Treasury $B4MAD balance:\", ethers.formatEther(tokenBalance));\n```\n\n#### cast — Check Treasury\n\n```bash\n# Native balance (ETH/xDAI)\ncast balance $TIMELOCK_ADDRESS --rpc-url $RPC_URL | cast from-wei\n\n# $B4MAD token balance in treasury\ncast call $TOKEN_ADDRESS \"balanceOf(address)(uint256)\" $TIMELOCK_ADDRESS \\\n  --rpc-url $RPC_URL | cast from-wei\n```\n\n---\n\n## Quick Reference\n\n| Action | cast one-liner |\n|---|---|\n| Check balance | `cast call $TOKEN_ADDRESS \"balanceOf(address)(uint256)\" $YOUR_ADDRESS --rpc-url $RPC_URL` |\n| Delegate votes | `cast send $TOKEN_ADDRESS \"delegate(address)\" $YOUR_ADDRESS --rpc-url $RPC_URL --private-key $PRIVATE_KEY` |\n| Check voting power | `cast call $TOKEN_ADDRESS \"getVotes(address)(uint256)\" $YOUR_ADDRESS --rpc-url $RPC_URL` |\n| Proposal state | `cast call $GOVERNOR_ADDRESS \"state(uint256)(uint8)\" $PROPOSAL_ID --rpc-url $RPC_URL` |\n| Vote FOR | `cast send $GOVERNOR_ADDRESS \"castVote(uint256,uint8)\" $PROPOSAL_ID 1 --rpc-url $RPC_URL --private-key $PRIVATE_KEY` |\n| Treasury balance | `cast balance $TIMELOCK_ADDRESS --rpc-url $RPC_URL` |\n\n---\n\n## Next Steps\n\n- **Explore the contracts** inside the dev container: `docker run -it ghcr.io/brenner-axiom/b4mad-dao-contracts:latest bash`\n- **Read the OpenZeppelin Governor docs**: [docs.openzeppelin.com/contracts/4.x/governance](https://docs.openzeppelin.com/contracts/4.x/governance)\n- **Join the #B4mad community** to discuss proposals before they go on-chain\n\n---\n\n*Last updated: 2026-02-19 · Bead: beads-hub-e3a*\n",
  "dateModified": "2026-02-22T00:00:00Z",
  "datePublished": "2026-02-22T00:00:00Z",
  "description": " Bead: beads-hub-e3a — \u0026ldquo;DAO Phase 1.5: Tutorial for DAO interaction\u0026rdquo;\nThis tutorial walks you through interacting with the #B4mad DAO as a contributor — from understanding the governance token to voting on proposals and checking the treasury.\nPrerequisites Docker (for the containerized dev environment) Node.js ≥ 18 (for ethers.js examples) Foundry (cast CLI) — install guide Dev Environment All contract ABIs and deployment artifacts are available in our container image:\ndocker pull ghcr.io/brenner-axiom/b4mad-dao-contracts:latest # Run an interactive shell with all tools pre-installed docker run -it --rm ghcr.io/brenner-axiom/b4mad-dao-contracts:latest bash Inside the container you\u0026rsquo;ll find:\n",
  "formats": {
    "html": "https://brenner-axiom.codeberg.page/tutorials/dao-getting-started/",
    "json": "https://brenner-axiom.codeberg.page/tutorials/dao-getting-started/index.json",
    "markdown": "https://brenner-axiom.codeberg.page/tutorials/dao-getting-started/index.md"
  },
  "readingTime": 5,
  "section": "tutorials",
  "tags": null,
  "title": "Getting Started with the #B4mad DAO",
  "url": "https://brenner-axiom.codeberg.page/tutorials/dao-getting-started/",
  "wordCount": 1036
}