Skip to content

Commit

Permalink
negative tests mentioned in threat model + bundler integration test
Browse files Browse the repository at this point in the history
  • Loading branch information
livingrockrises committed Nov 20, 2023
1 parent 402a442 commit c7dc1df
Show file tree
Hide file tree
Showing 4 changed files with 301 additions and 34 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,10 @@ import {
} from "@biconomy-devx/account-contracts-v2/dist/types";
import { arrayify, hexConcat, parseEther } from "ethers/lib/utils";
import { BigNumber, BigNumberish, Contract, Signer } from "ethers";
import { BundlerTestEnvironment } from "../environment/bundlerEnvironment";
import {
BundlerTestEnvironment,
UserOperationSubmissionError,
} from "../environment/bundlerEnvironment";

export const AddressZero = ethers.constants.AddressZero;

Expand Down Expand Up @@ -205,7 +208,7 @@ describe("EntryPoint with VerifyingPaymaster Singleton", function () {
const userOp1 = await fillAndSign(
{
sender: walletAddress,
verificationGasLimit: 50000, // for positive case 200k
verificationGasLimit: 200000, // for positive case 200k
preVerificationGas: 55000, // min expected by bundler is 46k
},
walletOwner,
Expand Down Expand Up @@ -269,5 +272,171 @@ describe("EntryPoint with VerifyingPaymaster Singleton", function () {
);
expect(feeCollectorBalanceAfter).to.be.greaterThan(BigNumber.from(0));
});

it("fails if verificationGasLimit is not enough", async () => {
const feeCollectorBalanceBefore =
await verifyingSingletonPaymaster.getBalance(
await feeCollector.getAddress()
);
expect(feeCollectorBalanceBefore).to.be.equal(BigNumber.from(0));
const signer = await verifyingSingletonPaymaster.verifyingSigner();
const offchainSignerAddress = await offchainSigner.getAddress();
expect(signer).to.be.equal(offchainSignerAddress);

await verifyingSingletonPaymaster.depositFor(
await offchainSigner.getAddress(),
{ value: ethers.utils.parseEther("1") }
);
const userOp1 = await fillAndSign(
{
sender: walletAddress,
verificationGasLimit: 50000, // for positive case 200k
preVerificationGas: 55000, // min expected by bundler is 46k
},
walletOwner,
entryPoint,
"nonce"
);

const hash = await verifyingSingletonPaymaster.getHash(
userOp1,
await offchainSigner.getAddress(),
MOCK_VALID_UNTIL,
MOCK_VALID_AFTER,
dynamicMarkup
);
const sig = await offchainSigner.signMessage(arrayify(hash));
const userOp = await fillAndSign(
{
...userOp1,
paymasterAndData: hexConcat([
paymasterAddress,
ethers.utils.defaultAbiCoder.encode(
["address", "uint48", "uint48", "uint32"],
[
await offchainSigner.getAddress(),
MOCK_VALID_UNTIL,
MOCK_VALID_AFTER,
dynamicMarkup,
]
),
sig,
]),
},
walletOwner,
entryPoint,
"nonce"
);

const signatureWithModuleAddress = ethers.utils.defaultAbiCoder.encode(
["bytes", "address"],
[userOp.signature, ecdsaModule.address]
);
userOp.signature = signatureWithModuleAddress;

console.log("userop VGL ", userOp.verificationGasLimit.toString());
console.log("userop PVG ", userOp.preVerificationGas.toString());

let thrownError: Error | null = null;

try {
await environment.sendUserOperation(userOp, entryPoint.address);
} catch (e) {
thrownError = e as Error;
}

const expectedError = new UserOperationSubmissionError(
'{"message":"account validation failed: AA40 over verificationGasLimit'
);

expect(thrownError).to.contain(expectedError);

await expect(
entryPoint.handleOps([userOp], await offchainSigner.getAddress())
).to.be.reverted;
});

it("fails if preVerificationGas is not enough", async () => {
const feeCollectorBalanceBefore =
await verifyingSingletonPaymaster.getBalance(
await feeCollector.getAddress()
);
expect(feeCollectorBalanceBefore).to.be.equal(BigNumber.from(0));
const signer = await verifyingSingletonPaymaster.verifyingSigner();
const offchainSignerAddress = await offchainSigner.getAddress();
expect(signer).to.be.equal(offchainSignerAddress);

await verifyingSingletonPaymaster.depositFor(
await offchainSigner.getAddress(),
{ value: ethers.utils.parseEther("1") }
);
const userOp1 = await fillAndSign(
{
sender: walletAddress,
verificationGasLimit: 200000, // for positive case 200k
// preVerificationGas: 55000, // min expected by bundler is 46k
},
walletOwner,
entryPoint,
"nonce"
);

const hash = await verifyingSingletonPaymaster.getHash(
userOp1,
await offchainSigner.getAddress(),
MOCK_VALID_UNTIL,
MOCK_VALID_AFTER,
dynamicMarkup
);
const sig = await offchainSigner.signMessage(arrayify(hash));
const userOp = await fillAndSign(
{
...userOp1,
paymasterAndData: hexConcat([
paymasterAddress,
ethers.utils.defaultAbiCoder.encode(
["address", "uint48", "uint48", "uint32"],
[
await offchainSigner.getAddress(),
MOCK_VALID_UNTIL,
MOCK_VALID_AFTER,
dynamicMarkup,
]
),
sig,
]),
},
walletOwner,
entryPoint,
"nonce"
);

const signatureWithModuleAddress = ethers.utils.defaultAbiCoder.encode(
["bytes", "address"],
[userOp.signature, ecdsaModule.address]
);
userOp.signature = signatureWithModuleAddress;

console.log("userop VGL ", userOp.verificationGasLimit.toString());
console.log("userop PVG ", userOp.preVerificationGas.toString());

let thrownError: Error | null = null;

try {
await environment.sendUserOperation(userOp, entryPoint.address);
} catch (e) {
thrownError = e as Error;
}

const expectedError = new UserOperationSubmissionError(
'{"message":"preVerificationGas too low: expected at least 45916'
);

expect(thrownError).to.contain(expectedError);

// await expect(
// entryPoint.handleOps([userOp], await offchainSigner.getAddress())
// ).to.be.reverted;
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,9 @@ describe("Biconomy Token Paymaster (with Bundler)", function () {
.connect(deployer)
.transfer(walletAddress, ethers.utils.parseEther("100"));

const accountBalBefore = await token.balanceOf(walletAddress);
const feeReceiverBalBefore = await token.balanceOf(paymasterAddress);

const userOp1 = await fillAndSign(
{
sender: walletAddress,
Expand Down Expand Up @@ -294,8 +297,14 @@ describe("Biconomy Token Paymaster (with Bundler)", function () {

await environment.sendUserOperation(userOp, entryPoint.address);

const ev = await getUserOpEvent(entryPoint);
expect(ev.args.success).to.be.true;
// const ev = await getUserOpEvent(entryPoint);
// expect(ev.args.success).to.be.true;

const accountBalAfter = await token.balanceOf(walletAddress);
const feeReceiverBalAfter = await token.balanceOf(paymasterAddress);

expect(accountBalAfter).to.be.lt(accountBalBefore);
expect(feeReceiverBalAfter).to.be.gt(feeReceiverBalBefore);

await expect(
entryPoint.handleOps([userOp], await offchainSigner.getAddress())
Expand Down
19 changes: 16 additions & 3 deletions test/bundler-integration/token-paymaster/btpm-undeployed-wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,9 @@ describe("Biconomy Token Paymaster (with Bundler)", function () {
.connect(deployer)
.transfer(walletAddress, ethers.utils.parseEther("100"));

const accountBalBefore = await token.balanceOf(walletAddress);
const feeReceiverBalBefore = await token.balanceOf(paymasterAddress);

const owner = await walletOwner.getAddress();
const AccountFactory = await ethers.getContractFactory(
"SmartAccountFactory"
Expand Down Expand Up @@ -306,10 +309,20 @@ describe("Biconomy Token Paymaster (with Bundler)", function () {

userOp.signature = signatureWithModuleAddress;

await environment.sendUserOperation(userOp, entryPoint.address);
const response = await environment.sendUserOperation(
userOp,
entryPoint.address
);
console.log("response", response);

// const ev = await getUserOpEvent(entryPoint);
// expect(ev.args.success).to.be.true;

const accountBalAfter = await token.balanceOf(walletAddress);
const feeReceiverBalAfter = await token.balanceOf(paymasterAddress);

const ev = await getUserOpEvent(entryPoint);
expect(ev.args.success).to.be.true;
expect(accountBalAfter).to.be.lt(accountBalBefore);
expect(feeReceiverBalAfter).to.be.gt(feeReceiverBalBefore);

await expect(
entryPoint.handleOps([userOp], await offchainSigner.getAddress())
Expand Down
130 changes: 103 additions & 27 deletions test/sponsorship-paymaster/biconomy-verifying-paymaster-v2-specs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,33 +161,7 @@ describe("EntryPoint with VerifyingPaymaster Singleton", function () {
);
}

describe("#validatePaymasterUserOp", () => {
it("Should parse data properly", async () => {
const paymasterAndData = hexConcat([
paymasterAddress,
ethers.utils.defaultAbiCoder.encode(
["address", "uint48", "uint48", "uint32"],
[
await offchainSigner.getAddress(),
MOCK_VALID_UNTIL,
MOCK_VALID_AFTER,
dynamicMarkup,
]
),
"0x" + "00".repeat(65),
]);

const res = await verifyingSingletonPaymaster.parsePaymasterAndData(
paymasterAndData
);

expect(res.paymasterId).to.equal(await offchainSigner.getAddress());
expect(res.validUntil).to.equal(ethers.BigNumber.from(MOCK_VALID_UNTIL));
expect(res.validAfter).to.equal(ethers.BigNumber.from(MOCK_VALID_AFTER));
expect(res.priceMarkup).to.equal(dynamicMarkup);
expect(res.signature).to.equal("0x" + "00".repeat(65));
});

describe("Verifying paymaster basic positive tests", () => {
it("Should Fail when there is no deposit for paymaster id", async () => {
const paymasterId = await depositorSigner.getAddress();
console.log("paymaster Id ", paymasterId);
Expand Down Expand Up @@ -271,4 +245,106 @@ describe("EntryPoint with VerifyingPaymaster Singleton", function () {
expect(feeCollectorBalanceAfter).to.be.greaterThan(BigNumber.from(0));
});
});

describe("Verifying Paymaster - read methods and state checks", () => {
it("Should parse data properly", async () => {
const paymasterAndData = hexConcat([
paymasterAddress,
ethers.utils.defaultAbiCoder.encode(
["address", "uint48", "uint48", "uint32"],
[
await offchainSigner.getAddress(),
MOCK_VALID_UNTIL,
MOCK_VALID_AFTER,
dynamicMarkup,
]
),
"0x" + "00".repeat(65),
]);

const res = await verifyingSingletonPaymaster.parsePaymasterAndData(
paymasterAndData
);

expect(res.paymasterId).to.equal(await offchainSigner.getAddress());
expect(res.validUntil).to.equal(ethers.BigNumber.from(MOCK_VALID_UNTIL));
expect(res.validAfter).to.equal(ethers.BigNumber.from(MOCK_VALID_AFTER));
expect(res.priceMarkup).to.equal(dynamicMarkup);
expect(res.signature).to.equal("0x" + "00".repeat(65));
});

it("Invalid paymasterAndData causes revert", async () => {
const paymasterAndData =
"0x9c145aed00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000124468721a7000000000000000000000000831153c6b9537d0ff5b7db830c2749de3042e77600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000649b890b7000000000000000000000000071bc333f52a7971dde5de5b6c1ab0e7341e9724c00000000000000000000000021a6f9fa7246de45762e6f9db1f55e3c0f8566db00000000000000000000000000000000000000000000000000000000000000";

await expect(
verifyingSingletonPaymaster.parsePaymasterAndData(paymasterAndData)
).to.be.reverted;
});

it("should check the correct states set on the paymaster", async () => {
const owner = await verifyingSingletonPaymaster.owner();

const verifyingSigner =
await verifyingSingletonPaymaster.verifyingSigner();

const feeReceiver = await verifyingSingletonPaymaster.feeCollector();

expect(owner).to.be.equal(deployer.address);
expect(verifyingSigner).to.be.equal(offchainSigner.address);
expect(feeReceiver).to.be.equal(feeCollector.address);
});
});

describe("Verifying Paymaster - deposit and withdraw tests", () => {
it("Deposits into the specified address", async () => {
const paymasterId = await depositorSigner.getAddress();

await verifyingSingletonPaymaster.depositFor(paymasterId, {
value: parseEther("1"),
});

const balance = await verifyingSingletonPaymaster.getBalance(paymasterId);
expect(balance).to.be.equal(parseEther("1"));
});

it("Does not allow 0 value deposits", async () => {
const paymasterId = await depositorSigner.getAddress();

await expect(
verifyingSingletonPaymaster.depositFor(paymasterId, {
value: parseEther("0"),
})
).to.be.revertedWithCustomError(
verifyingSingletonPaymaster,
"DepositCanNotBeZero"
);
});

it("Does not allow deposits for 0 address paymasterId", async () => {
const paymasterId = ethers.constants.AddressZero;

await expect(
verifyingSingletonPaymaster.depositFor(paymasterId, {
value: parseEther("0.5"),
})
).to.be.revertedWithCustomError(
verifyingSingletonPaymaster,
"PaymasterIdCannotBeZero"
);
});

it("Reverts when paymasterIdBalance is not enough", async () => {
const paymasterId = await depositorSigner.getAddress();

await expect(
verifyingSingletonPaymaster
.connect(depositorSigner)
.withdrawTo(paymasterId, parseEther("1.1"))
).to.be.revertedWithCustomError(
verifyingSingletonPaymaster,
"InsufficientBalance"
);
});
});
});

0 comments on commit c7dc1df

Please sign in to comment.