Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
283 changes: 245 additions & 38 deletions test/PurchaseTracker.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -17,31 +17,52 @@ contract TestPaymentAndTracker is DeploymentSetup {
PaymentEscrow internal payEscrow;
PurchaseTracker internal tracker;
ISystemSettings internal systemSettings1;
ISecurityContext internal securityContext;

address internal payer;
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;

function setUp() public virtual override {
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
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);
Expand All @@ -53,47 +74,21 @@ 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");
assertEq(storedPayment.payer, payer, "Incorrect payer");
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);
}

storedPayment = payEscrow.getPayment(paymentId);
assertTrue(storedPayment.released, "Payment should be fully released now");
// 3. Release from payer side
releaseEscrow(payEscrow, paymentId);

// 6. Validate PurchaseTracker recorded net amount
// 4. Validate PurchaseTracker recorded net amount
uint256 feeBps = payEscrowSettingsFee();
uint256 expectedFee = (payAmount * feeBps) / 10000;
uint256 netAmount = payAmount - expectedFee;
Expand All @@ -107,6 +102,176 @@ 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 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 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");
Expand Down Expand Up @@ -257,8 +422,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
});
_escrow.placePayment(input);
vm.stopPrank();

// 3. Fetch payment details
EscrowLib.Payment memory storedPayment = _escrow.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);
}
}
}