Skip to content

Commit

Permalink
Also Move Testing Utilies To Local Bundler Package
Browse files Browse the repository at this point in the history
  • Loading branch information
nlordell committed Mar 8, 2024
1 parent c2b7f01 commit 4fd20e4
Show file tree
Hide file tree
Showing 21 changed files with 352 additions and 585 deletions.
2 changes: 1 addition & 1 deletion modules/4337/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@
},
"overrides": {
"@safe-global/safe-contracts": {
"ethers": "^6.11.0"
"ethers": "^6.11.1"
}
},
"dependencies": {
Expand Down
60 changes: 4 additions & 56 deletions modules/4337/src/utils/safe.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import { AddressLike, JsonRpcProvider, Provider, Signer, ethers } from 'ethers'
import { MultiProvider4337, RpcProvider } from '@safe-global/safe-4337-local-bundler'
import { Provider, Signer, ethers } from 'ethers'

// Import from Safe contracts repo once it is upgraded to ethers v6 and can be installed via npm
import { MetaTransaction, SafeSignature, SignedSafeTransaction, buildSignatureBytes } from './execution'
import { PackedUserOperation, UserOperation, EIP712_SAFE_OPERATION_TYPE, packGasParameters, unpackUserOperation } from './userOp'

export { MultiProvider4337 }

const AddressOne = '0x0000000000000000000000000000000000000001'

const INTERFACES = new ethers.Interface([
Expand Down Expand Up @@ -91,61 +94,6 @@ const actionCalldata = (action: MetaTransaction): string => {
return INTERFACES.encodeFunctionData('executeUserOp', [action.to, action.value, action.data, action.operation])
}

export interface RpcProvider extends Provider {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
send(method: string, params: unknown[]): Promise<any>
}

export class MultiProvider4337 extends JsonRpcProvider {
generalProvider: RpcProvider
constructor(aaProviderUrl: string, generalProvider: RpcProvider) {
super(aaProviderUrl)
this.generalProvider = generalProvider
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
send(method: string, params: unknown[]): Promise<any> {
if (
[
'eth_supportedEntryPoints',
'eth_estimateUserOperationGas',
'eth_sendUserOperation',
'eth_getUserOperationByHash',
'eth_getUserOperationReceipt',
].indexOf(method) >= 0
) {
return super.send(method, params)
} else {
return this.generalProvider.send(method, params)
}
}

public async sendUserOperation(userOp: UserOperation, entryPoint: AddressLike): Promise<string> {
const jsonUserOp = {
sender: ethers.getAddress(userOp.sender),
nonce: ethers.toBeHex(userOp.nonce),
callData: ethers.hexlify(userOp.callData),
callGasLimit: ethers.toBeHex(userOp.callGasLimit),
verificationGasLimit: ethers.toBeHex(userOp.verificationGasLimit),
preVerificationGas: ethers.toBeHex(userOp.preVerificationGas),
maxFeePerGas: ethers.toBeHex(userOp.maxFeePerGas),
maxPriorityFeePerGas: ethers.toBeHex(userOp.maxPriorityFeePerGas),
signature: ethers.hexlify(userOp.signature),
} as Record<string, unknown>
if (userOp.factory) {
jsonUserOp.factory = ethers.getAddress(userOp.factory)
jsonUserOp.factoryData = ethers.hexlify(userOp.factoryData!)
}
if (userOp.paymaster) {
jsonUserOp.paymaster = ethers.getAddress(userOp.paymaster)
jsonUserOp.paymasterVerificationGasLimit = ethers.toBeHex(userOp.paymasterVerificationGasLimit!)
jsonUserOp.paymasterPostOpGasLimit = ethers.toBeHex(userOp.paymasterPostOpGasLimit!)
jsonUserOp.paymasterData = ethers.hexlify(userOp.paymasterData!)
}
return await super.send('eth_sendUserOperation', [jsonUserOp, await ethers.resolveAddress(entryPoint, this)])
}
}

export class Safe4337Operation {
private safe: Safe4337
private action: MetaTransaction
Expand Down
21 changes: 2 additions & 19 deletions modules/4337/src/utils/userOp.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { UserOperation } from '@safe-global/safe-4337-local-bundler'
import { BigNumberish, BytesLike, Contract, Signer, ethers } from 'ethers'
import { PackedUserOperationStruct as PackedUserOperation } from '../../typechain-types/contracts/Safe4337Module'
import { SafeSignature } from './execution'

export { PackedUserOperation }
export { PackedUserOperation, UserOperation }

type OptionalExceptFor<T, TRequired extends keyof T = keyof T> = Partial<Pick<T, Exclude<keyof T, TRequired>>> &
Required<Pick<T, TRequired>>
Expand All @@ -15,24 +16,6 @@ export type SafeUserOperation = {
} & GasParameters &
Omit<PackedUserOperation, 'sender' | 'signature' | keyof PackedGasParameters>

export type UserOperation = {
sender: string
nonce: BigNumberish
factory?: string
factoryData?: BytesLike
callData: BytesLike
callGasLimit: BigNumberish
verificationGasLimit: BigNumberish
preVerificationGas: BigNumberish
maxFeePerGas: BigNumberish
maxPriorityFeePerGas: BigNumberish
paymaster?: string
paymasterVerificationGasLimit?: BigNumberish
paymasterPostOpGasLimit?: BigNumberish
paymasterData?: BytesLike
signature: BytesLike
}

export const EIP712_SAFE_OPERATION_TYPE = {
SafeOp: [
{ type: 'address', name: 'safe' },
Expand Down
4 changes: 2 additions & 2 deletions modules/4337/test/e2e/4337NestedSafe.spec.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { bundlerRpc, prepareAccounts, waitForUserOp } from '@safe-global/safe-4337-local-bundler'
import { expect } from 'chai'
import { deployments, ethers, network } from 'hardhat'
import {
Expand All @@ -20,7 +21,6 @@ import {
} from '../../src/utils/userOp'
import { chainId } from '../utils/encoding'
import { Safe4337 } from '../../src/utils/safe'
import { BUNDLER_MNEMONIC, bundlerRpc, prepareAccounts, waitForUserOp } from '../utils/e2e'
import { BigNumberish, Signer } from 'ethers'
import { assert } from 'console'

Expand Down Expand Up @@ -299,7 +299,7 @@ describe('Nested Safes With An Execution Initiated by a Leaf 4337 Safe [@4337]',

const setupTests = async () => {
const { SafeModuleSetup, EntryPoint, HariWillibaldToken, Safe4337Module, SafeL2, SafeProxyFactory } = await deployments.run()
const [user, user2, user3] = await prepareAccounts(BUNDLER_MNEMONIC, 3)
const [user, user2, user3] = await prepareAccounts({ count: 3 })
const bundler = bundlerRpc()

const entryPoint = new ethers.Contract(EntryPoint.address, EntryPoint.abi, ethers.provider)
Expand Down
2 changes: 1 addition & 1 deletion modules/4337/test/e2e/LocalBundler.spec.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { bundlerRpc, prepareAccounts, waitForUserOp } from '@safe-global/safe-4337-local-bundler'
import { expect } from 'chai'
import { deployments, ethers, network } from 'hardhat'
import { buildSignatureBytes } from '../../src/utils/execution'
import { buildRpcUserOperationFromSafeUserOperation, buildSafeUserOpTransaction, signSafeOp } from '../../src/utils/userOp'
import { chainId, timestamp } from '../utils/encoding'
import { Safe4337 } from '../../src/utils/safe'
import { bundlerRpc, prepareAccounts, waitForUserOp } from '../utils/e2e'

describe('Local Bundler [@4337]', () => {
before(function () {
Expand Down
3 changes: 2 additions & 1 deletion modules/4337/test/e2e/SingletonSigners.spec.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { bundlerRpc, prepareAccounts, waitForUserOp } from '@safe-global/safe-4337-local-bundler'
import { expect } from 'chai'
import { deployments, ethers, network } from 'hardhat'
import { buildSignatureBytes } from '../../src/utils/execution'
Expand All @@ -6,7 +7,7 @@ import {
buildRpcUserOperationFromSafeUserOperation,
buildSafeUserOpTransaction,
} from '../../src/utils/userOp'
import { bundlerRpc, encodeMultiSendTransactions, prepareAccounts, waitForUserOp } from '../utils/e2e'
import { encodeMultiSendTransactions } from '../utils/encoding'

describe('Singleton Signers [@4337]', () => {
before(function () {
Expand Down
2 changes: 1 addition & 1 deletion modules/4337/test/e2e/UniqueSigner.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { bundlerRpc, prepareAccounts, waitForUserOp } from '@safe-global/safe-4337-local-bundler'
import { expect } from 'chai'
import { deployments, ethers, network } from 'hardhat'
import { bundlerRpc, prepareAccounts, waitForUserOp } from '../utils/e2e'
import { chainId } from '../utils/encoding'
import { packGasParameters, unpackUserOperation } from '../../src/utils/userOp'

Expand Down
2 changes: 1 addition & 1 deletion modules/4337/test/e2e/WebAuthnSigner.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { bundlerRpc, prepareAccounts, waitForUserOp } from '@safe-global/safe-4337-local-bundler'
import { expect } from 'chai'
import { deployments, ethers, network } from 'hardhat'
import { bundlerRpc, prepareAccounts, waitForUserOp } from '../utils/e2e'
import { chainId } from '../utils/encoding'
import {
UserVerificationRequirement,
Expand Down
3 changes: 2 additions & 1 deletion modules/4337/test/e2e/WebAuthnSingletonSigner.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { bundlerRpc, prepareAccounts, waitForUserOp } from '@safe-global/safe-4337-local-bundler'
import { expect } from 'chai'
import { deployments, ethers, network } from 'hardhat'
import { bundlerRpc, encodeMultiSendTransactions, prepareAccounts, waitForUserOp } from '../utils/e2e'
import { encodeMultiSendTransactions } from '../utils/encoding'
import {
UserVerificationRequirement,
WebAuthnCredentials,
Expand Down
16 changes: 16 additions & 0 deletions modules/4337/test/utils/encoding.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { AddressLike, BigNumberish, BytesLike } from 'ethers'
import { ethers } from 'hardhat'

export const Erc20 = [
Expand Down Expand Up @@ -25,3 +26,18 @@ export const timestamp = async () => {
}
return block.timestamp
}

export interface MultiSendTransaction {
op: 0 | 1
to: AddressLike
value?: BigNumberish
data: BytesLike
}

export function encodeMultiSendTransactions(transactions: MultiSendTransaction[]) {
return ethers.concat(
transactions.map(({ op, to, value, data }) =>
ethers.solidityPacked(['uint8', 'address', 'uint256', 'uint256', 'bytes'], [op, to, value ?? 0, ethers.dataLength(data), data]),
),
)
}
2 changes: 1 addition & 1 deletion modules/passkey/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@
},
"overrides": {
"@safe-global/safe-contracts": {
"ethers": "^6.11.0"
"ethers": "^6.11.1"
}
},
"dependencies": {
Expand Down
9 changes: 5 additions & 4 deletions modules/passkey/test/4337/WebAuthnSigner.spec.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { expect } from 'chai'
import { deployments, ethers, network } from 'hardhat'
import { packGasParameters, unpackUserOperation } from '@safe-global/safe-4337/dist/src/utils/userOp'
import { bundlerRpc, prepareAccounts, waitForUserOp } from '../utils/e2e'
import { chainId } from '../utils/encoding'
import { bundlerRpc, prepareAccounts, waitForUserOp } from '@safe-global/safe-4337-local-bundler'
import { WebAuthnCredentials, decodePublicKey, encodeWebAuthnSignature } from '../utils/webauthn'

describe('WebAuthn Signers [@4337]', () => {
Expand Down Expand Up @@ -64,6 +63,8 @@ describe('WebAuthn Signers [@4337]', () => {
webAuthnVerifier,
SafeL2,
} = await setupTests()

const { chainId } = await ethers.provider.getNetwork()
const webAuthnVerifierAddress = await webAuthnVerifier.getAddress()

const credential = navigator.credentials.create({
Expand Down Expand Up @@ -95,7 +96,7 @@ describe('WebAuthn Signers [@4337]', () => {
fallbackHandler: module.target,
}
const safeInitHash = ethers.TypedDataEncoder.hash(
{ verifyingContract: await signerLaunchpad.getAddress(), chainId: await chainId() },
{ verifyingContract: await signerLaunchpad.getAddress(), chainId },
{
SafeInit: [
{ type: 'address', name: 'singleton' },
Expand Down Expand Up @@ -171,7 +172,7 @@ describe('WebAuthn Signers [@4337]', () => {
entryPoint: entryPoint.target,
}
const safeInitOpHash = ethers.TypedDataEncoder.hash(
{ verifyingContract: await signerLaunchpad.getAddress(), chainId: await chainId() },
{ verifyingContract: await signerLaunchpad.getAddress(), chainId },
{
SafeInitOp: [
{ type: 'bytes32', name: 'userOpHash' },
Expand Down
56 changes: 0 additions & 56 deletions modules/passkey/test/utils/e2e.ts

This file was deleted.

5 changes: 0 additions & 5 deletions modules/passkey/test/utils/encoding.ts

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
import { expect } from 'chai'
import CBOR from 'cbor'
import { ethers } from 'ethers'
import { WebAuthnCredentials, base64UrlEncode } from './utils/webauthn'
import { WebAuthnCredentials, base64UrlEncode } from '../utils/webauthn'

describe('WebAuthn Shim', () => {
const navigator = {
Expand Down
Loading

0 comments on commit 4fd20e4

Please sign in to comment.