Skip to content

Commit

Permalink
update tests
Browse files Browse the repository at this point in the history
  • Loading branch information
livingrockrises committed Oct 25, 2024
1 parent ecd920a commit b4250ea
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 11 deletions.
2 changes: 2 additions & 0 deletions contracts/token/BiconomyTokenPaymaster.sol
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,7 @@ contract BiconomyTokenPaymaster is
uint192 tokenPrice = _getPrice(tokenAddress);
uint256 tokenAmount;

// TODO: Account for penalties here
{
// Calculate token amount to precharge
uint256 maxFeePerGas = UserOperationLib.unpackMaxFeePerGas(userOp);
Expand Down Expand Up @@ -565,6 +566,7 @@ contract BiconomyTokenPaymaster is
/// @dev This function is used to get the latest price from the tokenOracle or nativeAssetToUsdOracle.
/// @param oracle The oracle contract to fetch the price from.
/// @return price The latest price fetched from the oracle.
/// Note: We could do this using oracle aggregator, so we can also use Pyth. or Twap based oracle and just not chainlink.
function _fetchPrice(IOracle oracle) internal view returns (uint192 price) {
(, int256 answer,, uint256 updatedAt,) = oracle.latestRoundData();
if (answer <= 0) {
Expand Down
34 changes: 34 additions & 0 deletions test/base/TestBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { IPaymaster } from "account-abstraction/interfaces/IPaymaster.sol";
import { Nexus } from "@nexus/contracts/Nexus.sol";
import { CheatCodes } from "@nexus/test/foundry/utils/CheatCodes.sol";
import { BaseEventsAndErrors } from "./BaseEventsAndErrors.sol";
import { MockToken } from "@nexus/contracts/mocks/MockToken.sol";

import { BiconomySponsorshipPaymaster } from "../../contracts/sponsorship/BiconomySponsorshipPaymaster.sol";

Expand Down Expand Up @@ -436,6 +437,39 @@ abstract contract TestBase is CheatCodes, TestHelper, BaseEventsAndErrors {
assertApproxEqRel(totalGasFeePaid + actualPriceMarkup + maxPenalty, gasPaidByDapp, 0.02e18);
}

function calculateAndAssertAdjustmentsForTokenPaymaster(
BiconomyTokenPaymaster tokenPaymaster,
MockToken token,
uint256 initialBundlerBalance,
uint256 initialPaymasterEpBalance,
uint256 initialUserTokenBalance,
uint256 initialPaymasterTokenBalance,
uint32 priceMarkup,
uint256 maxPenalty
)
internal
view
{
uint256 totalGasFeePaid = BUNDLER.addr.balance - initialBundlerBalance;

// Assert that what paymaster paid is the same as what the bundler received
assertEq(totalGasFeePaid, initialPaymasterEpBalance - tokenPaymaster.getDeposit());

uint256 gasPaidBySAInERC20 = initialUserTokenBalance - token.balanceOf(address(ALICE_ACCOUNT));

uint256 gasCollectedInERC20ByPaymaster = token.balanceOf(address(tokenPaymaster)) - initialPaymasterTokenBalance;

// Accounts for refund etc
// Revirw if it should be exact equal
assertApproxEqRel(gasPaidBySAInERC20, gasCollectedInERC20ByPaymaster, 0.02e18);

// assertGt(gasPaidBySAInERC20 * tokenPrice, BUNDLER.addr.balance - initialBundlerBalance);

// Ensure that max 2% difference between total gas paid + the adjustment premium and gas paid by smart account (ERC20 charge * token gas price) (from
// Todo
// assertApproxEqRel(totalGasFeePaid + actualPriceMarkup + maxPenalty, gasPaidByDapp, 0.02e18);
}

function _toSingletonArray(address addr) internal pure returns (address[] memory) {
address[] memory array = new address[](1);
array[0] = addr;
Expand Down
16 changes: 16 additions & 0 deletions test/unit/concrete/TestTokenPaymaster.Base.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// SPDX-License-Identifier: Unlicensed
pragma solidity ^0.8.27;

import "../../base/TestBase.sol";
import {
BiconomyTokenPaymaster,
IBiconomyTokenPaymaster,
BiconomyTokenPaymasterErrors,
IOracle
} from "../../../contracts/token/BiconomyTokenPaymaster.sol";
import { MockOracle } from "../../mocks/MockOracle.sol";
import { MockToken } from "@nexus/contracts/mocks/MockToken.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "../../../contracts/token/swaps/Uniswapper.sol";

// Todo: work on fork of Base.
Empty file.
37 changes: 26 additions & 11 deletions test/unit/concrete/TestTokenPaymaster.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ contract TestTokenPaymaster is TestBase {

// Deploy mock oracles and tokens
swapRouter = ISwapRouter(address(SWAP_ROUTER_ADDRESS));
nativeAssetToUsdOracle = new MockOracle(100_000_000, 8); // Oracle with 8 decimals for ETH
tokenOracle = new MockOracle(100_000_000, 8); // Oracle with 8 decimals for ERC20 token
nativeAssetToUsdOracle = new MockOracle(100_000_000, 8); // Oracle with 8 decimals for ETH // ETH/USD
tokenOracle = new MockOracle(100_000_000, 8); // Oracle with 8 decimals for ERC20 token // TKN/USD
testToken = new MockToken("Test Token", "TKN");
testToken2 = new MockToken("Test Token 2", "TKN2");

Expand Down Expand Up @@ -389,15 +389,21 @@ contract TestTokenPaymaster is TestBase {
testToken.approve(address(tokenPaymaster), testToken.balanceOf(address(ALICE_ACCOUNT)));
vm.stopPrank();

uint256 aliceBalanceBefore = testToken.balanceOf(address(ALICE_ACCOUNT));
uint256 tokenPaymasterBalanceBefore = testToken.balanceOf(address(tokenPaymaster));
// Warm up the ERC20 balance slot for paymaster by making some tokens held initially
testToken.mint(address(tokenPaymaster), 100_000 * (10 ** testToken.decimals()));


uint256 initialBundlerBalance = BUNDLER.addr.balance;
uint256 initialPaymasterEpBalance = tokenPaymaster.getDeposit();
uint256 initialUserTokenBalance = testToken.balanceOf(address(ALICE_ACCOUNT));
uint256 initialPaymasterTokenBalance = testToken.balanceOf(address(tokenPaymaster));

// Build the user operation for external mode
PackedUserOperation memory userOp = buildUserOpWithCalldata(ALICE, "", address(VALIDATOR_MODULE));
uint48 validUntil = uint48(block.timestamp + 1 days);
uint48 validAfter = uint48(block.timestamp);
uint128 tokenPrice = 1e8; // Assume 1 token = 1 USD
uint32 externalPriceMarkup = 1e6;
uint32 externalPriceMarkup = 1e6; // no premium

TokenPaymasterData memory pmData = TokenPaymasterData({
paymasterValGasLimit: 3e6,
Expand Down Expand Up @@ -433,8 +439,15 @@ contract TestTokenPaymaster is TestBase {
// Execute the operation
ENTRYPOINT.handleOps(ops, payable(BUNDLER.addr));

assertLt(testToken.balanceOf(address(ALICE_ACCOUNT)), aliceBalanceBefore);
assertGt(testToken.balanceOf(address(tokenPaymaster)), tokenPaymasterBalanceBefore);
calculateAndAssertAdjustmentsForTokenPaymaster(
tokenPaymaster,
testToken,
initialBundlerBalance,
initialPaymasterEpBalance,
initialUserTokenBalance,
initialPaymasterTokenBalance,
100000,
this.getMaxPenalty(ops[0]));
}

function test_Success_TokenPaymaster_IndependentMode() external {
Expand All @@ -444,8 +457,10 @@ contract TestTokenPaymaster is TestBase {
testToken.approve(address(tokenPaymaster), testToken.balanceOf(address(ALICE_ACCOUNT)));
vm.stopPrank();

uint256 aliceBalanceBefore = testToken.balanceOf(address(ALICE_ACCOUNT));
uint256 tokenPaymasterBalanceBefore = testToken.balanceOf(address(tokenPaymaster));
uint256 initialBundlerBalance = BUNDLER.addr.balance;
uint256 initialPaymasterEpBalance = tokenPaymaster.getDeposit();
uint256 initialUserTokenBalance = testToken.balanceOf(address(ALICE_ACCOUNT));
uint256 initialPaymasterTokenBalance = testToken.balanceOf(address(tokenPaymaster));

PackedUserOperation memory userOp = buildUserOpWithCalldata(ALICE, "", address(VALIDATOR_MODULE));

Expand All @@ -472,7 +487,7 @@ contract TestTokenPaymaster is TestBase {

ENTRYPOINT.handleOps(ops, payable(BUNDLER.addr));

assertLt(testToken.balanceOf(address(ALICE_ACCOUNT)), aliceBalanceBefore);
assertGt(testToken.balanceOf(address(tokenPaymaster)), tokenPaymasterBalanceBefore);
assertLt(testToken.balanceOf(address(ALICE_ACCOUNT)), initialUserTokenBalance);
assertGt(testToken.balanceOf(address(tokenPaymaster)), initialPaymasterTokenBalance);
}
}

0 comments on commit b4250ea

Please sign in to comment.