Skip to content

[feat] Configure Lista OFT v2 bridge - #110

Merged
razww merged 31 commits into
masterfrom
feat/lista-oft
Jul 8, 2026
Merged

[feat] Configure Lista OFT v2 bridge#110
razww merged 31 commits into
masterfrom
feat/lista-oft

Conversation

@lawson-ccy

@lawson-ccy lawson-ccy commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR configures the Lista OFT v2 bridge flow for BSC and ETH, and updates the LayerZero DVN policy used by the Foundry scripts.

Changes

  • Adds Foundry OFT v2 deployment/configuration support for the Lista bridge route.
  • Adds a reusable bridge test script for Lista OFT v2 transfers.
  • Updates role naming to use MANAGER and PAUSER constants consistently.
  • Configures testnet LayerZero endpoint, send/receive library, executor, DVN, peer, and transfer-limit settings.
  • Updates DVN policy:
    • Mainnet: 4 optional DVNs with threshold 3 (3-of-4).
    • Testnet: 2 optional DVNs with threshold 1 (1-of-2): Google and LayerZero Labs.
  • Makes SetDVNConfig idempotent by skipping send/receive library updates when the same explicit library is already configured.
  • Adds explorer metadata for BSC/BSC Testnet/Ethereum/Sepolia verification.

Add the official LayerZero V2 upgradeable contracts needed for the
upgradeable Lista OFT bridge:
- @layerzerolabs/oft-evm-upgradeable
- @layerzerolabs/oapp-evm-upgradeable
- @layerzerolabs/oapp-evm
- @layerzerolabs/oft-evm

These provide OFTUpgradeable / OFTAdapterUpgradeable and the OApp
upgradeable bases (ERC-7201 namespaced storage), compatible with the
repo's OpenZeppelin 4.9.3 (contracts accept ^4.8.1 || ^5.0.0).
Port the v1 TransferLimiter into an upgradeable abstract base for the v2
OFT contracts. Logic (daily/global + per-address transfer limits) is
unchanged; differences:
- no external setters (the inheriting contract gates them by role)
- a storage gap reserved for future appended variables
Upgradeable LayerZero V2 OFT Adapter for the canonical LISTA token on
BNB Chain. Locks/unlocks the native token (single lockbox for the mesh).

- UUPS upgradeable; only DEFAULT_ADMIN_ROLE may authorize upgrades
- PausableUpgradeable: PAUSER_ROLE pause(), MANAGER_ROLE unpause()
- MANAGER_ROLE gates the transfer limiter and the LayerZero OApp config
  (setPeer / setEnforcedOptions / setMsgInspector / setPreCrime) and is
  the LZ endpoint delegate
- _debit/_credit enforce the transfer limiter and pause guard
Upgradeable LayerZero V2 OFT representing LISTA on remote chains (ETH).
Mints on inbound bridge messages, burns on outbound; the canonical
supply stays escrowed in ListaOFTAdapterV2 on BNB Chain.

- same role model as the adapter (DEFAULT_ADMIN / MANAGER / PAUSER)
- UUPS upgradeable, DEFAULT_ADMIN_ROLE gated
- EIP-2612 permit via ERC20PermitUpgradeable
- _debit/_credit enforce the transfer limiter and pause guard
Split mainnet/testnet deploy entrypoints plus LayerZero wiring scripts
for the upgradeable Lista OFT bridge.

- deploy.mainnet.ts / deploy.testnet.ts: UUPS proxy deploy via
  hardhat-upgrades (constructorArgs for the immutable token/endpoint,
  unsafeAllow constructor + state-variable-immutable), writes the proxy
  address back to the live config
- setPeer.ts / setTransferLimit.ts / setDVNConfig.ts: MANAGER-run wiring
- setDVNConfig encodes the ULN (DVN) + executor config: required DVNs
  LayerZero Labs + Nethermind + Google, optional USDT0; configurable
- oftChainsV2.{mainnet,testnet}.example.json: real endpoint / send /
  receive / executor / DVN addresses for BSC + ETH (and bscTestnet +
  sepolia); live configs are git-ignored
- local.t.sol: end-to-end bridge over two mock LayerZero endpoints
  (lock -> mint -> burn -> unlock), plus transfer limiter, pause/role
  gating, MANAGER-only OApp config, UUPS upgrade and EIP-2612 permit
- fork.t.sol: forks BNB Chain and exercises the adapter against the REAL
  canonical LISTA token and the REAL LayerZero endpoint — initialize,
  lock/unlock custody, transfer limiter, pause guard and admin-only
  upgrade (skips automatically when no BSC fork is available)

All 14 tests pass (forge test, solc 0.8.24).
Replace the hardhat deploy flow with native foundry (forge script) for the
upgradeable Lista OFT bridge. Network is selected by --rpc-url and resolved
from block.chainid.

- OFTConfig.sol: per-chain endpoint / send-lib / receive-lib / executor /
  token / eid / DVN set / transfer limits (BSC 56, ETH 1, BSC testnet 97,
  Sepolia 11155111)
- OFTScriptBase.sol: role resolution, transfer-limit build, DVN sort helpers
- DeployListaOFTAdapterV2.s.sol / DeployListaOFTv2.s.sol: UUPS proxy deploy
- SetPeer.s.sol / SetTransferLimit.s.sol / SetDVNConfig.s.sol: LZ wiring
- SetDVNConfig encodes the ULN (required: LayerZero Labs + Nethermind +
  Google; optional: USDT0) + executor config and sets the send/receive libs

Verified by forking BNB Chain in anvil: deploy + setPeer + setTransferLimit +
setDVNConfig all broadcast on-chain successfully against the real LayerZero
endpoint.
The v2 OFT deploy/config flow now runs entirely on foundry
(scripts/foundry/oft/v2). Remove the superseded hardhat TypeScript scripts
and revert the related .gitignore entries.
…GER/PAUSER

Rename the access-control role identifiers and their keccak256 string seeds
in the v2 OFT contracts (ListaOFTv2, ListaOFTAdapterV2), so the on-chain role
ids become keccak256("MANAGER") / keccak256("PAUSER"). Update the v2 tests and
foundry scripts/docs to match.
@lawson-ccy lawson-ccy changed the title [codex] Configure Lista OFT v2 bridge [feat] Configure Lista OFT v2 bridge Jun 26, 2026
@lista-dao lista-dao deleted a comment from hashdit-bot Bot Jun 26, 2026
@lawson-ccy
lawson-ccy marked this pull request as ready for review June 26, 2026 03:58
@hashdit-bot

hashdit-bot Bot commented Jun 26, 2026

Copy link
Copy Markdown

Pull Request Review

This PR adds a full LayerZero OFT v2 bridge implementation for LISTA using upgradeable Solidity contracts, including a lock/unlock adapter on BSC (ListaOFTAdapterV2), a mint/burn OFT token on ETH (ListaOFTv2), and a shared upgradeable transfer-limiter module. It also introduces Foundry deployment/configuration/bridge scripts and chain-specific config for BSC, Ethereum, BSC testnet, and Sepolia, including DVN/executor/send/receive library settings and idempotent DVN config behavior. Tooling updates include new LayerZero dependencies, Foundry RPC/etherscan config expansion, and new local/fork tests for the OFT v2 flow.

Sensitive Content

Blockchain Address:

  • 0x1a44076050125825900e736c501f859c50fE728c (LayerZero endpoint) in scripts/foundry/oft/v2/OFTConfig.sol
  • 0x9F8C645f2D0b2159767Bd6E0839DE4BE49e823DE (send library) in scripts/foundry/oft/v2/OFTConfig.sol
  • 0xB217266c3A98C8B2709Ee26836C98cf12f6cCEC1 (receive library) in scripts/foundry/oft/v2/OFTConfig.sol
  • 0x3ebD570ed38B1b3b4BC886999fcF507e9D584859 (executor) in scripts/foundry/oft/v2/OFTConfig.sol
  • 0xFceB31A79F71AC9CBDCF853519c1b12D379EdC46 (canonical LISTA token) in scripts/foundry/oft/v2/OFTConfig.sol
  • 0xfD6865c841c2d64565562fCc7e05e619A30615f0 (DVN) in scripts/foundry/oft/v2/OFTConfig.sol
  • 0x31F748a368a893Bdb5aBB67ec95F232507601A73 (DVN) in scripts/foundry/oft/v2/OFTConfig.sol
  • 0xD56e4eAb23cb81f43168F9F45211Eb027b9aC7cc (DVN) in scripts/foundry/oft/v2/OFTConfig.sol
  • 0x72F697797aC173F09eDa73Dd9C11a141376d2b57 (DVN) in scripts/foundry/oft/v2/OFTConfig.sol
  • 0xbB2Ea70C9E858123480642Cf96acbcCE1372dCe1 (send library) in scripts/foundry/oft/v2/OFTConfig.sol
  • 0xc02Ab410f0734EFa3F14628780e6e695156024C2 (receive library) in scripts/foundry/oft/v2/OFTConfig.sol
  • 0x173272739Bd7Aa6e4e214714048a9fE699453059 (executor) in scripts/foundry/oft/v2/OFTConfig.sol
  • 0x589dEDbD617e0CBcB916A9223F4d1300c294236b (DVN) in scripts/foundry/oft/v2/OFTConfig.sol
  • 0xa59BA433ac34D2927232918Ef5B2eaAfcF130BA5 (DVN) in scripts/foundry/oft/v2/OFTConfig.sol
  • 0x3b0531eB02Ab4aD72e7a531180beeF9493a00dD2 (DVN) in scripts/foundry/oft/v2/OFTConfig.sol
  • 0x6EDCE65403992e310A62460808c4b910D972f10f (testnet endpoint) in scripts/foundry/oft/v2/OFTConfig.sol
  • 0x55f16c442907e86D764AFdc2a07C2de3BdAc8BB7 (testnet send library) in scripts/foundry/oft/v2/OFTConfig.sol
  • 0x188d4bbCeD671A7aA2b5055937F79510A32e9683 (testnet receive library) in scripts/foundry/oft/v2/OFTConfig.sol
  • 0x31894b190a8bAbd9A067Ce59fde0BfCFD2B18470 (testnet executor) in scripts/foundry/oft/v2/OFTConfig.sol
  • 0x90b94D605E069569Adf33C0e73E26a83637c94B1 (test LISTA token) in scripts/foundry/oft/v2/OFTConfig.sol
  • 0x6f99eA3Fc9206E2779249E15512D7248dAb0B52e (DVN) in scripts/foundry/oft/v2/OFTConfig.sol
  • 0x0eE552262f7B562eFcED6DD4A7e2878AB897d405 (DVN) in scripts/foundry/oft/v2/OFTConfig.sol
  • 0xcc1ae8Cf5D3904Cef3360A9532B477529b177cCE (testnet send library) in scripts/foundry/oft/v2/OFTConfig.sol
  • 0xdAf00F5eE2158dD58E0d3857851c432E34A3A851 (testnet receive library) in scripts/foundry/oft/v2/OFTConfig.sol
  • 0x718B92b5CB0a5552039B593faF724D182A881eDA (testnet executor) in scripts/foundry/oft/v2/OFTConfig.sol
  • 0x4F675c48FaD936cb4c3cA07d7cBF421CeeAE0C75 (DVN) in scripts/foundry/oft/v2/OFTConfig.sol
  • 0x8eebf8b423B73bFCa51a1Db4B7354AA0bFCA9193 (DVN) in scripts/foundry/oft/v2/OFTConfig.sol

Security Issues

No serious security issues detected.


Generated by Hashdit Bot. This tool can absolutely NOT replace manual audits.

lawson-ccy added 12 commits July 1, 2026 08:52
TransferLimiterV2 rejected a zero amount (commonly a sub-1e12 input
rounded down to 0 by _removeDust) with TransferLimitExceeded, which
misleads operators into thinking a configured limit was breached.

Add a dedicated ZeroAmount() error and use it for the _amount == 0
case, leaving TransferLimitExceeded for genuine limit breaches. Adds a
unit test that drives the internal limiter through an exposed harness.
The single-transfer upper/lower bound check ran after the daily-reset
SLOADs and conditional SSTOREs, so an out-of-range amount wasted those
reads/writes before reverting. The check depends only on the in-memory
limit and _amount, so hoist it above the reset blocks; out-of-range
amounts now revert without touching lastUpdatedTime / counter storage.
Behavior is otherwise unchanged.
_checkAndUpdateTransferLimit rewrote lastUpdatedTime / lastUserUpdatedTime
on every successful transfer and reset the daily counters only when the
gap since the last transfer exceeded 24h. On a continuously active route
that gap never elapses, so the counters never reset and the global cap
behaved as a cumulative ceiling consumed across many days instead of a
daily allowance (M01).

Reset on a fixed 00:00 UTC boundary instead: compare the UTC day index
(timestamp / 1 days) of the stored last-transfer time against the current
block. This also removes isMoreThanACalendarDay, whose name implied a
calendar reset it never did and whose subtraction could underflow if
called with a > b (I08); the new _dayIndex helper does no subtraction.

lastUpdatedTime / lastUserUpdatedTime keep storing block.timestamp, so
the storage layout is unchanged. Adds a regression test proving two
transfers ~20s apart but on different UTC days now reset the counters.
TransferLimiterV2 declares 6 storage mappings plus a uint256[45] gap,
totalling 51 reserved slots and breaking the OpenZeppelin 4.9.3
used + gap = 50 convention that the sibling contracts (ListaOFTv2,
ListaOFTAdapterV2, each 0 used + gap[50]) follow. Shrink the gap to
uint256[44] so used(6) + gap(44) = 50.

Storage-layout-safe: this only moves the boundary between TransferLimiterV2's
trailing gap and the derived contract's gap, both of which are empty reserved
space. No used slot changes position, so the existing testnet proxies can
take this via an implementation upgrade; mainnet is not yet deployed.
setPeer defines the trusted LayerZero peer for an endpoint id and is the
core cross-chain trust anchor: a forged or malicious peer lets an
attacker deliver messages the contract accepts as authentic, minting
unbacked tokens (ListaOFTv2) or unlocking escrow (ListaOFTAdapterV2).

It was gated by MANAGER, a hot operational role that also holds pause,
DVN/options config, and (per OFTScriptBase._roles) can default to the
deployer EOA. Move it to DEFAULT_ADMIN_ROLE so the trust anchor sits
behind the same authority as the upgrade path (_authorizeUpgrade); the
admin should be a timelock/multisig. Emergency severing is unaffected
because it goes through pause() (PAUSER/MANAGER), and peer changes are
rare, non-emergency setup operations.

Also updates SetPeer.s.sol (broadcaster must now hold DEFAULT_ADMIN_ROLE)
and the setPeer test to assert a MANAGER-only account is rejected.
The default per-address daily cap was 200,000 against a 1,000,000 global
cap (20%), so as few as 5 addresses could fill the shared daily bucket
and brick the bridge for everyone until the counters reset. The 100
per-address attempt cap gave little extra protection since an address
reached its amount cap in ~2 transfers.

Rebalance the defaults so the per-address cap is 2% of the global cap
(~50 distinct addresses to exhaust it) while keeping a usable single
transfer size:
  maxDailyTransferAmount        1,000,000 -> 2,000,000
  singleTransferUpperLimit        100,000 ->    30,000
  dailyTransferAmountPerAddress   200,000 ->    40,000
  dailyTransferAttemptPerAddress      100 ->        20
  singleTransferLowerLimit            0.1  (unchanged)

Constraints in _setTransferLimitConfig still hold (2M > 40k > 30k > 0.1).
Limits are pushed on-chain by SetTransferLimit, so they stay tunable
without a redeploy as confidence and volume grow.
Every network configured requiredDVNs = [] and relied entirely on an
all-optional threshold (mainnet 3-of-4, testnet 1-of-2). The LayerZero
ULN skips the required-DVN loop when it is empty, so any qualifying
optional subset could finalize a message (mint on the OFT / unlock on
the adapter) with no single operator that must sign every message.

Promote one operator into requiredDVNs on each network so a mandatory
DVN participates in every verification and cannot be routed around:
  - BSC / ETH mainnet: required = Google; optional = LayerZero Labs,
    Nethermind, USDT0 at 2-of-3.
  - BSC testnet / Sepolia: required = LayerZero Labs; optional = Google
    at 1-of-1.

required and optional sets stay disjoint and each is sorted by _sorted,
so the existing SetDVNConfig encoding (_dvnCount returns the real length
once non-empty) needs no change. Doc comments updated.
_ethMainnet() and _sepolia() configured the OFT token name as
"Lista DAO Token", while the canonical BSC LISTA that the adapter wraps
is named "Lista DAO". This produced inconsistent mesh metadata and a
different EIP-712 permit domain on the ETH side.

Align the name to "Lista DAO" on both the Ethereum mainnet and Sepolia
OFT configs. The name is baked in at __ERC20Permit_init on deploy, so
this must land before the ETH OFT is deployed (mainnet is not yet
deployed); the BSC side is an adapter and sets no name.
OFTScriptBase._roles resolves ADMIN/MANAGER/PAUSER via vm.envOr(name,
deployer), so an unset env var silently grants that role to the deployer
EOA. With all three unset, one hot key holds every sensitive role and no
script flags it.

Emit a loud console warning when any role resolves to the deployer.
Kept as a warning rather than a revert so single-key test/staging
deploys still work; operators should set distinct keys and rotate roles
to multisigs after deploy.
setEnforcedOptions exists and is MANAGER-gated on both OFTs, but no
deploy/config script ever called it, so enforcedOptions stayed empty and
there was no on-chain destination gas floor. A user who calls send() with
empty or insufficient extraOptions then produces a message with too
little lzReceive gas; _credit runs out of gas on the destination and the
funds stall in a stuck / retryable state mid-bridge.

Add SetEnforcedOptions.s.sol, which sets a type-3 enforced option with an
executor lzReceive gas floor (default 300000, matching the value the
bridge test script already uses) for the SEND message type per dstEid,
and wire it into the README bring-up sequence before the test transfer.
The bring-up sequence wired peers before configuring libraries/DVNs, so
a route could go live while the endpoint still resolved to LayerZero's
default library and DVN set, verifying messages under security settings
the protocol did not choose. Cross-chain wiring is also non-atomic.

Reorder the documented steps so SetDVNConfig (plus transfer limits and
enforced options) run before SetPeer, and make SetPeer the last config
step that brings a route live. Add a launch checklist: deploy paused;
verify libraries, DVNs, enforced options, limits, and peers on both
chains and both directions; only then unpause. Also refresh the DVN
policy section for the new required-DVN setup and correct the SetPeer
role note to DEFAULT_ADMIN_ROLE.
The testnet DVN policy (required LayerZero Labs + optional Google 1-of-1)
forced both DVNs to attest, but the Google testnet DVN does not reliably
attest, so cross-chain messages stalled at verification (committer never
completed). Drop the optional Google requirement on testnet and require only
the always-on LayerZero Labs DVN (optionalDVNs empty, threshold 0).

Mainnet is unchanged (required Google + 2-of-3 of LayerZero Labs / Nethermind
/ USDT0), where every DVN is production-grade.
Deploys a fresh implementation (lock/unlock adapter or mint/burn OFT per
chain, immutables read from OFTConfig) and calls upgradeTo on the existing
UUPS proxy. Used to roll the audit-remediation implementations onto the
already-deployed testnet proxies; storage layout is unchanged so no
reinitializer is needed.
@hashdit-bot

hashdit-bot Bot commented Jul 2, 2026

Copy link
Copy Markdown

Pull Request Review

This PR introduces a full LayerZero OFT v2 bridging stack for Lista, including two new upgradeable Solidity contracts (ListaOFTAdapterV2 and ListaOFTv2) plus a shared TransferLimiterV2 module with daily/per-address throttling and pause guards. It also adds comprehensive Foundry deployment/operations scripts and config for BSC/Ethereum mainnet and BSC testnet/Sepolia, including DVN, endpoint library, peer wiring, enforced options, transfer limits, and upgrade flow. Tooling and docs were updated to support multi-network RPC/verification and bridge runbooks, along with new OFT-focused tests and test refactors.

Sensitive Content

Blockchain Address:

  • 0x1a44076050125825900e736c501f859c50fE728c (LayerZero endpoint) in scripts/foundry/oft/v2/OFTConfig.sol
  • 0x9F8C645f2D0b2159767Bd6E0839DE4BE49e823DE (send library) in scripts/foundry/oft/v2/OFTConfig.sol
  • 0xB217266c3A98C8B2709Ee26836C98cf12f6cCEC1 (receive library) in scripts/foundry/oft/v2/OFTConfig.sol
  • 0x3ebD570ed38B1b3b4BC886999fcF507e9D584859 (executor) in scripts/foundry/oft/v2/OFTConfig.sol
  • 0xFceB31A79F71AC9CBDCF853519c1b12D379EdC46 (canonical LISTA token) in scripts/foundry/oft/v2/OFTConfig.sol
  • 0xD56e4eAb23cb81f43168F9F45211Eb027b9aC7cc (DVN) in scripts/foundry/oft/v2/OFTConfig.sol
  • 0xfD6865c841c2d64565562fCc7e05e619A30615f0 (DVN) in scripts/foundry/oft/v2/OFTConfig.sol
  • 0x31F748a368a893Bdb5aBB67ec95F232507601A73 (DVN) in scripts/foundry/oft/v2/OFTConfig.sol
  • 0x72F697797aC173F09eDa73Dd9C11a141376d2b57 (DVN) in scripts/foundry/oft/v2/OFTConfig.sol
  • 0xbB2Ea70C9E858123480642Cf96acbcCE1372dCe1 (send library) in scripts/foundry/oft/v2/OFTConfig.sol
  • 0xc02Ab410f0734EFa3F14628780e6e695156024C2 (receive library) in scripts/foundry/oft/v2/OFTConfig.sol
  • 0x173272739Bd7Aa6e4e214714048a9fE699453059 (executor) in scripts/foundry/oft/v2/OFTConfig.sol
  • 0x589dEDbD617e0CBcB916A9223F4d1300c294236b (DVN) in scripts/foundry/oft/v2/OFTConfig.sol
  • 0xa59BA433ac34D2927232918Ef5B2eaAfcF130BA5 (DVN) in scripts/foundry/oft/v2/OFTConfig.sol
  • 0x3b0531eB02Ab4aD72e7a531180beeF9493a00dD2 (DVN) in scripts/foundry/oft/v2/OFTConfig.sol
  • 0x6EDCE65403992e310A62460808c4b910D972f10f (testnet endpoint) in scripts/foundry/oft/v2/OFTConfig.sol
  • 0x55f16c442907e86D764AFdc2a07C2de3BdAc8BB7 (send library) in scripts/foundry/oft/v2/OFTConfig.sol
  • 0x188d4bbCeD671A7aA2b5055937F79510A32e9683 (receive library) in scripts/foundry/oft/v2/OFTConfig.sol
  • 0x31894b190a8bAbd9A067Ce59fde0BfCFD2B18470 (executor) in scripts/foundry/oft/v2/OFTConfig.sol
  • 0x90b94D605E069569Adf33C0e73E26a83637c94B1 (test LISTA token) in scripts/foundry/oft/v2/OFTConfig.sol
  • 0x0eE552262f7B562eFcED6DD4A7e2878AB897d405 (DVN) in scripts/foundry/oft/v2/OFTConfig.sol
  • 0xcc1ae8Cf5D3904Cef3360A9532B477529b177cCE (send library) in scripts/foundry/oft/v2/OFTConfig.sol
  • 0xdAf00F5eE2158dD58E0d3857851c432E34A3A851 (receive library) in scripts/foundry/oft/v2/OFTConfig.sol
  • 0x718B92b5CB0a5552039B593faF724D182A881eDA (executor) in scripts/foundry/oft/v2/OFTConfig.sol
  • 0x8eebf8b423B73bFCa51a1Db4B7354AA0bFCA9193 (DVN) in scripts/foundry/oft/v2/OFTConfig.sol

Security Issues

No serious security issues detected.


Generated by Hashdit Bot. This tool can absolutely NOT replace manual audits.

@hashdit-bot

hashdit-bot Bot commented Jul 7, 2026

Copy link
Copy Markdown

Pull Request Review

This PR introduces a full OFT v2 bridge stack for Lista across BSC and Ethereum (including testnets), adding new upgradeable contracts (ListaOFTAdapterV2, ListaOFTv2, and TransferLimiterV2) plus Foundry deployment/configuration/upgrade scripts. It also centralizes per-chain LayerZero configuration (endpoints, libraries, DVNs, executor, limits), updates Foundry/Etherscan settings, and adds local + fork tests for bridge behavior and limiter/role controls. Additionally, it includes idempotency logic in DVN config scripts and test updates for buyback mocking.

Sensitive Content

Blockchain Address:

  • 0x1a44076050125825900e736c501f859c50fE728c in scripts/foundry/oft/v2/OFTConfig.sol — LayerZero endpoint (mainnet)
  • 0x9F8C645f2D0b2159767Bd6E0839DE4BE49e823DE in scripts/foundry/oft/v2/OFTConfig.sol — send library (BSC mainnet)
  • 0xB217266c3A98C8B2709Ee26836C98cf12f6cCEC1 in scripts/foundry/oft/v2/OFTConfig.sol — receive library (BSC mainnet)
  • 0x3ebD570ed38B1b3b4BC886999fcF507e9D584859 in scripts/foundry/oft/v2/OFTConfig.sol — executor (BSC mainnet)
  • 0xFceB31A79F71AC9CBDCF853519c1b12D379EdC46 in scripts/foundry/oft/v2/OFTConfig.sol — canonical LISTA token
  • 0xD56e4eAb23cb81f43168F9F45211Eb027b9aC7cc in scripts/foundry/oft/v2/OFTConfig.sol — required DVN (Google)
  • 0xfD6865c841c2d64565562fCc7e05e619A30615f0 in scripts/foundry/oft/v2/OFTConfig.sol — optional DVN (LayerZero Labs)
  • 0x31F748a368a893Bdb5aBB67ec95F232507601A73 in scripts/foundry/oft/v2/OFTConfig.sol — optional DVN (Nethermind)
  • 0x72F697797aC173F09eDa73Dd9C11a141376d2b57 in scripts/foundry/oft/v2/OFTConfig.sol — optional DVN (USDT0)
  • 0xbB2Ea70C9E858123480642Cf96acbcCE1372dCe1 in scripts/foundry/oft/v2/OFTConfig.sol — send library (ETH mainnet)
  • 0xc02Ab410f0734EFa3F14628780e6e695156024C2 in scripts/foundry/oft/v2/OFTConfig.sol — receive library (ETH mainnet)
  • 0x173272739Bd7Aa6e4e214714048a9fE699453059 in scripts/foundry/oft/v2/OFTConfig.sol — executor (ETH mainnet)
  • 0x589dEDbD617e0CBcB916A9223F4d1300c294236b in scripts/foundry/oft/v2/OFTConfig.sol — optional DVN (LayerZero Labs)
  • 0xa59BA433ac34D2927232918Ef5B2eaAfcF130BA5 in scripts/foundry/oft/v2/OFTConfig.sol — optional DVN (Nethermind)
  • 0x3b0531eB02Ab4aD72e7a531180beeF9493a00dD2 in scripts/foundry/oft/v2/OFTConfig.sol — optional DVN (USDT0)
  • 0x6EDCE65403992e310A62460808c4b910D972f10f in scripts/foundry/oft/v2/OFTConfig.sol — LayerZero endpoint (testnet)
  • 0x55f16c442907e86D764AFdc2a07C2de3BdAc8BB7 in scripts/foundry/oft/v2/OFTConfig.sol — send library (BSC testnet)
  • 0x188d4bbCeD671A7aA2b5055937F79510A32e9683 in scripts/foundry/oft/v2/OFTConfig.sol — receive library (BSC testnet)
  • 0x31894b190a8bAbd9A067Ce59fde0BfCFD2B18470 in scripts/foundry/oft/v2/OFTConfig.sol — executor (BSC testnet)
  • 0x90b94D605E069569Adf33C0e73E26a83637c94B1 in scripts/foundry/oft/v2/OFTConfig.sol — test LISTA token
  • 0x0eE552262f7B562eFcED6DD4A7e2878AB897d405 in scripts/foundry/oft/v2/OFTConfig.sol — required DVN (LayerZero Labs)
  • 0xcc1ae8Cf5D3904Cef3360A9532B477529b177cCE in scripts/foundry/oft/v2/OFTConfig.sol — send library (Sepolia)
  • 0xdAf00F5eE2158dD58E0d3857851c432E34A3A851 in scripts/foundry/oft/v2/OFTConfig.sol — receive library (Sepolia)
  • 0x718B92b5CB0a5552039B593faF724D182A881eDA in scripts/foundry/oft/v2/OFTConfig.sol — executor (Sepolia)
  • 0x8eebf8b423B73bFCa51a1Db4B7354AA0bFCA9193 in scripts/foundry/oft/v2/OFTConfig.sol — required DVN (LayerZero Labs)

Security Issues

No serious security issues detected.


Generated by Hashdit Bot. This tool can absolutely NOT replace manual audits.

Size _defaultLimits against LISTA supply (totalSupply 1B, circulating
~415M, price ~$0.049):
  - maxDailyTransferAmount        2M   -> 8M   (<2% circulating, ~$392k)
  - singleTransferUpperLimit      30k  -> 200k
  - dailyTransferAmountPerAddress 40k  -> 400k (5% of global; 20 addrs to drain)

Per-address 400k/day lets operations bridge ~500k/week from one wallet.
DVN set remains the primary control; limiter is the backstop. Baked at
deploy; already-live proxies update via SetTransferLimit.
The DVN "Config error: Outbound confirmations must be >= inbound
confirmations" blocked ETH->BSC because SetDVNConfig set the receive-side
ULN confirmations to the local chain's value. Inbound confirmations must
mirror the REMOTE source chain's send confirmations.

- OFTConfig: add inboundConfirmations (BSC 15 from ETH, ETH 20 from BSC,
  testnets 5). Asymmetric mainnet values (BSC 20 / ETH 15) are why this
  only surfaced on mainnet; symmetric testnet values (5/5) masked it.
- SetDVNConfig: encode separate send (confirmations) and receive
  (inboundConfirmations) ULN configs.

Verified on mainnet: after re-running SetDVNConfig on both chains,
BSC->ETH (20>=20) and ETH->BSC (15>=15) both pass and a stuck in-flight
message unlocked.
…ipts

- SetDVNConfig: log send (outbound) and inbound (receive) confirmations so
  a confirmations mismatch is visible in the run output.
- SetTransferLimit: log all five limit values being pushed.
Two-step, verifiable handoff off the deployer EOA:

- GrantRolesAndTransferOwnership: grants ADMIN+owner to the Timelock,
  MANAGER+delegate to the Safe, PAUSER to the pauser, and transfers
  ownership, WITHOUT stripping the deployer. Requires the Timelock/Safe to
  have code on the current chain (guards a mistyped/cross-chain address)
  and rejects the deployer as any target (a role granted to the deployer
  would be stripped in step 2, leaving it holderless).
- RevokeDeployerRole: revoke-only. Preconditions assert the Timelock
  already holds ADMIN + owner before the deployer revokes MANAGER/PAUSER
  and renounces DEFAULT_ADMIN_ROLE last, so the handoff cannot lock the
  contract out of an admin.

Splitting grant from the irreversible renounce leaves a window to verify
the new holders are controllable before stripping the deployer.
@hashdit-bot

hashdit-bot Bot commented Jul 8, 2026

Copy link
Copy Markdown

Pull Request Review

This PR introduces a full OFT v2 bridge stack for Lista across BSC and Ethereum, including two new upgradeable contracts (ListaOFTAdapterV2 and ListaOFTv2), a shared TransferLimiterV2, and extensive Foundry scripts for deploy/config/upgrade/role handoff. It also updates environment and tooling config (foundry.toml, dependencies, .gitignore) and adds new local/fork tests for bridge behavior, access control, pause/limit logic, and upgrade paths. Additionally, it updates DVN/library configuration flow with idempotent behavior and documents the operational rollout procedure in a new scripts README.

Sensitive Content

Blockchain Address:

  • 0x1a44076050125825900e736c501f859c50fE728c (LayerZero endpoint) in scripts/foundry/oft/v2/OFTConfig.sol
  • 0x9F8C645f2D0b2159767Bd6E0839DE4BE49e823DE (send library) in scripts/foundry/oft/v2/OFTConfig.sol
  • 0xB217266c3A98C8B2709Ee26836C98cf12f6cCEC1 (receive library) in scripts/foundry/oft/v2/OFTConfig.sol
  • 0x3ebD570ed38B1b3b4BC886999fcF507e9D584859 (executor) in scripts/foundry/oft/v2/OFTConfig.sol
  • 0xFceB31A79F71AC9CBDCF853519c1b12D379EdC46 (canonical LISTA token) in scripts/foundry/oft/v2/OFTConfig.sol
  • 0xD56e4eAb23cb81f43168F9F45211Eb027b9aC7cc (DVN) in scripts/foundry/oft/v2/OFTConfig.sol
  • 0xfD6865c841c2d64565562fCc7e05e619A30615f0 (DVN) in scripts/foundry/oft/v2/OFTConfig.sol
  • 0x31F748a368a893Bdb5aBB67ec95F232507601A73 (DVN) in scripts/foundry/oft/v2/OFTConfig.sol
  • 0x72F697797aC173F09eDa73Dd9C11a141376d2b57 (DVN) in scripts/foundry/oft/v2/OFTConfig.sol
  • 0xbB2Ea70C9E858123480642Cf96acbcCE1372dCe1 (send library) in scripts/foundry/oft/v2/OFTConfig.sol
  • 0xc02Ab410f0734EFa3F14628780e6e695156024C2 (receive library) in scripts/foundry/oft/v2/OFTConfig.sol
  • 0x173272739Bd7Aa6e4e214714048a9fE699453059 (executor) in scripts/foundry/oft/v2/OFTConfig.sol
  • 0x589dEDbD617e0CBcB916A9223F4d1300c294236b (DVN) in scripts/foundry/oft/v2/OFTConfig.sol
  • 0xa59BA433ac34D2927232918Ef5B2eaAfcF130BA5 (DVN) in scripts/foundry/oft/v2/OFTConfig.sol
  • 0x3b0531eB02Ab4aD72e7a531180beeF9493a00dD2 (DVN) in scripts/foundry/oft/v2/OFTConfig.sol
  • 0x6EDCE65403992e310A62460808c4b910D972f10f (testnet endpoint) in scripts/foundry/oft/v2/OFTConfig.sol
  • 0x55f16c442907e86D764AFdc2a07C2de3BdAc8BB7 (testnet send library) in scripts/foundry/oft/v2/OFTConfig.sol
  • 0x188d4bbCeD671A7aA2b5055937F79510A32e9683 (testnet receive library) in scripts/foundry/oft/v2/OFTConfig.sol
  • 0x31894b190a8bAbd9A067Ce59fde0BfCFD2B18470 (testnet executor) in scripts/foundry/oft/v2/OFTConfig.sol
  • 0x90b94D605E069569Adf33C0e73E26a83637c94B1 (test LISTA token) in scripts/foundry/oft/v2/OFTConfig.sol
  • 0x0eE552262f7B562eFcED6DD4A7e2878AB897d405 (testnet DVN) in scripts/foundry/oft/v2/OFTConfig.sol
  • 0xcc1ae8Cf5D3904Cef3360A9532B477529b177cCE (testnet send library) in scripts/foundry/oft/v2/OFTConfig.sol
  • 0xdAf00F5eE2158dD58E0d3857851c432E34A3A851 (testnet receive library) in scripts/foundry/oft/v2/OFTConfig.sol
  • 0x718B92b5CB0a5552039B593faF724D182A881eDA (testnet executor) in scripts/foundry/oft/v2/OFTConfig.sol
  • 0x8eebf8b423B73bFCa51a1Db4B7354AA0bFCA9193 (testnet DVN) in scripts/foundry/oft/v2/OFTConfig.sol

Security Issues

No serious security issues detected.


Generated by Hashdit Bot. This tool can absolutely NOT replace manual audits.

@lawson-ccy
lawson-ccy requested a review from razww July 8, 2026 02:51

@razww razww left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM

@razww
razww merged commit ee4dfa4 into master Jul 8, 2026
1 check passed
@lawson-ccy

Copy link
Copy Markdown
Contributor Author

@audit-agent

@lista-audit-agent

Copy link
Copy Markdown

Cloud Auditor — automated review of PR #110

Scope: New Lista OFT v2 bridge (contracts/oft/v2/*) + LayerZero configuration scripts (scripts/foundry/oft/v2/*). This PR implements the HashDit 2026-06-30 audit remediation set plus additional operational hardening.

Severity summary

Sev Count Notes
Critical 0
High 0
Medium 1 BRG-001 pause-loop DoS via compromised PAUSER (~1-day recovery via timelock)
Low 4 BRG-002 per-addr DoS ratio (20 addr, cite HashDit M02), BRG-003/004 handoff-script role-collision guards, BRG-005 UpgradeOFT immutable assertion
Info 11 Docs/checklist drift + 7 HashDit-acknowledged cites (I01/I03/I06/I07/I10/I11/I12/I17)

HashDit 2026-06-30 remediation verification

All 12 "fixed" items independently verified against this PR's code:
✅ M01 _dayIndex UTC boundary · ✅ I02 bound check ordering · ✅ I05 gap 44+6=50 · ✅ I09 ZeroAmount · ✅ I04 setPeerDEFAULT_ADMIN_ROLE (both contracts) · ✅ I13 deployer-fallback warning · ✅ I14 Google-required + 2-of-3 optional DVN policy (8 DVN addresses cross-verified) · ✅ I15 tokenName = "Lista DAO" · ✅ I16 SetEnforcedOptions.s.sol (300k gas, MSG_TYPE_SEND=1) · ✅ I18 launch checklist (paused-deploy is aspirational — see INT-002)

Key non-Info findings

  • BRG-001 (Medium)PAUSER's role admin defaults to DEFAULT_ADMIN_ROLE (timelock). A compromised PAUSER key can re-pause after every MANAGER.unpause(); MANAGER cannot fast-revoke because it does not hold PAUSER's admin. Recovery requires the ~1-day timelock cycle → bridge liveness pinned offline. Fix: _setRoleAdmin(PAUSER, MANAGER) in initialize(), and require pauser to be a contract in the handoff script.
  • BRG-002 (Low) — Per-address cap = 5% of global (20-address DoS threshold, ~$392k capital), not the 1-2% HashDit recommended in M02. Deviates from the HashDit resolution memo (which assumed 2M/40k / 50 addresses). Attacker capital is not lost, only liquidity-locked; considered acknowledged design.
  • BRG-003/004 (Low)GrantRolesAndTransferOwnership.s.sol lacks timelock != safe / pauser != safe / pauser != timelock checks. If accidentally supplied identical, the entire two-party pause/unpause and separation-of-duties model collapses.
  • BRG-005 (Low)UpgradeOFT.s.sol reads token / lzEndpoint from OFTConfig at runtime and installs them as immutables in the new impl. A future OFTConfig typo would silently install an impl bound to the wrong token/endpoint with no post-upgrade assertion.

Verified clean

Reentrancy (13 checks — CEI order intact, LISTA is plain ERC-20 no ERC-777 hooks, _debit state before super), math (20 checks — dust/dayIndex/overflow all correct), storage collisions (LZ ERC-7201 namespace slots + sequential OZ 4.9.3 gaps trace clean), UUPS _authorizeUpgrade gating on both proxies, DVN addresses cross-verified against LayerZero metadata, confirmations symmetry BSC↔ETH 20/15.

Full report PDF delivered separately (bridge audit channel). Prior HashDit audit already covers most of this diff; this pass focused on regression verification and delta from the HashDit-endorsed resolution config.

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.

2 participants