Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes for v0.7 EntryPoint #37

Merged
merged 9 commits into from
Dec 13, 2024
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@bobanetwork/snap-account-abstraction-keyring-monorepo",
"version": "1.1.12",
"version": "1.1.13",
"private": true,
"description": "An account abstraction keyring snap that integrates with MetaMask accounts on Boba Network",
"keywords": [
Expand Down
2 changes: 1 addition & 1 deletion packages/site/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@bobanetwork/snap-account-abstraction-keyring-site",
"version": "1.1.12",
"version": "1.1.13",
"private": true,
"license": "(MIT-0 OR Apache-2.0)",
"scripts": {
Expand Down
2 changes: 1 addition & 1 deletion packages/site/src/config/snap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@
export const defaultSnapOrigin =
process.env.GATSBY_SNAP_ORIGIN ?? `local:http://localhost:8080`;

export const isLocal = defaultSnapOrigin.includes('local');
export const isLocalNetwork = process.env.USE_LOCAL_NETWORK === 'true';
11 changes: 5 additions & 6 deletions packages/site/src/pages/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -88,10 +88,8 @@ const Index = () => {
>();

const [transferToken, setTransferToken] = useState<string | null>('Boba');
const [targetAccount, setTargetAccount] = useState<string | null>(
'0xcF044AB1e5b55203dC258F47756daFb7F8F01760',
);
const [transferAmount, setTransferAmount] = useState<string>('0.01');
const [targetAccount, setTargetAccount] = useState<string | null>('');
const [transferAmount, setTransferAmount] = useState<string>('');

const [selectedAccount, setSelectedAccount] = useState<KeyringAccount>();
const [isLoading, setIsLoading] = useState<boolean>(false);
Expand Down Expand Up @@ -403,7 +401,7 @@ const Index = () => {

const sendBobaTx = async () => {
if (!snapState?.accounts || !selectedAccount) {
return false;
throw new Error('Source account not connected');
}

// Paymaster Setup steps (only first time or when required)
Expand Down Expand Up @@ -626,7 +624,8 @@ const Index = () => {
callback: async () => await sendBobaTx(),
label: 'Transfer',
},
successMessage: 'Funds transfer successful!',
successMessage: 'Funds transfer operation submitted',
failureMessage: 'Funds transfer operation failed',
},
];

Expand Down
10 changes: 5 additions & 5 deletions packages/site/src/utils/snap.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import snapPackageInfo from '../../../snap/package.json';
import { defaultSnapOrigin } from '../config';
import { isLocal } from '../config/snap';
import { isLocalNetwork } from '../config/snap';
import type { GetSnapsResponse, Snap } from '../types';

/**
Expand Down Expand Up @@ -29,9 +29,9 @@ export const connectSnap = async (
// check for current connected chain and force user to switch to boba sepolia.
const currentChain = window.ethereum.networkVersion;
// 901 = local boba network
const desiredChain: string = isLocal ? '901' : '28882';
const desiredChain: string = isLocalNetwork ? '901' : '28882';
if (currentChain !== desiredChain) {
const hexDesiredChain = isLocal ? '0x385' : '0x70d2';
const hexDesiredChain = isLocalNetwork ? '0x385' : '0x70d2';
try {
await window.ethereum.request({
method: 'wallet_switchEthereumChain',
Expand All @@ -48,9 +48,9 @@ export const connectSnap = async (
params: [
{
chainId: hexDesiredChain,
chainName: isLocal ? 'Boba Local' : 'Boba Sepolia',
chainName: isLocalNetwork ? 'Boba Local' : 'Boba Sepolia',
rpcUrls: [
isLocal
isLocalNetwork
? 'http://localhost:9545'
: 'https://sepolia.boba.network',
],
Expand Down
2 changes: 1 addition & 1 deletion packages/snap/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@bobanetwork/snap-account-abstraction-keyring-hc",
"version": "1.1.12",
"version": "1.1.13",
"description": "An account abstraction keyring snap that integrates with MetaMask accounts on Boba Network",
"keywords": [
"metamask",
Expand Down
4 changes: 2 additions & 2 deletions packages/snap/snap.manifest.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
{
"version": "1.1.12",
"version": "1.1.13",
"description": "An account abstraction keyring snap that integrates with MetaMask accounts on Boba Network",
"proposedName": "Boba Network Account Abstraction Keyring",
"repository": {
"type": "git",
"url": "git+https://github.com/bobanetwork/snap-account-abstraction-keyring.git"
},
"source": {
"shasum": "zOUnDDWSvgLaZNI4fX04mpBXTs1oz2438xLkQ1K3TTc=",
"shasum": "cpEn6gQpuf78uzl3iRxeKBEUc/f+Y5Si1opHRKEyi/w=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
Expand Down
16 changes: 14 additions & 2 deletions packages/snap/src/keyring.ts
Original file line number Diff line number Diff line change
Expand Up @@ -611,7 +611,7 @@ export class AccountAbstractionKeyring implements Keyring {
paymasterAddr: string,
tokenAddr: string,
overrides?: UserOpOverrides,
): Promise<EthUserOperation> {
): Promise<[any, any, EthUserOperation]> {
if (transactions.length !== 1) {
throwError(`[Snap] Only one transaction per UserOp supported`);
}
Expand Down Expand Up @@ -882,7 +882,7 @@ export class AccountAbstractionKeyring implements Keyring {
},
})) as any;

return ethBaseUserOp;
return [{ userOpHash }, bundlerRes, ethBaseUserOp];
}

async #getPaymasterAndData(
Expand Down Expand Up @@ -994,6 +994,18 @@ export class AccountAbstractionKeyring implements Keyring {
entryPointAddress: any,
bundlerUrl: string,
): Promise<IUserOpGasEstimate> {
// for v0.7 EntryPoint this field is not in the UserOperation RPC request
const { chainId } = await provider.getNetwork();
const chainConfig = this.#getChainConfig(Number(chainId));
if (chainConfig?.version !== '0.6.0') {
delete userOp.paymasterAndData;

if (userOp.initCode.length >= 42) {
userOp.factory = userOp.initCode.substring(0, 42);
userOp.factoryData = `0x${String(userOp.initCode).substring(42)}`;
}
}

const requestBody = {
method: 'eth_estimateUserOperationGas',
id: 1,
Expand Down
63 changes: 52 additions & 11 deletions packages/snap/src/utils/ecdsa.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import type { EthUserOperation } from '@metamask/keyring-api';
import { ethers } from 'ethers';

import { AA_CONFIG } from '../constants/aa-config';

/**
* Get the hash of a user operation.
*
Expand All @@ -15,7 +17,10 @@ export function getUserOperationHash(
chainId: string,
): string {
const chainIdDecimal = parseInt(chainId, 10);
const hash = ethers.keccak256(encodeUserOperation(userOperation));
const chainConfig = AA_CONFIG[chainIdDecimal];
const isV6 = chainConfig?.version === '0.6.0';

const hash = ethers.keccak256(encodeUserOperation(userOperation, isV6));

const data = ethers.AbiCoder.defaultAbiCoder().encode(
['bytes32', 'address', 'uint256'],
Expand All @@ -29,33 +34,69 @@ export function getUserOperationHash(
* Encode the user operation.
*
* @param userOperation - The user operation.
* @param isV6 - Flag to use the EntryPoint v0.6 packing method.
* @returns The encoded user operation.
*/
function encodeUserOperation(userOperation: EthUserOperation): string {
function encodeUserOperation(
userOperation: EthUserOperation,
isV6: boolean,
): string {
if (isV6) {
return ethers.AbiCoder.defaultAbiCoder().encode(
[
'address',
'uint256',
'bytes32',
'bytes32',
'uint256',
'uint256',
'uint256',
'uint256',
'uint256',
'bytes32',
],
[
userOperation.sender,
userOperation.nonce,
ethers.keccak256(userOperation.initCode),
ethers.keccak256(userOperation.callData),
userOperation.callGasLimit,
userOperation.verificationGasLimit,
userOperation.preVerificationGas,
userOperation.maxFeePerGas,
userOperation.maxPriorityFeePerGas,
ethers.keccak256(userOperation.paymasterAndData),
],
);
}
const accountGasLimits = ethers.solidityPacked(
['uint128', 'uint128'],
[userOperation.verificationGasLimit, userOperation.callGasLimit],
);
const gasFees = ethers.solidityPacked(
['uint128', 'uint128'],
[userOperation.maxPriorityFeePerGas, userOperation.maxFeePerGas],
);
return ethers.AbiCoder.defaultAbiCoder().encode(
[
'address',
'uint256',
'bytes32',
'bytes32',
'bytes32',
'uint256',
'uint256',
'uint256',
'uint256',
'uint256',
'bytes32',
'bytes32',
],
[
userOperation.sender,
userOperation.nonce,
ethers.keccak256(userOperation.initCode),
ethers.keccak256(userOperation.callData),
userOperation.callGasLimit,
userOperation.verificationGasLimit,
accountGasLimits,
userOperation.preVerificationGas,
userOperation.maxFeePerGas,
userOperation.maxPriorityFeePerGas,
ethers.keccak256(userOperation.paymasterAndData),
gasFees,
ethers.keccak256('0x'),
],
);
}
Loading