T31 · AI-assisted Crypto Development
Can a contract written by AI go to mainnet?
- Capabilities practiced
- AI LiteracyBuilder
- Hands on
- Have AI write a module. Review it line by line and count the nonexistent APIs it invented.
- AI Lab
- Give AI the trace of a failed transaction to diagnose; verify its conclusion yourself with a debugger.
A Real-World Problem
At four on Friday afternoon, you need a staking module: users deposit tokens, earn rewards over time, and can withdraw whenever they want.
You describe it to a model. Forty seconds later, you have two hundred lines. They compile. You ask for tests too: twelve cases, all green.
You stare at the screen. You have not read a single line, but it looks tidier than what you could write in half a day: complete comments, consistent names, even reentrancy protection.
It goes live on Monday. Do you deploy?
Before answering, answer two smaller questions:
- What does compilation prove? That the referenced symbols exist and types match. It does not prove that any line does what you intended.
- What do passing tests prove? The same model wrote them in the same conversation from the same possibly mistaken understanding. Passing proves only that the implementation and tests agree.
This chapter addresses that gap: your output speed grew twentyfold; your verification speed did not.
Thought Experiment
Replace the model with a person to make the problem clearer.
A new colleague has three traits:
- Extremely fast output. Describe a requirement and get a complete implementation in half an hour.
- Broad knowledge that stops at a particular date. They know the ecosystem from two years ago, not last month's release—and do not know what they do not know.
- Never says “I'm not sure.” If they cannot recall an interface name, they write the most plausible name and continue with the same confidence.
How would you work with them?
One thing is clear: their speed would not make you read one fewer line. The third trait changes your review: check logic, but also whether the things they use actually exist. That is not usually part of reviewing a human colleague.
Now suppose they produce five thousand lines a week and you can carefully read eight hundred.
The difference does not disappear. There are three places it can go:
- Stay up late to read everything; after several weeks, you start cutting corners.
- Read only some; then decide which parts.
- Read none; then you are gambling.
Output capacity rose while acceptance capacity did not, moving the bottleneck from writing to reading. That is the chapter's real subject.
Your Decision
Back to the staking module: Monday release, two hours available. How do you use them?
What Happens
The approaches catch different kinds of problems.
| Approach | Can detect | Cannot detect | Unit cost |
|---|---|---|---|
| Manual testnet run | Main-flow failures, deployment configuration errors | Ordering, boundaries, economic logic | Very low |
| Second-model review | Missing common patterns, obvious antipatterns | Hallucinated APIs, requirement mismatch, shared errors | Very low |
| Line-by-line review and documentation | Hallucinated APIs, version drift, semantic errors | Global invariants, cross-function state ordering | Very high |
| Tiered review | All three error classes on fund-related paths | Errors on other paths | Medium |
| Compiler | Missing symbols, type mismatches | Existing symbols with wrong semantics | Zero |
| Tests written by the model | Implementation inconsistent with the model's understanding | Implementation consistent with its misunderstanding | Zero |
Pay particular attention to the last two rows.
The compiler is free and catches a classic model error: referencing something nonexistent. In a compiled language, an invented function name does not survive thirty seconds.
The dangerous case passes both compiler and tests: the symbol exists, types are correct, tests pass, but behavior does not match the requirement. Tests from the same understanding do not help. If “rewards accrue per second” was understood as “per block,” both code and tests use blocks and all twelve pass.
In short, AI made producing mistakes almost free without making their detection equally cheap. The risk distribution shifted toward code that looks entirely normal.
Build a Model
Classify generated-code errors. Their detectors differ, making this table particularly useful.
| Type | Appearance | Detector | Danger |
|---|---|---|---|
| Hallucinated API | Nonexistent method, argument, library, or event | Compiler / build tool | Low: noisy but usually harmless |
| Version drift | Symbol exists but its semantics changed | Human checking documentation | High |
| Semantic drift | Correct implementation of a wrong understanding | Human returning to requirements | Highest |
| Tautological tests | Tests and code confirm the same misunderstanding | Human or externally sourced invariant | Highest |
Danger runs opposite to how ridiculous an error looks. Invented names stand out and fail compilation. Polished semantic errors that run smoothly cause real incidents.
Arrange acceptance into a chain, giving each link the job it does well:
- Generation
- Compilation
- Static analysis
- Tiered human review
- External invariant tests
- Fork tests
- Security review
One rule governs this chain and the architecture in the next chapter:
Whoever deploys is responsible. Review responsibility does not move with the method of production.
The model has no account or assets and cannot be held accountable. Once you press deploy, these two hundred lines become your code, regardless of who typed them.
There is also unavoidable arithmetic. Call the fixed amount of code you can carefully accept each week your review budget:
Deployable code = min(generated code, review budget)
AI raises generated volume by 10–20 times.
Optimistically, it raises review budget by no more than twice.
Therefore deployable volume ≈ review budget, largely unchanged.The conclusion points to a useful approach: use AI to spend less time producing the amount of code you can actually accept, rather than simply producing more. AI also helps reading: explain an unfamiliar contract, analyze a failed transaction trace, or infer invariants from an implementation. Raising review budget can be more valuable than generating another two thousand lines.
What It Is Called
A nonexistent function, argument, event, configuration option, or package with a plausible name.
Generation follows likely next words: safeTransferWithDeadline sounds reasonable to a language model even if no such method exists.
Strongly typed compiled languages usually catch it; configuration, scripts, and dependency lists may not, because they lack that compiler backstop.
The model's knowledge stops at a date that it usually does not disclose.
This is costly in fast-moving Crypto ecosystems: compiler defaults and library signatures change, and former best practices become antipatterns.
The check is the update date of official documentation, rather than asking the model whether it is current. It will say yes.
Code correctly implements a mistaken understanding of requirements.
Compiler, static analysis, and types cannot detect a formally correct implementation. Return to the requirement and ask which sentence each line fulfills.
Tests and implementation come from the same understanding, making the test a second copy of the implementation.
A common sign is expected values derived from code rather than requirements. “Reward equals rewardPerBlock times block count” copies the implementation and passes even when that implementation is wrong.
Give expectations an independent source: hand calculation, a white-paper formula, another implementation, or an invariant.
The complete execution call path: who called whom, arguments, where it reverted, and Gas consumed.
On-chain failure messages can be only a four-byte identifier. Giving a complete trace to a model for localization is useful pattern matching, with conclusions that can immediately be verified. That last property matters.
The code volume you can genuinely accept per unit time, determined by knowledge, attention, and time.
Making it explicit helps you allocate it instead of following whatever output volume arrives.
Hands-on
The output is your own error-distribution table, rather than the module.
Write a specification before giving it to the model. Keep it under thirty lines and include:
State: whose balances, and when updates happen
Actions: deposit / withdraw / claim and their preconditions
Math: reward calculation, units, decimal precision
Boundaries: first deposit, last withdrawal, deposit and withdrawal in one block
Invariant: contract balance ≥ sum of all withdrawable balancesDo not skip this. Without a specification, you have nothing independent to compare against—only the model's own explanation.
Generate the implementation once. Keep the initial forty-second, two-hundred-line result rather than repairing it repeatedly in the conversation. Make it resemble the real situation.
Save that version separately. All statistics use it.
Read and label every line. Make four columns: line number, error class, how you found it, and danger.
Use the four classes above plus “correct.”
Rule: verify every external symbol in official documentation, including names, argument order, event signatures, modifiers, and dependencies. Open the documentation itself rather than relying on search summaries.
Count at least four things:
- Hallucinated APIs, and how many the compiler caught
- Version-drift instances and how far out of date each was
- Semantic errors and the kinds of logic containing them
- Minutes spent reading the two hundred lines
The fourth establishes your review-budget baseline for later chapters.
Verify the tests. For each of the twelve, ask: was this expected value calculated from requirements or copied from implementation?
Delete and rewrite those copied from code. This often removes more than half.
Deploy to a testnet and do three things: deposit, wait, withdraw. Hand-calculate rewards from the specification and compare on-chain values.
Use only testnet assets. Correctness has not yet been independently verified; mainnet requires the T28, T29, and T30 security process first.
Put the table in your documentation. It is more useful than a generic AI coding best-practices article, because it records this model's actual errors in your stack.
AI Lab
Trace analysis is a dependable AI use in Crypto development because conclusions can be independently checked in minutes. If it says the seventh call layer reverted, replay proves or disproves that claim. That differs from generating code without evidence.
Create a deliberate failure on a testnet: insufficient allowance, slippage protection, or access denial. Record the hash.
Provide complete material:
This transaction failed on a testnet.
[Full call trace: to, selector, calldata, return value at each layer]
[Transaction from, to, value, gas limit, actual gas used]
[Relevant contract source, or at least ABI]
[What I expected it to do]
Answer:
1. At which layer and call did it fail?
2. Which source error definition matches the error identifier?
3. What directly triggered the condition?
4. What upstream causes are possible? List hypotheses with confidence.
5. What should I change, and how do I verify the repair?
For questions 3 and 4, separate what is directly visible in the trace from inference.The final sentence is the most important: otherwise the model mixes inference and fact with identical confidence.
Verification order matters: first location, which takes about a minute; then error identifier in source, about five minutes; only then the hardest and most error-prone causal reasoning. If the first two fail, skip the third.
After five or six runs, you may see the pattern: localization is usually accurate, attribution often confidently wrong. That distribution is itself a takeaway.
Verify these points yourself after using AI
- Replay in a debugger to confirm the reported revert location
- Find the reported selector or error name in contract source
- Verify each cited function signature, argument order, and event in source or official documentation
- Explain why the proposed change prevents that step from reverting
- Relabel every conclusion as hypothesis or fact if possible causes were stated as certain
- Replay the same transaction in a Fork after the repair and confirm success
Real-World Case
Models suggest packages while generating code. Some names are invented: plausible purpose and name, but never published.
Researchers have repeatedly described the attack: collect frequently invented names, register them in public registries, add malicious code, and wait for developers to install the suggested dependency.
This bypasses the compiler: once the package exists, installation and compilation can work normally.
Defense is basic: manually verify every dependency, its publisher, downloads, repository, and latest update. In projects handling keys and funds, prioritize this over feature development.
Fees should be annualized and prorated by days for a partial year. The model instead prorates by call count and writes tests that perfectly confirm it.
All green. A reviewer glances at names and coverage and approves.
The shared feature is no independently sourced number. Expectations copy implementation, implementation follows a misunderstanding, and the loop is self-consistent.
Break it with an external anchor: calculate a value by hand, use the white-paper formula, or write an independent invariant such as fees never exceeding X% of principal. T30's invariant method applies directly.
A major contract-language update made overflow checking the default. Earlier production code needed a safe-math library; later code no longer did.
Models err in both directions: add an unnecessary old library to new code, wasting Gas, or assume automatic overflow checks in old code, creating a real vulnerability.
The latter has no obvious symptom. Code looks shorter, cleaner, and more modern, yet is wrong under one particular version.
Check version numbers before trusting the code.
A known psychological pattern is weaker scrutiny when automated output looks professional.
Generated code triggers it with names, comments, symmetry, and well-placed defensive checks, creating a feeling of professional endorsement.
Review records can expose the effect: the same reviewer raises nine comments on two hundred messy colleague-written lines, but two on two hundred polished model-written lines.
Change the reading direction: start at each state change and trace backward to where the value came from and who can influence it. Appearance matters less when reading backward.
Change One Variable
Hallucinated APIs fall considerably, version drift somewhat, while semantic drift stays largely unchanged.
The first two reflect knowledge gaps; capability can reduce them. Semantic errors often reflect imprecise requirements you supplied, a gap on your side.
Therefore stronger models make specifications more important. Weak models fail visibly; strong ones smoothly fill what you left unspecified with what they assume you meant.
Risk falls one or two levels, but remains.
Frontends can be redeployed, unlike irreversible contract mistakes. Still, scrutinize all transaction-construction code—units, precision, recipients, slippage—because errors lose real funds, and all dependency lists, because frontend dependency trees and poisoning opportunities are orders of magnitude larger.
Indexing has another risk: model-written reorganization handling often covers only the normal path, as T17 explained.
This is a useful, economical approach.
When you write implementation and the model receives requirements, the shared-origin problem automatically disappears: it need not inherit your private misunderstanding and may discover overlooked boundary combinations.
Beware its tendency to write tests that pass. Give it implementation and it may copy assertions from it, restoring the problem. Provide specifications without implementation.
This crosses the boundary on which the chapter relies: someone is reading.
It becomes the architecture prohibited in the next chapter—output gets irreversible execution power with nowhere to discover errors.
If deployment must be automated, automate an artifact that has already passed human review, rather than raw output. T32 turns this into an architecture for both code and transactions.
Takeaway Questions
AI lowers generation cost, while verification remains expensive. Cheap output expands, the expensive side stays constrained, and the bottleneck moves. Judge a coding tool by which cost it actually reduces.
If an AI-written contract loses funds, the person deploying it bears the consequences. The model has no account, assets, or contractual role. Review records therefore matter: after an incident, they may be your only evidence.
Keep at least three things human: dependency confirmation, because supply-chain risk cannot be automatically excluded; state-changing and fund-moving code, where semantic errors cost most; and deployment itself, the irreversible link. Put these in the CI checklist, beyond an informal team agreement.
The model has Level 2 authority here: read and write code and analyze traces, with no keys, deployment credentials, or production access. Draw that boundary before T32, which draws the same boundary at runtime.
Chapter Check
Compilation excludes symbol-level errors: referenced things exist, types and signatures match. This catches invented API names in strongly typed languages.
Tests exclude implementations inconsistent with their expectations. Those expectations may differ from requirements if tests and code share an understanding; the gap contains the risk.
Together they still do not exclude semantic drift: correctly implementing the wrong requirement.
Use it for screening, but a human signs off.
Training overlap can produce shared mistakes, and the second model also hallucinates. You have two items requiring verification, rather than a verified conclusion.
Responsibility is more fundamental: models bear no consequences. You deploy, so acceptance ends with you. This is another application of T32's rule against validating model output with another model.
Look at the source of the expected value.
Ask: would the assertion fail if the implementation were wrong?
- “Reward equals
rewardPerBlocktimes elapsed blocks” copies code and can pass the same mistake. Tautological. - “Deposit 100 tokens, wait 30 days, earn roughly 0.41 at 5% annualized” is hand-calculated from requirements and fails a wrong implementation. Independent.
Invariants are another option: they describe properties that must always hold and come from requirements rather than implementation.
Classify first.
Tier one is state changes, funds, and permissions. Read every line, hand-check math, and verify external calls in official documentation. Typically twenty or thirty lines take 80% of the time.
Tier two is views and read paths. Scan for returns trusted by tier one; a trusted read really belongs in tier one.
Tier three covers comments, events, logs, and formatting. Spend little time, but confirm event parameters match state changes so indexers receive correct data.
The point is knowing explicitly what you did not examine. Without tiers, you may still truly read only thirty lines but not know which thirty.
There is no standard answer. Check:
- Can you state the proportion? If not, the real problem is that you do not track it.
- Did unread code change state or move funds? If so, you have exposure of unknown size.
- Did lines reviewed per hour rise this month? Check whether you gained skill or merely began scanning.
- Have you used AI to accelerate reading: unfamiliar contracts, inferred invariants, traces? Using it only to write piles up the bottleneck.
The fourth is this chapter's main operational suggestion.
Key idea
AI increases output speed; responsibility for review does not move at all.