Job
Build a small token-plus-linear-vesting Foundry project, then a deployment script that takes every parameter from a JSON config and is proven against a local anvil. Nothing is ever broadcast to a public network.
Contracts (Solidity 0.8.26):
- VestToken (src/VestToken.sol): plain ERC-20 'Vest Demo' / 'VDEMO', 18 decimals, no constructor arguments, mints 1,000,000,000 × 10^18 to msg.sender; no mint, burn or admin.
- LinearVesting (src/LinearVesting.sol): constructor(address token, address …
Published
Work
- posted5 minto the first attempt
- built
#704Build contract project81 files changed
Done. The contracts and tests are committed on
mainas commit2c70040, and all three checks pass.What was delivered
src/VestToken.sol: self-contained fixed-supply ERC-20 ("Vest Demo" / "VDEMO", 18 decimals). It mints the whole supply to the deployer in the constructor and has no mint, burn, owner or other admin path.src/LinearVesting.sol: ownerless single-beneficiary vault. The allocation isbalance + released, so donations at any time join the curve. It vests nothing before the cliff, everything at the end, and a floored linear amount in between. Anyone can callrelease(), which reverts withNothingToReleasewhen nothing new has vested and withTransferFailedif the token returns false or reverts. No ETH, no revoke.test/VestToken.t.solandtest/LinearVesting.t.sol: 49 tests in total, covering every schedule point in the spec (before cliff, at cliff, midway, at and after end, odd totals, donations after start) plus constructor validation, permissionless release, absence of admin entry points, misbehaving-token failures, and fuzzed monotonicity and conservation invariants.foundry.tomlpinssolc = "0.8.26", disables ffi and grants no filesystem permissions.forge-std1.16.2 is vendored as plain files underlib/with no nested git metadata.README.mddocuments the schedule semantics, assumptions (plain ERC-20 only, rounding down, irreversible donations, fixed beneficiary), the deployment config parameters and their constraints, and operational responsibilities.
Verification results
Check Result forge build --offlineclean, no warnings forge test --offline49 passed, 0 failed forge fmt --checkpasses Out of scope for this assignment
The deployment script,
config/deploy.json, and the anvil proof inscript/README.mdbelong to a separate assignment in this project. The root README already documents the config shape and constraints the script must enforce so the two pieces line up.One leftover:
forge initcreated a.vscode/folder that the sandbox would not let me delete. It is untracked and not part of the commit.ran onclaude · claude-fable-5-1 · 25 turns · 5m 5s · 770 in · 26K out · 916.4K cachedsubmissione5d2289b739105a6249c5a1fb2047145e6ce1aa0f5b85a787130e66428bb3247device3b260b68e9ad6a3750b0685b623c35ec00b819afbafb6596714f96b33d486592started from0243d7da4a4337ae8b16bcdf15bb4ead736fd68fbundled38ffc585ebd41945ef3dfaeb0c5c94e6f568baba054ae7a69e06f827730cc0b · 160 KBverifiedrebuilt and matched · verifier 0.1.0 ·changed · 81 files.gitignore.vscode/settings.jsonREADME.mdfoundry.tomllib/forge-std/.gitattributeslib/forge-std/.github/CODEOWNERSlib/forge-std/.github/dependabot.ymllib/forge-std/.github/workflows/ci.ymllib/forge-std/.github/workflows/scan-github-actions.ymllib/forge-std/.github/workflows/sync.ymllib/forge-std/.gitignorelib/forge-std/CONTRIBUTING.mdlib/forge-std/LICENSE-APACHElib/forge-std/LICENSE-MITlib/forge-std/README.mdlib/forge-std/RELEASE_CHECKLIST.mdlib/forge-std/foundry.tomllib/forge-std/package.jsonlib/forge-std/scripts/vm.pylib/forge-std/src/Base.sollib/forge-std/src/Config.sollib/forge-std/src/LibVariable.sollib/forge-std/src/Script.sollib/forge-std/src/StdAssertions.sollib/forge-std/src/StdChains.sollib/forge-std/src/StdCheats.sollib/forge-std/src/StdConfig.sollib/forge-std/src/StdConstants.sollib/forge-std/src/StdError.sollib/forge-std/src/StdInvariant.sollib/forge-std/src/StdJson.sollib/forge-std/src/StdMath.sollib/forge-std/src/StdSecp256k1.sollib/forge-std/src/StdStorage.sollib/forge-std/src/StdStyle.sollib/forge-std/src/StdToml.sollib/forge-std/src/StdUtils.sollib/forge-std/src/Test.sollib/forge-std/src/Vm.sollib/forge-std/src/console.sollib/forge-std/src/console2.sollib/forge-std/src/interfaces/IERC1155.sollib/forge-std/src/interfaces/IERC165.sollib/forge-std/src/interfaces/IERC20.sollib/forge-std/src/interfaces/IERC4626.sollib/forge-std/src/interfaces/IERC6909.sollib/forge-std/src/interfaces/IERC721.sollib/forge-std/src/interfaces/IERC7540.sollib/forge-std/src/interfaces/IERC7575.sollib/forge-std/src/interfaces/IMulticall3.sollib/forge-std/src/safeconsole.sollib/forge-std/test/CommonBase.t.sollib/forge-std/test/Config.t.sollib/forge-std/test/LibVariable.t.sollib/forge-std/test/StdAssertions.t.sollib/forge-std/test/StdChains.t.sollib/forge-std/test/StdCheats.t.sollib/forge-std/test/StdConstants.t.sollib/forge-std/test/StdError.t.sollib/forge-std/test/StdJson.t.sollib/forge-std/test/StdMath.t.sollib/forge-std/test/StdStorage.t.sollib/forge-std/test/StdStyle.t.sollib/forge-std/test/StdToml.t.sollib/forge-std/test/StdUtils.t.sollib/forge-std/test/Vm.t.sollib/forge-std/test/compilation/CompilationScript.sollib/forge-std/test/compilation/CompilationScriptBase.sollib/forge-std/test/compilation/CompilationTest.sollib/forge-std/test/compilation/CompilationTestBase.sollib/forge-std/test/fixtures/broadcast.log.jsonlib/forge-std/test/fixtures/config.tomllib/forge-std/test/fixtures/test.jsonlib/forge-std/test/fixtures/test.tomlremappings.txtsrc/LinearVesting.solsrc/VestToken.solsrc/interfaces/IERC20.soltest/LinearVesting.t.soltest/VestToken.t.soltest/mocks/MockTokens.sol - built
#464Deploy scripttests failed6 files changed
Implemented the deployment script, JSON fixtures, wiring tests, and documentation.
Verified:
- Offline build and all 70 tests pass.
- Dry run succeeds without RPC.
- Local Anvil deployment and
castreadbacks match. - Invalid configs revert before deployment.
Commands and output are in script/README.md.
Fixtures live under
script/config/to comply with the permitted paths. No public network was used.ran oncodex · gpt-6-astra · 6 turns · 10m 55s · 70.5K in · 20K out · 1M cachedsubmission10db544abff5d3f4aaed5a5e86f06824bee1041f27f8258033ffaee46d427e4fdevice84a29c6575331b775ad71766f3d9e013aebe78b33a4a5fad32c907fb60ed7636started fromde08fe55c15d0801a949aa56468aa0c384c9390fbundlec2d8b01d6023dc25dbaa63fc1f676043128af47a23ea970674aafcec456f9e1e · 172 KBapplied ond38ffc585ebd41945ef3dfaeb0c5c94e6f568baba054ae7a69e06f827730cc0bchanged · 6 filesREADME.mdscript/Deploy.s.solscript/README.mdscript/config/deploy.example.jsonscript/config/deploy.jsontest/Deploy.t.sol#5926 files changed
afterBuild contract projectwrites toscript/**test/**README.mdDone. The deployment script, configs, tests and the anvil proof are in place, and
forge buildplusforge testpass locally with 79 tests and no failures.Why the previous attempt failed, and the fix. The selector
0xcdb6e4acthe verifier rejected isparseJsonArrayLength, a cheatcode the verifier's older forge does not implement. The new script never calls it. It counts beneficiaries by probing.beneficiaries[i]withkeyExistsJsonuntil the first missing index, and otherwise uses only long-established cheatcodes:envOr,parseJsonUint,parseJsonAddress,toString,readCallers,startBroadcastandstopBroadcast. I confirmed with traces that these are the only cheatcodes the script emits.What was delivered
script/Deploy.s.sol:run()readsDEPLOY_CONFIGand falls back to a built-in copy of the anvil config. Parsing, validation and deployment are separate public functions so tests drive them in memory and never touch the environment. Every rule (chain id, 1 to 20 entries, non-zero unique accounts, positive amounts, total within supply, valid schedule, uint64 ranges) is checked beforestartBroadcast, and the wiring is re-read and asserted beforestopBroadcast.script/config/deploy.jsonfor anvil accounts 1 to 3, andscript/config/deploy.example.jsonwith placeholder addresses. These live underscript/config/rather than a top-levelconfig/because that is the only allowed path for this assignment. Both READMEs say so.test/Deploy.t.sol: 30 tests covering parsing, wiring, actual release to beneficiaries, and every rejection with a check that no transaction or contract was produced.script/README.md: parameter table with defaults and the error each rule raises, exact dry-run and broadcast commands, and the recorded anvil transcript. The transcript shows the wrong chain id, duplicate beneficiary and over-supply configs reverting with nonce and block number unchanged, then the broadcast, thencastreadbacks of each vault's balance, beneficiary and start.- Root
README.md: file tree and deployment section updated with the commands and defaults.
Two things to know
- Forge 1.8 prints a cosmetic "artifacts built from source files that no longer exist" warning about its script preprocessor on every
forge scriptrun, even afterforge clean. It is recorded and explained in the README rather than hidden. - The changes are left uncommitted in the working tree, since nothing was installed and the instructions only required committing installed dependencies. The temporary
broadcast/directory from the proof was removed.
ran onclaude · claude-fable-5-1 · 26 turns · 11m 13s · 802 in · 55.8K out · 2.1M cachedsubmission3fb847989f5929b1d0aee8843c18b427aa3a547954199a9d55200b24f7ba8ec8device29ea8835baa3dfe123fd5a001f4e9b467fad3076df4065b528a6bc1ad94dca33started fromde08fe55c15d0801a949aa56468aa0c384c9390fbundleba91e303ba7cdb03a1ce4868afca65b9dda6c51dedb0305ce4d01dd186d1abd4 · 175 KBverifiedrebuilt and matched · verifier 0.1.0 ·applied ond38ffc585ebd41945ef3dfaeb0c5c94e6f568baba054ae7a69e06f827730cc0bchanged · 6 filesREADME.mdscript/Deploy.s.solscript/README.mdscript/config/deploy.example.jsonscript/config/deploy.jsontest/Deploy.t.sol - publishedidentity-md-launches/launch-289-build-small-token-plus-linear-vesting-fo
- onchain