From 30946b39215604a33a15b52e927691d2ce2c9e92 Mon Sep 17 00:00:00 2001 From: Tronky Date: Fri, 7 Mar 2025 22:10:39 +0700 Subject: [PATCH 1/3] added a new test --- test/PurchaseTracker.t.sol | 166 ++++++++++++++++++++++++++++--------- 1 file changed, 129 insertions(+), 37 deletions(-) diff --git a/test/PurchaseTracker.t.sol b/test/PurchaseTracker.t.sol index 5599925..d4b0166 100644 --- a/test/PurchaseTracker.t.sol +++ b/test/PurchaseTracker.t.sol @@ -22,6 +22,15 @@ contract TestPaymentAndTracker is DeploymentSetup { address internal seller; address internal arbiter; + address internal payer1; + address internal seller1; + + address internal payer2; + address internal seller2; + + address internal payer3; + address internal seller3; + uint256 internal constant INITIAL_USER_BALANCE = 10_000 ether; IERC20 internal loot; @@ -35,13 +44,24 @@ contract TestPaymentAndTracker is DeploymentSetup { systemSettings1 = ISystemSettings(systemSettings); // Define test addresses - payer = makeAddr("payer"); - seller = makeAddr("seller"); + payer1 = makeAddr("payer1"); + seller1 = makeAddr("seller1"); arbiter = makeAddr("arbiter"); + payer2 = makeAddr("payer2"); + seller2 = makeAddr("seller2"); + + payer3 = makeAddr("payer3"); + seller3 = makeAddr("seller3"); + + payer = payer1; + seller = seller1; + // Fund the payer with both ETH and ERC20 tokens vm.deal(payer, INITIAL_USER_BALANCE); - deal(address(loot), payer, INITIAL_USER_BALANCE); + deal(address(loot), payer1, INITIAL_USER_BALANCE); + deal(address(loot), payer2, INITIAL_USER_BALANCE); + deal(address(loot), payer3, INITIAL_USER_BALANCE); // Ensure the PaymentEscrow contract is authorized in the tracker vm.prank(admin); @@ -53,22 +73,10 @@ contract TestPaymentAndTracker is DeploymentSetup { bytes32 paymentId = keccak256("payment-test-1"); uint256 payAmount = 1000; - // 2. Place the payment - vm.startPrank(payer); - loot.approve(address(payEscrow), payAmount); - - PaymentInput memory input = PaymentInput({ - id: paymentId, - payer: payer, - receiver: seller, - currency: address(loot), - amount: payAmount - }); - payEscrow.placePayment(input); - vm.stopPrank(); - - // 3. Fetch payment details - EscrowLib.Payment memory storedPayment = payEscrow.getPayment(paymentId); + // 2. Place the payment & fetch payment details + EscrowLib.Payment memory storedPayment = placePayment( + payEscrow, paymentId, payer, seller, address(loot), payAmount + ); // Validate initial state assertEq(storedPayment.amount, payAmount, "Incorrect escrowed amount"); @@ -76,24 +84,10 @@ contract TestPaymentAndTracker is DeploymentSetup { assertEq(storedPayment.receiver, seller, "Incorrect seller"); assertFalse(storedPayment.released, "Payment should not be released yet"); - // 4. Release from payer side - vm.prank(payer); - payEscrow.releaseEscrow(paymentId); - - // Check updated state if autoRelease is enabled - if (!autoRelease) { - storedPayment = payEscrow.getPayment(paymentId); - assertFalse(storedPayment.released, "Should still require seller's release if autoReleaseFlag is false"); - - // 5. Release from seller side - vm.prank(seller); - payEscrow.releaseEscrow(paymentId); - } + // 3. Release from payer side + releaseEscrow(payEscrow, paymentId); - storedPayment = payEscrow.getPayment(paymentId); - assertTrue(storedPayment.released, "Payment should be fully released now"); - - // 6. Validate PurchaseTracker recorded net amount + // 4. Validate PurchaseTracker recorded net amount uint256 feeBps = payEscrowSettingsFee(); uint256 expectedFee = (payAmount * feeBps) / 10000; uint256 netAmount = payAmount - expectedFee; @@ -107,6 +101,62 @@ contract TestPaymentAndTracker is DeploymentSetup { assertEq(tracker.totalSalesAmount(seller), netAmount, "Seller sales amount mismatch"); } + function testFullReleaseRecordsPurchaseMultiplePayments() public { + // 1. Setup data + bytes32 paymentId1 = keccak256("payment-test-1"); + bytes32 paymentId2 = keccak256("payment-test-2"); + bytes32 paymentId3 = keccak256("payment-test-3"); + uint256 payAmount1 = 1200; + uint256 payAmount2 = 2020; + uint256 payAmount3 = 3001; + + // 2. Place the payments + EscrowLib.Payment memory payment1 = placePayment( + payEscrow, paymentId1, payer1, seller1, address(loot), payAmount1 + ); + EscrowLib.Payment memory payment2 = placePayment( + payEscrow, paymentId2, payer1, seller2, address(loot), payAmount2 + ); + EscrowLib.Payment memory payment3 = placePayment( + payEscrow, paymentId3, payer2, seller2, address(loot), payAmount3 + ); + + //3. Release escrows + releaseEscrow(payEscrow, paymentId1); + releaseEscrow(payEscrow, paymentId2); + releaseEscrow(payEscrow, paymentId3); + + //4. Verify released + payment1 = payEscrow.getPayment(paymentId1); + payment2 = payEscrow.getPayment(paymentId2); + payment3 = payEscrow.getPayment(paymentId3); + + assertTrue(payment1.released, "Payment 1 should be fully released now"); + assertTrue(payment2.released, "Payment 2 should be fully released now"); + assertTrue(payment3.released, "Payment 3 should be fully released now"); + + // 5. Validate PurchaseTracker recorded net amount + uint256 feeBps = payEscrowSettingsFee(); + uint256 expectedFee1 = (payAmount1 * feeBps) / 10000; + uint256 netAmount1 = payAmount1 - expectedFee1; + uint256 expectedFee2 = (payAmount2 * feeBps) / 10000; + uint256 netAmount2 = payAmount2 - expectedFee2; + uint256 expectedFee3 = (payAmount3 * feeBps) / 10000; + uint256 netAmount3 = payAmount3 - expectedFee3; + + // Buyer checks + assertEq(tracker.totalPurchaseCount(payer1), 2, "Buyer 1 purchase count mismatch"); + assertEq(tracker.totalPurchaseAmount(payer1), netAmount1 + netAmount2, "Buyer 1 purchase amount mismatch"); + assertEq(tracker.totalPurchaseCount(payer2), 1, "Buyer 2 purchase count mismatch"); + assertEq(tracker.totalPurchaseAmount(payer2), netAmount3, "Buyer 2 purchase amount mismatch"); + + // Seller checks + assertEq(tracker.totalSalesCount(seller1), 1, "Seller 1 sales count mismatch"); + assertEq(tracker.totalSalesAmount(seller1), netAmount1, "Seller 1 sales amount mismatch"); + assertEq(tracker.totalSalesCount(seller2), 2, "Seller 2 sales count mismatch"); + assertEq(tracker.totalSalesAmount(seller2), netAmount2 + netAmount3, "Seller 2 sales amount mismatch"); + } + function testPartialRefundThenRelease() public { // 1. Setup data bytes32 paymentId = keccak256("payment-test-2"); @@ -257,8 +307,50 @@ contract TestPaymentAndTracker is DeploymentSetup { tracker.distributeReward(arbiter); } - function payEscrowSettingsFee() internal view returns (uint256) { return systemSettings1.feeBps(); } + + function placePayment( + PaymentEscrow escrow, + bytes32 paymentId, + address _payer, + address _seller, + address currency, + uint256 amount + ) private returns(EscrowLib.Payment memory) { + vm.startPrank(payer); + loot.approve(address(escrow), amount); + + PaymentInput memory input = PaymentInput({ + id: paymentId, + payer: _payer, + receiver: _seller, + currency: currency, + amount: amount + }); + payEscrow.placePayment(input); + vm.stopPrank(); + + // 3. Fetch payment details + EscrowLib.Payment memory storedPayment = payEscrow.getPayment(paymentId); + return storedPayment; + } + + function releaseEscrow(PaymentEscrow escrow, bytes32 paymentId) private { + EscrowLib.Payment memory payment = escrow.getPayment(paymentId); + + //payer release + vm.prank(payment.payer); + escrow.releaseEscrow(paymentId); + + // Check updated state if autoRelease is enabled + if (!autoRelease) { + assertFalse(payment.released, "Should still require seller's release if autoReleaseFlag is false"); + + // 5. Release from seller side + vm.prank(payment.receiver); + escrow.releaseEscrow(paymentId); + } + } } From 3bf2dcfd61abe5922afcec41c41f2a12941d131c Mon Sep 17 00:00:00 2001 From: Tronky Date: Fri, 7 Mar 2025 23:37:07 +0700 Subject: [PATCH 2/3] added test for multiple escrows, unauthorized escrows --- test/PurchaseTracker.t.sol | 104 +++++++++++++++++++++++++++++++++---- 1 file changed, 95 insertions(+), 9 deletions(-) diff --git a/test/PurchaseTracker.t.sol b/test/PurchaseTracker.t.sol index d4b0166..8547797 100644 --- a/test/PurchaseTracker.t.sol +++ b/test/PurchaseTracker.t.sol @@ -17,6 +17,7 @@ contract TestPaymentAndTracker is DeploymentSetup { PaymentEscrow internal payEscrow; PurchaseTracker internal tracker; ISystemSettings internal systemSettings1; + ISecurityContext internal securityContext; address internal payer; address internal seller; @@ -38,10 +39,10 @@ contract TestPaymentAndTracker is DeploymentSetup { super.setUp(); // Cast addresses into actual contract instances - payEscrow = PaymentEscrow(payable(escrow)); tracker = PurchaseTracker(purchaseTracker); loot = IERC20(lootToken); systemSettings1 = ISystemSettings(systemSettings); + payEscrow = PaymentEscrow(payable(escrow)); // Define test addresses payer1 = makeAddr("payer1"); @@ -157,6 +158,91 @@ contract TestPaymentAndTracker is DeploymentSetup { assertEq(tracker.totalSalesAmount(seller2), netAmount2 + netAmount3, "Seller 2 sales amount mismatch"); } + function testFullReleaseRecordsPurchaseMultipleEscrows() public { + // additional escrows + PaymentEscrow payEscrow1 = payEscrow; + PaymentEscrow payEscrow2 = new PaymentEscrow(payEscrow.securityContext(), systemSettings1, autoRelease, IPurchaseTracker(purchaseTracker)); + PaymentEscrow payEscrow3 = new PaymentEscrow(payEscrow.securityContext(), systemSettings1, autoRelease, IPurchaseTracker(purchaseTracker)); + + //authorize the extra escrows + vm.startPrank(admin); + tracker.authorizeEscrow(address(payEscrow2)); + tracker.authorizeEscrow(address(payEscrow3)); + vm.stopPrank(); + + // 1. Setup data + bytes32 paymentId1 = keccak256("payment-test-1"); + bytes32 paymentId2 = keccak256("payment-test-2"); + bytes32 paymentId3 = keccak256("payment-test-3"); + uint256 payAmount1 = 1200; + uint256 payAmount2 = 2020; + uint256 payAmount3 = 3001; + + // 2. Place the payments + EscrowLib.Payment memory payment1 = placePayment( + payEscrow1, paymentId1, payer1, seller1, address(loot), payAmount1 + ); + EscrowLib.Payment memory payment2 = placePayment( + payEscrow2, paymentId2, payer1, seller2, address(loot), payAmount2 + ); + EscrowLib.Payment memory payment3 = placePayment( + payEscrow3, paymentId3, payer2, seller2, address(loot), payAmount3 + ); + + //3. Release escrows + releaseEscrow(payEscrow1, paymentId1); + releaseEscrow(payEscrow2, paymentId2); + releaseEscrow(payEscrow3, paymentId3); + + //4. Verify released + payment1 = payEscrow1.getPayment(paymentId1); + payment2 = payEscrow2.getPayment(paymentId2); + payment3 = payEscrow3.getPayment(paymentId3); + + assertTrue(payment1.released, "Payment 1 should be fully released now"); + assertTrue(payment2.released, "Payment 2 should be fully released now"); + assertTrue(payment3.released, "Payment 3 should be fully released now"); + + // 5. Validate PurchaseTracker recorded net amount + uint256 feeBps = payEscrowSettingsFee(); + uint256 expectedFee1 = (payAmount1 * feeBps) / 10000; + uint256 netAmount1 = payAmount1 - expectedFee1; + uint256 expectedFee2 = (payAmount2 * feeBps) / 10000; + uint256 netAmount2 = payAmount2 - expectedFee2; + uint256 expectedFee3 = (payAmount3 * feeBps) / 10000; + uint256 netAmount3 = payAmount3 - expectedFee3; + + // Buyer checks + assertEq(tracker.totalPurchaseCount(payer1), 2, "Buyer 1 purchase count mismatch"); + assertEq(tracker.totalPurchaseAmount(payer1), netAmount1 + netAmount2, "Buyer 1 purchase amount mismatch"); + assertEq(tracker.totalPurchaseCount(payer2), 1, "Buyer 2 purchase count mismatch"); + assertEq(tracker.totalPurchaseAmount(payer2), netAmount3, "Buyer 2 purchase amount mismatch"); + + // Seller checks + assertEq(tracker.totalSalesCount(seller1), 1, "Seller 1 sales count mismatch"); + assertEq(tracker.totalSalesAmount(seller1), netAmount1, "Seller 1 sales amount mismatch"); + assertEq(tracker.totalSalesCount(seller2), 2, "Seller 2 sales count mismatch"); + assertEq(tracker.totalSalesAmount(seller2), netAmount2 + netAmount3, "Seller 2 sales amount mismatch"); + } + + function testUnauthorizedEscrow() public { + //create an unauthorized escrow + PaymentEscrow payEscrow2 = new PaymentEscrow(payEscrow.securityContext(), systemSettings1, autoRelease, IPurchaseTracker(purchaseTracker)); + + // 1. Setup data + bytes32 paymentId = keccak256("payment-test-1"); + uint256 payAmount = 1000; + + // 2. Place the payment with an unauthorized escrow + placePayment( + payEscrow2, paymentId, payer, seller, address(loot), payAmount + ); + + vm.prank(payer); + vm.expectRevert("PurchaseTracker: Not authorized"); + payEscrow2.releaseEscrow(paymentId); + } + function testPartialRefundThenRelease() public { // 1. Setup data bytes32 paymentId = keccak256("payment-test-2"); @@ -312,7 +398,7 @@ contract TestPaymentAndTracker is DeploymentSetup { } function placePayment( - PaymentEscrow escrow, + PaymentEscrow _escrow, bytes32 paymentId, address _payer, address _seller, @@ -320,7 +406,7 @@ contract TestPaymentAndTracker is DeploymentSetup { uint256 amount ) private returns(EscrowLib.Payment memory) { vm.startPrank(payer); - loot.approve(address(escrow), amount); + loot.approve(address(_escrow), amount); PaymentInput memory input = PaymentInput({ id: paymentId, @@ -329,20 +415,20 @@ contract TestPaymentAndTracker is DeploymentSetup { currency: currency, amount: amount }); - payEscrow.placePayment(input); + _escrow.placePayment(input); vm.stopPrank(); // 3. Fetch payment details - EscrowLib.Payment memory storedPayment = payEscrow.getPayment(paymentId); + EscrowLib.Payment memory storedPayment = _escrow.getPayment(paymentId); return storedPayment; } - function releaseEscrow(PaymentEscrow escrow, bytes32 paymentId) private { - EscrowLib.Payment memory payment = escrow.getPayment(paymentId); + function releaseEscrow(PaymentEscrow _escrow, bytes32 paymentId) private { + EscrowLib.Payment memory payment = _escrow.getPayment(paymentId); //payer release vm.prank(payment.payer); - escrow.releaseEscrow(paymentId); + _escrow.releaseEscrow(paymentId); // Check updated state if autoRelease is enabled if (!autoRelease) { @@ -350,7 +436,7 @@ contract TestPaymentAndTracker is DeploymentSetup { // 5. Release from seller side vm.prank(payment.receiver); - escrow.releaseEscrow(paymentId); + _escrow.releaseEscrow(paymentId); } } } From ef7cb21b312bfd3cadf0b486fa3b7a6bf3428ba2 Mon Sep 17 00:00:00 2001 From: Tronky Date: Sat, 8 Mar 2025 01:22:13 +0700 Subject: [PATCH 3/3] added tests for authorizing/deauthorizing escrows --- test/PurchaseTracker.t.sol | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/test/PurchaseTracker.t.sol b/test/PurchaseTracker.t.sol index 8547797..55babe2 100644 --- a/test/PurchaseTracker.t.sol +++ b/test/PurchaseTracker.t.sol @@ -243,6 +243,35 @@ contract TestPaymentAndTracker is DeploymentSetup { payEscrow2.releaseEscrow(paymentId); } + function testTestAbilityToAuthorizeEscrow() public { + //create an unauthorized escrow + PaymentEscrow payEscrow2 = new PaymentEscrow(payEscrow.securityContext(), systemSettings1, autoRelease, IPurchaseTracker(purchaseTracker)); + + //non-admin can't do it + vm.expectRevert(); + tracker.authorizeEscrow(address(payEscrow2)); + assertFalse(tracker.authorizedEscrows(address(payEscrow2))); + + //admin can do it + vm.prank(admin); + tracker.authorizeEscrow(address(payEscrow2)); + assertTrue(tracker.authorizedEscrows(address(payEscrow2))); + } + + function testTestAbilityToDeauthorizeEscrow() public { + assertTrue(tracker.authorizedEscrows(address(payEscrow))); + + //non-admin can't do it + vm.expectRevert(); + tracker.deauthorizeEscrow(address(payEscrow)); + assertTrue(tracker.authorizedEscrows(address(payEscrow))); + + //admin can do it + vm.prank(admin); + tracker.deauthorizeEscrow(address(payEscrow)); + assertFalse(tracker.authorizedEscrows(address(payEscrow))); + } + function testPartialRefundThenRelease() public { // 1. Setup data bytes32 paymentId = keccak256("payment-test-2");