Skip to content
This repository has been archived by the owner on Oct 20, 2024. It is now read-only.

Creating a UserOperation from scratch and then sending it to bundler for execution #48

Open
iamgauravpant opened this issue Jun 2, 2023 · 4 comments

Comments

@iamgauravpant
Copy link

I wanted to create a UserOperation from scratch and then send it to bundler . I am using stackup's bundler URL .
Below is the test I wrote to send a UserOperation to the bundler for inclusion but I am getting an error related to signature .
The smart account ( 0x1e87a1Eca600313aE1388D04e71ea473AD468CAE ) was created by calling SimpleAccountFactory contract deployed at 0x9406Cc6185a346906296840746125a0E44976454 .

Error Logged :
{
error: {
code: -32507,
data: null,
message: 'Invalid UserOp signature or paymaster signature'
},
id: 1,
jsonrpc: '2.0'
}

it("creating a user operation from scratch and then send it to bundler", async function () {
const EntryPoint_Addr = "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789";
const RECEIVER_ADDR = "0x53C242Cc21d129155aCC5FAc321abDfe83C35Af7"; // sending ether to this address
const amount = '1000000000000000'; // amount to send to RECEIVER_ADDR

const sender = "0x1e87a1Eca600313aE1388D04e71ea473AD468CAE"; // smart account address , created by calling createAccount method of AccountFactory ( deployed at 0x9406Cc6185a346906296840746125a0E44976454 ) passing RECEIVER_ADDRESS as address and 1 as salt value
const nonce = '1'; // used salt's value as 1 at the time of smart account creation . that's why I kept it same .
const callGasLimit = '500000';
const verificationGasLimit = '200000';
const preVerificationGas = '50000';
const maxFeePerGas = '1000000000'; // adjust the value according to your needs
const maxPriorityFeePerGas = '100000000'; // adjust the value according to your needs
const account = new ethers.utils.Interface(accountABI); // ethers code
const calldata = account.encodeFunctionData('execute',[RECEIVER_ADDR, amount, "0x"]);
// calldata : 0xb61d27f600000000000000000000000053c242cc21d129155acc5fac321abdfe83c35af700000000000000000000000000000000000000000000000000038d7ea4c6800000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000
    
// getUserOpHash function is taken from StackUp's UserOp library . Reference : https://github.com/stackup-wallet/userop.js/blob/main/src/context.ts
const getUserOpHash = () => {
            const packed = ethers.utils.defaultAbiCoder.encode(
              [
                "address",
                "uint256",
                "bytes32",
                "bytes32",
                "uint256",
                "uint256",
                "uint256",
                "uint256",
                "uint256",
                "bytes32",
              ],
              [
                sender,
                nonce,
                ethers.utils.keccak256('0x'),             ethers.utils.keccak256('0xb61d27f600000000000000000000000053c242cc21d129155acc5fac321abdfe83c35af700000000000000000000000000000000000000000000000000038d7ea4c6800000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000'),
                callGasLimit,
                verificationGasLimit,
                preVerificationGas,
                maxFeePerGas,
                maxPriorityFeePerGas,
                ethers.utils.keccak256('0x'),
              ]
            );
        
            const enc = ethers.utils.defaultAbiCoder.encode(
              ["bytes32", "address", "uint256"],
              [ethers.utils.keccak256(packed), EntryPoint_Addr, 80001]
            );
        
            return ethers.utils.keccak256(enc);
         }

         const userOpHash = getUserOpHash();
        //  userOpHash value : 0x1a23a91638f4a6c594c64b1b17bb930f9505c244b7a9cbc7065213bfccc71ba9

        // Arraified the userOpHash . Reference : https://github.com/stackup-wallet/userop.js/blob/main/src/preset/middleware/signature.ts
        
        const arraifiedHash =  ethers.utils.arrayify(userOpHash);
        console.log("arraified Hash :",arraifiedHash);
        const provider = new ethers.providers.JsonRpcProvider("https://polygon-mumbai.infura.io/v3/infurakey"); 
        const wallet = new ethers.Wallet('myprivatekey', provider);
        const signer = wallet.provider.getSigner(wallet.address);
        const serializeObj = JSON.stringify(arraifiedHash);   // **error suspect 1**
        const signature = signer.signMessage(serializeObj);

        const options = {
          method: "POST",
          url: "https://api.stackup.sh/v1/node/stackupkey",
          headers: {
            accept: "application/json",
            "content-type": "application/json",
          },
          data: {
            jsonrpc: "2.0",
            id: 1,
            method: "eth_sendUserOperation",
            params: [
              {
                sender: "0x1e87a1Eca600313aE1388D04e71ea473AD468CAE",
                nonce: "0x0",
                initCode: '0x',
                callData: "0xb61d27f600000000000000000000000053c242cc21d129155acc5fac321abdfe83c35af700000000000000000000000000000000000000000000000000038d7ea4c6800000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000",
                callGasLimit: "500000",
                verificationGasLimit: "200000",
                preVerificationGas: "50000",
                maxFeePerGas: "1000000000",
                maxPriorityFeePerGas: "100000000",
                paymasterAndData: "0x",
                signature:signature
              },
              "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789",
            ],
          },
        }; 
        await axios
          .request(options)
          .then(function (response) {
            console.log(response.data);
          })
          .catch(function (error) {
            console.error(error);
          });
});


@hazim-j
Copy link
Member

hazim-j commented Jun 2, 2023

Since you're not using a paymaster, this error means your signature value is incorrect.

From a quick scan of your code, this part seems off. Any reason why you are passing a JSON.stringify version of the userOpHash?

const serializeObj = JSON.stringify(arraifiedHash);   // **error suspect 1**
const signature = signer.signMessage(serializeObj);

@iamgauravpant
Copy link
Author

Hi @hazim-j , glad that you replied .
Reference : https://github.com/stackup-wallet/userop.js/blob/main/src/preset/middleware/signature.ts
Line of Code :
ctx.op.signature = await signer.signMessage(ethers.utils.arrayify(ctx.getUserOpHash()));
Here you have used signer.signMessage method of ethers and inside it is the arrayified version of UserOpHash .
But when I try to do the same , I get an error .
I'll rewrite the code that produces the error along with the snapshot of the error message I get .

         const getUserOpHash = () => {
            const packed = ethers.utils.defaultAbiCoder.encode(
              [
                "address",
                "uint256",
                "bytes32",
                "bytes32",
                "uint256",
                "uint256",
                "uint256",
                "uint256",
                "uint256",
                "bytes32",
              ],
              [
                sender,
                nonce,
                ethers.utils.keccak256('0x'),
                ethers.utils.keccak256('0xb61d27f600000000000000000000000053c242cc21d129155acc5fac321abdfe83c35af700000000000000000000000000000000000000000000000000038d7ea4c6800000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000'),
                callGasLimit,
                verificationGasLimit,
                preVerificationGas,
                maxFeePerGas,
                maxPriorityFeePerGas,
                ethers.utils.keccak256('0x'),
              ]
            );
        
            const enc = ethers.utils.defaultAbiCoder.encode(
              ["bytes32", "address", "uint256"],
              [ethers.utils.keccak256(packed), EntryPoint_Addr, 80001]
            );
        
            return ethers.utils.keccak256(enc);
         }
      const userOpHash = getUserOpHash();
      const provider = new ethers.providers.JsonRpcProvider("https://polygon-mumbai.infura.io/v3/key"); 
      const wallet = new ethers.Wallet('myprivatekey', provider);
      const signer = wallet.provider.getSigner(wallet.address);
      const sig = await signer.signMessage(ethers.utils.arrayify(userOpHash));
      console.log("sig :",sig);


Screenshot 2023-06-02 181739

To get away with this error , I used web3js library , first I stringified the array version of userophash and then signed it using web3.eth.accounts.sign() . I was able to get the signature and when I passed it to send the UserOperation to bundler I get the error mentioned below :
{
code: -32507,
data: null,
message: 'Invalid UserOp signature or paymaster signature'
},
id: 1,
jsonrpc: '2.0'
}
I knew it would be wrong to first stringify the array and then sign it but found no solution , so did it anyway . It would be really great if you can help me with this . My guess is the problem is maybe with the provider , I have used infura's . Is there any special provider url for ERC 4337 ?

@pablogeek
Copy link

that is also an issue for me using infura. Infura also claims to have support for ERC 4337? should it be all a common protocol?

@pablogeek
Copy link

pablogeek commented Dec 8, 2023

apparently this works using alchemy.com. So apparently infura is not fully supporting it

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants