How continuous monitoring catches what a point-in-time audit misses: a worked example

continuous monitoring
Table of Contents

Every so often somebody publishes a way to open a lock that has been on doors for years. Nothing about the lock changes. The metal is the same metal, the fitter’s paperwork is still honest about the day it was fitted, and the door still shuts. What changed is that the method is now written down, so the number of people who can use it goes from one to anyone who reads.

Code has the same property, and that is the whole case for post-deployment smart contract monitoring. A review is accurate about a moment. The world keeps moving afterwards. Below is one sequence, timed to the second and read off the chain, that shows precisely where a monitoring pass would have fired and where an audit could not have.

In one paragraph

On 7 February 2023 at 10:10:30 UTC, a lending protocol created a new collateral vault on Arbitrum. Seventy-two seconds later it attached a price model that reads get_virtual_price() from an external Curve pool. That function had been publicly documented as manipulable since 11 October 2022, 121 days before the exploit. The vault was exploited 60 hours and 59 minutes after it was created, and its Optimism twin 40 seconds after that.

The timeline, read off the chain

Everything in this table was pulled from a public node on 9 September 2026. Block numbers, timestamps and token amounts are the chain’s own record, not a summary of somebody’s write-up.

When (UTC) What happened Where it is recorded
2022-04-14 Curve and the affected projects known at the time are privately told that get_virtual_price can be manipulated The disclosing researchers’ own post-mortem
2022-10-11 The full technical detail is published, including working attack code The same post-mortem, dated on the page
2023-02-07 10:10:30 dForce creates vault 0x2ce498b7 on Arbitrum, a market that accepts a Curve ETH/wstETH gauge position as collateral Contract creation transaction, to: null
2023-02-07 10:11:42 Its price oracle attaches price model 0x9a0b5702 to that new market. The model’s runtime bytecode contains the selector for get_virtual_price() Oracle event log, plus eth_getCode
2023-02-07 13:33 to 13:43 The first four user transactions hit the vault Contract logs
2023-02-07 20:54:59 Balancer merges a reentrancy guard library into its own codebase, a mitigation for the same class of flaw in its own pools Pull request 2207
2023-02-08 02:13:22 The same deployer address creates the Optimism twin, 0xdfec2ea8 Contract creation transaction, to: null
2023-02-09 23:10:22 The Arbitrum vault is exploited in block 59,527,634 The transaction
2023-02-09 23:11:02 The Optimism twin is exploited by the same account, 40 seconds later, in block 73,284,102 The transaction

The Arbitrum transaction is worth reading in full, because what it does not record is as instructive as what it does. Its receipt holds 113 events. The account borrows roughly 68,429 WETH from nine lenders in one flash loan sequence, unwraps it, and adds it to the Curve ETH/wstETH pool for 65,343.3531 LP tokens. It stakes 1,904.7619 of those, deposits the resulting gauge position into the brand new dForce vault, and mints 2,080,000 USX, the protocol’s own stablecoin, against it.

Then the part that matters, still inside the same transaction:

receipt 0x5db5c240..., Arbitrum block 59,527,634, 113 events, in order

log  10   LP minted to the attacker                    65,343.3531 wstETHCRV
log  27   vault shares minted                           1,904.7619 vwstETHCRV-gauge
log  31   USX minted                                2,080,000.0000 USX
log  36   USX burned                                  560,525.5265 USX
log  42   USX burned                                  300,037.0341 USX
log  45   vault shares in, from 0x916792f7...           1,019.5773 vwstETHCRV-gauge
log  47   vault shares burned                           2,924.3392 vwstETHCRV-gauge
log  50   gauge position out of the vault               2,924.3392 wstETHCRV-gauge
log 111   USX to the attacker's own account            719,437.4394 USX

Read the middle of that. 860,562.5606 USX of debt is burned, and 1,019.5773 vault shares arrive from a third address. That is what a liquidation looks like in a receipt: somebody else’s position, repaid and taken. The attacker then redeems 2,924.3392 shares, which is exactly the 1,904.7619 it deposited plus the 1,019.5773 it took, pulls its liquidity back out of the Curve pool, repays every lender before the transaction ends, and keeps 719,437.4394 USX.

The manipulation itself emits nothing. The Curve pool pays out native ETH, so the moment the attacker’s contract is handed control part way through a withdrawal is a fallback function, and a fallback writes no log. What the price model read during that window is not in the receipt and cannot be. That is the practical reason a behaviour watch has to reason about a whole transaction instead of matching single events.

Sixty-one hours, less eight seconds. That is how long that contract had been live.

What a reviewer could have seen, and when

Three things about this get collapsed into one complaint about audits. They are separate.

The Curve pool was not broken. The disclosing researchers say so explicitly, noting that the pool guarded every state-altering function with Vyper’s native reentrancy locks. What was unguarded was a view function, which by definition writes nothing and therefore looks harmless. Their sentence is the one to keep: “Protocols integrating with get_virtual_price were trusting the return value blindly.” The mistake lives on the reading side of the boundary, not the writing side.

The flaw was not knowable when most of this code was written. Before 11 October 2022 the class was not public. A reviewer working in 2021 or early 2022 was not going to flag a view function on a well-regarded pool for a manipulation nobody had described yet. That is not a missed finding, it is a finding that did not exist. It was not a niche pattern: the same post-mortem names MakerDAO, Enzyme, Abracadabra, TribeDAO and Opyn as price feeds that had been exposed to it and were secured before the details went out.

And the thing that actually broke was 61 hours old. dForce is not an unaudited protocol, and it publishes audit reports in a public repository. This is not a story about whoever wrote those reports. No report from anyone covered 0x2ce498b7, because on 6 February that contract did not exist.

Three moments a monitoring pass would have fired

The same timeline, read as a security operations problem instead of a code problem. Each row is a different watch, and each one had a different amount of warning.

The moment What kind of watch sees it Warning available What it can do
2022-10-11, the class is published A dependency and vulnerability watch, matched against the external contracts your own code reads 121 days Prevent. You stop reading the value, or you add the lock check
2023-02-07 10:11:42, the new market is priced A deployment and configuration watch on your own addresses 60 hours 58 minutes Prevent. The new market is reviewed before it holds anything
2023-02-09 23:10:22, the attack executes A behaviour watch on live transactions Seconds Alert only. It shortens the gap between the event and someone knowing

The first row costs the least to run, and it is the one fewest teams operate. It requires knowing which external functions your contracts call, then watching advisories, disclosures and project post-mortems against that list. In this case the answer had been sitting in public for four months, with working attack code attached.

That list is more tractable than it sounds. It is every external address your contracts call, every library you import with the version you pinned, and the compiler that built the bytecode, and it only changes when you deploy something. The first version usually comes straight out of your own deployment scripts. What makes it useful afterwards is that somebody owns keeping it current, and that a new entry on it triggers a look instead of a note.

The knowledge was not obscure by February 2023 either. On the same day that Arbitrum vault went live, Balancer merged a library into its own codebase whose whole job is to make this class of reentrancy fail against its pools. Two protocols were reacting to the same published research inside the same 11 hours. One was shipping a guard. The other was listing a market that read the unguarded value.

The second row is the one this incident really turns on. A new market, a new collateral type or a new price model is a deployment, not a commit, so scanning a repository will not necessarily see it. Watching your own deployer and admin addresses will. A watch of that kind would have reported a plain sentence on 7 February: a new market is live, priced by a model that reads a function from a contract you do not control, and that function is on the list.

The third row is the weakest of the three, and it is the one the word ‘monitoring’ usually means. It is still worth a great deal, because the alternative is finding out from a user on social media. It just cannot undo the transaction it is telling you about.

Two kinds of decay met in one incident

Neither of them is a review failing at its job.

One is that knowledge moved. The code was the same code the day before and the day after 11 October 2022, and its risk was not. This is the half of smart contract audit expiry that is hardest to plan for, because it sounds like nobody’s fault, which is exactly what it is.

The other is that the protocol shipped. Adding a market to a live lending protocol is a completely ordinary thing to do on a Tuesday. It also moves the trust boundary outward, because the protocol now depends on the correctness of a contract it did not write and cannot upgrade.

One more detail: the 40 seconds between the two chains. Whatever watches Arbitrum has to watch Optimism too, and it has to be the same list on both, because the same deployment happened twice and the same attacker did not need a second idea.

What to actually watch after deployment

These are the four watches, in the order the incident above argues for.

Watch What it reads What it fires on
Dependency and vulnerability watch Advisories, audit-firm post-mortems and the security disclosures of the protocols you integrate with, matched against a maintained list of every external contract, library and compiler version your code depends on A published flaw affecting something you call
Deployment and configuration watch Contract creations, proxy upgrades, role grants and oracle or parameter changes from your own privileged addresses A new market, a new price model, a new signer, an implementation swap
Oracle and integration checks The value your pricing actually returns, including whether it can be read part way through an external call A price read inside a callback, a virtual price that falls, two sources diverging
Behaviour watch Live transactions and state Flash loan sequences, unusual volume and slippage, access control irregularities, griefing patterns, governance manipulation, oracle price deviation

Two rules make the difference between a watch and a dashboard. Every alert needs a severity and a named person who answers it, and the list of what you depend on has to be updated when you deploy, not when you remember. A monitoring setup that was configured at launch and never touched again decays the same way a report does.

Where Fidesium fits

Fidesium builds the second half of that table as a service. Post-deployment monitoring means custom detection tuned to a protocol’s own logic rather than generic thresholds, watching griefing patterns, loss socialisation, unusual volume and slippage, access control irregularities, flash loan sequences, governance manipulation and oracle price deviation, with severity classification and a documented escalation path. It is quoted per protocol, because the detection has to be written against your contracts.

The first half is the audit. Manual smart contract audits start at $5,000 and are line by line human review of logic, economics and intent, with two rounds of fix verification and one month of continuous scanning included. The reports are public: the audit portfolio is the work itself rather than a summary of it.

Between the two sits scanning on every commit and pull request, which runs from $399 a month and keeps the distance between the reviewed commit and the deployed bytecode small enough to see. Recent work on that side, including machine-readable findings that travel into your existing tooling, is in the January 2026 product update.

Buying all three still does not make a contract safe. Nobody honest will tell you otherwise. What the three of them do together is shorten the distance between the moment something becomes true about your code and the moment somebody on your side knows it. In the sequence above that distance was 121 days, then 61 hours, then nothing at all.

Every date, block number and address on this page was read from a public node in September 2026. Check the dates before relying on any of it.

Frequently asked questions

What does post-deployment smart contract monitoring actually watch?

Three different things, and they are often confused. It watches your own privileged activity, meaning deployments, upgrades, role changes and parameter changes. It watches live on-chain behaviour, meaning transactions and state that do not match how your protocol is supposed to work. And it watches the outside world, meaning published flaws in the contracts, libraries and compilers your code depends on.

What are the best practices for smart contract monitoring after deployment?

Keep a current list of every external contract and library your code reads, and check published advisories against it. Watch your own deployer and admin addresses, so a new market or a new price model raises a flag before it holds money. Give every alert a severity and a named person who answers it. Run identical monitoring on every chain you have deployed to, including the quiet ones.

Which methods and techniques does post-deployment monitoring use?

Detection tuned to a protocol’s own logic rather than generic thresholds, which means rules written against your contracts and your economics. Common techniques are invariant checks that assert something which should always hold, deviation checks between two independent price sources, sequence detection for patterns such as a flash loan wrapped around a state change, and event watching on privileged functions. Dependency tracking sits alongside them and is not on-chain at all.

Can monitoring catch a vulnerability that did not exist when the code was reviewed?

Yes, and that is the strongest argument for it. The manipulation used against a dForce vault in February 2023 was published in October 2022, and the practice it defeated, reading a pool’s virtual price and trusting the number, was already widespread by then. A review that ran before that publication date could not have flagged it. A watch matching published disclosures against the external functions your contracts call would have raised it 121 days before the loss.

How soon does a newly deployed contract need to be monitored?

Immediately, and the February 2023 case is the reason. That vault was created at 10:10:30 UTC on 7 February, was given a price model 72 seconds later, took its first user transactions just over three hours after that, and was exploited 60 hours and 59 minutes after it existed. A monitoring setup that only covers the contracts you deployed last quarter is not covering the ones most likely to be wrong.

What is read-only reentrancy?

A reentrancy that goes back into a view function rather than a state-changing one. View functions are usually left unguarded because they write nothing, so they stay callable while the contract is halfway through an operation and its state is briefly inconsistent. Anything that trusts the returned number gets a wrong answer. In the Curve case the affected value was get_virtual_price, which other protocols used to price liquidity positions.

Does listing a new market or a new collateral type need a security review?

Yes, and it is the change most often treated as configuration rather than as code. A new collateral type introduces a new price path and usually a dependency on a contract you did not write. The review is small, because it is scoped to that market and its pricing rather than to your whole system, so it should not be quoted or scheduled like a fresh audit.

How do you monitor the same protocol across more than one chain?

With the same rules, deployed everywhere, and one place that collects the alerts. The dForce vaults on Arbitrum and Optimism were created a day apart and exploited 40 seconds apart by the same account, so a watch on one chain would have been reporting an incident while the second one was already happening. Chain differences belong in the configuration, not in the coverage.

Share:

More Posts

Scan your project now for free

Tell us your security needs