The contract
Arc
An arc is the graduated sixth of a circle the instrument is named after: the part that holds the reading. An Arc holds one tokenised equity and mints Verniers against it — and it has two exits, because only one of them needs a price.
properties, executed. 20 held on the last build.
state-changing functions in the compiled ABI, checked against a written-down surface.
sabotages that must be caught. One more is expected not to be, and says why.
of them caught by nothing in Solidity — only by reading the compiled ABI.
The design
Only one of the two exits needs a price
fixInKind
Pays your share of what the Arc holds, pro rata. No price appears in it at all — a ratio of two integers the contract already knows.
P9 executes this: the pool is moved up, down and back across four Sights, and the payout for the same shares must be identical to the wei each time.
fixToStable
Pays in USDG, which means the Arc buys your equity from you. That needs a number, and it uses the worse of its last two observations.
P13 executes this: sight either side of a move, in either direction, and the exit never beats the in-kind claim at the lower mark. And it refuses rather than marking its way out.
There is no maxAge
anywhere in this contract, and that is a decision rather than an omission. markAge() is
public because a caller may want it; nothing here treats it as safety.
| property | held | |
|---|---|---|
| P00 | the first deposit mints, and an in-kind Fix returns exactly what went in | ok |
| P01 | rounding never favours the holder, at every size | ok |
| P02 | a stray transfer moves no price until accumulate accounts for it | ok |
| P03 | the inflation attack is unprofitable: the victim keeps 99.9% after a 100x donation | ok |
| P04 | both exits pay exactly what their preview quoted, to the wei | ok |
| P05 | totalAssets tracks the position rather than the token balance | ok |
| P06 | a Dip above the cap in code reverts, and one at the cap is accepted | ok |
| P07 | a stranger reaches none of the owner-only functions | ok |
| P08 | accumulate charges exactly the Dip on the harvest, and no more | ok |
| P09 | IN KIND NEEDS NO PRICE: move the pool anywhere and the same shares pay the same wei | ok |
| P10 | accrued Dip is outside every holder's claim and survives a full exit | ok |
| P11 | Stow halts Sight, commit, deposit and accumulate — and halts NEITHER exit | ok |
| P12 | the stable exit converts at the WORSE of the last two marks | ok |
| P13 | THE OPTION IS CLOSED: sight either side of a move and the stable exit never beats the in-kind claim at the lower mark | ok |
| P14 | a Sight that moves further than the band in one step reverts | ok |
| P15 | a second Sight in the same block reverts, which is what makes the worse-of-two rule mean anything | ok |
| P16 | transferring a Vernier moves the claim and moves no price | ok |
| P17 | a zero deposit, either Fix, a commit or a fund all revert | ok |
| P18 | the pool's orientation is read from the pool, both ways round, and a pool of another pair is refused | ok |
| P19 | the stable exit refuses rather than marking its way out, and the in-kind exit still works in the same block | ok |
The second half is the point
Break it, and watch the suite bite
A passing test suite is evidence of nothing until you have seen it fail on purpose. Each entry below is a copy of the contract with one deliberate defect, compiled fresh and put through the same properties. A sabotage that passes is a hole in the suite and it fails the build.
| defect | caught by |
|---|---|
| the stable exit quotes the latest mark instead of the worse of the last two the whole design. Without it a redeemer picks the better of two observations and the difference comes out of everyone who stayed | P12, P13 |
| the in-kind exit is routed through the mark the exit that needs no price is the one thing on this contract that cannot be wrong. Route it through an observation and it can | P00 |
| the Dip cap is not enforced a fee capped in prose is not capped | P06 |
| the position is read from the token balance the classic donation vector, and the reason the accounting is tracked | P05, P08 |
| a second Sight in the same block is allowed without it an attacker moves the pool, sights twice, and both marks are the price they just made — which empties the worse-of-two rule of all its content | P15 |
| a Sight accepts any size of step a pool that moves thirty per cent in one block has been traded through, not repriced, and the answer is to stop rather than to quote it | P14 |
| Stow also halts the in-kind exit an emergency stop that traps holders is the emergency | P11 |
| the pool orientation is assumed rather than read a vault pointed at a pool the wrong way round does not revert. It quotes a reciprocal, and the only symptom is a price that looks odd to somebody who already knows the answer | P18 |
| an owner-only rescue() is added caught by NOTHING in Solidity. Every property still holds — none of them says anything about a function that does not exist — and the owner can take the principal. Only a mechanical read of the compiled ABI against a written-down surface sees a function that was added | the ABI surface — rescue(address) |
| the amount check at the top of deposit is removed NOT caught, and correctly so: previewDeposit(0) is 0, so the share guard one line below refuses exactly the same calls and P17 still holds. Two guards, one hole. This is why nothing on the site claims the amount check is what makes a zero deposit impossible | nothing |
The one worth reading twice is rescue(). Adding it breaks no
property — none of them says anything about a function that does not exist — and it hands
the owner the principal. A hand-written list of “functions the owner can call” would have
tested the list.