Skip to content

Commit 4e2b876

Browse files
committed
faucet contract
1 parent 4ea5ce8 commit 4e2b876

28 files changed

Lines changed: 3579 additions & 48 deletions

.docs/changeAndIntegrateWithPolymarketSystemsAlike.md

Lines changed: 1127 additions & 0 deletions
Large diffs are not rendered by default.

.docs/differences.md

Lines changed: 940 additions & 0 deletions
Large diffs are not rendered by default.

.docs/spec.md

Lines changed: 503 additions & 0 deletions
Large diffs are not rendered by default.

script/DeployBetaTestnet.s.sol

Lines changed: 331 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,331 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity 0.8.24;
3+
4+
/// @title DeployBetaTestnet
5+
/// @notice Deploys the full testnet stack with MockUSDC + Faucet for beta testers.
6+
/// @dev Beta users: claim mock USDC from Faucet, then deposit into MultiAssetVault/CollateralVault to trade.
7+
/// Reuses DeployTestnet topology; SETTLEMENT_TOKEN is replaced by deployed MockUSDC.
8+
9+
import {Script, console2} from "forge-std/Script.sol";
10+
11+
import {MockUSDC} from "../src/mockTest/token/MockUSDC.sol";
12+
import {Faucet} from "../src/mockTest/faucet/Faucet.sol";
13+
14+
import {OutcomeToken1155} from "../src/execution/OutcomeToken1155.sol";
15+
import {MarketRiskManager} from "../src/execution/MarketRiskManager.sol";
16+
import {ChannelSettlement} from "../src/execution/ChannelSettlement.sol";
17+
import {MultiAssetVault} from "../src/execution/MultiAssetVault.sol";
18+
import {CollateralVault} from "../src/execution/CollateralVault.sol";
19+
import {MarketRegistry} from "../src/core/MarketRegistry.sol";
20+
21+
import {FeeManager} from "../src/fees/FeeManager.sol";
22+
import {FeePool} from "../src/fees/FeePool.sol";
23+
import {TreasuryPool} from "../src/fees/TreasuryPool.sol";
24+
25+
import {ReportValidator} from "../src/oracle/ReportValidator.sol";
26+
import {CREReceiver} from "../src/oracle/CREReceiver.sol";
27+
import {OracleCoordinator} from "../src/oracle/OracleCoordinator.sol";
28+
import {SettlementRouter} from "../src/core/SettlementRouter.sol";
29+
30+
import {MarketPolicy} from "../src/curation/MarketPolicy.sol";
31+
import {MarketDraftBoard} from "../src/curation/MarketDraftBoard.sol";
32+
import {DraftClaimManager} from "../src/curation/DraftClaimManager.sol";
33+
import {LiquidityVaultFactory} from "../src/curation/LiquidityVaultFactory.sol";
34+
import {CREPublishReceiver} from "../src/curation/CREPublishReceiver.sol";
35+
import {MarketFactory} from "../src/core/MarketFactory.sol";
36+
37+
contract DeployBetaTestnet is Script {
38+
struct Deployed {
39+
MockUSDC mockUSDC;
40+
Faucet faucet;
41+
OutcomeToken1155 outcomeToken;
42+
MarketRiskManager riskManager;
43+
ChannelSettlement channelSettlement;
44+
MultiAssetVault multiAssetVault;
45+
CollateralVault collateralVault;
46+
MarketRegistry marketRegistry;
47+
FeeManager feeManager;
48+
FeePool feePool;
49+
TreasuryPool treasuryPool;
50+
ReportValidator reportValidator;
51+
CREReceiver creReceiver;
52+
OracleCoordinator oracleCoordinator;
53+
SettlementRouter settlementRouter;
54+
MarketPolicy marketPolicy;
55+
MarketDraftBoard draftBoard;
56+
DraftClaimManager draftClaimManager;
57+
LiquidityVaultFactory liquidityVaultFactory;
58+
CREPublishReceiver crePublishReceiver;
59+
MarketFactory marketFactory;
60+
}
61+
62+
function run() external returns (Deployed memory d) {
63+
// ---- Required env ----
64+
address operator = _envAddressReq("OPERATOR");
65+
address forwarder = _resolveForwarder();
66+
67+
// ---- Faucet config (optional, with defaults) ----
68+
uint96 faucetAmountPerClaim = uint96(_envUintOr("FAUCET_AMOUNT_PER_CLAIM", 1000 * 1e6)); // 1000 USDC (6 decimals)
69+
uint32 faucetCooldownSecs = uint32(_envUintOr("FAUCET_COOLDOWN_SECS", 3600)); // 1 hour
70+
uint256 faucetMintSupply = _envUintOr("FAUCET_MINT_SUPPLY", 1_000_000 * 1e6); // 1M USDC
71+
72+
// ---- Validations / configs (reuse DeployTestnet env) ----
73+
uint16 minConfidence = uint16(_envUintReq("MIN_CONFIDENCE"));
74+
uint16 protocolFeeBps = uint16(_envUintReq("PROTOCOL_FEE_BPS"));
75+
uint16 lpFeeShareBps = uint16(_envUintReq("LP_FEE_SHARE_BPS"));
76+
uint16 creatorFeeShareBps = uint16(_envUintReq("CREATOR_FEE_SHARE_BPS"));
77+
bool useReceiverAllowlist = _envBoolOr("USE_RECEIVER_ALLOWLIST", true);
78+
bool approveRegistryReceiver = _envBoolOr("APPROVE_MARKET_REGISTRY_RECEIVER", true);
79+
80+
address expectedAuthor = _envAddressOr("EXPECTED_WORKFLOW_AUTHOR", address(0));
81+
bytes32 expectedWorkflowId = _envBytes32Or("EXPECTED_WORKFLOW_ID", bytes32(0));
82+
string memory expectedWorkflowName = _envStringOr("EXPECTED_WORKFLOW_NAME", "");
83+
84+
require(operator != address(0), "OPERATOR is zero");
85+
require(forwarder != address(0), "CHAINLINK_FORWARDER resolved to zero");
86+
require(protocolFeeBps <= 10_000, "PROTOCOL_FEE_BPS > 10000");
87+
require(lpFeeShareBps <= 10_000, "LP_FEE_SHARE_BPS > 10000");
88+
require(creatorFeeShareBps <= 10_000, "CREATOR_FEE_SHARE_BPS > 10000");
89+
90+
vm.startBroadcast();
91+
92+
// 0) Mock token + Faucet (beta test only)
93+
d.mockUSDC = new MockUSDC();
94+
d.faucet = new Faucet(msg.sender);
95+
96+
d.mockUSDC.mint(address(d.faucet), faucetMintSupply);
97+
d.faucet.setToken(address(d.mockUSDC), true, faucetAmountPerClaim, faucetCooldownSecs);
98+
99+
address settlementToken = address(d.mockUSDC);
100+
101+
// 1) V3 Execution lane: OutcomeToken1155 + MarketRiskManager
102+
d.outcomeToken = new OutcomeToken1155("https://api.retropick.xyz/outcome/{id}.json");
103+
d.riskManager = new MarketRiskManager();
104+
d.multiAssetVault = new MultiAssetVault(address(0));
105+
d.collateralVault = new CollateralVault(settlementToken, address(0));
106+
d.channelSettlement = new ChannelSettlement(address(d.collateralVault), address(0), operator);
107+
d.marketRegistry = new MarketRegistry(address(d.collateralVault), address(0));
108+
109+
d.outcomeToken.setChannelSettlement(address(d.channelSettlement));
110+
d.outcomeToken.setMarketRegistry(address(d.marketRegistry));
111+
d.riskManager.setChannelSettlement(address(d.channelSettlement));
112+
d.channelSettlement.setOutcomeToken(address(d.outcomeToken));
113+
d.channelSettlement.setRiskManager(address(d.riskManager));
114+
d.marketRegistry.setOutcomeToken(address(d.outcomeToken));
115+
d.multiAssetVault.setChannelSettlement(address(d.channelSettlement));
116+
d.multiAssetVault.setMarketRegistry(address(d.marketRegistry));
117+
d.collateralVault.setChannelSettlement(address(d.channelSettlement));
118+
d.collateralVault.setMarketRegistry(address(d.marketRegistry));
119+
d.channelSettlement.setMarketRegistry(address(d.marketRegistry));
120+
d.channelSettlement.setMultiAssetVault(address(d.multiAssetVault));
121+
d.marketRegistry.setMultiAssetVault(address(d.multiAssetVault));
122+
d.marketRegistry.setDefaultSettlementAsset(settlementToken);
123+
124+
// 2) Fees
125+
d.feeManager = new FeeManager(protocolFeeBps);
126+
d.feePool = new FeePool();
127+
d.treasuryPool = new TreasuryPool();
128+
129+
d.feeManager.setLpFeeShareBps(lpFeeShareBps);
130+
d.feeManager.setCreatorFeeShareBps(creatorFeeShareBps);
131+
d.feePool.setFeeCollector(address(d.channelSettlement));
132+
d.feePool.setTreasuryPool(address(d.treasuryPool));
133+
d.channelSettlement.setFeeManager(address(d.feeManager));
134+
d.channelSettlement.setFeePool(address(d.feePool));
135+
136+
// 3) Oracle and routing
137+
d.reportValidator = new ReportValidator(minConfidence);
138+
d.oracleCoordinator = new OracleCoordinator();
139+
d.settlementRouter = new SettlementRouter();
140+
d.creReceiver = new CREReceiver(forwarder, address(d.oracleCoordinator));
141+
142+
d.oracleCoordinator.setCreReceiver(address(d.creReceiver));
143+
d.oracleCoordinator.setSettlementRouter(address(d.settlementRouter));
144+
d.oracleCoordinator.setReportValidator(address(d.reportValidator));
145+
146+
d.settlementRouter.setOracleCoordinator(address(d.oracleCoordinator));
147+
d.settlementRouter.setChannelSettlement(address(d.channelSettlement));
148+
d.settlementRouter.setUseReceiverAllowlist(useReceiverAllowlist);
149+
if (approveRegistryReceiver) {
150+
d.settlementRouter.setMarketReceiverApproved(address(d.marketRegistry), true);
151+
}
152+
d.marketRegistry.setSettlementRouter(address(d.settlementRouter));
153+
154+
// 4) Curated lane
155+
d.marketPolicy = new MarketPolicy();
156+
d.draftBoard = new MarketDraftBoard();
157+
d.draftClaimManager = new DraftClaimManager(address(d.draftBoard));
158+
d.liquidityVaultFactory = new LiquidityVaultFactory(address(d.channelSettlement));
159+
d.marketFactory = new MarketFactory(forwarder, address(d.marketRegistry));
160+
d.crePublishReceiver = new CREPublishReceiver(
161+
forwarder,
162+
address(d.draftBoard),
163+
address(d.draftClaimManager),
164+
address(d.marketPolicy),
165+
address(d.marketFactory)
166+
);
167+
168+
d.draftBoard.setDraftClaimManager(address(d.draftClaimManager));
169+
d.draftBoard.grantPublishCaller(address(d.marketFactory));
170+
171+
address aiOracle = _envAddressOr("AI_ORACLE_ADDRESS", address(0));
172+
if (aiOracle != address(0)) {
173+
d.draftBoard.grantRole(d.draftBoard.AI_ORACLE_ROLE(), aiOracle);
174+
}
175+
d.draftClaimManager.setLiquidityVaultFactory(address(d.liquidityVaultFactory));
176+
177+
d.marketFactory.setMarketRegistry(address(d.marketRegistry));
178+
d.marketFactory.setDraftBoard(address(d.draftBoard));
179+
d.marketFactory.setDraftClaimManager(address(d.draftClaimManager));
180+
d.marketFactory.setMarketPolicy(address(d.marketPolicy));
181+
d.marketFactory.setRiskManager(address(d.riskManager));
182+
d.marketFactory.setPublishReceiverApproved(address(d.crePublishReceiver), true);
183+
d.riskManager.setMarketFactory(address(d.marketFactory));
184+
185+
d.marketRegistry.setMarketFactory(address(d.marketFactory));
186+
187+
_configureReceiverTemplate(address(d.creReceiver), expectedAuthor, expectedWorkflowId, expectedWorkflowName);
188+
_configureReceiverTemplate(address(d.crePublishReceiver), expectedAuthor, expectedWorkflowId, expectedWorkflowName);
189+
_configureReceiverTemplate(address(d.marketFactory), expectedAuthor, expectedWorkflowId, expectedWorkflowName);
190+
191+
vm.stopBroadcast();
192+
193+
_log(d);
194+
return d;
195+
}
196+
197+
function _configureReceiverTemplate(
198+
address receiver,
199+
address expectedAuthor,
200+
bytes32 expectedWorkflowId,
201+
string memory expectedWorkflowName
202+
) internal {
203+
if (expectedAuthor != address(0)) {
204+
(bool okAuthor, ) = receiver.call(abi.encodeWithSignature("setExpectedAuthor(address)", expectedAuthor));
205+
require(okAuthor, "setExpectedAuthor failed");
206+
}
207+
if (expectedWorkflowId != bytes32(0)) {
208+
(bool okWorkflowId, ) =
209+
receiver.call(abi.encodeWithSignature("setExpectedWorkflowId(bytes32)", expectedWorkflowId));
210+
require(okWorkflowId, "setExpectedWorkflowId failed");
211+
}
212+
if (bytes(expectedWorkflowName).length != 0) {
213+
(bool okWorkflowName, ) =
214+
receiver.call(abi.encodeWithSignature("setExpectedWorkflowName(string)", expectedWorkflowName));
215+
require(okWorkflowName, "setExpectedWorkflowName failed");
216+
}
217+
}
218+
219+
function _log(Deployed memory d) internal view {
220+
console2.log("deployer", msg.sender);
221+
console2.log("MockUSDC", address(d.mockUSDC));
222+
console2.log("Faucet", address(d.faucet));
223+
console2.log("OutcomeToken1155", address(d.outcomeToken));
224+
console2.log("MarketRiskManager", address(d.riskManager));
225+
console2.log("ChannelSettlement", address(d.channelSettlement));
226+
console2.log("MultiAssetVault", address(d.multiAssetVault));
227+
console2.log("CollateralVault", address(d.collateralVault));
228+
console2.log("MarketRegistry", address(d.marketRegistry));
229+
console2.log("FeeManager", address(d.feeManager));
230+
console2.log("FeePool", address(d.feePool));
231+
console2.log("TreasuryPool", address(d.treasuryPool));
232+
console2.log("ReportValidator", address(d.reportValidator));
233+
console2.log("CREReceiver", address(d.creReceiver));
234+
console2.log("OracleCoordinator", address(d.oracleCoordinator));
235+
console2.log("SettlementRouter", address(d.settlementRouter));
236+
console2.log("MarketPolicy", address(d.marketPolicy));
237+
console2.log("MarketDraftBoard", address(d.draftBoard));
238+
console2.log("DraftClaimManager", address(d.draftClaimManager));
239+
console2.log("LiquidityVaultFactory", address(d.liquidityVaultFactory));
240+
console2.log("MarketFactory", address(d.marketFactory));
241+
console2.log("CREPublishReceiver", address(d.crePublishReceiver));
242+
console2.log("");
243+
console2.log("--- Beta test: claim mock USDC from Faucet ---");
244+
console2.log(" faucet.claim(mockUSDC) -> deposit into CollateralVault / MultiAssetVault -> trade");
245+
console2.log("");
246+
console2.log("--- Post-deploy checklist ---");
247+
console2.log("1. Relayer (apps/relayer/.env): CHANNEL_SETTLEMENT_ADDRESS=%s", address(d.channelSettlement));
248+
console2.log("2. CRE workflows: CREReceiver=%s, CREPublishReceiver=%s, MarketFactory=%s",
249+
address(d.creReceiver), address(d.crePublishReceiver), address(d.marketFactory));
250+
}
251+
252+
function _envAddressReq(string memory key) internal view returns (address) {
253+
try vm.envAddress(key) returns (address v) {
254+
return v;
255+
} catch {
256+
revert(string.concat("Missing/invalid env address: ", key));
257+
}
258+
}
259+
260+
function _envAddressOr(string memory key, address fallback_) internal view returns (address) {
261+
try vm.envAddress(key) returns (address v) {
262+
return v;
263+
} catch {
264+
return fallback_;
265+
}
266+
}
267+
268+
function _envUintReq(string memory key) internal view returns (uint256) {
269+
try vm.envUint(key) returns (uint256 v) {
270+
return v;
271+
} catch {
272+
revert(string.concat("Missing/invalid env uint: ", key));
273+
}
274+
}
275+
276+
function _envUintOr(string memory key, uint256 fallback_) internal view returns (uint256) {
277+
try vm.envUint(key) returns (uint256 v) {
278+
return v;
279+
} catch {
280+
return fallback_;
281+
}
282+
}
283+
284+
function _envBoolOr(string memory key, bool fallback_) internal view returns (bool) {
285+
try vm.envBool(key) returns (bool v) {
286+
return v;
287+
} catch {
288+
return fallback_;
289+
}
290+
}
291+
292+
function _envBytes32Or(string memory key, bytes32 fallback_) internal view returns (bytes32) {
293+
try vm.envBytes32(key) returns (bytes32 v) {
294+
return v;
295+
} catch {
296+
return fallback_;
297+
}
298+
}
299+
300+
function _envStringOr(string memory key, string memory fallback_) internal view returns (string memory) {
301+
try vm.envString(key) returns (string memory v) {
302+
return v;
303+
} catch {
304+
return fallback_;
305+
}
306+
}
307+
308+
function _resolveForwarder() internal view returns (address forwarder) {
309+
string memory raw;
310+
try vm.envString("CHAINLINK_FORWARDER") returns (string memory v) {
311+
raw = v;
312+
} catch {
313+
revert("Missing env: CHAINLINK_FORWARDER");
314+
}
315+
bytes memory b = bytes(raw);
316+
if (b.length == 0) revert("CHAINLINK_FORWARDER is empty");
317+
if (b[0] == bytes1("$")) {
318+
string memory key = _slice(raw, 1, b.length - 1);
319+
forwarder = _envAddressReq(key);
320+
return forwarder;
321+
}
322+
forwarder = vm.parseAddress(raw);
323+
}
324+
325+
function _slice(string memory s, uint256 start, uint256 len) internal pure returns (string memory) {
326+
bytes memory b = bytes(s);
327+
bytes memory out = new bytes(len);
328+
for (uint256 i = 0; i < len; i++) out[i] = b[start + i];
329+
return string(out);
330+
}
331+
}

src/core/AccessManager.sol

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// SPDX-License-Identifier: MIT
2-
pragma solidity ^0.8.28;
2+
pragma solidity 0.8.24;
33

4-
import {Errors} from "../libraries/Errors.sol";
4+
import {Errors} from "../libs/Errors.sol";
55

66
contract AccessManager {
77
address public owner;

src/core/MarketFactory.sol

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
66
import {MessageHashUtils} from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol";
77
import {MarketDraftBoard} from "../curation/MarketDraftBoard.sol";
88
import {DraftClaimManager} from "../curation/DraftClaimManager.sol";
9+
import {MarketPolicy} from "../curation/MarketPolicy.sol";
10+
import {IMarketRiskManager} from "../interfaces/IMarketRiskManager.sol";
911
import {ILiquidityVault4626} from "../interfaces/ILiquidityVault4626.sol";
1012

1113
/// @notice Interface for the prediction market.
@@ -201,6 +203,8 @@ contract MarketFactory is ReceiverTemplate {
201203
IMarketRegistryCreate public marketRegistry;
202204
MarketDraftBoard public draftBoard;
203205
DraftClaimManager public draftClaimManager;
206+
MarketPolicy public marketPolicy;
207+
IMarketRiskManager public riskManager;
204208
mapping(address => bool) public approvedPublishReceivers;
205209
mapping(uint256 => bytes32) public draftIdByMarketId;
206210

@@ -242,6 +246,16 @@ contract MarketFactory is ReceiverTemplate {
242246
draftClaimManager = DraftClaimManager(claimManager);
243247
}
244248

249+
/// @notice Set MarketPolicy for lpExposureMultiplier (risk cap).
250+
function setMarketPolicy(address policy) external onlyOwner {
251+
marketPolicy = MarketPolicy(policy);
252+
}
253+
254+
/// @notice Set MarketRiskManager for LP payout caps.
255+
function setRiskManager(address rm) external onlyOwner {
256+
riskManager = IMarketRiskManager(rm);
257+
}
258+
245259
/// @notice Approve or revoke a publish receiver (e.g. CREPublishReceiver).
246260
function setPublishReceiverApproved(address receiver, bool approved) external onlyOwner {
247261
approvedPublishReceivers[receiver] = approved;
@@ -329,6 +343,11 @@ contract MarketFactory is ReceiverTemplate {
329343

330344
if (liquidityVault != address(0)) {
331345
marketRegistry.setLiquidityVault(marketId, liquidityVault);
346+
if (address(riskManager) != address(0)) {
347+
uint256 mult = address(marketPolicy) != address(0) ? marketPolicy.lpExposureMultiplier() : 1;
348+
uint256 cap = d.minSeed * mult;
349+
riskManager.setMaxLpPayout(marketId, cap);
350+
}
332351
}
333352

334353
draftBoard.markPublished(draftId, marketId);

0 commit comments

Comments
 (0)