diff --git a/script/BaseScript.s.sol b/script/BaseScript.s.sol index 90c8568..59e3698 100644 --- a/script/BaseScript.s.sol +++ b/script/BaseScript.s.sol @@ -9,9 +9,13 @@ import {console2, Script} from "forge-std/Script.sol"; // Currency Symbol ETH // Block Explorer https://sepolia.uniscan.xyz/ +// Chain Name Base Sepolia +// https://base-sepolia-rpc.publicnode.com + contract BaseScript is Script { function setUp() public virtual { - vm.createSelectFork("https://mainnet.base.org"); + vm.createSelectFork("https://base-sepolia-rpc.publicnode.com"); + // vm.createSelectFork("https://mainnet.base.org"); uint256 deployerPrivateKey = vm.envUint("PRI_KEY"); vm.startBroadcast(deployerPrivateKey); } diff --git a/script/base/00_DeployAll.s.sol b/script/base/00_DeployAll.s.sol index 4ecf737..3daab17 100644 --- a/script/base/00_DeployAll.s.sol +++ b/script/base/00_DeployAll.s.sol @@ -2,52 +2,20 @@ pragma solidity ^0.8.24; import {console2, Script} from "forge-std/Script.sol"; +import {MockERC20} from "src/token/MockERC20.sol"; import {CTFExchange} from "src/exchange/CTFExchange.sol"; import {ConditionalTokens} from "src/token/ConditionalTokens.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; +import {BaseScript} from "../BaseScript.s.sol"; -// ───────────────────────────────────────────────────────────── -// Inline MockUSDC — open mint, 6 decimals (testnet only!) -// ───────────────────────────────────────────────────────────── -contract MockUSDC is ERC20 { - uint8 private immutable _dec; - - constructor() ERC20("Mock USDC", "mUSDC") { - _dec = 6; - } - - function decimals() public view override returns (uint8) { return _dec; } - - /// @notice Anyone can mint on testnet - function mint(address to, uint256 amount) external { - _mint(to, amount); - } -} - -// ───────────────────────────────────────────────────────────── -/** - * @title DeployAll - * @notice One-shot script: deploys MockUSDC + ConditionalTokens + CTFExchange, - * prepares a prediction market, and funds maker/taker accounts. - * - * Usage: - * forge script script/base/00_DeployAll.s.sol:DeployAll \ - * --rpc-url $BASE_SEPOLIA_RPC \ - * --broadcast \ - * --private-key $PRI_KEY \ - * -vvvv - * - * After running, add the printed values to your .env and then run: - * bash script/base/demo_loop.sh - */ -contract DeployAll is Script { +contract DeployAll is BaseScript { // ── Config ────────────────────────────────────────────── - uint256 constant FUND_AMOUNT = 10_000 * 1e6; // 10,000 mock USDC - uint256 constant SPLIT_AMOUNT = 5_000 * 1e6; // 5,000 split into YES/NO tokens + uint256 constant FUND_AMOUNT = 10_000 * 1e6; // 10,000 mock USDC + uint256 constant SPLIT_AMOUNT = 5_000 * 1e6; // 5,000 split into YES/NO tokens // ── State (set during run) ─────────────────────────────── - address public collateral; + address public collateral = 0xB54Cfef749d104b54D514538cAdb281f2E951079; address public ctf; address public exchange; bytes32 public conditionId; @@ -62,94 +30,90 @@ contract DeployAll is Script { address public maker; address public taker; - function run() external { - // Load keys - deployerKey = vm.envUint("PRI_KEY"); - makerKey = vm.envOr("MAKER_PRIVATE_KEY", deployerKey); - takerKey = vm.envOr("TAKER_PRIVATE_KEY", deployerKey); - deployer = vm.addr(deployerKey); - maker = vm.addr(makerKey); - taker = vm.addr(takerKey); - - console2.log("\n====================================================="); - console2.log(" Base Sepolia - Prediction Market - Full Deploy"); - console2.log("====================================================="); - console2.log("Chain ID :", block.chainid); - console2.log("Deployer :", deployer); - console2.log("Maker :", maker); - console2.log("Taker :", taker); - - // ── Step 1: Deploy contracts (as deployer) ─────────── - vm.startBroadcast(deployerKey); - - collateral = address(new MockUSDC()); - console2.log("\n[1] MockUSDC :", collateral); + function run() public { + // 1.deploy mock usdc + deployUSDCToken(); - ctf = address(new ConditionalTokens()); - console2.log("[2] ConditionalTokens:", ctf); + // 2. deploy CTF Token + // deployCTFToken(); - exchange = address(new CTFExchange(collateral, ctf, address(0), address(0))); - console2.log("[3] CTFExchange :", exchange); - - // Add operator (deployer == matching engine) - CTFExchange(exchange).addOperator(deployer); - console2.log("[4] Operator added :", deployer); - - // Prepare market: "Will ETH reach $10k in 2026?" - bytes32 questionId = keccak256(abi.encodePacked("Will ETH reach $10k in 2026?", uint256(block.chainid))); - ConditionalTokens(ctf).prepareCondition(deployer, questionId, 2); - conditionId = ConditionalTokens(ctf).getConditionId(deployer, questionId, 2); - console2.log("[5] Condition ID :", vm.toString(conditionId)); - - // Compute YES / NO token IDs - bytes32 colIdYes = ConditionalTokens(ctf).getCollectionId(bytes32(0), conditionId, 1); - bytes32 colIdNo = ConditionalTokens(ctf).getCollectionId(bytes32(0), conditionId, 2); - tokenIdYes = ConditionalTokens(ctf).getPositionId(IERC20(collateral), colIdYes); - tokenIdNo = ConditionalTokens(ctf).getPositionId(IERC20(collateral), colIdNo); - console2.log("[6] tokenIdYes :", tokenIdYes); - console2.log(" tokenIdNo :", tokenIdNo); - - // Register tokens on exchange - CTFExchange(exchange).registerToken(tokenIdYes, tokenIdNo, conditionId); - console2.log("[7] Tokens registered on exchange"); - - // Mint mock USDC to maker and taker - MockUSDC(collateral).mint(maker, FUND_AMOUNT); - MockUSDC(collateral).mint(taker, FUND_AMOUNT); - console2.log("[8] Minted", FUND_AMOUNT / 1e6, "mUSDC to maker & taker"); - - vm.stopBroadcast(); + + } - // ── Step 2: Maker splits USDC -> YES+NO tokens ─────── - vm.startBroadcast(makerKey); - IERC20(collateral).approve(ctf, SPLIT_AMOUNT); - uint256[] memory partition = new uint256[](2); - partition[0] = 1; partition[1] = 2; - ConditionalTokens(ctf).splitPosition( - IERC20(collateral), bytes32(0), conditionId, partition, SPLIT_AMOUNT - ); - // Approve exchange to move maker's outcome tokens - ConditionalTokens(ctf).setApprovalForAll(exchange, true); - vm.stopBroadcast(); - console2.log("[9] Maker split USDC -> YES/NO and approved exchange"); + /** + * // // Add operator (deployer == matching engine) + * // CTFExchange(exchange).addOperator(deployer); + * // console2.log("[4] Operator added :", deployer); + * + * // // Prepare market: "Will ETH reach $10k in 2026?" + * // bytes32 questionId = keccak256(abi.encodePacked("Will ETH reach $10k in 2026?", uint256(block.chainid))); + * // ConditionalTokens(ctf).prepareCondition(deployer, questionId, 2); + * // conditionId = ConditionalTokens(ctf).getConditionId(deployer, questionId, 2); + * // console2.log("[5] Condition ID :", vm.toString(conditionId)); + * + * // // Compute YES / NO token IDs + * // bytes32 colIdYes = ConditionalTokens(ctf).getCollectionId(bytes32(0), conditionId, 1); + * // bytes32 colIdNo = ConditionalTokens(ctf).getCollectionId(bytes32(0), conditionId, 2); + * // tokenIdYes = ConditionalTokens(ctf).getPositionId(IERC20(collateral), colIdYes); + * // tokenIdNo = ConditionalTokens(ctf).getPositionId(IERC20(collateral), colIdNo); + * // console2.log("[6] tokenIdYes :", tokenIdYes); + * // console2.log(" tokenIdNo :", tokenIdNo); + * + * // // Register tokens on exchange + * // CTFExchange(exchange).registerToken(tokenIdYes, tokenIdNo, conditionId); + * // console2.log("[7] Tokens registered on exchange"); + * + * // // Mint mock USDC to maker and taker + * // MockUSDC(collateral).mint(maker, FUND_AMOUNT); + * // MockUSDC(collateral).mint(taker, FUND_AMOUNT); + * // console2.log("[8] Minted", FUND_AMOUNT / 1e6, "mUSDC to maker & taker"); + * + * // vm.stopBroadcast(); + * + * // // ── Step 2: Maker splits USDC -> YES+NO tokens ─────── + * // vm.startBroadcast(makerKey); + * // IERC20(collateral).approve(ctf, SPLIT_AMOUNT); + * // uint256[] memory partition = new uint256[](2); + * // partition[0] = 1; + * // partition[1] = 2; + * // ConditionalTokens(ctf).splitPosition(IERC20(collateral), bytes32(0), conditionId, partition, SPLIT_AMOUNT); + * // // Approve exchange to move maker's outcome tokens + * // ConditionalTokens(ctf).setApprovalForAll(exchange, true); + * // vm.stopBroadcast(); + * // console2.log("[9] Maker split USDC -> YES/NO and approved exchange"); + * + * // // ── Step 3: Taker approves exchange to spend USDC ─── + * // vm.startBroadcast(takerKey); + * // IERC20(collateral).approve(exchange, FUND_AMOUNT); + * // vm.stopBroadcast(); + * // console2.log("[10] Taker approved exchange for USDC"); + * + * // // ── Print env summary ──────────────────────────────── + * // console2.log("\n====================================================="); + * // console2.log(" COPY INTO YOUR .env FILE"); + * // console2.log("====================================================="); + * // console2.log("CTF_EXCHANGE=%s", exchange); + * // console2.log("CTF=%s", ctf); + * // console2.log("COLLATERAL=%s", collateral); + * // console2.log("TOKEN_ID_YES=%s", vm.toString(bytes32(tokenIdYes))); + * // console2.log("TOKEN_ID_NO=%s", vm.toString(bytes32(tokenIdNo))); + * // console2.log("CONDITION_ID=%s", vm.toString(conditionId)); + * // console2.log("====================================================="); + * // console2.log("Next step: bash script/base/demo_loop.sh\n"); + * // } + */ + function deployUSDCToken() public { + MockERC20 usdc = new MockERC20(); + console2.log("deployed usdc token "); + } - // ── Step 3: Taker approves exchange to spend USDC ─── - vm.startBroadcast(takerKey); - IERC20(collateral).approve(exchange, FUND_AMOUNT); - vm.stopBroadcast(); - console2.log("[10] Taker approved exchange for USDC"); + function deployCTFToken() public { + ConditionalTokens ctf = new ConditionalTokens(); + console2.log("ctf token ", address(ctf)); + } - // ── Print env summary ──────────────────────────────── - console2.log("\n====================================================="); - console2.log(" COPY INTO YOUR .env FILE"); - console2.log("====================================================="); - console2.log("CTF_EXCHANGE=%s", exchange); - console2.log("CTF=%s", ctf); - console2.log("COLLATERAL=%s", collateral); - console2.log("TOKEN_ID_YES=%s", vm.toString(bytes32(tokenIdYes))); - console2.log("TOKEN_ID_NO=%s", vm.toString(bytes32(tokenIdNo))); - console2.log("CONDITION_ID=%s", vm.toString(conditionId)); - console2.log("====================================================="); - console2.log("Next step: bash script/base/demo_loop.sh\n"); + function deployCTFExchange() public { + CTFExchange exchange = new CTFExchange(collateral, ctf, address(0), address(0)); + console2.log("[3] CTFExchange :", address(exchange)); } } diff --git a/script/base/01_MatchOnce.s.sol b/script/base/01_MatchOnce.s.sol index 8c26bfb..9a523d6 100644 --- a/script/base/01_MatchOnce.s.sol +++ b/script/base/01_MatchOnce.s.sol @@ -44,8 +44,8 @@ contract MatchOnce is Script { // ── Order size (realistic small fills for demo) ────────── /// Sell 100 YES tokens at 0.60 USDC each - uint256 constant FILL_TOKENS = 100 * 1e6; // 100 tokens (1e6 precision) - uint256 constant FILL_USDC = 60 * 1e6; // 60 USDC (6 decimals) + uint256 constant FILL_TOKENS = 100 * 1e6; // 100 tokens (1e6 precision) + uint256 constant FILL_USDC = 60 * 1e6; // 60 USDC (6 decimals) /// Refill threshold / amount — restock when balance drops below FILL_* * 10 uint256 constant REFILL_AMOUNT = 10_000 * 1e6; @@ -55,19 +55,19 @@ contract MatchOnce is Script { function run() external { // ── Read environment ────────────────────────────────── - exchange = vm.envAddress("CTF_EXCHANGE"); + exchange = vm.envAddress("CTF_EXCHANGE"); tokenIdYes = vm.envUint("TOKEN_ID_YES"); deployerKey = vm.envUint("PRI_KEY"); - makerKey = vm.envOr("MAKER_PRIVATE_KEY", deployerKey); - takerKey = vm.envOr("TAKER_PRIVATE_KEY", deployerKey); - maker = vm.addr(makerKey); - taker = vm.addr(takerKey); - runCounter = vm.envOr("RUN_COUNTER", uint256(0)); + makerKey = vm.envOr("MAKER_PRIVATE_KEY", deployerKey); + takerKey = vm.envOr("TAKER_PRIVATE_KEY", deployerKey); + maker = vm.addr(makerKey); + taker = vm.addr(takerKey); + runCounter = vm.envOr("RUN_COUNTER", uint256(0)); // ── Resolve collateral / CTF from exchange ──────────── CTFExchange exchangeContract = CTFExchange(exchange); address collateral = exchangeContract.getCollateral(); - address ctf = exchangeContract.getCtf(); + address ctf = exchangeContract.getCtf(); bytes32 conditionId = vm.envBytes32("CONDITION_ID"); // ── Read on-chain nonces ────────────────────────────── @@ -88,36 +88,36 @@ contract MatchOnce is Script { // ── Build maker order (SELL YES tokens) ────────────── Order memory makerOrder = Order({ - salt: uint256(keccak256(abi.encodePacked(block.timestamp, maker, runCounter, "maker"))), - maker: maker, - signer: maker, - taker: address(0), - tokenId: tokenIdYes, - makerAmount: FILL_TOKENS, // sell 100 YES tokens - takerAmount: FILL_USDC, // receive 60 USDC - expiration: block.timestamp + 1 hours, - nonce: makerNonce, - feeRateBps: 0, - side: Side.SELL, + salt: uint256(keccak256(abi.encodePacked(block.timestamp, maker, runCounter, "maker"))), + maker: maker, + signer: maker, + taker: address(0), + tokenId: tokenIdYes, + makerAmount: FILL_TOKENS, // sell 100 YES tokens + takerAmount: FILL_USDC, // receive 60 USDC + expiration: block.timestamp + 1 hours, + nonce: makerNonce, + feeRateBps: 0, + side: Side.SELL, signatureType: SignatureType.EOA, - signature: "" + signature: "" }); // ── Build taker order (BUY YES tokens) ─────────────── Order memory takerOrder = Order({ - salt: uint256(keccak256(abi.encodePacked(block.timestamp, taker, runCounter, "taker"))), - maker: taker, - signer: taker, - taker: address(0), - tokenId: tokenIdYes, - makerAmount: FILL_USDC, // pay 60 USDC - takerAmount: FILL_TOKENS, // receive 100 YES tokens - expiration: block.timestamp + 1 hours, - nonce: takerNonce, - feeRateBps: 0, - side: Side.BUY, + salt: uint256(keccak256(abi.encodePacked(block.timestamp, taker, runCounter, "taker"))), + maker: taker, + signer: taker, + taker: address(0), + tokenId: tokenIdYes, + makerAmount: FILL_USDC, // pay 60 USDC + takerAmount: FILL_TOKENS, // receive 100 YES tokens + expiration: block.timestamp + 1 hours, + nonce: takerNonce, + feeRateBps: 0, + side: Side.BUY, signatureType: SignatureType.EOA, - signature: "" + signature: "" }); // ── Sign orders (EIP-712) ───────────────────────────── @@ -128,10 +128,10 @@ contract MatchOnce is Script { takerOrder.signature = _sign(takerKey, takerHash); // ── Broadcast matchOrders as operator ──────────────── - Order[] memory makerOrders = new Order[](1); + Order[] memory makerOrders = new Order[](1); uint256[] memory makerFillAmounts = new uint256[](1); - makerOrders[0] = makerOrder; - makerFillAmounts[0] = FILL_TOKENS; + makerOrders[0] = makerOrder; + makerFillAmounts[0] = FILL_TOKENS; vm.startBroadcast(deployerKey); exchangeContract.matchOrders(takerOrder, makerOrders, FILL_USDC, makerFillAmounts); @@ -148,21 +148,14 @@ contract MatchOnce is Script { * @dev Ensures taker has enough USDC and has approved the exchange. * Calls MockUSDC.mint() if balance is insufficient (testnet only). */ - function _ensureTakerReady( - address collateral, - address exch, - uint256 takerPk, - address takerAddr - ) internal { - uint256 bal = IERC20(collateral).balanceOf(takerAddr); + function _ensureTakerReady(address collateral, address exch, uint256 takerPk, address takerAddr) internal { + uint256 bal = IERC20(collateral).balanceOf(takerAddr); uint256 allw = IERC20(collateral).allowance(takerAddr, exch); if (bal < FILL_USDC) { console2.log(" [auto] taker USDC low, minting", REFILL_AMOUNT); - vm.startBroadcast(takerPk); // mint to self; MockUSDC allows any caller - (bool ok,) = collateral.call( - abi.encodeWithSignature("mint(address,uint256)", takerAddr, REFILL_AMOUNT) - ); + vm.startBroadcast(takerPk); // mint to self; MockUSDC allows any caller + (bool ok,) = collateral.call(abi.encodeWithSignature("mint(address,uint256)", takerAddr, REFILL_AMOUNT)); vm.stopBroadcast(); require(ok, "mint() failed - is COLLATERAL a MockUSDC?"); } @@ -188,17 +181,15 @@ contract MatchOnce is Script { address makerAddr, uint256 yesId ) internal { - uint256 yesBal = IERC1155(ctf).balanceOf(makerAddr, yesId); - bool approved = IERC1155(ctf).isApprovedForAll(makerAddr, exch); + uint256 yesBal = IERC1155(ctf).balanceOf(makerAddr, yesId); + bool approved = IERC1155(ctf).isApprovedForAll(makerAddr, exch); if (yesBal < FILL_TOKENS) { console2.log(" [auto] maker YES low, minting + splitting", REFILL_AMOUNT); // 1. Mint MockUSDC to maker vm.startBroadcast(makerPk); - (bool ok,) = collateral.call( - abi.encodeWithSignature("mint(address,uint256)", makerAddr, REFILL_AMOUNT) - ); + (bool ok,) = collateral.call(abi.encodeWithSignature("mint(address,uint256)", makerAddr, REFILL_AMOUNT)); require(ok, "mint() failed - is COLLATERAL a MockUSDC?"); // 2. Approve CTF to spend USDC @@ -208,9 +199,7 @@ contract MatchOnce is Script { uint256[] memory partition = new uint256[](2); partition[0] = 1; // YES partition[1] = 2; // NO - ConditionalTokens(ctf).splitPosition( - IERC20(collateral), bytes32(0), conditionId, partition, REFILL_AMOUNT - ); + ConditionalTokens(ctf).splitPosition(IERC20(collateral), bytes32(0), conditionId, partition, REFILL_AMOUNT); vm.stopBroadcast(); } @@ -224,11 +213,7 @@ contract MatchOnce is Script { // ── Helpers ─────────────────────────────────────────────── - function _sign(uint256 privateKey, bytes32 finalHash) - internal - pure - returns (bytes memory sig) - { + function _sign(uint256 privateKey, bytes32 finalHash) internal pure returns (bytes memory sig) { (uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, finalHash); sig = abi.encodePacked(r, s, v); } @@ -250,12 +235,22 @@ contract MatchOnce is Script { } function _hashOrder(Order memory o) internal pure returns (bytes32) { - return keccak256(abi.encode( - ORDER_TYPEHASH, - o.salt, o.maker, o.signer, o.taker, - o.tokenId, o.makerAmount, o.takerAmount, - o.expiration, o.nonce, o.feeRateBps, - uint8(o.side), uint8(o.signatureType) - )); + return keccak256( + abi.encode( + ORDER_TYPEHASH, + o.salt, + o.maker, + o.signer, + o.taker, + o.tokenId, + o.makerAmount, + o.takerAmount, + o.expiration, + o.nonce, + o.feeRateBps, + uint8(o.side), + uint8(o.signatureType) + ) + ); } } diff --git a/script/base/02_DeployBase.s.sol b/script/base/02_DeployBase.s.sol index 8434946..96ebcce 100644 --- a/script/base/02_DeployBase.s.sol +++ b/script/base/02_DeployBase.s.sol @@ -46,7 +46,7 @@ contract DeployBase is Script { function run() external { uint256 deployerKey = vm.envUint("PRI_KEY"); - address deployer = vm.addr(deployerKey); + address deployer = vm.addr(deployerKey); console2.log("\n====================================================="); console2.log(" Base Mainnet - Prediction Market - Deploy"); @@ -71,18 +71,16 @@ contract DeployBase is Script { // 4. Prepare prediction market condition // "Will ETH reach $10k in 2026?" — oracle = deployer for demo - bytes32 questionId = keccak256( - abi.encodePacked("Will ETH reach $10k in 2026?", uint256(block.chainid)) - ); + bytes32 questionId = keccak256(abi.encodePacked("Will ETH reach $10k in 2026?", uint256(block.chainid))); ConditionalTokens(ctf).prepareCondition(deployer, questionId, 2); conditionId = ConditionalTokens(ctf).getConditionId(deployer, questionId, 2); console2.log("[4] conditionId :", vm.toString(conditionId)); // 5. Compute YES / NO position IDs bytes32 colIdYes = ConditionalTokens(ctf).getCollectionId(bytes32(0), conditionId, 1); - bytes32 colIdNo = ConditionalTokens(ctf).getCollectionId(bytes32(0), conditionId, 2); + bytes32 colIdNo = ConditionalTokens(ctf).getCollectionId(bytes32(0), conditionId, 2); tokenIdYes = ConditionalTokens(ctf).getPositionId(IERC20(USDC), colIdYes); - tokenIdNo = ConditionalTokens(ctf).getPositionId(IERC20(USDC), colIdNo); + tokenIdNo = ConditionalTokens(ctf).getPositionId(IERC20(USDC), colIdNo); console2.log("[5] tokenIdYes :", tokenIdYes); console2.log(" tokenIdNo :", tokenIdNo); @@ -96,12 +94,12 @@ contract DeployBase is Script { console2.log("\n====================================================="); console2.log(" COPY INTO YOUR .env FILE"); console2.log("====================================================="); - console2.log("CTF_EXCHANGE=%s", exchange); - console2.log("CTF=%s", ctf); - console2.log("COLLATERAL=%s", USDC); - console2.log("TOKEN_ID_YES=%s", vm.toString(bytes32(tokenIdYes))); - console2.log("TOKEN_ID_NO=%s", vm.toString(bytes32(tokenIdNo))); - console2.log("CONDITION_ID=%s", vm.toString(conditionId)); + console2.log("CTF_EXCHANGE=%s", exchange); + console2.log("CTF=%s", ctf); + console2.log("COLLATERAL=%s", USDC); + console2.log("TOKEN_ID_YES=%s", vm.toString(bytes32(tokenIdYes))); + console2.log("TOKEN_ID_NO=%s", vm.toString(bytes32(tokenIdNo))); + console2.log("CONDITION_ID=%s", vm.toString(conditionId)); console2.log("====================================================="); console2.log("Next step: forge script script/base/03_PrepareMarket.s.sol:PrepareMarket"); } diff --git a/script/base/03_PrepareMarket.s.sol b/script/base/03_PrepareMarket.s.sol index fd9f708..7ad3420 100644 --- a/script/base/03_PrepareMarket.s.sol +++ b/script/base/03_PrepareMarket.s.sol @@ -37,15 +37,15 @@ contract PrepareMarket is Script { function run() external { // ── Read env ────────────────────────────────────────── - address exchange = vm.envAddress("CTF_EXCHANGE"); - address ctf = vm.envAddress("CTF"); - address collateral = vm.envAddress("COLLATERAL"); + address exchange = vm.envAddress("CTF_EXCHANGE"); + address ctf = vm.envAddress("CTF"); + address collateral = vm.envAddress("COLLATERAL"); bytes32 conditionId = vm.envBytes32("CONDITION_ID"); uint256 makerKey = vm.envOr("MAKER_PRIVATE_KEY", vm.envUint("PRI_KEY")); uint256 takerKey = vm.envOr("TAKER_PRIVATE_KEY", vm.envUint("PRI_KEY")); - address maker = vm.addr(makerKey); - address taker = vm.addr(takerKey); + address maker = vm.addr(makerKey); + address taker = vm.addr(takerKey); console2.log("\n====================================================="); console2.log(" Base Mainnet - Prepare Market"); @@ -70,9 +70,7 @@ contract PrepareMarket is Script { uint256[] memory partition = new uint256[](2); partition[0] = 1; // YES (index set bit 0) partition[1] = 2; // NO (index set bit 1) - ConditionalTokens(ctf).splitPosition( - IERC20(collateral), bytes32(0), conditionId, partition, SPLIT_AMOUNT - ); + ConditionalTokens(ctf).splitPosition(IERC20(collateral), bytes32(0), conditionId, partition, SPLIT_AMOUNT); console2.log("[2] Maker split", SPLIT_AMOUNT / 1e6, "USDC -> YES/NO tokens"); // Maker approves exchange to move outcome tokens diff --git a/script/unichain/05_CTFExchange.s.sol b/script/unichain/05_CTFExchange.s.sol index 16effd7..0b45b16 100644 --- a/script/unichain/05_CTFExchange.s.sol +++ b/script/unichain/05_CTFExchange.s.sol @@ -15,8 +15,6 @@ contract DeployCTFExchange is BaseScript { function run() external { // 1. deploy CTFExchange deployCTF(); - - } function deployCTF() public { diff --git a/script/unichain/06_DeployCTFExchange.s.sol b/script/unichain/06_DeployCTFExchange.s.sol index c5c611a..19abde6 100644 --- a/script/unichain/06_DeployCTFExchange.s.sol +++ b/script/unichain/06_DeployCTFExchange.s.sol @@ -32,7 +32,7 @@ contract DeployCTFExchange is BaseScript { // Deployed exchange address (will be set after deployment) address public exchange; - function run() external { + function run() external { console2.log("=== Deploying CTFExchange on Unichain Testnet ===\n"); console2.log("Deployer:", msg.sender); console2.log("Chain ID:", block.chainid); @@ -44,12 +44,7 @@ contract DeployCTFExchange is BaseScript { // Deploy CTFExchange console2.log("Deploying CTFExchange..."); - CTFExchange exchangeContract = new CTFExchange( - COLLATERAL, - CTF, - PROXY_FACTORY, - SAFE_FACTORY - ); + CTFExchange exchangeContract = new CTFExchange(COLLATERAL, CTF, PROXY_FACTORY, SAFE_FACTORY); exchange = address(exchangeContract); @@ -87,7 +82,7 @@ contract DeployCTFExchange is BaseScript { * @notice Setup exchange after deployment (optional) * @dev Call this after deployment to setup operator and initial configuration */ - function setupExchange(address operator) external { + function setupExchange(address operator) external { require(exchange != address(0), "Deploy exchange first"); require(operator != address(0), "Invalid operator"); diff --git a/script/unichain/MatchOrders.s.sol b/script/unichain/MatchOrders.s.sol index 96cdd04..72e1ad8 100644 --- a/script/unichain/MatchOrders.s.sol +++ b/script/unichain/MatchOrders.s.sol @@ -6,11 +6,7 @@ import {BaseScript} from "../BaseScript.s.sol"; import {CTFExchange} from "src/exchange/CTFExchange.sol"; import {ConditionalTokens} from "src/token/ConditionalTokens.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import { - Order, - Side, - SignatureType -} from "src/exchange/libraries/OrderStructs.sol"; +import {Order, Side, SignatureType} from "src/exchange/libraries/OrderStructs.sol"; /** * @title MatchOrders Script @@ -64,9 +60,7 @@ contract MatchOrders is BaseScript { * @notice Main entry point - deploys exchange and matches orders */ function run() external { - console2.log( - "\n=== Starting Order Matching Script (Using Existing Exchange) ===\n" - ); + console2.log("\n=== Starting Order Matching Script (Using Existing Exchange) ===\n"); console2.log("Using Exchange at:", exchange); // 1. Setup exchange (admin, operator, tokens) @@ -85,7 +79,7 @@ contract MatchOrders is BaseScript { /** * @notice Setup exchange: add operator and register tokens */ - function setupExchange() public { + function setupExchange() public { console2.log("\n=== Setting Up Exchange ==="); CTFExchange exchangeContract = CTFExchange(exchange); @@ -100,15 +94,13 @@ contract MatchOrders is BaseScript { /** * @notice Setup a test market for trading */ - function setupMarket() public { + function setupMarket() public { console2.log("\n=== Setting Up Test Market ==="); ConditionalTokens ctf = ConditionalTokens(CTF); // Create a question ID (unique identifier for the market) - questionId = keccak256( - abi.encodePacked("Will ETH reach $10k in 2026?", block.timestamp) - ); + questionId = keccak256(abi.encodePacked("Will ETH reach $10k in 2026?", block.timestamp)); // Prepare condition on CTF ctf.prepareCondition( @@ -120,16 +112,8 @@ contract MatchOrders is BaseScript { conditionId = ctf.getConditionId(operator, questionId, 2); // Calculate token IDs for YES and NO outcomes - bytes32 collectionIdYes = ctf.getCollectionId( - bytes32(0), - conditionId, - 1 - ); // YES = index 1 - bytes32 collectionIdNo = ctf.getCollectionId( - bytes32(0), - conditionId, - 2 - ); // NO = index 2 + bytes32 collectionIdYes = ctf.getCollectionId(bytes32(0), conditionId, 1); // YES = index 1 + bytes32 collectionIdNo = ctf.getCollectionId(bytes32(0), conditionId, 2); // NO = index 2 tokenIdYes = ctf.getPositionId(IERC20(USDC), collectionIdYes); tokenIdNo = ctf.getPositionId(IERC20(USDC), collectionIdNo); @@ -148,7 +132,7 @@ contract MatchOrders is BaseScript { /** * @notice Fund test accounts with USDC and outcome tokens */ - function fundAccounts() public { + function fundAccounts() public { console2.log("\n=== Funding Accounts ==="); IERC20 usdc = IERC20(USDC); @@ -184,11 +168,7 @@ contract MatchOrders is BaseScript { ); vm.stopBroadcast(); - console2.log( - "Maker split", - fundAmount / 1e6, - "USDC into YES/NO tokens" - ); + console2.log("Maker split", fundAmount / 1e6, "USDC into YES/NO tokens"); // Approve exchange to spend tokens vm.startBroadcast(makerPrivateKey); @@ -213,7 +193,7 @@ contract MatchOrders is BaseScript { /** * @notice Create and match orders */ - function createAndMatchOrders() public { + function createAndMatchOrders() public { console2.log("\n=== Creating and Matching Orders ==="); CTFExchange exchangeContract = CTFExchange(exchange); @@ -229,21 +209,13 @@ contract MatchOrders is BaseScript { console2.log(" Token ID:", makerOrder.tokenId); console2.log(" Maker Amount:", makerOrder.makerAmount); console2.log(" Taker Amount:", makerOrder.takerAmount); - console2.log( - " Price:", - (makerOrder.takerAmount * 1e18) / makerOrder.makerAmount / 1e12, - "USDC per token" - ); + console2.log(" Price:", (makerOrder.takerAmount * 1e18) / makerOrder.makerAmount / 1e12, "USDC per token"); console2.log("\nTaker Order (BUY):"); console2.log(" Token ID:", takerOrder.tokenId); console2.log(" Maker Amount:", takerOrder.makerAmount); console2.log(" Taker Amount:", takerOrder.takerAmount); - console2.log( - " Price:", - (takerOrder.makerAmount * 1e18) / takerOrder.takerAmount / 1e12, - "USDC per token" - ); + console2.log(" Price:", (takerOrder.makerAmount * 1e18) / takerOrder.takerAmount / 1e12, "USDC per token"); // Match orders Order[] memory makerOrders = new Order[](1); @@ -258,12 +230,7 @@ contract MatchOrders is BaseScript { console2.log(" Taker fill amount:", takerFillAmount / 1e6); console2.log(" Maker fill amount:", makerFillAmounts[0] / 1e6); - exchangeContract.matchOrders( - takerOrder, - makerOrders, - takerFillAmount, - makerFillAmounts - ); + exchangeContract.matchOrders(takerOrder, makerOrders, takerFillAmount, makerFillAmounts); console2.log("Orders matched successfully!"); } @@ -273,9 +240,7 @@ contract MatchOrders is BaseScript { */ function createMakerOrder() internal view returns (Order memory) { Order memory order = Order({ - salt: uint256( - keccak256(abi.encodePacked(block.timestamp, maker, "maker")) - ), + salt: uint256(keccak256(abi.encodePacked(block.timestamp, maker, "maker"))), maker: maker, signer: maker, taker: address(0), // Public order @@ -292,13 +257,8 @@ contract MatchOrders is BaseScript { // Sign the order bytes32 orderHash = hashOrder(order); - bytes32 ethSignedHash = keccak256( - abi.encodePacked("\x19Ethereum Signed Message:\n32", orderHash) - ); - (uint8 v, bytes32 r, bytes32 s) = vm.sign( - makerPrivateKey, - ethSignedHash - ); + bytes32 ethSignedHash = keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", orderHash)); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(makerPrivateKey, ethSignedHash); order.signature = abi.encodePacked(r, s, v); return order; @@ -309,9 +269,7 @@ contract MatchOrders is BaseScript { */ function createTakerOrder() internal view returns (Order memory) { Order memory order = Order({ - salt: uint256( - keccak256(abi.encodePacked(block.timestamp, taker, "taker")) - ), + salt: uint256(keccak256(abi.encodePacked(block.timestamp, taker, "taker"))), maker: taker, signer: taker, taker: address(0), // Public order @@ -328,13 +286,8 @@ contract MatchOrders is BaseScript { // Sign the order bytes32 orderHash = hashOrder(order); - bytes32 ethSignedHash = keccak256( - abi.encodePacked("\x19Ethereum Signed Message:\n32", orderHash) - ); - (uint8 v, bytes32 r, bytes32 s) = vm.sign( - takerPrivateKey, - ethSignedHash - ); + bytes32 ethSignedHash = keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", orderHash)); + (uint8 v, bytes32 r, bytes32 s) = vm.sign(takerPrivateKey, ethSignedHash); order.signature = abi.encodePacked(r, s, v); return order; @@ -344,25 +297,24 @@ contract MatchOrders is BaseScript { * @notice Hash an order for signing */ function hashOrder(Order memory order) internal pure returns (bytes32) { - return - keccak256( - abi.encode( - keccak256( - "Order(uint256 salt,address maker,address signer,address taker,uint256 tokenId,uint256 makerAmount,uint256 takerAmount,uint256 expiration,uint256 nonce,uint256 feeRateBps,uint8 side,uint8 signatureType)" - ), - order.salt, - order.maker, - order.signer, - order.taker, - order.tokenId, - order.makerAmount, - order.takerAmount, - order.expiration, - order.nonce, - order.feeRateBps, - uint8(order.side), - uint8(order.signatureType) - ) - ); + return keccak256( + abi.encode( + keccak256( + "Order(uint256 salt,address maker,address signer,address taker,uint256 tokenId,uint256 makerAmount,uint256 takerAmount,uint256 expiration,uint256 nonce,uint256 feeRateBps,uint8 side,uint8 signatureType)" + ), + order.salt, + order.maker, + order.signer, + order.taker, + order.tokenId, + order.makerAmount, + order.takerAmount, + order.expiration, + order.nonce, + order.feeRateBps, + uint8(order.side), + uint8(order.signatureType) + ) + ); } } diff --git a/script/unichain/MatchOrdersOnly.s.sol b/script/unichain/MatchOrdersOnly.s.sol index 822ae67..e82d517 100644 --- a/script/unichain/MatchOrdersOnly.s.sol +++ b/script/unichain/MatchOrdersOnly.s.sol @@ -20,19 +20,19 @@ import {Order, Side, SignatureType} from "src/exchange/libraries/OrderStructs.so contract MatchOrdersOnly is BaseScript { // ============ UPDATE THESE ADDRESSES ============ address constant EXCHANGE = address(0); // Your deployed CTFExchange address - address constant MAKER = address(0); // Maker address - address constant TAKER = address(0); // Taker address - uint256 constant TOKEN_ID = 0; // Token ID to trade + address constant MAKER = address(0); // Maker address + address constant TAKER = address(0); // Taker address + uint256 constant TOKEN_ID = 0; // Token ID to trade // Maker order parameters - uint256 constant MAKER_AMOUNT = 100 * 1e6; // Sell 100 tokens - uint256 constant MAKER_PRICE = 60 * 1e6; // For 60 USDC (0.6 per token) + uint256 constant MAKER_AMOUNT = 100 * 1e6; // Sell 100 tokens + uint256 constant MAKER_PRICE = 60 * 1e6; // For 60 USDC (0.6 per token) // Taker order parameters - uint256 constant TAKER_AMOUNT = 100 * 1e6; // Buy 100 tokens - uint256 constant TAKER_PRICE = 60 * 1e6; // Pay 60 USDC (0.6 per token) + uint256 constant TAKER_AMOUNT = 100 * 1e6; // Buy 100 tokens + uint256 constant TAKER_PRICE = 60 * 1e6; // Pay 60 USDC (0.6 per token) - function run() external { + function run() external { require(EXCHANGE != address(0), "Set EXCHANGE address"); require(MAKER != address(0), "Set MAKER address"); require(TAKER != address(0), "Set TAKER address"); @@ -87,12 +87,7 @@ contract MatchOrdersOnly is BaseScript { console2.log("Amount:", MAKER_AMOUNT / 1e6); console2.log("Price:", MAKER_PRICE / 1e6, "USDC"); - exchange.matchOrders( - takerOrder, - makerOrders, - TAKER_AMOUNT, - makerFillAmounts - ); + exchange.matchOrders(takerOrder, makerOrders, TAKER_AMOUNT, makerFillAmounts); console2.log("Orders matched successfully!"); } diff --git a/test/CTFExchangeTest.t.sol b/test/CTFExchangeTest.t.sol index c9a6f69..e69520a 100644 --- a/test/CTFExchangeTest.t.sol +++ b/test/CTFExchangeTest.t.sol @@ -204,10 +204,10 @@ contract CTFExchangeTest is Test { // Taker (Order 1): BUY YES (tokenId) // Maker (Order 2): BUY NO (tokenId1) // MatchType.MINT - + uint256 takerAmountCollateral = 60 ether; // Taker pays 60 USDC uint256 takerWantedShares = 100 ether; // Taker wants 100 YES - + uint256 makerAmountCollateral = 40 ether; // Maker pays 40 USDC uint256 makerWantedShares = 100 ether; // Maker wants 100 NO @@ -261,7 +261,7 @@ contract CTFExchangeTest is Test { uint256 takerCollateralBefore = collateral.balanceOf(taker); uint256 makerCollateralBefore = collateral.balanceOf(maker); - + // Match Engine acts as Operator vm.prank(taker); // 'taker' acts as registered operator in setup exchange.matchOrders( @@ -279,7 +279,7 @@ contract CTFExchangeTest is Test { // Maker paid 40 USDC, got 100 NO assertEq(collateral.balanceOf(maker), makerCollateralBefore - 40 ether); assertEq(ctf.balanceOf(maker, tokenId1), 100 ether); - + // Exchange should have 100 USDC locked as collateral for the CTF tokens assertEq(collateral.balanceOf(address(ctf)), 100 ether); }