Skip to content

Commit

Permalink
Added Paymaster, fixed verifier
Browse files Browse the repository at this point in the history
  • Loading branch information
KyrylR committed Nov 2, 2024
1 parent 5d892b6 commit 3fae2c3
Show file tree
Hide file tree
Showing 7 changed files with 79 additions and 14 deletions.
12 changes: 9 additions & 3 deletions DEPLOYMENTS.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
# Deployments

## Sepolia

Entry Point: 0x0000000071727De22E5E9d8BAf0edAc6f37da032
Smart Account Factory: 0xeF0aB9F24f826657E11B6b4E3a682ee46254A22C
Paymaster: 0xe8CCF8dd49e297C357FDc1f84f9A6E2FED83C426

## Q Testnet

Entry Point: 0x820692eaD6ba469d76c6c443FA97fC8B5bef309A
Smart Account Factory: 0x76C9b5c8Bc736e58F5b54BA721571c77059CAa68
Some Token: 0x07ECE004fF33ce444f82F8d538A5687849Df67AC
Entry Point: 0xF813bB572bcBC95eB004958064F986f584A1572D
Smart Account Factory: 0xEdd83559B519e85bC4294B5435EfD64E9a37d9C2
Some Token: 0x4881139AEd3b3eA32bf43780fCD85Dc63dF64421

## Q Mainnet

Expand Down
27 changes: 27 additions & 0 deletions contracts/Paymaster.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {BasePaymaster} from "@account-abstraction/contracts/core/BasePaymaster.sol";
import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import {IEntryPoint} from "@account-abstraction/contracts/interfaces/IEntryPoint.sol";
import {PackedUserOperation} from "@account-abstraction/contracts/interfaces/PackedUserOperation.sol";

import {SIG_VALIDATION_SUCCESS} from "@account-abstraction/contracts/core/Helpers.sol";
import {SetHelper} from "@solarity/solidity-lib/libs/arrays/SetHelper.sol";

contract Paymaster is BasePaymaster {
using EnumerableSet for EnumerableSet.AddressSet;
using SetHelper for EnumerableSet.AddressSet;

constructor(IEntryPoint entryPoint_) BasePaymaster(entryPoint_) {}

function _validatePaymasterUserOp(
PackedUserOperation calldata,
bytes32,
uint256
) internal view override returns (bytes memory context, uint256 validationData) {
context = new bytes(0);

validationData = SIG_VALIDATION_SUCCESS;
}
}
4 changes: 4 additions & 0 deletions contracts/SmartAccount.sol
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,10 @@ contract SmartAccount is IAccount, Initializable, UUPSUpgradeable, ERC1155Holder
}
}

function getTransactionHistoryLength() external view returns (uint256) {
return history.length;
}

function _validateSignature(
PackedUserOperation calldata userOp,
bytes32 userOpHash
Expand Down
6 changes: 3 additions & 3 deletions contracts/verifiers/IdentityAuthVerifier.sol
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ contract IdentityAuthVerifier {
mstore(add(pointer_, 32), y_)
mstore(add(pointer_, 64), s_)

res_ := staticcall(sub(gas(), 2000), 7, pointer_, 96, pointer_, 64) // ecMul
res_ := staticcall(1000000, 7, pointer_, 96, pointer_, 64) // ecMul
res_ := and(res_, gt(returndatasize(), 0)) // check that multiplication succeeded

if iszero(res_) {
Expand All @@ -84,7 +84,7 @@ contract IdentityAuthVerifier {
mstore(add(pointer_, 64), mload(pR_))
mstore(add(pointer_, 96), mload(add(pR_, 32)))

res_ := staticcall(sub(gas(), 2000), 6, pointer_, 128, pR_, 64) // ecAdd
res_ := staticcall(1000000, 6, pointer_, 128, pR_, 64) // ecAdd
res_ := and(res_, gt(returndatasize(), 0)) // check that addition succeeded
}

Expand Down Expand Up @@ -145,7 +145,7 @@ contract IdentityAuthVerifier {
mstore(add(pPairing_, 704), DELTA_Y1)
mstore(add(pPairing_, 736), DELTA_Y2)

res_ := staticcall(sub(gas(), 2000), 8, pPairing_, 768, pPairing_, 32) // ecPairing
res_ := staticcall(1000000, 8, pPairing_, 768, pPairing_, 32) // ecPairing
res_ := and(res_, mload(pPairing_)) // check that pairing succeeded
}

Expand Down
12 changes: 11 additions & 1 deletion deploy-local/2_account.factory.migration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ import {
EntryPoint__factory,
ERC1967Proxy__factory,
IdentityAuthVerifier__factory,
Paymaster__factory,
SmartAccount__factory,
SmartAccountFactory__factory,
} from "@ethers-v6";
import { ethers } from "ethers";

export = async (deployer: Deployer) => {
const entryPoint = await deployer.deployed(EntryPoint__factory);
Expand All @@ -26,5 +28,13 @@ export = async (deployer: Deployer) => {

await accountFactory.__SmartAccountFactory_init(await accountImplementation.getAddress());

Reporter.reportContracts(["SmartAccountFactory", await accountFactory.getAddress()]);
let paymaster = await deployer.deploy(Paymaster__factory, [await entryPoint.getAddress()]);

await paymaster.deposit({ value: ethers.parseEther("2") });

Reporter.reportContracts(
["SmartAccountFactory", await accountFactory.getAddress()],
["EntryPoint", await entryPoint.getAddress()],
["Paymaster", await paymaster.getAddress()],
);
};
17 changes: 13 additions & 4 deletions test/SmartAccount.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { SignerWithAddress } from "@nomicfoundation/hardhat-ethers/signers";
import { Reverter } from "@test-helpers";
import { deployAA, deployEntryPoint } from "@deployment";

import { EntryPoint, SmartAccount, SmartAccount__factory, SmartAccountFactory } from "@ethers-v6";
import { EntryPoint, Paymaster, SmartAccount, SmartAccount__factory, SmartAccountFactory } from "@ethers-v6";
import {
executeViaEntryPoint,
getDefaultPackedUserOperation,
Expand All @@ -33,6 +33,7 @@ describe("SmartAccount", () => {
let accountFactory: SmartAccountFactory;

let account: SmartAccount;
let paymaster: Paymaster;

let identityAuth: IdentityAuth;

Expand All @@ -59,7 +60,13 @@ describe("SmartAccount", () => {
await accountFactory.deploySmartAccount(accountNullifier);
account = await ethers.getContractAt("SmartAccount", await accountFactory.getSmartAccount(accountNullifier));

const Paymaster = await ethers.getContractFactory("Paymaster");
paymaster = await Paymaster.deploy(await entryPoint.getAddress());

await setBalance(await account.getAddress(), ethers.parseEther("20"));
await setBalance(await paymaster.getAddress(), ethers.parseEther("20"));

await paymaster.deposit({ value: ethers.parseEther("20") });

await reverter.snapshot();
});
Expand Down Expand Up @@ -122,6 +129,7 @@ describe("SmartAccount", () => {
await executeViaEntryPoint(
entryPoint,
account,
paymaster,
privateKey,
EVENT_ID,
await accountFactory.getAddress(),
Expand Down Expand Up @@ -172,6 +180,7 @@ describe("SmartAccount", () => {
executeViaEntryPoint(
entryPoint,
account,
paymaster,
secondPrivateKey,
EVENT_ID,
await accountFactory.getAddress(),
Expand Down Expand Up @@ -215,7 +224,7 @@ describe("SmartAccount", () => {
});

it("should revert if nonce is not valid", async () => {
const nonSignedOp = await getDefaultPackedUserOperation(account);
const nonSignedOp = await getDefaultPackedUserOperation(account, paymaster);
nonSignedOp.nonce = ethers.MaxUint256 - 2n;
const signedOp = await getSignedPackedUserOperation(entryPoint, privateKey, EVENT_ID, nonSignedOp);

Expand All @@ -225,7 +234,7 @@ describe("SmartAccount", () => {
});

it("should revert if trying to validate User Operation not from entrypoint", async () => {
const nonSignedOp = await getDefaultPackedUserOperation(account);
const nonSignedOp = await getDefaultPackedUserOperation(account, paymaster);

await expect(account.validateUserOp(nonSignedOp, ethers.ZeroHash, 0n))
.to.be.revertedWithCustomError(account, "NotFromEntryPoint")
Expand All @@ -237,7 +246,7 @@ describe("SmartAccount", () => {
const entryPointAsSigner = await ethers.provider.getSigner(await entryPoint.getAddress());
await setBalance(await entryPoint.getAddress(), ethers.parseEther("20"));

const nonSignedOp = await getDefaultPackedUserOperation(account);
const nonSignedOp = await getDefaultPackedUserOperation(account, paymaster);
const signedOp = await getSignedPackedUserOperation(entryPoint, privateKey, EVENT_ID, nonSignedOp);
const hashOp = await entryPoint.getUserOpHash(signedOp);

Expand Down
15 changes: 12 additions & 3 deletions test/helpers/aa-helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { setBalance } from "@nomicfoundation/hardhat-network-helpers";

import { encodeIdentityProof, signRawPoseidon } from "@scripts";

import { EntryPoint, SmartAccount, SmartAccount__factory, SmartAccountFactory } from "@ethers-v6";
import { EntryPoint, Paymaster, SmartAccount, SmartAccount__factory, SmartAccountFactory } from "@ethers-v6";
import { PackedUserOperationStruct } from "@/generated-types/ethers/@account-abstraction/contracts/core/EntryPoint";

export async function getSignature(signerPk: bigint, eventId: bigint, messageHash: string) {
Expand Down Expand Up @@ -37,6 +37,7 @@ export async function getSignature(signerPk: bigint, eventId: bigint, messageHas
export async function executeViaEntryPoint(
entryPoint: EntryPoint,
account: SmartAccount,
paymaster: Paymaster,
signerPk: bigint,
eventId: bigint,
destination: string,
Expand All @@ -47,7 +48,7 @@ export async function executeViaEntryPoint(
await setBalance(await account.getAddress(), ethers.parseEther("20"));
}

const userOp = await getDefaultPackedUserOperation(account);
const userOp = await getDefaultPackedUserOperation(account, paymaster);
userOp.callData = SmartAccount__factory.createInterface().encodeFunctionData("execute(address,uint256,bytes)", [
destination,
value,
Expand Down Expand Up @@ -90,15 +91,23 @@ export async function getEmptyPackedUserOperation() {
};
}

export async function getDefaultPackedUserOperation(account: SmartAccount) {
export async function getDefaultPackedUserOperation(account: SmartAccount, paymaster: Paymaster) {
const emptyUserOp = await getEmptyPackedUserOperation();

emptyUserOp.sender = await account.getAddress();
emptyUserOp.nonce = await account.getCurrentNonce();
emptyUserOp.paymasterAndData = getPaymasterAndData(await paymaster.getAddress());

return emptyUserOp;
}

export function getPaymasterAndData(paymaster: string): string {
return (
ethers.zeroPadBytes(paymaster + ethers.toBeHex("0x10000", 16).slice(2, 32), 36) +
ethers.zeroPadValue("0x1000", 16).slice(2)
);
}

export async function getSignedPackedUserOperation(
entryPoint: EntryPoint,
signerPk: bigint,
Expand Down

0 comments on commit 3fae2c3

Please sign in to comment.