agent-bober 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (212) hide show
  1. package/.claude-plugin/plugin.json +9 -0
  2. package/LICENSE +21 -0
  3. package/README.md +495 -0
  4. package/agents/bober-evaluator.md +323 -0
  5. package/agents/bober-generator.md +245 -0
  6. package/agents/bober-planner.md +248 -0
  7. package/dist/cli/commands/eval.d.ts +6 -0
  8. package/dist/cli/commands/eval.d.ts.map +1 -0
  9. package/dist/cli/commands/eval.js +129 -0
  10. package/dist/cli/commands/eval.js.map +1 -0
  11. package/dist/cli/commands/init.d.ts +5 -0
  12. package/dist/cli/commands/init.d.ts.map +1 -0
  13. package/dist/cli/commands/init.js +547 -0
  14. package/dist/cli/commands/init.js.map +1 -0
  15. package/dist/cli/commands/plan.d.ts +5 -0
  16. package/dist/cli/commands/plan.d.ts.map +1 -0
  17. package/dist/cli/commands/plan.js +87 -0
  18. package/dist/cli/commands/plan.js.map +1 -0
  19. package/dist/cli/commands/run.d.ts +5 -0
  20. package/dist/cli/commands/run.d.ts.map +1 -0
  21. package/dist/cli/commands/run.js +120 -0
  22. package/dist/cli/commands/run.js.map +1 -0
  23. package/dist/cli/commands/sprint.d.ts +6 -0
  24. package/dist/cli/commands/sprint.d.ts.map +1 -0
  25. package/dist/cli/commands/sprint.js +206 -0
  26. package/dist/cli/commands/sprint.js.map +1 -0
  27. package/dist/cli/index.d.ts +3 -0
  28. package/dist/cli/index.d.ts.map +1 -0
  29. package/dist/cli/index.js +124 -0
  30. package/dist/cli/index.js.map +1 -0
  31. package/dist/config/defaults.d.ts +15 -0
  32. package/dist/config/defaults.d.ts.map +1 -0
  33. package/dist/config/defaults.js +226 -0
  34. package/dist/config/defaults.js.map +1 -0
  35. package/dist/config/index.d.ts +4 -0
  36. package/dist/config/index.d.ts.map +1 -0
  37. package/dist/config/index.js +8 -0
  38. package/dist/config/index.js.map +1 -0
  39. package/dist/config/loader.d.ts +18 -0
  40. package/dist/config/loader.d.ts.map +1 -0
  41. package/dist/config/loader.js +189 -0
  42. package/dist/config/loader.js.map +1 -0
  43. package/dist/config/schema.d.ts +904 -0
  44. package/dist/config/schema.d.ts.map +1 -0
  45. package/dist/config/schema.js +181 -0
  46. package/dist/config/schema.js.map +1 -0
  47. package/dist/contracts/eval-result.d.ts +205 -0
  48. package/dist/contracts/eval-result.d.ts.map +1 -0
  49. package/dist/contracts/eval-result.js +87 -0
  50. package/dist/contracts/eval-result.js.map +1 -0
  51. package/dist/contracts/index.d.ts +4 -0
  52. package/dist/contracts/index.d.ts.map +1 -0
  53. package/dist/contracts/index.js +16 -0
  54. package/dist/contracts/index.js.map +1 -0
  55. package/dist/contracts/spec.d.ts +101 -0
  56. package/dist/contracts/spec.d.ts.map +1 -0
  57. package/dist/contracts/spec.js +51 -0
  58. package/dist/contracts/spec.js.map +1 -0
  59. package/dist/contracts/sprint-contract.d.ts +141 -0
  60. package/dist/contracts/sprint-contract.d.ts.map +1 -0
  61. package/dist/contracts/sprint-contract.js +80 -0
  62. package/dist/contracts/sprint-contract.js.map +1 -0
  63. package/dist/evaluators/builtin/api-check.d.ts +13 -0
  64. package/dist/evaluators/builtin/api-check.d.ts.map +1 -0
  65. package/dist/evaluators/builtin/api-check.js +152 -0
  66. package/dist/evaluators/builtin/api-check.js.map +1 -0
  67. package/dist/evaluators/builtin/build-check.d.ts +17 -0
  68. package/dist/evaluators/builtin/build-check.d.ts.map +1 -0
  69. package/dist/evaluators/builtin/build-check.js +155 -0
  70. package/dist/evaluators/builtin/build-check.js.map +1 -0
  71. package/dist/evaluators/builtin/command-runner.d.ts +26 -0
  72. package/dist/evaluators/builtin/command-runner.d.ts.map +1 -0
  73. package/dist/evaluators/builtin/command-runner.js +114 -0
  74. package/dist/evaluators/builtin/command-runner.js.map +1 -0
  75. package/dist/evaluators/builtin/lint.d.ts +17 -0
  76. package/dist/evaluators/builtin/lint.d.ts.map +1 -0
  77. package/dist/evaluators/builtin/lint.js +264 -0
  78. package/dist/evaluators/builtin/lint.js.map +1 -0
  79. package/dist/evaluators/builtin/playwright.d.ts +16 -0
  80. package/dist/evaluators/builtin/playwright.d.ts.map +1 -0
  81. package/dist/evaluators/builtin/playwright.js +238 -0
  82. package/dist/evaluators/builtin/playwright.js.map +1 -0
  83. package/dist/evaluators/builtin/typescript-check.d.ts +12 -0
  84. package/dist/evaluators/builtin/typescript-check.d.ts.map +1 -0
  85. package/dist/evaluators/builtin/typescript-check.js +155 -0
  86. package/dist/evaluators/builtin/typescript-check.js.map +1 -0
  87. package/dist/evaluators/builtin/unit-test.d.ts +18 -0
  88. package/dist/evaluators/builtin/unit-test.d.ts.map +1 -0
  89. package/dist/evaluators/builtin/unit-test.js +279 -0
  90. package/dist/evaluators/builtin/unit-test.js.map +1 -0
  91. package/dist/evaluators/index.d.ts +11 -0
  92. package/dist/evaluators/index.d.ts.map +1 -0
  93. package/dist/evaluators/index.js +13 -0
  94. package/dist/evaluators/index.js.map +1 -0
  95. package/dist/evaluators/plugin-interface.d.ts +50 -0
  96. package/dist/evaluators/plugin-interface.d.ts.map +1 -0
  97. package/dist/evaluators/plugin-interface.js +2 -0
  98. package/dist/evaluators/plugin-interface.js.map +1 -0
  99. package/dist/evaluators/plugin-loader.d.ts +18 -0
  100. package/dist/evaluators/plugin-loader.d.ts.map +1 -0
  101. package/dist/evaluators/plugin-loader.js +107 -0
  102. package/dist/evaluators/plugin-loader.js.map +1 -0
  103. package/dist/evaluators/registry.d.ts +78 -0
  104. package/dist/evaluators/registry.d.ts.map +1 -0
  105. package/dist/evaluators/registry.js +238 -0
  106. package/dist/evaluators/registry.js.map +1 -0
  107. package/dist/index.d.ts +17 -0
  108. package/dist/index.d.ts.map +1 -0
  109. package/dist/index.js +22 -0
  110. package/dist/index.js.map +1 -0
  111. package/dist/orchestrator/context-handoff.d.ts +543 -0
  112. package/dist/orchestrator/context-handoff.d.ts.map +1 -0
  113. package/dist/orchestrator/context-handoff.js +133 -0
  114. package/dist/orchestrator/context-handoff.js.map +1 -0
  115. package/dist/orchestrator/evaluator-agent.d.ts +15 -0
  116. package/dist/orchestrator/evaluator-agent.d.ts.map +1 -0
  117. package/dist/orchestrator/evaluator-agent.js +233 -0
  118. package/dist/orchestrator/evaluator-agent.js.map +1 -0
  119. package/dist/orchestrator/generator-agent.d.ts +16 -0
  120. package/dist/orchestrator/generator-agent.d.ts.map +1 -0
  121. package/dist/orchestrator/generator-agent.js +147 -0
  122. package/dist/orchestrator/generator-agent.js.map +1 -0
  123. package/dist/orchestrator/pipeline.d.ts +24 -0
  124. package/dist/orchestrator/pipeline.d.ts.map +1 -0
  125. package/dist/orchestrator/pipeline.js +290 -0
  126. package/dist/orchestrator/pipeline.js.map +1 -0
  127. package/dist/orchestrator/planner-agent.d.ts +10 -0
  128. package/dist/orchestrator/planner-agent.d.ts.map +1 -0
  129. package/dist/orchestrator/planner-agent.js +187 -0
  130. package/dist/orchestrator/planner-agent.js.map +1 -0
  131. package/dist/state/helpers.d.ts +5 -0
  132. package/dist/state/helpers.d.ts.map +1 -0
  133. package/dist/state/helpers.js +8 -0
  134. package/dist/state/helpers.js.map +1 -0
  135. package/dist/state/history.d.ts +39 -0
  136. package/dist/state/history.d.ts.map +1 -0
  137. package/dist/state/history.js +162 -0
  138. package/dist/state/history.js.map +1 -0
  139. package/dist/state/index.d.ts +8 -0
  140. package/dist/state/index.d.ts.map +1 -0
  141. package/dist/state/index.js +22 -0
  142. package/dist/state/index.js.map +1 -0
  143. package/dist/state/plan-state.d.ts +21 -0
  144. package/dist/state/plan-state.d.ts.map +1 -0
  145. package/dist/state/plan-state.js +108 -0
  146. package/dist/state/plan-state.js.map +1 -0
  147. package/dist/state/sprint-state.d.ts +20 -0
  148. package/dist/state/sprint-state.d.ts.map +1 -0
  149. package/dist/state/sprint-state.js +98 -0
  150. package/dist/state/sprint-state.js.map +1 -0
  151. package/dist/utils/fs.d.ts +31 -0
  152. package/dist/utils/fs.d.ts.map +1 -0
  153. package/dist/utils/fs.js +67 -0
  154. package/dist/utils/fs.js.map +1 -0
  155. package/dist/utils/git.d.ts +35 -0
  156. package/dist/utils/git.d.ts.map +1 -0
  157. package/dist/utils/git.js +84 -0
  158. package/dist/utils/git.js.map +1 -0
  159. package/dist/utils/index.d.ts +4 -0
  160. package/dist/utils/index.d.ts.map +1 -0
  161. package/dist/utils/index.js +4 -0
  162. package/dist/utils/index.js.map +1 -0
  163. package/dist/utils/logger.d.ts +45 -0
  164. package/dist/utils/logger.d.ts.map +1 -0
  165. package/dist/utils/logger.js +73 -0
  166. package/dist/utils/logger.js.map +1 -0
  167. package/hooks/hooks.json +10 -0
  168. package/package.json +67 -0
  169. package/scripts/detect-stack.sh +287 -0
  170. package/scripts/init-project.sh +206 -0
  171. package/scripts/run-eval.sh +175 -0
  172. package/skills/bober.anchor/SKILL.md +365 -0
  173. package/skills/bober.anchor/references/anchor-guide.md +567 -0
  174. package/skills/bober.brownfield/SKILL.md +422 -0
  175. package/skills/bober.brownfield/references/codebase-analysis.md +304 -0
  176. package/skills/bober.eval/SKILL.md +235 -0
  177. package/skills/bober.eval/references/eval-strategies.md +407 -0
  178. package/skills/bober.eval/references/feedback-format.md +182 -0
  179. package/skills/bober.plan/SKILL.md +244 -0
  180. package/skills/bober.plan/references/clarification-guide.md +124 -0
  181. package/skills/bober.plan/references/spec-schema.md +253 -0
  182. package/skills/bober.react/SKILL.md +330 -0
  183. package/skills/bober.react/references/react-scaffold.md +344 -0
  184. package/skills/bober.run/SKILL.md +303 -0
  185. package/skills/bober.solidity/SKILL.md +416 -0
  186. package/skills/bober.solidity/references/solidity-guide.md +487 -0
  187. package/skills/bober.sprint/SKILL.md +280 -0
  188. package/skills/bober.sprint/references/contract-schema.md +251 -0
  189. package/templates/base/CLAUDE.md +20 -0
  190. package/templates/base/bober.config.json +35 -0
  191. package/templates/brownfield/CLAUDE.md +34 -0
  192. package/templates/brownfield/bober.config.json +37 -0
  193. package/templates/presets/anchor/CLAUDE.md +163 -0
  194. package/templates/presets/anchor/bober.config.json +9 -0
  195. package/templates/presets/api-node/CLAUDE.md +153 -0
  196. package/templates/presets/api-node/bober.config.json +10 -0
  197. package/templates/presets/nextjs/CLAUDE.md +82 -0
  198. package/templates/presets/nextjs/bober.config.json +14 -0
  199. package/templates/presets/python-api/CLAUDE.md +202 -0
  200. package/templates/presets/python-api/bober.config.json +9 -0
  201. package/templates/presets/react-vite/CLAUDE.md +71 -0
  202. package/templates/presets/react-vite/bober.config.json +53 -0
  203. package/templates/presets/react-vite/scaffold/package.json +45 -0
  204. package/templates/presets/react-vite/scaffold/server/index.ts +38 -0
  205. package/templates/presets/react-vite/scaffold/server/tsconfig.json +24 -0
  206. package/templates/presets/react-vite/scaffold/src/App.tsx +37 -0
  207. package/templates/presets/react-vite/scaffold/src/index.html +12 -0
  208. package/templates/presets/react-vite/scaffold/src/main.tsx +12 -0
  209. package/templates/presets/react-vite/scaffold/tsconfig.json +27 -0
  210. package/templates/presets/react-vite/scaffold/vite.config.ts +34 -0
  211. package/templates/presets/solidity/CLAUDE.md +106 -0
  212. package/templates/presets/solidity/bober.config.json +9 -0
@@ -0,0 +1,487 @@
1
+ # Solidity Development Reference Guide
2
+
3
+ ## Hardhat vs Foundry Project Structure
4
+
5
+ ### Hardhat Project Structure
6
+
7
+ ```
8
+ project-root/
9
+ contracts/ # Solidity source files
10
+ interfaces/ # Contract interfaces
11
+ libraries/ # Shared libraries
12
+ mocks/ # Mock contracts for testing
13
+ MyContract.sol
14
+ test/ # Test files (TypeScript/JavaScript)
15
+ unit/ # Unit tests
16
+ integration/ # Integration tests
17
+ helpers/ # Test utilities and fixtures
18
+ scripts/ # Deployment and utility scripts
19
+ deploy/
20
+ artifacts/ # Compiled contract artifacts (generated)
21
+ cache/ # Hardhat cache (generated)
22
+ typechain-types/ # TypeScript bindings (generated)
23
+ hardhat.config.ts # Hardhat configuration
24
+ .solhint.json # Solhint linter configuration
25
+ package.json
26
+ tsconfig.json
27
+ ```
28
+
29
+ **Key config (`hardhat.config.ts`):**
30
+ ```typescript
31
+ import { HardhatUserConfig } from "hardhat/config";
32
+ import "@nomicfoundation/hardhat-toolbox";
33
+
34
+ const config: HardhatUserConfig = {
35
+ solidity: {
36
+ version: "0.8.24",
37
+ settings: {
38
+ optimizer: { enabled: true, runs: 200 },
39
+ viaIR: false,
40
+ },
41
+ },
42
+ networks: {
43
+ sepolia: {
44
+ url: process.env.SEPOLIA_RPC_URL || "",
45
+ accounts: process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : [],
46
+ },
47
+ },
48
+ etherscan: {
49
+ apiKey: process.env.ETHERSCAN_API_KEY || "",
50
+ },
51
+ };
52
+
53
+ export default config;
54
+ ```
55
+
56
+ ### Foundry Project Structure
57
+
58
+ ```
59
+ project-root/
60
+ src/ # Solidity source files
61
+ interfaces/
62
+ libraries/
63
+ MyContract.sol
64
+ test/ # Solidity test files
65
+ unit/
66
+ integration/
67
+ mocks/
68
+ script/ # Deployment scripts (Solidity)
69
+ lib/ # Installed dependencies (git submodules)
70
+ forge-std/
71
+ openzeppelin-contracts/
72
+ out/ # Compiled artifacts (generated)
73
+ cache/ # Foundry cache (generated)
74
+ foundry.toml # Foundry configuration
75
+ remappings.txt # Import remappings
76
+ ```
77
+
78
+ **Key config (`foundry.toml`):**
79
+ ```toml
80
+ [profile.default]
81
+ src = "src"
82
+ out = "out"
83
+ libs = ["lib"]
84
+ solc_version = "0.8.24"
85
+ optimizer = true
86
+ optimizer_runs = 200
87
+ via_ir = false
88
+
89
+ [profile.default.fuzz]
90
+ runs = 256
91
+ max_test_rejects = 65536
92
+
93
+ [rpc_endpoints]
94
+ sepolia = "${SEPOLIA_RPC_URL}"
95
+ mainnet = "${MAINNET_RPC_URL}"
96
+
97
+ [etherscan]
98
+ sepolia = { key = "${ETHERSCAN_API_KEY}" }
99
+ mainnet = { key = "${ETHERSCAN_API_KEY}" }
100
+ ```
101
+
102
+ ## Common Contract Patterns
103
+
104
+ ### ERC-20 Token
105
+
106
+ ```solidity
107
+ // SPDX-License-Identifier: MIT
108
+ pragma solidity ^0.8.20;
109
+
110
+ import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
111
+ import {ERC20Burnable} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";
112
+ import {ERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";
113
+ import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
114
+
115
+ contract MyToken is ERC20, ERC20Burnable, ERC20Permit, Ownable {
116
+ constructor(address initialOwner)
117
+ ERC20("MyToken", "MTK")
118
+ ERC20Permit("MyToken")
119
+ Ownable(initialOwner)
120
+ {
121
+ _mint(initialOwner, 1_000_000 * 10 ** decimals());
122
+ }
123
+
124
+ function mint(address to, uint256 amount) public onlyOwner {
125
+ _mint(to, amount);
126
+ }
127
+ }
128
+ ```
129
+
130
+ ### ERC-721 NFT
131
+
132
+ ```solidity
133
+ // SPDX-License-Identifier: MIT
134
+ pragma solidity ^0.8.20;
135
+
136
+ import {ERC721} from "@openzeppelin/contracts/token/ERC721/ERC721.sol";
137
+ import {ERC721URIStorage} from "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
138
+ import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
139
+
140
+ contract MyNFT is ERC721, ERC721URIStorage, Ownable {
141
+ uint256 private _nextTokenId;
142
+
143
+ constructor(address initialOwner)
144
+ ERC721("MyNFT", "MNFT")
145
+ Ownable(initialOwner)
146
+ {}
147
+
148
+ function safeMint(address to, string memory uri) public onlyOwner {
149
+ uint256 tokenId = _nextTokenId++;
150
+ _safeMint(to, tokenId);
151
+ _setTokenURI(tokenId, uri);
152
+ }
153
+
154
+ // Required overrides
155
+ function tokenURI(uint256 tokenId)
156
+ public view override(ERC721, ERC721URIStorage) returns (string memory)
157
+ {
158
+ return super.tokenURI(tokenId);
159
+ }
160
+
161
+ function supportsInterface(bytes4 interfaceId)
162
+ public view override(ERC721, ERC721URIStorage) returns (bool)
163
+ {
164
+ return super.supportsInterface(interfaceId);
165
+ }
166
+ }
167
+ ```
168
+
169
+ ### ERC-1155 Multi-Token
170
+
171
+ ```solidity
172
+ // SPDX-License-Identifier: MIT
173
+ pragma solidity ^0.8.20;
174
+
175
+ import {ERC1155} from "@openzeppelin/contracts/token/ERC1155/ERC1155.sol";
176
+ import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol";
177
+
178
+ contract MyMultiToken is ERC1155, AccessControl {
179
+ bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
180
+
181
+ constructor(address admin) ERC1155("https://api.example.com/metadata/{id}.json") {
182
+ _grantRole(DEFAULT_ADMIN_ROLE, admin);
183
+ _grantRole(MINTER_ROLE, admin);
184
+ }
185
+
186
+ function mint(address to, uint256 id, uint256 amount, bytes memory data)
187
+ public onlyRole(MINTER_ROLE)
188
+ {
189
+ _mint(to, id, amount, data);
190
+ }
191
+
192
+ function supportsInterface(bytes4 interfaceId)
193
+ public view override(ERC1155, AccessControl) returns (bool)
194
+ {
195
+ return super.supportsInterface(interfaceId);
196
+ }
197
+ }
198
+ ```
199
+
200
+ ### Governor (DAO Governance)
201
+
202
+ ```solidity
203
+ // SPDX-License-Identifier: MIT
204
+ pragma solidity ^0.8.20;
205
+
206
+ import {Governor} from "@openzeppelin/contracts/governance/Governor.sol";
207
+ import {GovernorSettings} from "@openzeppelin/contracts/governance/extensions/GovernorSettings.sol";
208
+ import {GovernorCountingSimple} from "@openzeppelin/contracts/governance/extensions/GovernorCountingSimple.sol";
209
+ import {GovernorVotes} from "@openzeppelin/contracts/governance/extensions/GovernorVotes.sol";
210
+ import {GovernorVotesQuorumFraction} from "@openzeppelin/contracts/governance/extensions/GovernorVotesQuorumFraction.sol";
211
+ import {GovernorTimelockControl} from "@openzeppelin/contracts/governance/extensions/GovernorTimelockControl.sol";
212
+ import {IVotes} from "@openzeppelin/contracts/governance/utils/IVotes.sol";
213
+ import {TimelockController} from "@openzeppelin/contracts/governance/TimelockController.sol";
214
+
215
+ contract MyGovernor is
216
+ Governor,
217
+ GovernorSettings,
218
+ GovernorCountingSimple,
219
+ GovernorVotes,
220
+ GovernorVotesQuorumFraction,
221
+ GovernorTimelockControl
222
+ {
223
+ constructor(IVotes _token, TimelockController _timelock)
224
+ Governor("MyGovernor")
225
+ GovernorSettings(7200 /* 1 day */, 50400 /* 1 week */, 0)
226
+ GovernorVotes(_token)
227
+ GovernorVotesQuorumFraction(4)
228
+ GovernorTimelockControl(_timelock)
229
+ {}
230
+
231
+ // Required overrides omitted for brevity
232
+ }
233
+ ```
234
+
235
+ ### Timelock Controller
236
+
237
+ ```solidity
238
+ // SPDX-License-Identifier: MIT
239
+ pragma solidity ^0.8.20;
240
+
241
+ import {TimelockController} from "@openzeppelin/contracts/governance/TimelockController.sol";
242
+
243
+ contract MyTimelock is TimelockController {
244
+ constructor(
245
+ uint256 minDelay,
246
+ address[] memory proposers,
247
+ address[] memory executors,
248
+ address admin
249
+ ) TimelockController(minDelay, proposers, executors, admin) {}
250
+ }
251
+ ```
252
+
253
+ ## Testing Patterns
254
+
255
+ ### Hardhat Testing (TypeScript)
256
+
257
+ ```typescript
258
+ import { expect } from "chai";
259
+ import { ethers } from "hardhat";
260
+ import { loadFixture } from "@nomicfoundation/hardhat-toolbox/network-helpers";
261
+
262
+ describe("MyToken", function () {
263
+ async function deployFixture() {
264
+ const [owner, addr1, addr2] = await ethers.getSigners();
265
+ const MyToken = await ethers.getContractFactory("MyToken");
266
+ const token = await MyToken.deploy(owner.address);
267
+ return { token, owner, addr1, addr2 };
268
+ }
269
+
270
+ describe("Deployment", function () {
271
+ it("should set the right owner", async function () {
272
+ const { token, owner } = await loadFixture(deployFixture);
273
+ expect(await token.owner()).to.equal(owner.address);
274
+ });
275
+
276
+ it("should assign the total supply to the owner", async function () {
277
+ const { token, owner } = await loadFixture(deployFixture);
278
+ const ownerBalance = await token.balanceOf(owner.address);
279
+ expect(await token.totalSupply()).to.equal(ownerBalance);
280
+ });
281
+ });
282
+
283
+ describe("Transfers", function () {
284
+ it("should transfer tokens between accounts", async function () {
285
+ const { token, owner, addr1, addr2 } = await loadFixture(deployFixture);
286
+ await expect(token.transfer(addr1.address, 50))
287
+ .to.changeTokenBalances(token, [owner, addr1], [-50, 50]);
288
+ });
289
+
290
+ it("should emit Transfer event", async function () {
291
+ const { token, owner, addr1 } = await loadFixture(deployFixture);
292
+ await expect(token.transfer(addr1.address, 50))
293
+ .to.emit(token, "Transfer")
294
+ .withArgs(owner.address, addr1.address, 50);
295
+ });
296
+
297
+ it("should revert when sender has insufficient balance", async function () {
298
+ const { token, addr1, addr2 } = await loadFixture(deployFixture);
299
+ await expect(token.connect(addr1).transfer(addr2.address, 1))
300
+ .to.be.revertedWithCustomError(token, "ERC20InsufficientBalance");
301
+ });
302
+ });
303
+
304
+ describe("Access Control", function () {
305
+ it("should only allow owner to mint", async function () {
306
+ const { token, addr1 } = await loadFixture(deployFixture);
307
+ await expect(token.connect(addr1).mint(addr1.address, 100))
308
+ .to.be.revertedWithCustomError(token, "OwnableUnauthorizedAccount");
309
+ });
310
+ });
311
+ });
312
+ ```
313
+
314
+ ### Foundry Testing (Solidity)
315
+
316
+ ```solidity
317
+ // SPDX-License-Identifier: MIT
318
+ pragma solidity ^0.8.20;
319
+
320
+ import {Test, console2} from "forge-std/Test.sol";
321
+ import {MyToken} from "../src/MyToken.sol";
322
+
323
+ contract MyTokenTest is Test {
324
+ MyToken public token;
325
+ address public owner;
326
+ address public addr1;
327
+ address public addr2;
328
+
329
+ function setUp() public {
330
+ owner = makeAddr("owner");
331
+ addr1 = makeAddr("addr1");
332
+ addr2 = makeAddr("addr2");
333
+
334
+ vm.prank(owner);
335
+ token = new MyToken(owner);
336
+ }
337
+
338
+ function test_OwnerIsSetCorrectly() public view {
339
+ assertEq(token.owner(), owner);
340
+ }
341
+
342
+ function test_TotalSupplyAssignedToOwner() public view {
343
+ assertEq(token.balanceOf(owner), token.totalSupply());
344
+ }
345
+
346
+ function test_Transfer() public {
347
+ vm.prank(owner);
348
+ token.transfer(addr1, 50);
349
+ assertEq(token.balanceOf(addr1), 50);
350
+ }
351
+
352
+ function test_RevertWhen_InsufficientBalance() public {
353
+ vm.prank(addr1);
354
+ vm.expectRevert();
355
+ token.transfer(addr2, 1);
356
+ }
357
+
358
+ function test_RevertWhen_NonOwnerMints() public {
359
+ vm.prank(addr1);
360
+ vm.expectRevert();
361
+ token.mint(addr1, 100);
362
+ }
363
+
364
+ // Fuzz test example
365
+ function testFuzz_Transfer(uint256 amount) public {
366
+ amount = bound(amount, 0, token.balanceOf(owner));
367
+ vm.prank(owner);
368
+ token.transfer(addr1, amount);
369
+ assertEq(token.balanceOf(addr1), amount);
370
+ }
371
+ }
372
+ ```
373
+
374
+ ## Deployment and Verification Workflow
375
+
376
+ ### Hardhat Deployment
377
+
378
+ ```typescript
379
+ // scripts/deploy.ts
380
+ import { ethers } from "hardhat";
381
+
382
+ async function main() {
383
+ const [deployer] = await ethers.getSigners();
384
+ console.log("Deploying with:", deployer.address);
385
+
386
+ const MyToken = await ethers.getContractFactory("MyToken");
387
+ const token = await MyToken.deploy(deployer.address);
388
+ await token.waitForDeployment();
389
+
390
+ const address = await token.getAddress();
391
+ console.log("MyToken deployed to:", address);
392
+
393
+ // Wait for block confirmations before verifying
394
+ console.log("Waiting for confirmations...");
395
+ await token.deploymentTransaction()?.wait(5);
396
+
397
+ // Verify on Etherscan
398
+ console.log("Verifying on Etherscan...");
399
+ await hre.run("verify:verify", {
400
+ address: address,
401
+ constructorArguments: [deployer.address],
402
+ });
403
+ }
404
+
405
+ main().catch((error) => {
406
+ console.error(error);
407
+ process.exitCode = 1;
408
+ });
409
+ ```
410
+
411
+ ### Foundry Deployment
412
+
413
+ ```solidity
414
+ // script/Deploy.s.sol
415
+ // SPDX-License-Identifier: MIT
416
+ pragma solidity ^0.8.20;
417
+
418
+ import {Script, console2} from "forge-std/Script.sol";
419
+ import {MyToken} from "../src/MyToken.sol";
420
+
421
+ contract DeployScript is Script {
422
+ function run() public {
423
+ uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
424
+ address deployer = vm.addr(deployerPrivateKey);
425
+
426
+ vm.startBroadcast(deployerPrivateKey);
427
+ MyToken token = new MyToken(deployer);
428
+ vm.stopBroadcast();
429
+
430
+ console2.log("MyToken deployed to:", address(token));
431
+ }
432
+ }
433
+ ```
434
+
435
+ ```bash
436
+ # Deploy with Foundry
437
+ forge script script/Deploy.s.sol --rpc-url $SEPOLIA_RPC_URL --broadcast --verify
438
+ ```
439
+
440
+ ### Verification
441
+
442
+ ```bash
443
+ # Hardhat
444
+ npx hardhat verify --network sepolia <CONTRACT_ADDRESS> <CONSTRUCTOR_ARGS>
445
+
446
+ # Foundry (automatic with --verify flag during deployment)
447
+ forge verify-contract <CONTRACT_ADDRESS> MyToken --chain sepolia
448
+ ```
449
+
450
+ ## Security Checklist
451
+
452
+ Before considering a contract ready for deployment:
453
+
454
+ ### Critical
455
+
456
+ - [ ] No reentrancy vulnerabilities (checks-effects-interactions or ReentrancyGuard)
457
+ - [ ] All external/public functions have appropriate access control
458
+ - [ ] No unchecked external call return values (use SafeERC20 for token transfers)
459
+ - [ ] Integer arithmetic is safe (Solidity 0.8+ default, check unchecked blocks)
460
+ - [ ] No delegatecall to untrusted contracts
461
+ - [ ] Upgradeable contracts have proper storage gaps and initializers
462
+
463
+ ### High Priority
464
+
465
+ - [ ] Front-running protection where needed (commit-reveal, deadlines)
466
+ - [ ] Oracle data has staleness checks and fallback mechanisms
467
+ - [ ] No unbounded loops that could hit block gas limit
468
+ - [ ] Signature replay protection (nonces, deadlines, chain ID via EIP-712)
469
+ - [ ] Proper event emissions for all state changes
470
+ - [ ] Constructor/initializer sets all critical state variables
471
+
472
+ ### Medium Priority
473
+
474
+ - [ ] Gas optimization: storage packing, calldata usage, view/pure modifiers
475
+ - [ ] NatSpec documentation on all public interfaces
476
+ - [ ] Consistent error handling (custom errors preferred over require strings)
477
+ - [ ] Immutable and constant keywords used where applicable
478
+ - [ ] No floating pragma (use exact version: `pragma solidity 0.8.24;`)
479
+
480
+ ### Pre-Mainnet
481
+
482
+ - [ ] Professional audit completed
483
+ - [ ] Testnet deployment tested end-to-end
484
+ - [ ] Deployment scripts tested on forked mainnet
485
+ - [ ] Emergency pause mechanism if appropriate
486
+ - [ ] Admin key management plan (multisig, timelock)
487
+ - [ ] Monitoring and alerting set up for critical events