Skip to content

fix(hooks): [SUP-21367] honour usePrevHookAmount in ApproveERC20Hook; stop value-moving HyperCore leaves forwarding - #1029

Merged
supervaulter merged 6 commits into
devfrom
cosmin-sup-21367-update-passthrough-behavior-of-approve-and-hypercore-hooks
Oct 8, 2026
Merged

supervaulter merged 6 commits into
devfrom
cosmin-sup-21367-update-passthrough-behavior-of-approve-and-hypercore-hooks

Conversation

@supervaulter

@supervaulter supervaulter commented Oct 8, 2026 •

Copy link
Copy Markdown
Collaborator

Closes SUP-21367.

Origin: the Slack thread on BatchTransferHook → ApproveERC20Hook → swap producing a 0-amount swap. Covers both halves of the ticket — but with different fixes, because they turned out to be different defects. The approve hook needed its flag honoured; the HyperCore hooks needed their _pipeMode() corrected.

Note on review history: my first comment below argued the HyperCore hooks needed no change at all. That conclusion was wrong and there's a correction further down. The reasoning in it still holds — those hooks genuinely cannot publish an output — I just drew the wrong conclusion from it. Both are left in place so the reasoning is auditable.

The bug, in one line

ApproveERC20Hook._preExecute gated on prevHook != address(0) alone, so mid-chain it always delegated to BaseHook's PASSTHROUGH. usePrevHookAmount therefore only ever reached the allowance in _buildHookExecutions — from a downstream consumer's point of view the flag was ignored.

- if (prevHook != address(0)) {
+ if (prevHook != address(0) && _decodeBool(data, USE_PREV_HOOK_AMOUNT_POSITION)) {

Measured before the change, to show the flag really had no effect on what was reported onward:

chain position approve's own amount usePrev reported onward
mid-chain, prev → 0 1000 false 0
mid-chain, prev → 777 1000 false 777
position 0 1000 false 1000

The rule now

The hook reports whatever allowance it set. usePrevHookAmount governs both halves consistently:

  • true → allowance is the previous hook's outAmount, and that same value is forwarded
  • false → allowance is this hook's own amount, and that is reported onward

Position 0 already behaved the second way, so this also removes a positional inconsistency: the same hook with the same flag no longer reports different kinds of thing depending on where it sits.

What it fixes

A chain whose source produces no output no longer silently zeroes. A multi-token BatchTransferHook reports 0 — it has no single (amount, token) output, and its decodeAmounts() / amountRoles() are empty by design — and 0 is indistinguishable from a real zero. So batchTransfer → approve(usePrev=false) → swap(usePrev=true) swapped nothing, and setting usePrev=false on the approve did not help, because the flag never reached the pipe.

Read this before approving — the trade-off is real

An allowance is an upper bound, not a quantity held. With usePrev=false, a downstream consumer now inherits a figure the account may not actually have:

swap (receives 777) → approve(1000) → deposit(usePrev=true)
   before: deposit gets 777  ✅ works
   after:  deposit gets 1000 ❌ reverts on the shortfall

Blast radius: any chain with a mid-chain approve at usePrev=false followed by a usePrev=true consumer, where the approve amount ≠ the upstream output. Approving a generous round-number cap is a normal pattern, so this wants grepping in OMS/app templates before it ships. I can't see those templates from the repo.

test_PreExecute_UsePrevFalse_ReportsOwnAmount_AllowanceAboveBalanceIsCallersProblem records the choice deliberately, so a future change back to 777 is a conscious reversal rather than an accident.

Tests

Six added. None existed for this before — the two prior getOutAmount assertions only call postExecute, so the behaviour everyone was debating was untested, and this change would have passed CI silently in either direction.

test/unit/hooks/* 3716 · accounting 1312 · executors 54 · SuperExecutor 11 · validators 59 · script 35 · invariant 29 — all green.

Deployment

Bytecode moved, so all three artifacts are regenerated. Same deploy name: the CREATE2 address is keccak256(0xff ++ deployer ++ salt ++ keccak256(initcode)), so new code lands at a new address under the unchanged salt, and the old address stays live for roots already signed against it — the same pattern as SUP-21143's LOAN hook re-pin.

namespace new address
PROD1.0.0 0x17DdFE8988cF71BBd672e5daFc4f6D02Cf279fA4
STAGING1.0.0 0x0cEE1a19c3d099f16626678501f4D0F0ACA2F91D

Two operational notes: __deployContractIfNeeded skips a name already recorded in script/output/.../latest.json, so those records need updating or the deploy no-ops; and manifests/hooks.json carries per-chain addresses for this hook, which regenerate from the deployment output afterwards (unchanged by this PR).

Part 2 — HyperCore: the leaves that move value stop forwarding

Same ticket, different defect. Hook1 → HyperCoreSendAssetHook → Hook3(usePrevHookAmount = true) had Hook3 operate on Hook1's amount. Nothing looked wrong — the number was plausible, just stale, because SendAsset had moved the assets onto HyperCore in between.

The cause was not the missing usePrevHookAmount field. It was _pipeMode(). PASSTHROUGH is a claim — "the upstream amount still describes the value in flight" — and BaseHyperCoreWriterHook made it for every leaf and locked it non-virtual ("no leaf may regress to TRANSFORM"). True for a leaf that changes no balance; false for one that moves assets.

So _pipeMode() is virtual again, PASSTHROUGH stays the base default, and only the two value-movers override:

hook mode why
HyperCoreSendAssetHook TRANSFORM sends a spot balance away from the account
HyperCoreUsdClassTransferHook TRANSFORM moves USD between spot and perp classes
HyperCoreAddApiWalletHook PASSTHROUGH unchanged — registers an agent, moves nothing
HyperCoreApproveBuilderFeeHook PASSTHROUGH unchanged — a fee rate, moves nothing

These two publish nothing of their own, because there is nothing publishable: their amounts (amountWei, ntl) are uint64 HyperCore units offset per token by weiDecimals/evmExtraWeiDecimals, and BaseHyperCoreWriterHook records that CoreWriter "never reverts, never validates the action id, and never reports". A downstream consumer therefore reads 0 and fails — Swap1InchHook, Deposit4626VaultHook, ApproveAndDeposit4626VaultHook, AaveV4LendHook and TransferERC20Hook all guard amount == 0. Failing is the right outcome: no hook after these two can legitimately derive its amount from them.

Cheap, because _pipeMode() is internal pure and inlined — not in the calldata, not in storage, not in the data layout. No new field, no data-length change, no inspector/template/manifest update, no roots invalidated. And adding virtual to the base left the other three leaves byte-identical, so AddApiWallet, ApproveBuilderFee and ApproveAndHyperCoreDeposit keep their live addresses. Only two move:

hook deployed (HyperEVM 999) new (PROD1.0.0)
HyperCoreSendAssetHook 0x65aea557e595b9e841Da9F47aae8EacC4650Ea6a 0x541ce604cED66dFb2FE0F2Cc951Bffb3f36eEd7D
HyperCoreUsdClassTransferHook 0x0E4eDd421D0707A454C3c0435DeCF5D708A8b29C 0xa02f963Af997032D9b5bdaF42BC5A1Db15e1c57C

Staging: 0x035799A5871634F7B24562804273B139Aadb5AE6 and 0xDef5387D00384B5a65a50f7B07ed6455b4F0B93B.

One existing test pinned the old behaviour and is updated rather than deleted — test_Passthrough_ForwardsPrevHookOutput made its assertion with classTransfer, which now moves value; it asserts the same thing with addAgent, which doesn't. Three tests added, including test_PipeMode_Chain_DownstreamFailsInsteadOfUsingAStaleAmount, which runs the reported chain with TransferERC20Hook as a real consumer and asserts it reverts AMOUNT_NOT_VALID through SendAsset but still builds through AddApiWallet.

Tooling gap closed on the way: the entire HyperCore family was missing from regenerate_bytecode.sh, so a source change to any of those five left the artifacts stale and a deploy would have shipped the previous bytecode. Added as its own group.

Known gap, deliberately not in scope

Six other hooks share the exact same shape — PASSTHROUGH plus a usePrevHookAmount field — and still ignore the flag for reporting: RecordPurchasePendlePTHook, RecordRedemptionPendlePTHook, and the four *AmortizedOracleHook / V2 variants. They don't even override _preExecute, so they go straight to BaseHook's unconditional forward.

I left them because their usePrev=true path is pinned to one specific predecessor (if (prevHook != APPROVED_PENDLE_PT_HOOK) revert PREV_HOOK_NOT_VALID(), plus operation/market/token checks), so forwarding is correct there by construction; their usePrev=false path is documented as standalone, where there is no predecessor to forward from; and they record rather than move value, so ptAmount mirrors PT that PendlePTHook already moved and already reported.

The honest cost: usePrevHookAmount now governs onward reporting in approve but not in those six. Happy to extend it if we'd rather have uniformity — same one-line pattern, six more hooks re-pinned.

🤖 Generated with Claude Code

https://claude.ai/code/session_0157RUiyLFJjzULEEq6h6biV

…ports onward

`_preExecute` gated on `prevHook != address(0)` alone, so mid-chain it ALWAYS delegated to
`BaseHook`'s PASSTHROUGH. `usePrevHookAmount` therefore only ever reached the allowance in
`_buildHookExecutions` -- from a downstream consumer's point of view the flag was ignored.

    if (prevHook != address(0)) {                                                    // before
    if (prevHook != address(0) && _decodeBool(data, USE_PREV_HOOK_AMOUNT_POSITION)) { // after

The rule is now uniform: the hook reports whatever allowance it set.

  usePrevHookAmount = true   allowance = prev's outAmount, and that value is forwarded
  usePrevHookAmount = false  allowance = this hook's own amount, and THAT is reported

Position 0 already behaved the second way, so this also removes a positional inconsistency: the
same hook with the same flag no longer reports different kinds of thing depending on where it sits
in the chain.

WHAT THIS FIXES. A chain whose SOURCE produces no output no longer silently zeroes. A multi-token
`BatchTransferHook` reports 0 -- it has no single (amount, token) output, and `decodeAmounts()` /
`amountRoles()` are empty by design -- and 0 is indistinguishable from a real zero. So
`batchTransfer -> approve(usePrev=false) -> swap(usePrev=true)` swapped nothing, and setting
`usePrev=false` on the approve did not help because the flag never reached the pipe.

THE TRADE-OFF, deliberate and pinned by a test. An allowance is an upper bound, not a quantity
held, so a downstream consumer can now inherit a figure the account does not have:
`swap(receives 777) -> approve(1000) -> deposit(usePrev=true)` now carries 1000 and reverts on the
shortfall, where before it carried 777 and worked. Chains with a generous approval cap mid-chain
followed by a `usePrev=true` consumer are the blast radius and should be grepped in OMS before this
ships. `test_..._AllowanceAboveBalanceIsCallersProblem` records the choice so a future change to it
is a conscious one.

Six tests added. None existed for this: the two prior `getOutAmount` assertions only called
`postExecute`, so the behaviour under debate was untested and this change would have passed CI
silently either way.

Bytecode moved, so all three artifacts are regenerated. Same deploy name -- the CREATE2 address is
keccak256(0xff ++ deployer ++ salt ++ keccak256(initcode)), so new code lands at a new address under
the unchanged salt, and the old address stays live for roots already signed against it (same pattern
as SUP-21143's LOAN hook re-pin). New addresses:

  PROD1.0.0      0x17DdFE8988cF71BBd672e5daFc4f6D02Cf279fA4
  STAGING1.0.0   0x0cEE1a19c3d099f16626678501f4D0F0ACA2F91D

NOT CHANGED, and why: the HyperCore hooks. `HyperCoreSendAssetHook` and
`HyperCoreUsdClassTransferHook` are PASSTHROUGH and move value, so they look like the same problem,
but they cannot report an output at all. Their amounts (`amountWei`, `ntl`) are uint64 HyperCore
units which are not the scale of an EVM token amount -- both hooks already document that this is
exactly why they omit `usePrevHookAmount` -- and `BaseHyperCoreWriterHook` records that CoreWriter
"never reverts, never validates the action id, and never reports", so there is no EVM-observable
delta to measure instead. Adding the flag to them would only let them suppress forwarding while
still having nothing of their own to publish, which returns a downstream `usePrev=true` to 0. Their
case needs the chain-level fix (make "no output" distinct from 0 and revert when consumed), not a
per-hook one.

test/unit/hooks/* 3713, accounting 1312, executors 54, SuperExecutor 11, validators 59, script 35,
invariant 29 -- all green. Nothing in the suite depended on the old behaviour.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0157RUiyLFJjzULEEq6h6biV
@linear-code

linear-code Bot commented Oct 8, 2026

Copy link
Copy Markdown

SUP-21367

@supervaulter

Copy link
Copy Markdown
Collaborator Author

Why the HyperCore half of SUP-21367 is not in this PR

The ticket covers "approve and hypercore hooks". I looked at the HyperCore ones and concluded they need no change — not as a deferral, but because the fix applied here is inapplicable to them and the fix I originally proposed turns out to be impossible. Recording the reasoning so it doesn't get re-litigated.

First, only two of the four move value at all

hook amount in calldata moves value verdict
HyperCoreSendAssetHook uint64 amountWei @80 yes see below
HyperCoreUsdClassTransferHook uint64 ntl @52 yes see below
HyperCoreAddApiWalletHook none (agent + name) no unconditional passthrough already correct
HyperCoreApproveBuilderFeeHook none (maxFeeRate is a rate) no unconditional passthrough already correct

1. They have no usePrevHookAmount field — by design

This PR's fix is one line that reads that flag. These hooks don't have one, and both say why in their own docblocks:

HyperCoreSendAssetHook: "wei is in HyperCore units for the token, which differ from EVM units per token via weiDecimals and evmExtraWeiDecimals. This hook therefore does NOT support usePrevHookAmount and does not implement ISuperHookContextAware — the caller supplies a converted value."

HyperCoreUsdClassTransferHook: "It is NOT the same scale as an arbitrary EVM token amount: the EVM offset varies per token via weiDecimals and evmExtraWeiDecimals. This hook therefore does NOT support usePrevHookAmount…"

Adding the flag means changing their calldata layout → new data length → new inspectors, templates and manifest entries, and any signed roots for them invalidated.

2. Even with the flag, they would have nothing to report

The flag only lets a hook suppress forwarding. These two would still publish nothing of their own, so a downstream usePrevHookAmount = true would read 0 — exactly the bug this ticket exists to fix. The change would accomplish nothing.

3. And there is no EVM-observable delta to report instead

My first proposal was to reclassify them TRANSFORM and have them report a real balance delta, the way TransferHook and the swap hooks do with a pre/post read. That is not possible. From BaseHyperCoreWriterHook:

"CoreWriter cannot fail. It never reverts, never validates the action id, and never reports"

The value moves on HyperCore, not on the EVM, so there is no pre/post balance to diff. The only number available is the calldata amountWei / ntl, which is in HyperCore units — publishing that into outAmount, which downstream hooks consume as an EVM token quantity, would be a wrong-unit figure. That is worse than 0, because it looks plausible.

What would actually fix the HyperCore case

Not a per-hook change. Two things, in order of how soon they can ship:

  1. Off-chain, today, no contract change. Reject at intent-build time any usePrevHookAmount = true that follows a hook declaring no usable amount (amountRoles() empty). The sizing interface from feat: hooks standardization #923 already supplies that metadata — it just isn't being checked. This catches the HyperCore hooks and BatchTransferHook by the same rule.
  2. Chain-level, as its own release. Make "no output" a distinct state from 0 in BaseHook and revert when a consumer reads it. Then a swap placed after a HyperCore hook fails loudly in simulation instead of silently moving nothing. This re-pins every hook's bytecode (~120), so it wants to be a deliberate release rather than a rider on this PR.

The practical takeaway

Subhasish was right that the HyperCore hooks are affected — but by a different defect with a different fix. Worth being explicit about, otherwise the natural follow-up ("add usePrevHookAmount to the HyperCore hooks") gets filed and won't help.

Separately: the uint64 HyperCore-unit reasoning above is read off the hooks' own documentation rather than from Hyperliquid experience, so @vik should sanity-check point 3 — specifically whether a spot↔perp class transfer has any EVM-observable effect I've missed.

🤖 Generated with Claude Code

https://claude.ai/code/session_0157RUiyLFJjzULEEq6h6biV

@subhasishgoswami subhasishgoswami left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This fixes the Bundler incident's approval-output failure: with usePrevHookAmount=false, approval reports its configured amount and token, allowing the following dynamic Kyber swap to consume the adjusted amount.

Verified that the PR's compiled runtime, after immutable substitution, is byte-for-byte identical to the runtime used in the successful historical replay at Base block 52307672. The unchanged control reproduced ZERO_AMOUNT; changing only approval behavior completed both swaps, the combined vault deposit, and the final transfers to the owner. This validates the captured destination build simulation, before later fee-hook setup.

Successful replay: https://dashboard.tenderly.co/superform/v2/simulator/76393756-4600-4eb3-88f8-2c7904845e7c
Control: https://dashboard.tenderly.co/superform/v2/simulator/3dab580b-bcce-471a-9987-fc729647b4f2

No executor scaling or HyperCore change is needed for this incident. Rollout still requires deploying the corrected hook and updating Bundler registry selection/cache while preserving old hook identities. Build and manifest checks have passed; the test job was still running when reviewed.

supervaulter and others added 2 commits October 8, 2026 15:08
…ing the previous hook's output

`Hook1 -> HyperCoreSendAssetHook -> Hook3(usePrevHookAmount = true)` had Hook3 operate on HOOK1's
amount. Nothing looked wrong: the number was plausible, just stale -- SendAsset had moved the assets
onto HyperCore in between, so the figure described a balance the account no longer had.

The cause was not the missing `usePrevHookAmount` field. It was `_pipeMode()`. `PASSTHROUGH` is a
CLAIM -- "the upstream amount still describes the value in flight" -- and `BaseHyperCoreWriterHook`
made that claim for every leaf and locked it non-virtual ("no leaf may regress to TRANSFORM"). True
for a leaf that changes no balance; false for one that moves assets.

So `_pipeMode()` is virtual again, `PASSTHROUGH` stays the base default, and the two value-moving
leaves override `TRANSFORM`:

  HyperCoreSendAssetHook          TRANSFORM   sends a spot balance away from the account
  HyperCoreUsdClassTransferHook   TRANSFORM   moves USD between spot and perp classes
  HyperCoreAddApiWalletHook       PASSTHROUGH unchanged -- registers an agent, moves nothing
  HyperCoreApproveBuilderFeeHook  PASSTHROUGH unchanged -- a fee rate, moves nothing

`TRANSFORM` makes `BaseHook._preExecute` write nothing, and these two publish nothing of their own
because there is nothing publishable: their amounts (`amountWei`, `ntl`) are uint64 HyperCore units
offset per token by weiDecimals/evmExtraWeiDecimals, so they can never be an EVM-scale `outAmount`,
and `BaseHyperCoreWriterHook` records that CoreWriter "never reverts, never validates the action id,
and never reports", so there is no EVM-observable delta to measure instead.

A downstream consumer therefore reads 0 and FAILS rather than proceeding on a stale figure --
`Swap1InchHook`, `Deposit4626VaultHook`, `ApproveAndDeposit4626VaultHook`, `AaveV4LendHook` and
`TransferERC20Hook` all guard `amount == 0`. Failing is the correct outcome: no hook after these two
can legitimately derive its amount from them, so the chain shape is unsupportable and should say so.

WHY THIS IS CHEAP. `_pipeMode()` is `internal pure` and inlined -- it is not in the calldata, not in
storage, not in the hook's data layout. No new field, no data-length change, no inspector/template/
manifest update, no signed roots invalidated. Only bytecode moves, and only for the two leaves that
changed: adding `virtual` to the base left `AddApiWallet`, `ApproveBuilderFee` and
`ApproveAndHyperCoreDeposit` byte-identical, so they keep their live addresses.

                                  deployed (HyperEVM 999)                 new (PROD1.0.0)
  HyperCoreSendAssetHook          0x65aea557e595b9e841Da9F47aae8EacC4650Ea6a  0x541ce604cED66dFb2FE0F2Cc951Bffb3f36eEd7D
  HyperCoreUsdClassTransferHook   0x0E4eDd421D0707A454C3c0435DeCF5D708A8b29C  0xa02f963Af997032D9b5bdaF42BC5A1Db15e1c57C

Staging: 0x035799A5871634F7B24562804273B139Aadb5AE6 and 0xDef5387D00384B5a65a50f7B07ed6455b4F0B93B.

One existing test pinned the old behaviour and is updated rather than deleted:
`test_Passthrough_ForwardsPrevHookOutput` made its assertion with `classTransfer`, which now moves
value; it asserts the same thing with `addAgent`, which does not. Two tests added --
`test_PipeMode_ValueMovingLeavesDoNotForward` for the fix, and
`test_PipeMode_SideEffectLeavesStillForward` so the split is pinned as deliberate and a chain
legitimately relying on transparency through the other leaves keeps working.

Also closes a tooling gap found on the way: the HyperCore family was missing from
`regenerate_bytecode.sh` entirely, so a source change to any of these five left the artifacts stale
and a deploy would have shipped the previous bytecode. Added as its own group.

test/unit/hooks/* 3715, accounting 1312, executors 54, script 35 -- all green.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0157RUiyLFJjzULEEq6h6biV
…nstead of using a stale amount

The previous commit asserted what the two value-moving leaves REPORT. This asserts what a real
consumer then DOES, which is the actual reported scenario:

    Hook1 (outputs 1234) -> HyperCoreSendAssetHook -> TransferERC20Hook(usePrevHookAmount = true)

`TransferERC20Hook` is a production hook, not a stub: it reads `prevHook.getOutAmount` when the flag
is set and reverts `AMOUNT_NOT_VALID` on zero. Before the pipe-mode fix it received Hook1's 1234 and
would have transferred that much -- an amount that predates the HyperCore send. It now receives 0 and
refuses, which is correct: nothing after `SendAsset` can legitimately derive its amount from it.

The second half is the control, and it is what makes the first half mean something. The same
consumer, the same Hook1, routed through `HyperCoreAddApiWalletHook` instead, still receives 1234 and
builds. So the change is scoped to leaves that move value rather than having broken chaining through
the CoreWriter family generally.

Artifacts were verified unchanged while adding this: the four HyperCore JSONs re-emitted by
`regenerate_bytecode.sh` differ from HEAD only in build metadata (`id`, `metadata`, `sourceMap`), and
`bytecode.object` -- the only field `vm.getCode` and CREATE2 consume -- is byte-identical, so nothing
is re-committed. All five HyperCore artifacts and all three ApproveERC20Hook artifacts match a fresh
compile in both `generated-bytecode/` and `locked-bytecode/`.

test/unit/hooks/* 3716, all green.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0157RUiyLFJjzULEEq6h6biV
@supervaulter supervaulter changed the title fix(hooks): [SUP-21367] ApproveERC20Hook honours usePrevHookAmount for what it reports onward fix(hooks): [SUP-21367] honour usePrevHookAmount in ApproveERC20Hook; stop value-moving HyperCore leaves forwarding Oct 8, 2026
supervaulter and others added 3 commits October 8, 2026 16:30
…inned hooks

Predicted CREATE2 addresses for the contracts whose bytecode moved. 19 files: the 18 prod chain
records plus `prod/latest.json`. Staging deliberately untouched.

  AaveV4LendHook                 0x51a2C8c6c427f5EB14b0dF826F87D518c8fFfEa0 -> 0xD39deF3542B33d5b309A8D4e69D7a8075e736Fd5
  AaveV4RedeemHook               0x320Fa43658093BA332293AA85D86608fb0ffFEbA -> 0x06fD9C9272a343aab5919fA3041D0a763E3cB4cC
  AaveV4ReserveOracle            0x1Cc0C873Ac9397D5e0aA301B2f594A95E8d573bC -> 0xFC6a204A1E9bFde616654F7305C978550B59630c
  ApproveERC20Hook               0x1851A98471ADE4a115B6FB7bd42934a200e58d9E -> 0x17DdFE8988cF71BBd672e5daFc4f6D02Cf279fA4
  HyperCoreSendAssetHook         0x65aea557e595b9e841Da9F47aae8EacC4650Ea6a -> 0x90dc9d2BB3cFF8E3b228F4a48Bb9678eCF52CD2E
  HyperCoreUsdClassTransferHook  0x0E4eDd421D0707A454C3c0435DeCF5D708A8b29C -> 0x50c925F20993E0A1C91623df444b226417866DEE

ApproveERC20Hook and the two HyperCore hooks move because of this PR. AaveV4LendHook,
AaveV4RedeemHook and AaveV4ReserveOracle move because this branch sits on dev post-#1026, which
re-pinned them; their records had not caught up.

Applied as a value-based replacement, so each file changed only where the old address actually
appeared: 4 contracts on most chains, 1 on Base (which does not carry the Aave trio at those
addresses), 6 on HyperEVM, and the aggregate in `latest.json` (Aave x17, ApproveERC20Hook x18).
All 19 files re-validated as JSON and no old address remains anywhere under `script/output/prod/`.

Note on the HyperCore addresses: these hooks take a `coreWriter_` constructor argument, so their
CREATE2 initcode is `bytecode ++ abi.encode(coreWriter)`. Earlier figures quoted in fa59e4d's
message and in the PR body were computed from the bare bytecode and are WRONG
(0x541ce604... and 0xa02f963A...). The addresses above are correct and match what the deploy script
itself computes; ApproveERC20Hook takes no constructor argument, so its figure was already right.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0157RUiyLFJjzULEEq6h6biV
…e-pinned hooks

Staging counterpart to the prod records. 10 files: 9 staging chain records plus `staging/latest.json`.

  AaveV4LendHook                 0xc8a7E20Cb33aF34345EDF32104F2Ea0A8e46281B -> 0xeb1a79E3a43A13833A3A3D42Cb567E07763D9478
  AaveV4RedeemHook               0xcfBAef145343c549a823E2621C4dd205483264BF -> 0x6a8b9495278Af714d5287A8Aa0779Cd6f91cf5f5
  HyperCoreSendAssetHook         0xeE0ef54d1BAA59aA1786AaA37B5F38D2D7e26193 -> 0x614dbD83E62F4eC7DEA965125f11AeA306ACdDAC
  HyperCoreUsdClassTransferHook  0x66194363DDeA94e301D8A128Bb0a1f4E3c605623 -> 0x20A19737c9D2ee8a2F5AC5221A02eA557e5b3491

Same value-based replacement as the prod commit, so each file changed only where the old address
appeared: 2 contracts on each of Ethereum, BNB, Arbitrum, Avalanche, Flare, RH, Arc and Plataberget,
4 on HyperEVM, and the aggregate in `latest.json` (Aave x9 each). All 10 re-validated as JSON, with
no old address left anywhere under `script/output/staging/`.

Two differences from prod, both intentional and worth noting for review:
- No `AaveV4ReserveOracle` entry. Staging does not carry it at the address prod moved from.
- No `ApproveERC20Hook` entry. Its staging bytecode moved in this PR too, so its staging address
  will change on redeploy (predicted 0x0cEE1a19c3d099f16626678501f4D0F0ACA2F91D under
  STAGING1.0.0), but it is not in the provided change set, so it is left alone rather than guessed at.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0157RUiyLFJjzULEEq6h6biV
@supervaulter
supervaulter merged commit c219e2d into dev Oct 8, 2026
1 check passed
@supervaulter
supervaulter deleted the cosmin-sup-21367-update-passthrough-behavior-of-approve-and-hypercore-hooks branch October 8, 2026 13:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants