Skip to content

[VPD-1862]: VIP to Configure Centrifuge YieldGroup to Liquidity Hub - #761

Draft
GitGuru7 wants to merge 4 commits into
mainfrom
vip/vpd-1862-centrifuge-adapter
Draft

[VPD-1862]: VIP to Configure Centrifuge YieldGroup to Liquidity Hub#761
GitGuru7 wants to merge 4 commits into
mainfrom
vip/vpd-1862-centrifuge-adapter

Conversation

@GitGuru7

Copy link
Copy Markdown
Contributor

Adds the Centrifuge YieldGroup to the Liquidity Hub (USDT) on BNB Chain Testnet:
grants the ACM roles on the new source, registers a Centrifuge ERC-7540 fund behind
AdapterCentrifuge, and adds the group to Hub_USDT.

Centrifuge is the first asynchronous source on the Hub — requests are escrowed and
settled later by the fund manager at a published NAV — so the group carries request,
cancel and claim operations that the synchronous IYieldGroupBase surface cannot
express.

Notes

The registered fund is a testnet mock. Centrifuge has no BSC-testnet deployment,
so there is no real ERC-7540 fund on chain 97. MOCK_CENTRIFUGE_VAULT_USDT is
Venus-controlled and has no mainnet counterpart — it must never appear in a mainnet
proposal.

Permissions: two holders, both with the full surface. The Normal Timelock, and the
Guardian multisig, which stands in for the Operator and the Keeper on this network.
Nobody else is granted anything. The FastTrack and Critical timelocks are left out —
granting all three exceeded the 30M block gas limit.

The Guardian is an ACM admin on testnet and granted itself ten signatures against
address(0) on 2026-08-03, which the ACM reads as "on any contract". Those already
cover the new source, so the proposal grants it only the 13 Centrifuge-specific
signatures the wildcards miss. Verified on chain: the Guardian holds all ten, all
three timelocks hold none, and no wildcard for this surface exists on mainnet.

The source itself gets pauseHub() on the Hub. A grant to a contract, not a role.
The drop guard's only reaction is the group calling pauseHub() with no try/catch, so
without it a genuine breach makes the permissionless enforceDropGuard revert instead
of pause — the breaker would be dead, not degraded.

No price guard is armed. Each needs a sizing decision against observed NAV
behaviour and an over-tight value is a self-inflicted halt. The roles are granted, so
arming them later needs no further VIP.

Withdraw queue only, Centrifuge first: [Centrifuge, FRV, Flux, Core]. The Hub
drains front to back, so settled assets earning nothing are spent before a productive
position; Core stays last as the liquid backstop. Centrifuge must be listed because the
Hub rejects a withdraw queue omitting a group that holds a balance. The deposit queue
is not touched — Centrifuge settles over days, so a user deposit routed there would sit
unwithdrawable in a pending request for a product the user never chose.

Testing

npx hardhat test simulations/vip-664/bsctestnet.ts --fork bsctestnet — 64 passing.

Covers, beyond the usual state assertions: that the Guardian's wildcards are live and
that the timelock has none (so the skipped grants are justified and its own grants are
load-bearing); that neither the FastTrack nor the Critical timelock holds anything; and
an end-to-end check that the Guardian can pause the new resource through the wildcard
with no per-contract grant of its own.

Follow-up

The mainnet proposal needs its own permission file. Mainnet separates the roles for
real — distinct Operator and Keeper addresses, a containment-only Guardian, price
guards kept with governance — and has no wildcards to lean on, so every signature must
be granted explicitly.

Grants the ACM roles on CentrifugeSource_USDT, registers a Centrifuge
ERC-7540 fund behind AdapterCentrifuge, and adds the group to Hub_USDT.
Centrifuge is the first asynchronous yield source on the Hub: requests are
escrowed and settled later by the fund manager at a published NAV, so the
group adds request, cancel and claim operations the synchronous
IYieldGroupBase surface cannot express.

The registered fund is a testnet mock controlled by Venus. Centrifuge has no
BSC-testnet deployment, so there is no real ERC-7540 fund on chain 97.

Centrifuge joins the WITHDRAW queue only. Settlement takes days, so routing
an ordinary user deposit there would park that user's capital in a pending
request, unwithdrawable, for a product they never chose; capital enters only
through an Operator reallocation targeted at the vault. It must still appear
in the withdraw queue, because the Hub rejects a queue omitting a registered
group that holds a balance.

The source is granted pauseHub() on the Hub — a grant to a contract, not to a
human role. The drop guard's only reaction is the group calling pauseHub()
with no try/catch, so without it a genuine breach makes the permissionless
enforceDropGuard revert rather than pause: the breaker would be dead, not
degraded.

No price guard is armed. Each needs a sizing decision against observed NAV
behaviour and an over-tight value is a self-inflicted halt; the roles are
granted so arming them later needs no further VIP.

Two constraints found by simulating rather than reasoning:
  - granting all three timelocks pushed the proposal to 30,151,896 gas, past
    the 30M block limit. Narrowed to the Normal Timelock, matching VIP-650's
    testnet proposal; now 9.55M to propose, 3.63M to execute.
  - testnet collapses Operator, Keeper and Guardian into one multisig, so
    pauseResource(address) was granted twice and produced byte-identical
    proposal actions, which GovernorBravo rejects with "identical proposal
    action already queued at eta". Grants are now deduplicated by
    (account, signature); on mainnet the holders differ and nothing is removed.

Simulation: 58 passing against a bsctestnet fork at block 128,298,000.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant