-
Notifications
You must be signed in to change notification settings - Fork 20
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
f71ecb5
commit 5f4b2ed
Showing
1 changed file
with
273 additions
and
0 deletions.
There are no files selected for viewing
273 changes: 273 additions & 0 deletions
273
test/bundler-integration/sponsorship-paymaster/biconomy-verifying-paymaster-v2-specs.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,273 @@ | ||
/* eslint-disable node/no-missing-import */ | ||
/* eslint-disable camelcase */ | ||
import { expect } from "chai"; | ||
import { ethers } from "hardhat"; | ||
|
||
import { | ||
BiconomyAccountImplementation, | ||
BiconomyAccountImplementation__factory, | ||
BiconomyAccountFactory, | ||
BiconomyAccountFactory__factory, | ||
VerifyingSingletonPaymasterV2, | ||
VerifyingSingletonPaymasterV2__factory, | ||
} from "../../../typechain-types"; | ||
import { fillAndSign } from "../../utils/userOp"; | ||
import { UserOperation } from "../../../lib/account-abstraction/test/UserOperation"; | ||
import { | ||
createAccount, | ||
simulationResultCatch, | ||
} from "../../../lib/account-abstraction/test/testutils"; | ||
import { | ||
EntryPoint, | ||
EntryPoint__factory, | ||
SimpleAccount, | ||
TestToken, | ||
TestToken__factory, | ||
} from "../../../lib/account-abstraction/typechain"; | ||
import { | ||
EcdsaOwnershipRegistryModule, | ||
EcdsaOwnershipRegistryModule__factory, | ||
} 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"; | ||
|
||
export const AddressZero = ethers.constants.AddressZero; | ||
|
||
const MOCK_VALID_UNTIL = "0x00000000deadbeef"; | ||
const MOCK_VALID_AFTER = "0x0000000000001234"; | ||
const dynamicMarkup = 1200000; // or 0 or 1100000 | ||
|
||
export async function deployEntryPoint( | ||
provider = ethers.provider | ||
): Promise<EntryPoint> { | ||
const epf = await (await ethers.getContractFactory("EntryPoint")).deploy(); | ||
return EntryPoint__factory.connect(epf.address, provider.getSigner()); | ||
} | ||
|
||
export async function getUserOpEvent(ep: EntryPoint) { | ||
const [log] = await ep.queryFilter( | ||
ep.filters.UserOperationEvent(), | ||
await ethers.provider.getBlockNumber() | ||
); | ||
return log; | ||
} | ||
|
||
describe("EntryPoint with VerifyingPaymaster Singleton", function () { | ||
let entryPoint: EntryPoint; | ||
let entryPointStatic: EntryPoint; | ||
let depositorSigner: Signer; | ||
let walletOwner: Signer; | ||
let walletAddress: string, paymasterAddress: string; | ||
let ethersSigner; | ||
|
||
let offchainSigner: Signer, deployer: Signer, feeCollector: Signer; | ||
|
||
let verifyingSingletonPaymaster: VerifyingSingletonPaymasterV2; | ||
let smartWalletImp: BiconomyAccountImplementation; | ||
let ecdsaModule: EcdsaOwnershipRegistryModule; | ||
let walletFactory: BiconomyAccountFactory; | ||
const abi = ethers.utils.defaultAbiCoder; | ||
|
||
let environment: BundlerTestEnvironment; | ||
|
||
beforeEach(async function () { | ||
// Setup the Bundler Environment | ||
const chainId = (await ethers.provider.getNetwork()).chainId; | ||
if (chainId !== BundlerTestEnvironment.BUNDLER_ENVIRONMENT_CHAIN_ID) { | ||
this.skip(); | ||
} | ||
environment = await BundlerTestEnvironment.getDefaultInstance(); | ||
|
||
ethersSigner = await ethers.getSigners(); | ||
entryPoint = EntryPoint__factory.connect(process.env.ENTRYPOINT!, deployer); | ||
entryPointStatic = entryPoint.connect(AddressZero); | ||
|
||
deployer = ethersSigner[0]; | ||
offchainSigner = ethersSigner[1]; | ||
depositorSigner = ethersSigner[2]; | ||
feeCollector = ethersSigner[3]; | ||
walletOwner = deployer; // ethersSigner[3]; | ||
|
||
const offchainSignerAddress = await offchainSigner.getAddress(); | ||
const walletOwnerAddress = await walletOwner.getAddress(); | ||
const feeCollectorAddress = await feeCollector.getAddress(); | ||
|
||
ecdsaModule = await new EcdsaOwnershipRegistryModule__factory( | ||
deployer | ||
).deploy(); | ||
|
||
verifyingSingletonPaymaster = | ||
await new VerifyingSingletonPaymasterV2__factory(deployer).deploy( | ||
await deployer.getAddress(), | ||
entryPoint.address, | ||
offchainSignerAddress, | ||
feeCollectorAddress | ||
); | ||
|
||
smartWalletImp = await new BiconomyAccountImplementation__factory( | ||
deployer | ||
).deploy(entryPoint.address); | ||
|
||
walletFactory = await new BiconomyAccountFactory__factory(deployer).deploy( | ||
smartWalletImp.address, | ||
walletOwnerAddress | ||
); | ||
|
||
await walletFactory | ||
.connect(deployer) | ||
.addStake(entryPoint.address, 86400, { value: parseEther("2") }); | ||
|
||
const ecdsaOwnershipSetupData = ecdsaModule.interface.encodeFunctionData( | ||
"initForSmartAccount", | ||
[walletOwnerAddress] | ||
); | ||
|
||
const smartAccountDeploymentIndex = 0; | ||
|
||
await walletFactory.deployCounterFactualAccount( | ||
ecdsaModule.address, | ||
ecdsaOwnershipSetupData, | ||
smartAccountDeploymentIndex | ||
); | ||
const expected = await walletFactory.getAddressForCounterFactualAccount( | ||
ecdsaModule.address, | ||
ecdsaOwnershipSetupData, | ||
smartAccountDeploymentIndex | ||
); | ||
|
||
walletAddress = expected; | ||
console.log(" wallet address ", walletAddress); | ||
|
||
paymasterAddress = verifyingSingletonPaymaster.address; | ||
console.log("Paymaster address is ", paymasterAddress); | ||
|
||
await entryPoint | ||
.connect(deployer) | ||
.depositTo(paymasterAddress, { value: parseEther("1") }); | ||
|
||
await verifyingSingletonPaymaster.connect(deployer).addStake(86400, { | ||
value: parseEther("10"), | ||
}); | ||
}); | ||
|
||
after(async function () { | ||
const chainId = (await ethers.provider.getNetwork()).chainId; | ||
if (chainId === BundlerTestEnvironment.BUNDLER_ENVIRONMENT_CHAIN_ID) { | ||
await Promise.all([ | ||
environment.revert(environment.defaultSnapshot!), | ||
environment.resetBundler(), | ||
]); | ||
} | ||
}); | ||
|
||
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)); | ||
}); | ||
|
||
it("succeed with valid signature", 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()); | ||
|
||
await environment.sendUserOperation(userOp, entryPoint.address); | ||
|
||
const ev = await getUserOpEvent(entryPoint); | ||
|
||
// eslint-disable-next-line no-unused-expressions | ||
expect(ev.args.success).to.be.true; | ||
|
||
await expect( | ||
entryPoint.handleOps([userOp], await offchainSigner.getAddress()) | ||
).to.be.reverted; | ||
|
||
const feeCollectorBalanceAfter = | ||
await verifyingSingletonPaymaster.getBalance( | ||
await feeCollector.getAddress() | ||
); | ||
expect(feeCollectorBalanceAfter).to.be.greaterThan(BigNumber.from(0)); | ||
}); | ||
}); | ||
}); |