polkamarkets-js 3.4.5 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +29 -0
- package/CLOB_AUDIT_REPORT.md +305 -0
- package/CLOB_SDK_REFERENCE.md +654 -0
- package/abis/AdminRegistry.json +1 -0
- package/abis/ConditionalTokens.json +1 -0
- package/abis/FeeModule.json +1 -0
- package/abis/MockERC20.json +1 -1
- package/abis/Multicall3.json +1 -0
- package/abis/MyriadCTFExchange.json +1 -0
- package/abis/NegRiskAdapter.json +1 -0
- package/abis/PredictionMarketV3ManagerCLOB.json +1 -0
- package/abis/WrappedCollateral.json +1 -0
- package/contracts/AdminRegistry.sol +74 -0
- package/contracts/ConditionalTokens.sol +129 -0
- package/contracts/FeeModule.sol +163 -0
- package/contracts/IMarketOracle.sol +18 -0
- package/contracts/IMyriadMarketManager.sol +28 -0
- package/contracts/MyriadCTFExchange.sol +887 -0
- package/contracts/NegRiskAdapter.sol +446 -0
- package/contracts/Outcomes.sol +10 -0
- package/contracts/PredictionMarketV3ManagerCLOB.sol +397 -0
- package/contracts/Swap11.sol +23 -0
- package/contracts/WrappedCollateral.sol +68 -0
- package/contracts/oracles/RealitioOracle.sol +90 -0
- package/foundry.toml +1 -1
- package/package.json +1 -1
- package/script/CreateCLOBMarket.s.sol +63 -0
- package/script/CreateNegRiskEvent.s.sol +98 -0
- package/script/DeployCLOB.s.sol +118 -0
- package/script/DeployNegRiskAdapter.s.sol +85 -0
- package/script/DeployOracles.s.sol +31 -0
- package/script/DeployRealitioTest.s.sol +30 -0
- package/script/ResolveMarket.s.sol +51 -0
- package/script/ResolveNegRiskEvent.s.sol +43 -0
- package/script/SetCLOBFees.s.sol +79 -0
- package/script/SetupCLOBOperator.s.sol +79 -0
- package/script/SubmitRealitioAnswer.s.sol +50 -0
- package/scripts/create_clob_markets.sh +411 -0
- package/scripts/resolve_clob_market.sh +246 -0
- package/scripts/setup_clob_operator.sh +54 -0
- package/src/Application.js +96 -0
- package/src/interfaces/index.js +6 -0
- package/src/models/AdminRegistryContract.js +28 -0
- package/src/models/ConditionalTokensContract.js +32 -0
- package/src/models/FeeModuleContract.js +75 -0
- package/src/models/Multicall3Contract.js +14 -0
- package/src/models/MyriadCTFExchangeContract.js +20 -0
- package/src/models/PredictionMarketV3ManagerCLOBContract.js +74 -0
- package/src/models/index.js +13 -1
- package/test/AdminRegistry.t.sol +326 -0
- package/test/ConditionalTokens.t.sol +579 -0
- package/test/FeeModule.t.sol +508 -0
- package/test/MyriadCTFExchange.t.sol +939 -0
- package/test/NegRiskAdapter.t.sol +1267 -0
- package/test/POC.t.sol +227 -0
- package/test/PredictionMarket.t.sol +895 -895
- package/test/PredictionMarketCLOB.t.sol +2948 -0
- package/test/PredictionMarketManager.t.sol +891 -891
- package/test/RealitioOracle.t.sol +361 -0
- /package/abis/{Test.json → test.json} +0 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"permissions": {
|
|
3
|
+
"allow": [
|
|
4
|
+
"WebFetch(domain:polygonscan.com)",
|
|
5
|
+
"WebFetch(domain:raw.githubusercontent.com)",
|
|
6
|
+
"WebSearch",
|
|
7
|
+
"Bash(git log:*)",
|
|
8
|
+
"Bash(forge build)",
|
|
9
|
+
"Bash(forge build --contracts test/PredictionMarketCLOB.t.sol)",
|
|
10
|
+
"Bash(forge build --contracts test/AdminRegistry.t.sol)",
|
|
11
|
+
"Bash(python3 -c \":*)",
|
|
12
|
+
"Bash(forge test --match-path \"test/AdminRegistry.t.sol\" -v)",
|
|
13
|
+
"Bash(forge test --match-test \"testGrantRoleByNonAdminReverts|testAfterTransfer\" -vvv --match-path \"test/AdminRegistry.t.sol\")",
|
|
14
|
+
"Bash(forge test --match-path \"test/FeeModule.t.sol\" -v)",
|
|
15
|
+
"Bash(forge test --match-path \"test/ConditionalTokens.t.sol\" -v)",
|
|
16
|
+
"Bash(forge test --match-path \"test/MyriadCTFExchange.t.sol\" -v)",
|
|
17
|
+
"Bash(forge test --match-path \"test/MyriadCTFExchange.t.sol\" --match-test \"testMatchOrdersWithFeesByNonOperatorReverts|testMarketMismatchReverts\" -vvv)",
|
|
18
|
+
"Bash(forge test --match-path \"test/AdminRegistry.t.sol|test/FeeModule.t.sol|test/ConditionalTokens.t.sol|test/MyriadCTFExchange.t.sol\" -v)",
|
|
19
|
+
"Bash(forge test --match-contract \"AdminRegistryTest|FeeModuleTest|ConditionalTokensTest|MyriadCTFExchangeTest\" -v)",
|
|
20
|
+
"Bash(forge test -v)",
|
|
21
|
+
"Bash(cast sig:*)",
|
|
22
|
+
"Bash(pip3 install:*)",
|
|
23
|
+
"Bash(pip install:*)",
|
|
24
|
+
"Bash(node -e \":*)",
|
|
25
|
+
"Bash(forge inspect:*)",
|
|
26
|
+
"Bash(curl -s \"https://api.github.com/repos/Polkamarkets/polkamarkets-js/releases?per_page=5\")"
|
|
27
|
+
]
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
# Myriad CLOB Smart Contract Audit Report
|
|
2
|
+
|
|
3
|
+
- Review date: 2026-04-08
|
|
4
|
+
- Repository: `polkamarkets/bepro-js`
|
|
5
|
+
- Review target: `/contracts` diff against `main`
|
|
6
|
+
- Reviewed branch: `fix-135-mint-merge-match-settling`
|
|
7
|
+
- Reviewer: Codex
|
|
8
|
+
|
|
9
|
+
## Executive Summary
|
|
10
|
+
|
|
11
|
+
This review covered the newly introduced CLOB contract stack added in the `/contracts` diff against `main`, with particular focus on:
|
|
12
|
+
|
|
13
|
+
- `MyriadCTFExchange`
|
|
14
|
+
- `ConditionalTokens`
|
|
15
|
+
- `PredictionMarketV3ManagerCLOB`
|
|
16
|
+
- `NegRiskAdapter`
|
|
17
|
+
- `WrappedCollateral`
|
|
18
|
+
- `FeeModule`
|
|
19
|
+
- `AdminRegistry`
|
|
20
|
+
- `RealitioOracle`
|
|
21
|
+
|
|
22
|
+
The system introduces a substantial new settlement surface: off-chain signed orders matched on-chain, ERC1155 conditional positions, per-market fee routing, pluggable oracle resolution, and a neg-risk adapter that uses unbacked wrapped collateral minting as an internal accounting primitive.
|
|
23
|
+
|
|
24
|
+
The review identified one high-severity issue and three medium-severity issues. The most important finding is a solvency failure in neg-risk void handling: malformed but currently allowed event-level void payouts can leave permanent unbacked `WCOL` in circulation and let those tokens drain future deposits.
|
|
25
|
+
|
|
26
|
+
## Scope
|
|
27
|
+
|
|
28
|
+
The following contracts were in scope because they are newly added or materially changed relative to `main`:
|
|
29
|
+
|
|
30
|
+
- `contracts/AdminRegistry.sol`
|
|
31
|
+
- `contracts/ConditionalTokens.sol`
|
|
32
|
+
- `contracts/FeeModule.sol`
|
|
33
|
+
- `contracts/IMarketOracle.sol`
|
|
34
|
+
- `contracts/IMyriadMarketManager.sol`
|
|
35
|
+
- `contracts/MyriadCTFExchange.sol`
|
|
36
|
+
- `contracts/NegRiskAdapter.sol`
|
|
37
|
+
- `contracts/Outcomes.sol`
|
|
38
|
+
- `contracts/PredictionMarketV3ManagerCLOB.sol`
|
|
39
|
+
- `contracts/WrappedCollateral.sol`
|
|
40
|
+
- `contracts/oracles/RealitioOracle.sol`
|
|
41
|
+
|
|
42
|
+
`CLOB_SDK_REFERENCE.md` was also reviewed because it describes expected system behavior and trust assumptions for launch.
|
|
43
|
+
|
|
44
|
+
## Methodology
|
|
45
|
+
|
|
46
|
+
The review was performed as a manual adversarial code audit focused on:
|
|
47
|
+
|
|
48
|
+
- asset accounting and solvency
|
|
49
|
+
- trust boundaries and role separation
|
|
50
|
+
- order lifecycle and stale-order risk
|
|
51
|
+
- settlement invariants across split, merge, redeem, and cross-market matching
|
|
52
|
+
- neg-risk event resolution and cleanup
|
|
53
|
+
- privileged operations, upgradeability, and wiring assumptions
|
|
54
|
+
|
|
55
|
+
`forge build` succeeds locally. `forge test` could not be executed in this environment because the installed Foundry nightly crashes before test execution in `system_configuration::dynamic_store`. As a result, findings are based on source review plus compile verification rather than a successful local test run.
|
|
56
|
+
|
|
57
|
+
## Severity Definitions
|
|
58
|
+
|
|
59
|
+
- High: direct loss of funds, persistent insolvency, or a launch-blocking safety failure
|
|
60
|
+
- Medium: meaningful integrity or availability risk, especially when combined with normal operator/admin actions
|
|
61
|
+
- Low: limited impact issue or hardening gap
|
|
62
|
+
- Informational: non-exploitable risk, documentation mismatch, or operational observation
|
|
63
|
+
|
|
64
|
+
## Findings Summary
|
|
65
|
+
|
|
66
|
+
| ID | Severity | Title |
|
|
67
|
+
| --- | --- | --- |
|
|
68
|
+
| H-01 | High | Neg-risk voiding can leave unbacked `WCOL` in circulation and drain future deposits |
|
|
69
|
+
| M-01 | Medium | Market admins can reopen naturally closed markets and revive stale GTC orders |
|
|
70
|
+
| M-02 | Medium | Market admins can swap the oracle after close and seize settlement |
|
|
71
|
+
| M-03 | Medium | Rotating the global neg-risk adapter can strand live events |
|
|
72
|
+
|
|
73
|
+
## Detailed Findings
|
|
74
|
+
|
|
75
|
+
### H-01: Neg-risk voiding can leave unbacked `WCOL` in circulation and drain future deposits
|
|
76
|
+
|
|
77
|
+
**Severity:** High
|
|
78
|
+
|
|
79
|
+
**Affected contracts:**
|
|
80
|
+
|
|
81
|
+
- `contracts/NegRiskAdapter.sol`
|
|
82
|
+
- `contracts/WrappedCollateral.sol`
|
|
83
|
+
- `contracts/PredictionMarketV3ManagerCLOB.sol`
|
|
84
|
+
|
|
85
|
+
**Description**
|
|
86
|
+
|
|
87
|
+
The neg-risk system relies on `WrappedCollateral` (`WCOL`) as a wrapper over real collateral plus an adapter-only unbacked mint path used during conversion and cross-market minting.
|
|
88
|
+
|
|
89
|
+
That design is only solvent if event-level resolution preserves the accounting invariant between:
|
|
90
|
+
|
|
91
|
+
- user-held YES sets
|
|
92
|
+
- adapter-held NO inventory
|
|
93
|
+
- adapter-minted unbacked `WCOL`
|
|
94
|
+
|
|
95
|
+
`voidEvent()` currently validates only each market in isolation:
|
|
96
|
+
|
|
97
|
+
- each per-market pair is forced to sum to `1e18` via `manager.adminVoidMarket(...)`
|
|
98
|
+
- but there is no event-level bound on `sum(yesPayouts)`
|
|
99
|
+
|
|
100
|
+
Relevant code:
|
|
101
|
+
|
|
102
|
+
- `contracts/NegRiskAdapter.sol`
|
|
103
|
+
- `voidEvent()` accepts arbitrary `yesPayouts` arrays with only a length check
|
|
104
|
+
- each market is then voided with `YES = yesPayouts[i]` and `NO = 1e18 - yesPayouts[i]`
|
|
105
|
+
- `contracts/PredictionMarketV3ManagerCLOB.sol`
|
|
106
|
+
- `adminVoidMarket()` validates only that each market's two payouts sum to `1e18`
|
|
107
|
+
- `contracts/NegRiskAdapter.sol`
|
|
108
|
+
- `redeemNOPositions()` burns `min(mintedWcolPerEvent[eventId], wcolRecovered)` and then zeroes the event accounting regardless of whether all minted `WCOL` was actually recovered
|
|
109
|
+
- `contracts/WrappedCollateral.sol`
|
|
110
|
+
- `unwrap()` is global and permissionless, so any surviving unbacked `WCOL` can later redeem against unrelated backing
|
|
111
|
+
|
|
112
|
+
For converted or cross-market-created full YES baskets, solvency requires `sum(yesPayouts) <= 1e18`. If the sum exceeds `1e18`, YES holders can redeem more `WCOL` than the adapter can recover from its NO positions. The deficit is not tracked after cleanup because `mintedWcolPerEvent[eventId]` is zeroed even when recovery is incomplete.
|
|
113
|
+
|
|
114
|
+
**Impact**
|
|
115
|
+
|
|
116
|
+
This can create persistent bad debt inside `WCOL`:
|
|
117
|
+
|
|
118
|
+
1. a neg-risk event is voided with an over-allocating YES vector
|
|
119
|
+
2. YES holders redeem more `WCOL` than the system can safely back
|
|
120
|
+
3. cleanup burns only the recoverable portion of minted `WCOL`
|
|
121
|
+
4. leftover unbacked `WCOL` remains transferable and unwrap-capable
|
|
122
|
+
5. future wrappers or unrelated users provide fresh underlying backing
|
|
123
|
+
6. the unbacked `WCOL` drains that new backing
|
|
124
|
+
|
|
125
|
+
This is a direct cross-user and cross-event solvency failure.
|
|
126
|
+
|
|
127
|
+
**Exploit sketch**
|
|
128
|
+
|
|
129
|
+
For a 3-outcome event:
|
|
130
|
+
|
|
131
|
+
- Alice deposits `100`
|
|
132
|
+
- Alice converts into a full YES basket across all three markets
|
|
133
|
+
- admin voids the event with `yesPayouts = [0.5, 0.5, 0.5]`
|
|
134
|
+
- Alice redeems `150 WCOL`
|
|
135
|
+
- adapter NO cleanup recovers only `150 WCOL` against `200 WCOL` minted during conversion, burns `150`, and zeroes the accounting
|
|
136
|
+
- `WCOL` supply still exceeds real underlying by `50`
|
|
137
|
+
- Alice can later drain a new user's wrapped deposit using the surviving unbacked `WCOL`
|
|
138
|
+
|
|
139
|
+
The repository now includes a proof-of-concept test at `test/POC.t.sol` demonstrating this behavior.
|
|
140
|
+
|
|
141
|
+
**Why this is especially concerning**
|
|
142
|
+
|
|
143
|
+
The current tests explicitly treat `50/50/50` event void payouts as acceptable for a 3-outcome event:
|
|
144
|
+
|
|
145
|
+
- `test/NegRiskAdapter.t.sol`
|
|
146
|
+
- `testVoidEvent5050`
|
|
147
|
+
- `testVoidEventRedeemNOPositions`
|
|
148
|
+
|
|
149
|
+
That makes the issue more than a hypothetical malicious-admin corner case. The repository already encodes an event-level payout shape that breaks wrapper solvency.
|
|
150
|
+
|
|
151
|
+
**Recommendations**
|
|
152
|
+
|
|
153
|
+
At minimum:
|
|
154
|
+
|
|
155
|
+
1. enforce an event-level invariant in `voidEvent()`:
|
|
156
|
+
- `sum(yesPayouts) <= 1e18`
|
|
157
|
+
2. do not zero `mintedWcolPerEvent[eventId]` unless full recovery occurred
|
|
158
|
+
3. if partial recovery is ever possible, preserve explicit bad-debt accounting and prevent unrelated backing from being consumed silently
|
|
159
|
+
|
|
160
|
+
Stronger mitigations:
|
|
161
|
+
|
|
162
|
+
1. silo wrapper accounting per event rather than sharing one global redeemable wrapper
|
|
163
|
+
2. treat over-allocating void vectors as invalid resolution inputs rather than as treasury/accounting events
|
|
164
|
+
3. add invariant tests asserting `underlying.balanceOf(wcol) >= wcol.totalSupply()` after every resolution path
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
### M-01: Market admins can reopen naturally closed markets and revive stale GTC orders
|
|
169
|
+
|
|
170
|
+
**Severity:** Medium
|
|
171
|
+
|
|
172
|
+
**Affected contracts:**
|
|
173
|
+
|
|
174
|
+
- `contracts/PredictionMarketV3ManagerCLOB.sol`
|
|
175
|
+
- `contracts/MyriadCTFExchange.sol`
|
|
176
|
+
|
|
177
|
+
**Description**
|
|
178
|
+
|
|
179
|
+
`adminSetClosesAt()` can move `closesAt` forward on any unresolved market, even if the market has already closed by time.
|
|
180
|
+
|
|
181
|
+
The manager does not persist a closed state transition. Instead, close status is derived at read time:
|
|
182
|
+
|
|
183
|
+
- if `market.state == open` and `block.timestamp >= closesAt`, `getMarketState()` returns `closed`
|
|
184
|
+
- `isMarketTradeable()` similarly depends on `block.timestamp < closesAt`
|
|
185
|
+
|
|
186
|
+
If a market has naturally closed and an admin later extends `closesAt`, the market becomes tradeable again.
|
|
187
|
+
|
|
188
|
+
**Impact**
|
|
189
|
+
|
|
190
|
+
The SDK documents `expiration = 0` as GTC order behavior. That means old signed orders can remain valid indefinitely. Reopening a closed market can therefore reactivate stale orders under new information, which is a serious integrity problem for an orderbook launch.
|
|
191
|
+
|
|
192
|
+
This is especially dangerous when:
|
|
193
|
+
|
|
194
|
+
- users rely on market close as a hard boundary for stale orders
|
|
195
|
+
- off-chain infra preserves order validity until explicit expiration or cancellation
|
|
196
|
+
- admins use close-time edits operationally without realizing they are reviving execution rights
|
|
197
|
+
|
|
198
|
+
**Recommendations**
|
|
199
|
+
|
|
200
|
+
1. forbid extending `closesAt` after a market has already closed
|
|
201
|
+
2. if edits are needed, allow only shortening while the market is still open
|
|
202
|
+
3. alternatively, permanently latch close once first reached
|
|
203
|
+
4. if reopening must exist, invalidate all outstanding orders for that market as part of the action
|
|
204
|
+
|
|
205
|
+
---
|
|
206
|
+
|
|
207
|
+
### M-02: Market admins can swap the oracle after close and seize settlement
|
|
208
|
+
|
|
209
|
+
**Severity:** Medium
|
|
210
|
+
|
|
211
|
+
**Affected contracts:**
|
|
212
|
+
|
|
213
|
+
- `contracts/PredictionMarketV3ManagerCLOB.sol`
|
|
214
|
+
|
|
215
|
+
**Description**
|
|
216
|
+
|
|
217
|
+
`updateMarketOracle()` is callable by `MARKET_ADMIN_ROLE` on any unresolved market. There is no restriction that the market must still be open.
|
|
218
|
+
|
|
219
|
+
As a result, after trading has ended but before settlement, a market admin can:
|
|
220
|
+
|
|
221
|
+
- replace the oracle with a malicious oracle that returns a chosen result
|
|
222
|
+
- replace the oracle with a non-resolving oracle and freeze permissionless settlement
|
|
223
|
+
- reinitialize oracle-side state with new `oracleData`
|
|
224
|
+
|
|
225
|
+
`resolveMarket()` then trusts the currently configured oracle.
|
|
226
|
+
|
|
227
|
+
**Impact**
|
|
228
|
+
|
|
229
|
+
This collapses the trust boundary between market administration and resolution. In the current role model, market creation/maintenance and final settlement are presented as separate concerns, but the contract lets a market admin rewrite the settlement source after trading is over.
|
|
230
|
+
|
|
231
|
+
That creates a meaningful integrity risk even if `RESOLUTION_ADMIN_ROLE` is intended to be tightly controlled.
|
|
232
|
+
|
|
233
|
+
**Recommendations**
|
|
234
|
+
|
|
235
|
+
1. freeze oracle changes once the market closes
|
|
236
|
+
2. preferably freeze oracle changes immediately after market creation unless there is a clearly documented emergency path
|
|
237
|
+
3. if emergency oracle replacement is required, gate it behind `RESOLUTION_ADMIN_ROLE` plus a timelock or pause
|
|
238
|
+
4. emit stronger operational guidance that oracle changes alter settlement trust assumptions
|
|
239
|
+
|
|
240
|
+
---
|
|
241
|
+
|
|
242
|
+
### M-03: Rotating the global neg-risk adapter can strand live events
|
|
243
|
+
|
|
244
|
+
**Severity:** Medium
|
|
245
|
+
|
|
246
|
+
**Affected contracts:**
|
|
247
|
+
|
|
248
|
+
- `contracts/PredictionMarketV3ManagerCLOB.sol`
|
|
249
|
+
- `contracts/MyriadCTFExchange.sol`
|
|
250
|
+
|
|
251
|
+
**Description**
|
|
252
|
+
|
|
253
|
+
The manager and exchange each store a single mutable `negRiskAdapter` address:
|
|
254
|
+
|
|
255
|
+
- manager uses it to authorize neg-risk market creation and neg-risk admin resolution
|
|
256
|
+
- exchange uses it for cross-market minting
|
|
257
|
+
|
|
258
|
+
However, neg-risk events are stateful:
|
|
259
|
+
|
|
260
|
+
- the adapter itself stores event membership and question text
|
|
261
|
+
- the adapter holds NO inventory
|
|
262
|
+
- the adapter tracks minted `WCOL` per event
|
|
263
|
+
|
|
264
|
+
If governance rotates `negRiskAdapter` after events are already live:
|
|
265
|
+
|
|
266
|
+
- the old adapter still owns the state and positions
|
|
267
|
+
- the manager stops recognizing the old adapter as the authorized neg-risk resolver
|
|
268
|
+
- the exchange starts calling the new adapter for cross-market operations
|
|
269
|
+
- the new adapter has no knowledge of old events
|
|
270
|
+
|
|
271
|
+
**Impact**
|
|
272
|
+
|
|
273
|
+
This can strand live events and freeze event-specific flows even if all parties are honest. It is an availability and operational safety issue with funds in flight.
|
|
274
|
+
|
|
275
|
+
**Recommendations**
|
|
276
|
+
|
|
277
|
+
1. make the adapter immutable once the first neg-risk event is created
|
|
278
|
+
2. or store the adapter address per event/market at creation time and always route through that stored adapter
|
|
279
|
+
3. document migration explicitly if adapter replacement is intended
|
|
280
|
+
|
|
281
|
+
## Informational Observations
|
|
282
|
+
|
|
283
|
+
### I-01: SDK/reference documentation is materially out of sync with the contracts
|
|
284
|
+
|
|
285
|
+
The reviewed `CLOB_SDK_REFERENCE.md` does not match the current contract surface in several important places:
|
|
286
|
+
|
|
287
|
+
- unresolved outcome docs describe `-2`, while the manager returns `-3`
|
|
288
|
+
- voided redemption example calls `redeemPositions`, while the contract exposes `redeemVoided`
|
|
289
|
+
- fee docs describe array-based fee schedules and a 3-argument `withdrawFees`, while the contract uses tier structs and a 2-argument withdrawal
|
|
290
|
+
|
|
291
|
+
This mismatch increases integration risk and could lead client code to make unsafe assumptions about settlement and fee behavior.
|
|
292
|
+
|
|
293
|
+
### I-02: The new stack depends on transient storage reentrancy guards
|
|
294
|
+
|
|
295
|
+
`PredictionMarketV3ManagerCLOB`, `MyriadCTFExchange`, and `NegRiskAdapter` use OpenZeppelin transient reentrancy guards, which require EIP-1153 support. Public BNB Chain sources indicate support is present, so this is not an immediate launch blocker for BSC, but it should be treated as a deployment precondition for any other target network.
|
|
296
|
+
|
|
297
|
+
### I-03: Local test execution was blocked by a Foundry nightly crash
|
|
298
|
+
|
|
299
|
+
The installed Foundry nightly panics before test execution in `system_configuration::dynamic_store`. `forge build` succeeds. The included PoC was therefore added and compile-checked, but not executed in this environment.
|
|
300
|
+
|
|
301
|
+
## Conclusion
|
|
302
|
+
|
|
303
|
+
The new CLOB contracts are thoughtfully structured and cover many important behaviors with tests, but the neg-risk accounting model currently has a launch-blocking solvency issue. The medium-severity findings also show that a few privileged operations remain too powerful relative to the role model described in the SDK and expected by traders.
|
|
304
|
+
|
|
305
|
+
I would not recommend launching the neg-risk component in its current form before H-01 is fixed and the privileged lifecycle behaviors are narrowed or explicitly accepted as protocol trust assumptions.
|