diff --git a/test/BuyerAgent.t.sol b/test/BuyerAgent.t.sol index 2aa7e43..848f43e 100644 --- a/test/BuyerAgent.t.sol +++ b/test/BuyerAgent.t.sol @@ -14,107 +14,15 @@ import {LLMOracleRegistry} from "@firstbatch/dria-oracle-contracts/LLMOracleRegi import {Swan} from "../src/Swan.sol"; contract BuyerAgentTest is Helper { - address agentOwner; - BuyerAgent agent; - - modifier deployment() { - token = new WETH9(); - - // deploy llm contracts - vm.startPrank(dria); - - address registryProxy = Upgrades.deployUUPSProxy( - "LLMOracleRegistry.sol", - abi.encodeCall( - LLMOracleRegistry.initialize, (stakes.generatorStakeAmount, stakes.validatorStakeAmount, address(token)) - ) - ); - - oracleRegistry = LLMOracleRegistry(registryProxy); - - address coordinatorProxy = Upgrades.deployUUPSProxy( - "LLMOracleCoordinator.sol", - abi.encodeCall( - LLMOracleCoordinator.initialize, - (address(oracleRegistry), address(token), fees.platformFee, fees.generatorFee, fees.validatorFee) - ) - ); - oracleCoordinator = LLMOracleCoordinator(coordinatorProxy); - - // deploy factory contracts - buyerAgentFactory = new BuyerAgentFactory(); - swanAssetFactory = new SwanAssetFactory(); - - require(address(buyerAgentFactory) != address(0), "BuyerAgentFactory not deployed"); - require(address(swanAssetFactory) != address(0), "SwanAssetFactory not deployed"); - - // deploy swan - address swanProxy = Upgrades.deployUUPSProxy( - "Swan.sol", - abi.encodeCall( - Swan.initialize, - ( - marketParameters, - oracleParameters, - address(oracleCoordinator), - address(token), - address(buyerAgentFactory), - address(swanAssetFactory) - ) - ) - ); - swan = Swan(swanProxy); - vm.stopPrank(); - - vm.label(address(swan), "Swan"); - vm.label(address(token), "WETH"); - vm.label(address(this), "BuyerAgentTest"); - vm.label(address(oracleRegistry), "LLMOracleRegistry"); - vm.label(address(oracleCoordinator), "LLMOracleCoordinator"); - vm.label(address(buyerAgentFactory), "BuyerAgentFactory"); - vm.label(address(swanAssetFactory), "SwanAssetFactory"); - _; - } - - modifier createBuyers() override { - for (uint256 i = 0; i < buyerAgentOwners.length; i++) { - // fund buyer agent owner - deal(address(token), buyerAgentOwners[i], 3 ether); - - vm.startPrank(buyerAgentOwners[i]); - BuyerAgent buyerAgent = swan.createBuyer( - buyerAgentParameters[i].name, - buyerAgentParameters[i].description, - buyerAgentParameters[i].feeRoyalty, - buyerAgentParameters[i].amountPerRound - ); - - buyerAgents.push(buyerAgent); - vm.label(address(buyerAgent), string.concat("BuyerAgent#", vm.toString(i + 1))); - - // transfer tokens to agent - token.transfer(address(buyerAgent), amountPerRound); - assertEq(token.balanceOf(address(buyerAgent)), amountPerRound); - vm.stopPrank(); - } - - agentOwner = buyerAgentOwners[0]; - agent = buyerAgents[0]; - - currPhase = BuyerAgent.Phase.Sell; - currRound = 0; - _; - } - /// @notice Buyer agent should be in sell phase - function test_InSellPhase() external deployment createBuyers { + function test_InSellPhase() external createBuyers { // get curr phase (, BuyerAgent.Phase _phase,) = agent.getRoundPhase(); assertEq(uint8(_phase), uint8(currPhase)); } /// @dev Agent owner cannot set feeRoyalty in sell phase - function test_RevertWhen_SetRoyaltyInSellPhase() external deployment createBuyers { + function test_RevertWhen_SetRoyaltyInSellPhase() external createBuyers { vm.prank(agentOwner); vm.expectRevert( abi.encodeWithSelector(BuyerAgent.InvalidPhase.selector, BuyerAgent.Phase.Sell, BuyerAgent.Phase.Withdraw) @@ -123,8 +31,10 @@ contract BuyerAgentTest is Helper { } /// @notice Test that the buyer agent is in buy phase - function test_InBuyPhase() external deployment createBuyers { - vm.warp(agent.createdAt() + marketParameters.sellInterval); + function test_InBuyPhase() external createBuyers { + uint256 timeToBuyPhaseOfTheFirstRound = agent.createdAt() + swan.getCurrentMarketParameters().sellInterval; + increaseTime(timeToBuyPhaseOfTheFirstRound, agent, BuyerAgent.Phase.Buy, 0); + currPhase = BuyerAgent.Phase.Buy; (, BuyerAgent.Phase _phase,) = agent.getRoundPhase(); @@ -132,8 +42,8 @@ contract BuyerAgentTest is Helper { } /// @dev Agent owner cannot set amountPerRound in buy phase - function test_RevertWhen_SetAmountPerRoundInBuyPhase() external deployment createBuyers { - vm.warp(agent.createdAt() + marketParameters.sellInterval); + function test_RevertWhen_SetAmountPerRoundInBuyPhase() external createBuyers { + increaseTime(agent.createdAt() + marketParameters.sellInterval, agent, BuyerAgent.Phase.Buy, 0); vm.prank(agentOwner); vm.expectRevert( @@ -143,9 +53,9 @@ contract BuyerAgentTest is Helper { } /// @notice Test that the buyer agent owner cannot withdraw in buy phase - function test_RevertWhen_WithdrawInBuyPhase() external deployment createBuyers { + function test_RevertWhen_WithdrawInBuyPhase() external createBuyers { // owner cannot withdraw more than minFundAmount from his agent - vm.warp(agent.createdAt() + marketParameters.sellInterval); + increaseTime(agent.createdAt() + marketParameters.sellInterval, agent, BuyerAgent.Phase.Buy, 0); // get the contract balance uint256 treasuary = agent.treasury(); @@ -157,14 +67,16 @@ contract BuyerAgentTest is Helper { } /// @notice Test that the non-owner cannot withdraw - function test_RevertWhen_WithdrawByAnotherOwner() external deployment createBuyers { + function test_RevertWhen_WithdrawByAnotherOwner() external createBuyers { // feeRoyalty can be set only in withdraw phase by only agent owner - vm.warp(agent.createdAt() + marketParameters.sellInterval + marketParameters.buyInterval); + increaseTime( + agent.createdAt() + marketParameters.sellInterval + marketParameters.buyInterval, + agent, + BuyerAgent.Phase.Withdraw, + 0 + ); currPhase = BuyerAgent.Phase.Withdraw; - (, BuyerAgent.Phase _phase,) = agent.getRoundPhase(); - assertEq(uint8(_phase), uint8(currPhase)); - // not allowed to withdraw by non owner vm.prank(buyerAgentOwners[1]); vm.expectRevert(abi.encodeWithSelector(BuyerAgent.Unauthorized.selector, buyerAgentOwners[1])); @@ -172,8 +84,13 @@ contract BuyerAgentTest is Helper { } /// @notice Test that the buyer agent owner must set feeRoyalty between 1-100 - function test_RevertWhen_SetFeeWithInvalidRoyalty() external deployment createBuyers { - vm.warp(agent.createdAt() + marketParameters.sellInterval + marketParameters.buyInterval); + function test_RevertWhen_SetFeeWithInvalidRoyalty() external createBuyers { + increaseTime( + agent.createdAt() + marketParameters.sellInterval + marketParameters.buyInterval, + agent, + BuyerAgent.Phase.Withdraw, + 0 + ); uint96 biggerRoyalty = 1000; uint96 smallerRoyalty = 0; @@ -188,8 +105,13 @@ contract BuyerAgentTest is Helper { } /// @notice Test that the buyer agent owner can set feeRoyalty and amountPerRound in withdraw phase - function test_SetRoyaltyAndAmountPerRound() external deployment createBuyers { - vm.warp(agent.createdAt() + marketParameters.sellInterval + marketParameters.buyInterval); + function test_SetRoyaltyAndAmountPerRound() external createBuyers { + increaseTime( + agent.createdAt() + marketParameters.sellInterval + marketParameters.buyInterval, + agent, + BuyerAgent.Phase.Withdraw, + 0 + ); uint96 newFeeRoyalty = 20; uint256 newAmountPerRound = 0.25 ether; @@ -205,8 +127,13 @@ contract BuyerAgentTest is Helper { } /// @notice Test that the buyer agent owner can withdraw in withdraw phase - function test_WithdrawInWithdrawPhase() external deployment createBuyers { - vm.warp(agent.createdAt() + marketParameters.sellInterval + marketParameters.buyInterval); + function test_WithdrawInWithdrawPhase() external createBuyers { + increaseTime( + agent.createdAt() + marketParameters.sellInterval + marketParameters.buyInterval, + agent, + BuyerAgent.Phase.Withdraw, + 0 + ); vm.startPrank(agentOwner); agent.withdraw(uint96(token.balanceOf(address(agent)))); diff --git a/test/Helper.t.sol b/test/Helper.t.sol index 2c1959f..f93fc96 100644 --- a/test/Helper.t.sol +++ b/test/Helper.t.sol @@ -51,6 +51,9 @@ abstract contract Helper is Test { SwanAssetFactory swanAssetFactory; BuyerAgent[] buyerAgents; + BuyerAgent agent; + address agentOwner; + WETH9 token; Swan swan; @@ -69,7 +72,7 @@ abstract contract Helper is Test { error InvalidNonceFromHelperTest(uint256 taskId, uint256 nonce, uint256 computedNonce, address caller); // Set parameters for the test - function setUp() public { + function setUp() public deployment { dria = vm.addr(1); validators = [vm.addr(2), vm.addr(3), vm.addr(4)]; generators = [vm.addr(5), vm.addr(6), vm.addr(7)]; @@ -111,6 +114,61 @@ abstract contract Helper is Test { MODIFIERS //////////////////////////////////////////////////////////////*/ + modifier deployment() { + _; + + token = new WETH9(); + + // deploy llm contracts + vm.startPrank(dria); + + address registryProxy = Upgrades.deployUUPSProxy( + "LLMOracleRegistry.sol", + abi.encodeCall( + LLMOracleRegistry.initialize, (stakes.generatorStakeAmount, stakes.validatorStakeAmount, address(token)) + ) + ); + oracleRegistry = LLMOracleRegistry(registryProxy); + + address coordinatorProxy = Upgrades.deployUUPSProxy( + "LLMOracleCoordinator.sol", + abi.encodeCall( + LLMOracleCoordinator.initialize, + (address(oracleRegistry), address(token), fees.platformFee, fees.generatorFee, fees.validatorFee) + ) + ); + oracleCoordinator = LLMOracleCoordinator(coordinatorProxy); + + // deploy factory contracts + buyerAgentFactory = new BuyerAgentFactory(); + swanAssetFactory = new SwanAssetFactory(); + + // deploy swan + address swanProxy = Upgrades.deployUUPSProxy( + "Swan.sol", + abi.encodeCall( + Swan.initialize, + ( + marketParameters, + oracleParameters, + address(oracleCoordinator), + address(token), + address(buyerAgentFactory), + address(swanAssetFactory) + ) + ) + ); + swan = Swan(swanProxy); + vm.stopPrank(); + + vm.label(address(swan), "Swan"); + vm.label(address(token), "WETH"); + vm.label(address(oracleRegistry), "LLMOracleRegistry"); + vm.label(address(oracleCoordinator), "LLMOracleCoordinator"); + vm.label(address(buyerAgentFactory), "BuyerAgentFactory"); + vm.label(address(swanAssetFactory), "SwanAssetFactory"); + } + /// @notice Register generators and validators modifier registerOracles() { for (uint256 i = 0; i < generators.length; i++) { @@ -189,14 +247,15 @@ abstract contract Helper is Test { assertEq(keccak256("BuyerCreated(address,address)"), eventSig); // decode owner & agent address from topics - address owner = abi.decode(abi.encode(buyerCreatedEvent.topics[1]), (address)); - address agent = abi.decode(abi.encode(buyerCreatedEvent.topics[2]), (address)); + address _owner = abi.decode(abi.encode(buyerCreatedEvent.topics[1]), (address)); + address _agent = abi.decode(abi.encode(buyerCreatedEvent.topics[2]), (address)); + assertEq(_owner, buyerAgentOwners[i]); // emitter should be swan assertEq(buyerCreatedEvent.emitter, address(swan)); // all guuud - buyerAgents.push(BuyerAgent(agent)); + buyerAgents.push(BuyerAgent(_agent)); vm.label(address(buyerAgents[i]), string.concat("BuyerAgent#", vm.toString(i + 1))); @@ -208,6 +267,9 @@ abstract contract Helper is Test { assertEq(buyerAgents.length, buyerAgentOwners.length); currPhase = BuyerAgent.Phase.Sell; + + agent = buyerAgents[0]; + agentOwner = buyerAgentOwners[0]; _; } @@ -227,6 +289,7 @@ abstract contract Helper is Test { /// @param assetCount Number of assets that will be listed. /// @param buyerAgent Agent that assets will be list for. modifier listAssets(address seller, uint256 assetCount, address buyerAgent) { + vm.recordLogs(); vm.startPrank(seller); for (uint256 i = 0; i < assetCount; i++) { swan.list( @@ -236,6 +299,46 @@ abstract contract Helper is Test { assetPrice, buyerAgent ); + + // From SwanAsset' constructor + // 1. OwnershipTransferred (from Ownable) + // 2. Transfer (_safeMint() related) + // 3. ApprovalForAll + + // From transferRoyalties() + // 4. Transfer (WETH9: royalty transfer to Swan) + // 5. Transfer (WETH9: royalty transfer to buyer agent) + // 6. Transfer (WETH9: royalty transfer to dria) + + // From Swan + // 7. AssetListed + Vm.Log[] memory entries = vm.getRecordedLogs(); + assertEq(entries.length, 7); + + // get the AssetListed event + Vm.Log memory assetListedEvent = entries[entries.length - 1]; + + // check event sig + bytes32 eventSig = assetListedEvent.topics[0]; + assertEq(keccak256("AssetListed(address,address,uint256)"), eventSig); + + // decode params from event + address _seller = abi.decode(abi.encode(assetListedEvent.topics[1]), (address)); + address asset = abi.decode(abi.encode(assetListedEvent.topics[2]), (address)); + uint256 price = abi.decode(assetListedEvent.data, (uint256)); + + // get asset details + Swan.AssetListing memory assetListing = swan.getListing(asset); + + assertEq(assetListing.seller, _seller); + assertEq(seller, _seller); + assertEq(assetListing.buyer, address(buyerAgent)); + + assertEq(uint8(assetListing.status), uint8(Swan.AssetStatus.Listed)); + assertEq(assetListing.price, price); + + // emitter should be swan + assertEq(assetListedEvent.emitter, address(swan)); } vm.stopPrank(); @@ -307,6 +410,21 @@ abstract contract Helper is Test { FUNCTIONS //////////////////////////////////////////////////////////////*/ + /// @notice Increases time in the test + function increaseTime( + uint256 timeInseconds, + BuyerAgent buyerAgent, + BuyerAgent.Phase expectedPhase, + uint256 expectedRound + ) public { + vm.warp(timeInseconds + 1); + + // get the current round and phase of buyer agent + (uint256 _currRound, BuyerAgent.Phase _currPhase,) = buyerAgent.getRoundPhase(); + assertEq(uint8(_currPhase), uint8(expectedPhase)); + assertEq(uint8(_currRound), uint8(expectedRound)); + } + /// @notice Responds to task /// @param responder Responder address /// @param output Output of the task @@ -367,13 +485,13 @@ abstract contract Helper is Test { } /// @dev Checks if the round, phase and timeRemaining is correct - function checkRoundAndPhase(BuyerAgent agent, BuyerAgent.Phase phase, uint256 round) + function checkRoundAndPhase(BuyerAgent buyerAgent, BuyerAgent.Phase phase, uint256 round) public view returns (uint256) { // get the current round and phase of buyer agent - (uint256 _currRound, BuyerAgent.Phase _currPhase,) = agent.getRoundPhase(); + (uint256 _currRound, BuyerAgent.Phase _currPhase,) = buyerAgent.getRoundPhase(); assertEq(uint8(_currPhase), uint8(phase)); assertEq(uint8(_currRound), uint8(round)); diff --git a/test/Swan.t.sol b/test/Swan.t.sol index 0f3f7fe..bc800a8 100644 --- a/test/Swan.t.sol +++ b/test/Swan.t.sol @@ -15,63 +15,9 @@ import { } from "@firstbatch/dria-oracle-contracts/LLMOracleCoordinator.sol"; contract SwanTest is Helper { - modifier deployment() { - token = new WETH9(); - - // deploy llm contracts - vm.startPrank(dria); - - address registryProxy = Upgrades.deployUUPSProxy( - "LLMOracleRegistry.sol", - abi.encodeCall( - LLMOracleRegistry.initialize, (stakes.generatorStakeAmount, stakes.validatorStakeAmount, address(token)) - ) - ); - oracleRegistry = LLMOracleRegistry(registryProxy); - - address coordinatorProxy = Upgrades.deployUUPSProxy( - "LLMOracleCoordinator.sol", - abi.encodeCall( - LLMOracleCoordinator.initialize, - (address(oracleRegistry), address(token), fees.platformFee, fees.generatorFee, fees.validatorFee) - ) - ); - oracleCoordinator = LLMOracleCoordinator(coordinatorProxy); - - // deploy factory contracts - buyerAgentFactory = new BuyerAgentFactory(); - swanAssetFactory = new SwanAssetFactory(); - - // deploy swan - address swanProxy = Upgrades.deployUUPSProxy( - "Swan.sol", - abi.encodeCall( - Swan.initialize, - ( - marketParameters, - oracleParameters, - address(oracleCoordinator), - address(token), - address(buyerAgentFactory), - address(swanAssetFactory) - ) - ) - ); - swan = Swan(swanProxy); - vm.stopPrank(); - - vm.label(address(swan), "Swan"); - vm.label(address(token), "WETH"); - vm.label(address(this), "SwanTest"); - vm.label(address(oracleRegistry), "LLMOracleRegistry"); - vm.label(address(oracleCoordinator), "LLMOracleCoordinator"); - vm.label(address(buyerAgentFactory), "BuyerAgentFactory"); - vm.label(address(swanAssetFactory), "SwanAssetFactory"); - _; - } - + /// @dev Fund geerators, validators, sellers, and dria modifier fund() { - scores = [1 ether]; + scores = [10]; // fund dria deal(address(token), dria, 1 ether); @@ -95,17 +41,7 @@ contract SwanTest is Helper { _; } - function test_Deployment() external deployment fund { - assertEq(oracleCoordinator.platformFee(), fees.platformFee); - assertEq(oracleCoordinator.generationFee(), fees.generatorFee); - assertEq(oracleCoordinator.validationFee(), fees.validatorFee); - - assertEq(oracleRegistry.generatorStakeAmount(), stakes.generatorStakeAmount); - assertEq(oracleRegistry.validatorStakeAmount(), stakes.validatorStakeAmount); - assertEq(swan.owner(), dria); - } - - function test_CreateBuyerAgents() external deployment createBuyers fund { + function test_CreateBuyerAgents() external createBuyers fund { assertEq(buyerAgents.length, buyerAgentOwners.length); for (uint256 i = 0; i < buyerAgents.length; i++) { @@ -120,7 +56,6 @@ contract SwanTest is Helper { /// @notice Sellers cannot list more than maxAssetCount function test_RevertWhen_ListMoreThanMaxAssetCount() external - deployment fund createBuyers sellersApproveToSwan @@ -136,7 +71,6 @@ contract SwanTest is Helper { /// @notice Buyer cannot call purchase() in sell phase function test_RevertWhen_PurchaseInSellPhase() external - deployment fund createBuyers sellersApproveToSwan @@ -154,7 +88,6 @@ contract SwanTest is Helper { /// @notice Seller cannot relist the asset in the same round (for same or different buyers) function test_RevertWhen_RelistInTheSameRound() external - deployment fund createBuyers sellersApproveToSwan @@ -172,7 +105,6 @@ contract SwanTest is Helper { /// @notice Buyer cannot purchase an asset that is not listed for him function test_RevertWhen_PurchaseByAnotherBuyer() external - deployment fund createBuyers sellersApproveToSwan @@ -182,8 +114,7 @@ contract SwanTest is Helper { address buyerToFail = buyerAgentOwners[0]; BuyerAgent buyerAgent = buyerAgents[1]; - uint256 buyPhaseOfTheFirstRound = buyerAgent.createdAt() + marketParameters.sellInterval; - vm.warp(buyPhaseOfTheFirstRound); + increaseTime(buyerAgent.createdAt() + marketParameters.sellInterval, buyerAgent, BuyerAgent.Phase.Buy, 0); currPhase = BuyerAgent.Phase.Buy; vm.expectRevert(abi.encodeWithSelector(Swan.Unauthorized.selector, buyerToFail)); @@ -194,7 +125,6 @@ contract SwanTest is Helper { /// @notice Buyer cannot spend more than amountPerRound per round function test_RevertWhen_PurchaseMoreThanAmountPerRound() external - deployment fund createBuyers sellersApproveToSwan @@ -204,8 +134,9 @@ contract SwanTest is Helper { address buyerToFail = buyerAgentOwners[0]; BuyerAgent buyerAgentToFail = buyerAgents[0]; - vm.warp(buyerAgentToFail.createdAt() + marketParameters.sellInterval); - currPhase = BuyerAgent.Phase.Buy; + increaseTime( + buyerAgentToFail.createdAt() + marketParameters.sellInterval, buyerAgentToFail, BuyerAgent.Phase.Buy, 0 + ); // get the listed assets as output address[] memory output = swan.getListedAssets(address(buyerAgentToFail), currRound); @@ -233,7 +164,6 @@ contract SwanTest is Helper { /// @dev asset price must be less than amountPerRound function test_PurchaseAnAsset() external - deployment fund createBuyers sellersApproveToSwan @@ -241,16 +171,12 @@ contract SwanTest is Helper { listAssets(sellers[0], marketParameters.maxAssetCount, address(buyerAgents[0])) { // increase time to buy phase to be able to purchase - vm.warp(buyerAgents[0].createdAt() + marketParameters.sellInterval); - safePurchase(buyerAgentOwners[0], buyerAgents[0], 1); + increaseTime( + buyerAgents[0].createdAt() + marketParameters.sellInterval, buyerAgents[0], BuyerAgent.Phase.Buy, 0 + ); - // 1. Transfer (from Swan) - // 2. Transfer (from Swan) - // 3. Transfer (from WETH9) - // 4. Transfer (from WETH9) - // 5. AssetSold + safePurchase(buyerAgentOwners[0], buyerAgents[0], 1); Vm.Log[] memory entries = vm.getRecordedLogs(); - assertEq(entries.length, 5); // get the AssetSold event Vm.Log memory assetSoldEvent = entries[entries.length - 1]; @@ -260,7 +186,7 @@ contract SwanTest is Helper { assertEq(keccak256("AssetSold(address,address,address,uint256)"), eventSig); // decode params from event - address seller = abi.decode(abi.encode(assetSoldEvent.topics[1]), (address)); + address _seller = abi.decode(abi.encode(assetSoldEvent.topics[1]), (address)); address agent = abi.decode(abi.encode(assetSoldEvent.topics[2]), (address)); address asset = abi.decode(abi.encode(assetSoldEvent.topics[3]), (address)); uint256 price = abi.decode(assetSoldEvent.data, (uint256)); @@ -271,7 +197,8 @@ contract SwanTest is Helper { // get asset details Swan.AssetListing memory assetListing = swan.getListing(asset); - assertEq(assetListing.seller, seller); + assertEq(assetListing.seller, _seller); + assertEq(sellers[0], _seller); assertEq(assetListing.buyer, address(buyerAgents[0])); assertEq(uint8(assetListing.status), uint8(Swan.AssetStatus.Sold)); @@ -283,7 +210,6 @@ contract SwanTest is Helper { function test_UpdateState() external - deployment fund createBuyers sellersApproveToSwan @@ -292,19 +218,22 @@ contract SwanTest is Helper { { address buyerAgentOwner = buyerAgentOwners[0]; BuyerAgent buyerAgent = buyerAgents[0]; - uint256 taskId = 1; - vm.warp(buyerAgent.createdAt() + marketParameters.sellInterval); - currPhase = BuyerAgent.Phase.Buy; + bytes memory newState = abi.encodePacked("0x", "after purchase"); + uint256 taskId = 1; - safePurchase(buyerAgentOwners[0], buyerAgents[0], taskId); - vm.warp(buyerAgents[0].createdAt() + marketParameters.sellInterval + marketParameters.buyInterval); + increaseTime(buyerAgent.createdAt() + marketParameters.sellInterval, buyerAgent, BuyerAgent.Phase.Buy, 0); + safePurchase(buyerAgentOwner, buyerAgent, taskId); taskId++; - bytes memory newState = abi.encodePacked("0x", "after purchase"); - + increaseTime( + buyerAgent.createdAt() + marketParameters.sellInterval + marketParameters.buyInterval, + buyerAgent, + BuyerAgent.Phase.Withdraw, + 0 + ); vm.prank(buyerAgentOwner); - buyerAgents[0].oracleStateRequest(input, models); + buyerAgent.oracleStateRequest(input, models); safeRespond(generators[0], newState, taskId); safeValidate(validators[0], taskId); @@ -315,31 +244,42 @@ contract SwanTest is Helper { } /// @notice Seller cannot list an asset in withdraw phase - function test_RevertWhen_ListInWithdrawPhase() external deployment fund createBuyers sellersApproveToSwan { - uint256 withdrawPhase = - buyerAgents[0].createdAt() + marketParameters.sellInterval + marketParameters.buyInterval; - vm.warp(withdrawPhase); + function test_RevertWhen_ListInWithdrawPhase() external fund createBuyers sellersApproveToSwan { + BuyerAgent agent = buyerAgents[0]; + + increaseTime( + agent.createdAt() + marketParameters.sellInterval + marketParameters.buyInterval, + agent, + BuyerAgent.Phase.Withdraw, + 0 + ); currPhase = BuyerAgent.Phase.Withdraw; vm.prank(sellers[0]); vm.expectRevert(abi.encodeWithSelector(BuyerAgent.InvalidPhase.selector, currPhase, BuyerAgent.Phase.Sell)); - swan.list("name", "symbol", "desc", 0.01 ether, address(buyerAgents[0])); + swan.list("name", "symbol", "desc", 0.01 ether, address(agent)); } /// @notice Buyer Agent Owner can setAmountPerRound in withdraw phase - function test_SetAmountPerRound() external deployment fund createBuyers sellersApproveToSwan { + function test_SetAmountPerRound() external fund createBuyers sellersApproveToSwan { + BuyerAgent agent = buyerAgents[0]; uint256 newAmountPerRound = 2 ether; - vm.warp(buyerAgents[0].createdAt() + marketParameters.sellInterval + marketParameters.buyInterval); + increaseTime( + agent.createdAt() + marketParameters.sellInterval + marketParameters.buyInterval, + agent, + BuyerAgent.Phase.Withdraw, + 0 + ); vm.prank(buyerAgentOwners[0]); - buyerAgents[0].setAmountPerRound(newAmountPerRound); - assertEq(buyerAgents[0].amountPerRound(), newAmountPerRound); + agent.setAmountPerRound(newAmountPerRound); + assertEq(agent.amountPerRound(), newAmountPerRound); } /// @notice Buyer Agent Owner cannot create buyer agent with invalid royalty /// @dev feeRoyalty must be between 0 - 100 - function test_RevertWhen_CreateBuyerWithInvalidRoyalty() external deployment fund { + function test_RevertWhen_CreateBuyerWithInvalidRoyalty() external fund { uint96 invalidRoyalty = 150; vm.prank(buyerAgentOwners[0]); @@ -353,7 +293,7 @@ contract SwanTest is Helper { } /// @notice Swan owner can set factories - function test_SetFactories() external deployment fund { + function test_SetFactories() external fund { SwanAssetFactory _swanAssetFactory = new SwanAssetFactory(); BuyerAgentFactory _buyerAgentFactory = new BuyerAgentFactory(); @@ -367,7 +307,6 @@ contract SwanTest is Helper { /// @notice Seller cannot relist an asset that is already purchased function test_RevertWhen_RelistAlreadyPurchasedAsset() external - deployment fund createBuyers sellersApproveToSwan @@ -379,12 +318,12 @@ contract SwanTest is Helper { uint256 taskId = 1; // increase time to buy phase - vm.warp(buyerAgent.createdAt() + marketParameters.sellInterval); + increaseTime(buyerAgent.createdAt() + marketParameters.sellInterval, buyerAgent, BuyerAgent.Phase.Buy, 0); safePurchase(buyer, buyerAgent, taskId); - uint256 sellPhaseOfTheSecondRound = - buyerAgent.createdAt() + marketParameters.buyInterval + marketParameters.withdrawInterval; - vm.warp(sellPhaseOfTheSecondRound); + uint256 sellPhaseOfTheSecondRound = buyerAgent.createdAt() + marketParameters.sellInterval + + marketParameters.buyInterval + marketParameters.withdrawInterval; + increaseTime(sellPhaseOfTheSecondRound, buyerAgent, BuyerAgent.Phase.Sell, 1); // get the asset address listedAssetAddr = swan.getListedAssets(address(buyerAgent), currRound)[0]; @@ -401,7 +340,6 @@ contract SwanTest is Helper { /// @notice Seller cannot relist another seller's asset function test_RevertWhen_RelistByAnotherSeller() external - deployment fund createBuyers sellersApproveToSwan @@ -414,7 +352,7 @@ contract SwanTest is Helper { // increase time to the sell phase of thze next round uint256 sellPhaseOfTheSecondRound = buyerAgent.createdAt() + marketParameters.sellInterval + marketParameters.buyInterval + marketParameters.withdrawInterval; - vm.warp(sellPhaseOfTheSecondRound); + increaseTime(sellPhaseOfTheSecondRound, buyerAgent, BuyerAgent.Phase.Sell, 1); // try to relist an asset by another seller vm.prank(sellers[1]); @@ -426,7 +364,6 @@ contract SwanTest is Helper { /// @dev Buyer Agent must be in Sell Phase function test_RelistAsset() external - deployment fund createBuyers sellersApproveToSwan @@ -441,7 +378,7 @@ contract SwanTest is Helper { // increase time to the sell phase of the next round uint256 sellPhaseOfTheSecondRound = buyerAgent.createdAt() + marketParameters.sellInterval + marketParameters.buyInterval + marketParameters.withdrawInterval; - vm.warp(sellPhaseOfTheSecondRound); + increaseTime(sellPhaseOfTheSecondRound, buyerAgent, BuyerAgent.Phase.Sell, 1); // try to relist an asset by another seller vm.recordLogs(); @@ -476,7 +413,6 @@ contract SwanTest is Helper { /// @notice Seller cannot relist an asset in Buy Phase function test_RevertWhen_RelistInBuyPhase() external - deployment fund createBuyers sellersApproveToSwan @@ -484,18 +420,14 @@ contract SwanTest is Helper { listAssets(sellers[0], marketParameters.maxAssetCount, address(buyerAgents[0])) { BuyerAgent buyerAgent = buyerAgents[0]; - address listedAssetAddr = swan.getListedAssets(address(buyerAgent), currRound)[0]; // increase time to the buy phase of the second round - uint256 buyPhaseOfTheSecondRound = buyerAgent.createdAt() + 2 * marketParameters.sellInterval - + marketParameters.buyInterval + marketParameters.withdrawInterval; - vm.warp(buyPhaseOfTheSecondRound); - currPhase = BuyerAgent.Phase.Buy; + uint256 buyPhaseOfTheSecondRound = buyerAgent.createdAt() + marketParameters.sellInterval + + marketParameters.buyInterval + marketParameters.withdrawInterval + marketParameters.sellInterval; - // check the current phase - (, BuyerAgent.Phase _phase,) = buyerAgent.getRoundPhase(); - require(uint8(currPhase) == uint8(_phase), "Phase is not 'Buy'"); + increaseTime(buyPhaseOfTheSecondRound, buyerAgent, BuyerAgent.Phase.Buy, 1); + currPhase = BuyerAgent.Phase.Buy; // try to relist vm.expectRevert(abi.encodeWithSelector(BuyerAgent.InvalidPhase.selector, currPhase, BuyerAgent.Phase.Sell)); @@ -506,7 +438,6 @@ contract SwanTest is Helper { /// @notice Seller cannot relist an asset in Withdraw Phase function test_RevertWhen_RelistInWithdrawPhase() external - deployment fund createBuyers sellersApproveToSwan @@ -517,9 +448,10 @@ contract SwanTest is Helper { address listedAssetAddr = swan.getListedAssets(address(buyerAgent), currRound)[0]; // increase time to the withdraw phase of the second round - uint256 withdrawPhaseOfSecondRound = 2 * marketParameters.buyInterval + 2 * marketParameters.sellInterval + uint256 withdrawPhaseOfSecondRound = (2 * marketParameters.sellInterval) + (2 * marketParameters.buyInterval) + marketParameters.withdrawInterval + buyerAgent.createdAt(); - vm.warp(withdrawPhaseOfSecondRound); + + increaseTime(withdrawPhaseOfSecondRound, buyerAgent, BuyerAgent.Phase.Withdraw, 1); currPhase = BuyerAgent.Phase.Withdraw; // try to relist @@ -530,9 +462,14 @@ contract SwanTest is Helper { /// @notice Swan owner can set market parameters /// @dev Only Swan owner can set market parameters - function test_SetMarketParameters() external deployment fund createBuyers { + function test_SetMarketParameters() external fund createBuyers { // increase time to the withdraw phase - vm.warp(buyerAgents[0].createdAt() + marketParameters.buyInterval + marketParameters.sellInterval); + increaseTime( + buyerAgents[0].createdAt() + marketParameters.sellInterval + marketParameters.buyInterval, + buyerAgents[0], + BuyerAgent.Phase.Withdraw, + 0 + ); SwanMarketParameters memory newMarketParameters = SwanMarketParameters({ withdrawInterval: 10 * 60, @@ -559,9 +496,14 @@ contract SwanTest is Helper { /// @notice Swan owner can set oracle parameters /// @dev Only Swan owner can set oracle parameters - function test_SetOracleParameters() external deployment fund createBuyers { + function test_SetOracleParameters() external fund createBuyers { // increase time to the withdraw phase - vm.warp(buyerAgents[0].createdAt() + marketParameters.buyInterval + marketParameters.sellInterval); + increaseTime( + buyerAgents[0].createdAt() + marketParameters.sellInterval + marketParameters.buyInterval, + buyerAgents[0], + BuyerAgent.Phase.Withdraw, + 0 + ); LLMOracleTaskParameters memory newOracleParameters = LLMOracleTaskParameters({difficulty: 5, numGenerations: 3, numValidations: 4}); diff --git a/test/SwanIntervals.t.sol b/test/SwanIntervals.t.sol index 9b678f4..75e0202 100644 --- a/test/SwanIntervals.t.sol +++ b/test/SwanIntervals.t.sol @@ -1,9 +1,10 @@ // SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.20; -import {Upgrades} from "@openzeppelin/foundry-upgrades/Upgrades.sol"; import {Vm} from "forge-std/Vm.sol"; +import {Upgrades} from "@openzeppelin/foundry-upgrades/Upgrades.sol"; import {Helper} from "./Helper.t.sol"; +import {console} from "forge-std/Test.sol"; import {BuyerAgent, BuyerAgentFactory} from "../src/BuyerAgent.sol"; import {SwanAssetFactory, SwanAsset} from "../src/SwanAsset.sol"; @@ -15,96 +16,61 @@ import { } from "@firstbatch/dria-oracle-contracts/LLMOracleCoordinator.sol"; contract SwanIntervalsTest is Helper { - modifier deployment() { - token = new WETH9(); - - // deploy llm contracts - vm.startPrank(dria); - - address registryProxy = Upgrades.deployUUPSProxy( - "LLMOracleRegistry.sol", - abi.encodeCall( - LLMOracleRegistry.initialize, (stakes.generatorStakeAmount, stakes.validatorStakeAmount, address(token)) - ) - ); - - oracleRegistry = LLMOracleRegistry(registryProxy); - - address coordinatorProxy = Upgrades.deployUUPSProxy( - "LLMOracleCoordinator.sol", - abi.encodeCall( - LLMOracleCoordinator.initialize, - (address(oracleRegistry), address(token), fees.platformFee, fees.generatorFee, fees.validatorFee) - ) - ); - oracleCoordinator = LLMOracleCoordinator(coordinatorProxy); - - // deploy factory contracts - buyerAgentFactory = new BuyerAgentFactory(); - swanAssetFactory = new SwanAssetFactory(); - - // deploy swan - address swanProxy = Upgrades.deployUUPSProxy( - "Swan.sol", - abi.encodeCall( - Swan.initialize, - ( - marketParameters, - oracleParameters, - address(oracleCoordinator), - address(token), - address(buyerAgentFactory), - address(swanAssetFactory) - ) - ) - ); - swan = Swan(swanProxy); - vm.stopPrank(); - - // label contracts to be able to identify them easily in console - vm.label(address(swan), "Swan"); - vm.label(address(token), "WETH"); - vm.label(address(this), "SwanIntervalsTest"); - vm.label(address(oracleRegistry), "LLMOracleRegistry"); - vm.label(address(oracleCoordinator), "LLMOracleCoordinator"); - vm.label(address(buyerAgentFactory), "BuyerAgentFactory"); - vm.label(address(swanAssetFactory), "SwanAssetFactory"); - _; - } - /// @notice Check the current phase is Sell right after creation of buyer agent - function test_InSellPhase() external deployment createBuyers { + function test_InSellPhase() external createBuyers { // agent should be in sell phase right after creation checkRoundAndPhase(buyerAgents[0], BuyerAgent.Phase.Sell, 0); } /// @notice Check the current phase is Buy after increase time to buy phase - function test_InBuyPhase() external deployment createBuyers { - vm.warp(buyerAgents[0].createdAt() + swan.getCurrentMarketParameters().sellInterval); - checkRoundAndPhase(buyerAgents[0], BuyerAgent.Phase.Buy, 0); + function test_InBuyPhase() external createBuyers { + BuyerAgent agent = buyerAgents[0]; + increaseTime(agent.createdAt() + swan.getCurrentMarketParameters().sellInterval, agent, BuyerAgent.Phase.Buy, 0); + checkRoundAndPhase(agent, BuyerAgent.Phase.Buy, 0); } /// @notice Check the current phase is Withdraw after increase time to withdraw phase - function test_InWithdrawPhase() external deployment createBuyers { - vm.warp( + function test_InWithdrawPhase() external createBuyers { + increaseTime( buyerAgents[0].createdAt() + swan.getCurrentMarketParameters().sellInterval - + swan.getCurrentMarketParameters().buyInterval + + swan.getCurrentMarketParameters().buyInterval, + buyerAgents[0], + BuyerAgent.Phase.Withdraw, + 0 ); checkRoundAndPhase(buyerAgents[0], BuyerAgent.Phase.Withdraw, 0); } /// @notice Change the intervals and check the current phase and round is are correct - function test_ChangeCycleTime() external deployment createBuyers { + function testFuzz_ChangeCycleTime( + uint256 sellIntervalForFirstSet, + uint256 buyIntervalForFirstset, + uint256 withdrawIntervalForFirstSet, + uint256 sellIntervalForSecondSet, + uint256 buyIntervalForSecondSet, + uint256 withdrawIntervalForSecondSet + ) external createBuyers { + vm.assume(sellIntervalForFirstSet > 15 minutes && sellIntervalForFirstSet < 2 days); + vm.assume(buyIntervalForFirstset > 15 minutes && buyIntervalForFirstset < 2 days); + vm.assume(withdrawIntervalForFirstSet > 15 minutes && withdrawIntervalForFirstSet < 2 days); + vm.assume(sellIntervalForSecondSet > 15 minutes && sellIntervalForSecondSet < 2 days); + vm.assume(buyIntervalForSecondSet > 15 minutes && buyIntervalForSecondSet < 2 days); + vm.assume(withdrawIntervalForSecondSet > 15 minutes && withdrawIntervalForSecondSet < 2 days); + // increase time to buy phase of the second round - vm.warp(buyerAgents[0].createdAt() + swan.getCurrentMarketParameters().sellInterval); - checkRoundAndPhase(buyerAgents[0], BuyerAgent.Phase.Buy, 0); + increaseTime( + buyerAgents[0].createdAt() + swan.getCurrentMarketParameters().sellInterval, + buyerAgents[0], + BuyerAgent.Phase.Buy, + 0 + ); - // decrease cycle time + // change cycle time setMarketParameters( SwanMarketParameters({ - withdrawInterval: 60, - sellInterval: 600, - buyInterval: 120, + withdrawInterval: withdrawIntervalForFirstSet, + sellInterval: sellIntervalForFirstSet, + buyInterval: buyIntervalForFirstset, platformFee: 2, maxAssetCount: 3, timestamp: block.timestamp, @@ -116,13 +82,22 @@ contract SwanIntervalsTest is Helper { // get all params SwanMarketParameters[] memory allParams = swan.getMarketParameters(); assertEq(allParams.length, 2); + (uint256 _currRound, BuyerAgent.Phase _phase,) = buyerAgents[0].getRoundPhase(); - // should be in sell phase of second round after set - uint256 currTimestamp = checkRoundAndPhase(buyerAgents[0], BuyerAgent.Phase.Sell, 1); + assertEq(_currRound, 1); + assertEq(uint8(_phase), uint8(BuyerAgent.Phase.Sell)); - // increase time to buy phase of the second round - vm.warp(currTimestamp + swan.getCurrentMarketParameters().sellInterval); - checkRoundAndPhase(buyerAgents[0], BuyerAgent.Phase.Buy, 1); + uint256 currTimestamp = block.timestamp; + + // increase time to buy phase of the second round but round comes +1 because of the setMarketParameters call + // buyerAgents[0] should be in buy phase of second round + increaseTime( + currTimestamp + (2 * swan.getCurrentMarketParameters().sellInterval) + + swan.getCurrentMarketParameters().buyInterval + swan.getCurrentMarketParameters().withdrawInterval, + buyerAgents[0], + BuyerAgent.Phase.Buy, + 2 + ); // deploy new buyer agent vm.prank(buyerAgentOwners[0]); @@ -133,18 +108,15 @@ contract SwanIntervalsTest is Helper { buyerAgentParameters[1].amountPerRound ); - // buyerAgents[0] should be in buy phase of second round - checkRoundAndPhase(buyerAgents[0], BuyerAgent.Phase.Buy, 1); - // agentAfterFirstSet should be in sell phase of the first round checkRoundAndPhase(agentAfterFirstSet, BuyerAgent.Phase.Sell, 0); - // increase cycle time + // change cycle time setMarketParameters( SwanMarketParameters({ - withdrawInterval: 600, - sellInterval: 1000, - buyInterval: 10, + withdrawInterval: withdrawIntervalForSecondSet, + sellInterval: sellIntervalForSecondSet, + buyInterval: buyIntervalForSecondSet, platformFee: 2, // percentage maxAssetCount: 3, timestamp: block.timestamp, @@ -157,8 +129,8 @@ contract SwanIntervalsTest is Helper { allParams = swan.getMarketParameters(); assertEq(allParams.length, 3); - // buyerAgents[0] should be in sell phase of the third round - checkRoundAndPhase(buyerAgents[0], BuyerAgent.Phase.Sell, 2); + // buyerAgents[0] should be in sell phase of the fourth round (2 more increase time + 2 for setting new params) + checkRoundAndPhase(buyerAgents[0], BuyerAgent.Phase.Sell, 3); // agentAfterFirstSet should be in sell phase of the second round checkRoundAndPhase(agentAfterFirstSet, BuyerAgent.Phase.Sell, 1);