Skip to content
Merged
Show file tree
Hide file tree
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
3 changes: 2 additions & 1 deletion src/ServiceManagerBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,8 @@ abstract contract ServiceManagerBase is IServiceManager, OwnableUpgradeable {
// transfer token to ServiceManager and approve PaymentCoordinator to transfer again
// in payForRange() call
rangePayments[i].token.transferFrom(msg.sender, address(this), rangePayments[i].amount);
rangePayments[i].token.approve(address(_paymentCoordinator), rangePayments[i].amount);
uint256 allowance = rangePayments[i].token.allowance(address(this), address(_paymentCoordinator));
rangePayments[i].token.approve(address(_paymentCoordinator), rangePayments[i].amount + allowance);
}

_paymentCoordinator.payForRange(rangePayments);
Expand Down
121 changes: 121 additions & 0 deletions test/unit/ServiceManagerBase.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -500,4 +500,125 @@ contract ServiceManagerBase_UnitTests is
);
}
}

function test_submitPayments_MultipleRangePaymentsSingleToken(
uint256 startTimestamp,
uint256 duration,
uint256 amount,
uint256 numPayments
) public {
cheats.assume(2 <= numPayments && numPayments <= 10);
cheats.prank(paymentCoordinator.owner());

IPaymentCoordinator.RangePayment[]
memory rangePayments = new IPaymentCoordinator.RangePayment[](
numPayments
);
bytes32[] memory rangePaymentHashes = new bytes32[](numPayments);
uint256 startPaymentNonce = paymentCoordinator.paymentNonce(
address(serviceManager)
);
IERC20 paymentToken = new ERC20PresetFixedSupply(
"dog wif hat",
"MOCK1",
mockTokenInitialSupply,
serviceManagerOwner
);
cheats.prank(serviceManagerOwner);
paymentToken.approve(address(serviceManager), mockTokenInitialSupply);
uint256 avsBalanceBefore = paymentToken.balanceOf(serviceManagerOwner);
uint256 paymentCoordinatorBalanceBefore = paymentToken.balanceOf(address(paymentCoordinator));
uint256 totalAmount = 0;

uint256[] memory amounts = new uint256[](numPayments);

// Create multiple range payments and their expected event
for (uint256 i = 0; i < numPayments; ++i) {
// 1. Bound fuzz inputs to valid ranges and amounts using randSeed for each
amount = bound(amount + i, 1, MAX_PAYMENT_AMOUNT);
amounts[i] = amount;
totalAmount += amount;
duration = bound(duration + i, 0, MAX_PAYMENT_DURATION);
duration = duration - (duration % CALCULATION_INTERVAL_SECONDS);
startTimestamp = bound(
startTimestamp + i,
uint256(
_maxTimestamp(
GENESIS_PAYMENT_TIMESTAMP,
uint32(block.timestamp) - MAX_RETROACTIVE_LENGTH
)
) +
CALCULATION_INTERVAL_SECONDS -
1,
block.timestamp + uint256(MAX_FUTURE_LENGTH)
);
startTimestamp =
startTimestamp -
(startTimestamp % CALCULATION_INTERVAL_SECONDS);

// 2. Create range payment input param
IPaymentCoordinator.RangePayment
memory rangePayment = IPaymentCoordinator.RangePayment({
strategiesAndMultipliers: defaultStrategyAndMultipliers,
token: paymentToken,
amount: amounts[i],
startTimestamp: uint32(startTimestamp),
duration: uint32(duration)
});
rangePayments[i] = rangePayment;

// 3. expected event emitted for this rangePayment
rangePaymentHashes[i] = keccak256(
abi.encode(
address(serviceManager),
startPaymentNonce + i,
rangePayments[i]
)
);
cheats.expectEmit(
true,
true,
true,
true,
address(paymentCoordinator)
);
emit RangePaymentCreated(
address(serviceManager),
startPaymentNonce + i,
rangePaymentHashes[i],
rangePayments[i]
);
}

// 4. call payForRange()
cheats.prank(serviceManagerOwner);
serviceManager.payForRange(rangePayments);

// 5. Check for paymentNonce() and rangePaymentHashes being set
assertEq(
startPaymentNonce + numPayments,
paymentCoordinator.paymentNonce(address(serviceManager)),
"Payment nonce not incremented properly"
);
assertEq(
avsBalanceBefore - totalAmount,
paymentToken.balanceOf(serviceManagerOwner),
"AVS balance not decremented by amount of range payments"
);
assertEq(
paymentCoordinatorBalanceBefore + totalAmount,
paymentToken.balanceOf(address(paymentCoordinator)),
"PaymentCoordinator balance not incremented by amount of range payments"
);

for (uint256 i = 0; i < numPayments; ++i) {
assertTrue(
paymentCoordinator.isRangePaymentHash(
address(serviceManager),
rangePaymentHashes[i]
),
"Range payment hash not submitted"
);
}
}
}