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

Multisign - Alchemy Account Kit SDK error (multisigAccountClient.proposeUserOperation) #1242

Open
shifas opened this issue Dec 19, 2024 · 2 comments
Labels
bug Something isn't working

Comments

@shifas
Copy link

shifas commented Dec 19, 2024

Followed document to tryout Multisign
multisigAccountClient.proposeUserOperation not working as expected
multisigAccountClient.sendUserOperation goes success, but data is not updated in chain

[REQUIRED] Environment

"@aa-sdk/core": "^4.7.0",
"@account-kit/infra": "^4.7.0",
"@account-kit/smart-contracts": "^4.7.0",

[REQUIRED] Describe the problem

file:///home/shifas/projects/alchemy-multisign/node_modules/viem/_esm/utils/abi/encodeAbiParameters.js:136
throw new InvalidAddressError({ address: value });
^

InvalidAddressError: Address "undefined" is invalid.

  • Address must be a hex value of 20 bytes (40 hex characters).
  • Address must match its checksum counterpart.

Version: [email protected]
at encodeAddress (file:///home/shifas/projects/alchemy-multisign/node_modules/viem/_esm/utils/abi/encodeAbiParameters.js:136:15)
at prepareParam (file:///home/shifas/projects/alchemy-multisign/node_modules/viem/_esm/utils/abi/encodeAbiParameters.js:83:16)
at prepareParams (file:///home/shifas/projects/alchemy-multisign/node_modules/viem/_esm/utils/abi/encodeAbiParameters.js:67:29)
at encodeAbiParameters (file:///home/shifas/projects/alchemy-multisign/node_modules/viem/_esm/utils/abi/encodeAbiParameters.js:55:28)
at encodeFunctionData (file:///home/shifas/projects/alchemy-multisign/node_modules/viem/_esm/utils/abi/encodeFunctionData.js:15:11)
at Object.encodeExecute (file:///home/shifas/projects/alchemy-multisign/node_modules/@account-kit/smart-contracts/dist/esm/src/msca/account/standardExecutor.js:6:16)
at _initUserOperation (file:///home/shifas/projects/alchemy-multisign/node_modules/@aa-sdk/core/dist/esm/actions/smartAccount/internal/initUserOperation.js:31:23)
at buildUserOperation (file:///home/shifas/projects/alchemy-multisign/node_modules/@aa-sdk/core/dist/esm/actions/smartAccount/buildUserOperation.js:38:12)
at sendUserOperation (file:///home/shifas/projects/alchemy-multisign/node_modules/@aa-sdk/core/dist/esm/actions/smartAccount/sendUserOperation.js:35:28)
at Object.sendUserOperation (file:///home/shifas/projects/alchemy-multisign/node_modules/@aa-sdk/core/dist/esm/client/decorators/smartAccountClient.js:34:34) {
details: undefined,
docsPath: undefined,
metaMessages: [
'- Address must be a hex value of 20 bytes (40 hex characters).',
'- Address must match its checksum counterpart.'
],
shortMessage: 'Address "undefined" is invalid.',
version: '2.21.55'
}

Error shown

Relevant code or sample repro:

This is my propose.js code (executing a simple update function by collecting multiple signatures)

import { config } from "dotenv";
import { initializeClient, signers, owners } from "./client.js";
import getEncodedCalldata from "./getDataForm.js";
import { ethers } from "ethers";

config();
const { CONTRACT_ADDR, RPC_URL } = process.env;
const provider = new ethers.JsonRpcProvider(RPC_URL); // For querying chain status

const blockNumber = await provider.getBlockNumber();
console.log("Connected to Polygon AMOY. Current Block Number:", blockNumber);

const num = 42;

(async () => {
    try {
        // Step 1: Initialize Multisig Client for Signer 1
        const multisigAccountClient1 = await initializeClient(0);
        const multisignAccountAddress = await multisigAccountClient1.getAddress();
        console.log("Multisign Account Address:", multisignAccountAddress);

        // Step 2: Check Balance
        const balance = await multisigAccountClient1.getBalance({
            address: multisignAccountAddress,
        });
        console.log("Balance:", balance.toString());

        // Step 3: Encode Function Call Data
        console.log("Encoded Call Data:", getEncodedCalldata(num));

        // Step 4: Propose UserOperation
        const uo = {
            target: CONTRACT_ADDR,
            data: getEncodedCalldata(num),
          };
          console.log("User Operation Object:", uo);
          
          const { request, signatureObj: firstSig } = await multisigAccountClient1.proposeUserOperation({
            uo
          });
          
        console.log("Proposed UserOperation:", request);

        // Step 5: Sign with Second Signer
        const multisigAccountClient2 = await initializeClient(1);
        const { aggregatedSignature, signatureObj: secondSig } = await multisigAccountClient2.signMultisigUserOperation({
            account: multisigAccountClient2.account,
            signatures: [firstSig],
            userOperationRequest: request,
        });
        console.log("Aggregated Signature (2 signers):", aggregatedSignature);

        // Step 6: Sign and Send with Third Signer
        const multisigAccountClient3 = await initializeClient(2);
        try {
            const result = await multisigAccountClient3.sendUserOperation({
                uo: request,
                context: {
                    aggregatedSignature,
                    signatures: [firstSig, secondSig],
                    userOpSignatureType: "ACTUAL",
                },
            });
            console.log("Transaction Result:", result);

            // Step 7: Query UserOperation Status
            const userOpHash = result.userOpHash; // Adjust based on your library's return value
            console.log("UserOperation Hash:", userOpHash);

            const status = await provider.send("eth_getUserOperationByHash", [userOpHash]);
            if (status) {
                console.log("UserOperation Status:", status);
            } else {
                console.log("UserOperation not yet included in a bundle.");
            }
        } catch (error) {
            throw error;
        }
    } catch (error) {
        console.error("Error in Multisign Process:", error);
    }
})();

My inference:

These are the functions where error originates

@aa-sdk/core/dist/esm/actions/smartAccount/internal/initUserOperation.js
Code 1

const callData = Array.isArray(uo)
      ? account.encodeBatchExecute(uo)
      : typeof uo === "string"
          ? uo
          : account.encodeExecute(uo);

and is being referred on
@account-kit/smart-contracts/dist/esm/src/msca/account/standardExecutor.js
Code 2

encodeExecute: async ({ target, data, value }) => {
        return encodeFunctionData({
            abi: IStandardExecutorAbi,
            functionName: "execute",
            args: [target, value ?? 0n, data],
        });
    },

So the problem here is when we're calling our client file code

const uo = {
            target: CONTRACT_ADDR,
            data: getEncodedCalldata(num),
        };
        const { request, signatureObj: firstSig } = await multisigAccountClient1.proposeUserOperation({
            uo
        });

Code 1 calls account.encodeExecute(uo) (encoded data is passed to Code 2)
So params in encodeExecute: async ({ target, data, value }) will be undefined

Possible Solution:

Decode the param uo before using its expected outputs in
encodeExecute: async ({ target, data, value })
otherwise params target, data, value will be undefined

@shifas shifas added the bug Something isn't working label Dec 19, 2024
@noam-alchemy
Copy link
Member

thanks for filing @shifas - we're triaging and will report back. if you have a good handle on this feel free to also open a PR against aa-sdk.

@shifas
Copy link
Author

shifas commented Dec 22, 2024

Sorry, Issue arised because
const result = await multisigAccountClient3.sendUserOperation({ uo: request, context: { aggregatedSignature, signatures: [firstSig, secondSig], userOpSignatureType: "ACTUAL", }, });

I had to pass uo: request.callData, now it works

But still my transaction is not done in the blockchain

Updated client code:

import { config } from "dotenv";
import { initializeClient, signers, owners } from "./client.js";
import getEncodedCalldata from "./getDataForm.js";
import { ethers } from "ethers";

config();
const { CONTRACT_ADDR, RPC_URL } = process.env;
const provider = new ethers.JsonRpcProvider(RPC_URL); // For querying chain status

const blockNumber = await provider.getBlockNumber();
console.log("Connected to Polygon AMOY. Current Block Number:", blockNumber);

const num = 42;

(async () => {
    try {
        // Step 1: Initialize Multisig Client for Signer 1
        const multisigAccountClient1 = await initializeClient(0);
        const multisignAccountAddress = await multisigAccountClient1.getAddress();
        console.log("Multisign Account Address:", multisignAccountAddress);

        // Step 2: Check Balance
        const balance = await multisigAccountClient1.getBalance({
            address: multisignAccountAddress,
        });
        console.log("Balance:", balance.toString());

        // Step 3: Encode Function Call Data
        console.log("Encoded Call Data:", getEncodedCalldata(num));

        // Step 4: Propose UserOperation
        const uo = {
            target: CONTRACT_ADDR,
            data: getEncodedCalldata(num),
        };
        // console.log("User Operation Object:", uo);

        const { request, signatureObj: firstSig } = await multisigAccountClient1.proposeUserOperation({
            uo
        });

        console.log("Proposed UserOperation:", request);

        // Step 5: Sign with Second Signer
        const multisigAccountClient2 = await initializeClient(1);
        const { aggregatedSignature, signatureObj: secondSig } = await multisigAccountClient2.signMultisigUserOperation({
            account: multisigAccountClient2.account,
            signatures: [firstSig],
            userOperationRequest: request,
        });
        console.log("Aggregated Signature (2 signers):", aggregatedSignature);

        // Step 6: Sign and Send with Third Signer
        const multisigAccountClient3 = await initializeClient(2);
        try {
            const result = await multisigAccountClient3.sendUserOperation({
                uo: request.callData,
                context: {
                    aggregatedSignature,
                    signatures: [firstSig, secondSig],
                    userOpSignatureType: "ACTUAL",
                },
            });
            console.log("Transaction Result:", result);

            // Step 7: Query UserOperation Status
            const userOpHash = result.hash; // Adjust based on your library's return value
            console.log("UserOperation Hash:", userOpHash);

            const status = await provider.send("eth_getUserOperationByHash", [userOpHash]);
            if (status) {
                console.log("UserOperation Status:", status);
            } else {
                console.log("UserOperation not yet included in a bundle.");
            }
        } catch (error) {
            throw error;
        }
    } catch (error) {
        console.error("Error in Multisign Process:", error);
    }
})();

Userop Status

userOperation: {
sender: '0x6951b4A030A013b4E663f64FEb80b9DE76c48C46',
nonce: '0x11',
initCode: '0x',
callData: '0xb61d27f6000000000000000000000000eb9eca2f5493bd739dcc6b76641893388665b2f40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000246057361d000000000000000000000000000000000000000000000000000000000000002a00000000000000000000000000000000000000000000000000000000',
callGasLimit: '0x3bb8',
verificationGasLimit: '0x1680c',
preVerificationGas: '0xbc7c',
maxFeePerGas: '0xb0051c8a3',
maxPriorityFeePerGas: '0x7558bdb00',
paymasterAndData: '0x',
signature: '0x0000000000000000000000000000000000000000000000000000000002e0446000000000000000000000000000000000000000000000000000000029e8d608450000000000000000000000000000000000000000000000000000000df84758006cfd33eb8e06e071a56fe6d5094e35094f3d0def4c950aaeb65f2f64ddffd59c3678a1eac2370fdadfa4b5c3411808a47654bc175023700e20c540ff8169991a1bdc5112e9bbce82de30b41ac97824fb61dc517516617ea21b03bab6bf162cea1c02c31481765272f50a9e2babcd1b1c70cd7e323b453e6853062f8e76f471684f3ca77f232c725828f84a004669921ec31bdfa7b78264dd884407aa02a5f00a11e766c47a546306f5e01d27d29e772844d967865837ab009ce0e5a05cfa417f99f11b'
},
entryPoint: '0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789',
blockNumber: null,
blockHash: null,
transactionHash: null
}

Can anyone help me find where I'm going wrong?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants