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

Function execTransaction is failing #768

Closed
Malikrehman00107 opened this issue Apr 9, 2024 · 35 comments
Closed

Function execTransaction is failing #768

Malikrehman00107 opened this issue Apr 9, 2024 · 35 comments

Comments

@Malikrehman00107
Copy link

We create a safe and when we send the funds form safe.

execTransaction( Is failing .

Here is Trx Hash: https://sepolia.etherscan.io/tx/0x02cbb2c32af0ae333d90e704a630d3638b53005ba9dbb3022028521b8fe9d71d

Safe Smart Contract : https://sepolia.etherscan.io/address/0x159bd467fafcfa348d2b5f357b23aa1d70e814a0

need your attention here please

@Malikrehman00107
Copy link
Author

`const provider = new ethers.providers.JsonRpcProvider(
sepoliaRpcUrl,
);
const owner1Signer = new ethers.Wallet(
firstOwnerPrivateKey,
provider,
);

const ethAdapterOwner1 = new EthersAdapter({
ethers,
signerOrProvider: owner1Signer,
});

const safeService = new SafeApiKit({
txServiceUrl: SepoliaTransactionServiceUrl,
ethAdapter: ethAdapterOwner1,
});

const value = ethers.utils
.parseUnits(amount.toString(), token.decimals)
.toString();

const tokenContract = new ethers.Contract(token.contractAddress, erc20Abi, provider)

const data = tokenContract.interface.encodeFunctionData('transfer', [address, value]);

const safeTransactionData: SafeTransactionDataPartial = {
to: ethers.utils.getAddress(token.contractAddress),
data,
value
};

const safeSdk: Safe = await Safe.create({
ethAdapter: ethAdapterOwner1,
safeAddress: safeAddress,
});

// Create a Safe transaction with the provided parameters
const safeTransaction = await safeSdk.createTransaction({
safeTransactionData
});
console.log(Owner 1 creates a transction..);

// Deterministic hash based on transaction parameters
const safeTxHash = await safeSdk.getTransactionHash(safeTransaction);

// Sign transaction to verify that the transaction is coming from owner 1
const senderSignature = await safeSdk.signTransactionHash(safeTxHash);
const checksumSafeAddresss = ethers.utils.getAddress(
safeAddress,
);
await safeService.proposeTransaction({
safeAddress: checksumSafeAddresss,
safeTransactionData: safeTransaction.data,
safeTxHash,
senderAddress: await owner1Signer.getAddress(),
senderSignature: senderSignature.data,
});

const owner2Signer = new ethers.Wallet(this.configService.get('OWNER_2_PRIVATE_KEY'), provider)
const ethAdapterOwner2 = new EthersAdapter({
ethers,
signerOrProvider: owner2Signer
})

const safeSdkOwner2 = await Safe.create({
ethAdapter: ethAdapterOwner2,
safeAddress: this.configService.get('SAFE_ADDRESS')
})

const signature = await safeSdkOwner2.signTransactionHash(safeTxHash)
await safeService.confirmTransaction(safeTxHash, signature.data)

const safeSdk: Safe = await Safe.create({ ethAdapter: ethAdapterOwner1, safeAddress })

const safeTransactionE = await safeService.getTransaction(safeTxHash)
const executeTxResponse = await safeSdk.executeTransaction(safeTransactionE)`

@Malikrehman00107
Copy link
Author

This is the code that i am using to transfer tokens from safe to a wallet. and i am getting success on the execute transaction. but when seen on block explorer there is an error in transaction like mentioned in the original issue above.

@Malikrehman00107
Copy link
Author

@faisal-anwar825

@Malikrehman00107
Copy link
Author

@germartinez

@dasanra
Copy link
Collaborator

dasanra commented Apr 9, 2024

There is an issue with the internal transaction. As the token contracts is not verified is very difficult to debug, but the error is affecting the internal transaction. Two things may be happening:

1.- The transfer function is not used correctly, or you are not fulfilling some of the transfer function requires
2.- I see you are setting the safeTxGas. Can't see it in the code example but indeed is set in the test functions you sent from the safe. You should leave this value unset or 0.

Appart from the points above, there is another error. In the safeTransactionData you set the value parameter to be equal to the value of ERC20 token you want to send. The value in safeTransactionData is meant only to transfer native tokens (Sepolia ETH in this case) and you shouldn't set it with the same variable as the ERC20 token value. You should set it explicitly to 0.

const safeTransactionData: SafeTransactionDataPartial = {
  to: ethers.utils.getAddress(token.contractAddress),
  data,
  value: 0
};

Please test with this changes

@Malikrehman00107
Copy link
Author

@faisal-anwar825

@faisal-anwar825
Copy link

I have set the value to zero and removed the safeTxGas param and now i am getting a different error
"insufficient funds for gas * price + value: balance 140885831780653, tx cost 152423076309126, overshot 11537244528473".

@Malikrehman00107
Copy link
Author

@germartinez @dasanra

@Malikrehman00107
Copy link
Author

@Malikrehman00107
Copy link
Author

@yagopv please!

@dasanra
Copy link
Collaborator

dasanra commented Apr 15, 2024

@Malikrehman00107 please, don't tag people like that.

@faisal-anwar825 The error now is self explanatory, the account that executes the transaction (the signer) doesn't have enough ETH to execute the transaction. Add funds to 0xb55D2195F400A137Fa67EB520B13B6b951770b76

@Malikrehman00107
Copy link
Author

@dasanra
My apologies, this issue has been stuck for days and testing is still pending that is why

@faisal-anwar825
Copy link

If i remove safeTxGas param then again it fails on executeTransaction method throwing error
'Error: cannot estimate gas; transaction may fail or may require manual gas limit

@dasanra
Copy link
Collaborator

dasanra commented Apr 16, 2024

I see you are executing now transactions with the correct configuration. We don't have any error like the one you describe above, so I would need to see a current version of the code to help further

@Malikrehman00107
Copy link
Author

@dasanra
Copy link
Collaborator

dasanra commented Apr 16, 2024

@Malikrehman00107 that contract is just a Safe contract.

I would need to see either the current SDK implementation and the code of this smart contract
https://sepolia.etherscan.io/address/0x419Fe9f14Ff3aA22e46ff1d03a73EdF3b70A62ED

@faisal-anwar825
Copy link

faisal-anwar825 commented Apr 17, 2024

`
const provider = new ethers.providers.JsonRpcProvider(
token.blockchain.rpcUrl,
);
const owner1Signer = new ethers.Wallet(
process.env.OWNER_1_PRIVATE_KEY,
provider,
);

  const ethAdapterOwner1 = new EthersAdapter({
    ethers,
    signerOrProvider: owner1Signer,
  });

const TXN_SERVICE_URL =
                await this.awsCOnfig.fetchVariableFromSecretsManager('TXN_SERVICE_URL');

  const safeService = new SafeApiKit({
    txServiceUrl: TXN_SERVICE_URL,
    ethAdapter: ethAdapterOwner1,
  });

  const value = ethers.utils
    .parseUnits(amount.toString(), token.decimals)
    .toString();

  const tokenContract = new ethers.Contract(token.contractAddress, erc20Abi, provider)

  const data = tokenContract.interface.encodeFunctionData('transfer', [address, value]);


  const safeTransactionData: SafeTransactionDataPartial = {
    to: ethers.utils.getAddress(token.contractAddress),
    data,
    value: '0',
    safeTxGas: '0'
  };

  const safeSdk: Safe = await Safe.create({
    ethAdapter: ethAdapterOwner1,
    safeAddress: safeAddress,
  });

  // Create a Safe transaction with the provided parameters
  const safeTransaction = await safeSdk.createTransaction({
    safeTransactionData
  });
  console.log(`Owner 1 creates a transction..`);

  // Deterministic hash based on transaction parameters
  const safeTxHash = await safeSdk.getTransactionHash(safeTransaction);

  // Sign transaction to verify that the transaction is coming from owner 1
  const senderSignature = await safeSdk.signTransactionHash(safeTxHash);
  const checksumSafeAddresss = ethers.utils.getAddress(
    safeAddress,
  );
  await safeService.proposeTransaction({
    safeAddress: checksumSafeAddresss,
    safeTransactionData: safeTransaction.data,
    safeTxHash,
    senderAddress: await owner1Signer.getAddress(),
    senderSignature: senderSignature.data,
  });
  
  const owner1Signer = new ethers.Wallet(OWNER_1_PRIVATE_KEY, provider);

        const ethAdapterOwner1 = new EthersAdapter({
            ethers,
            signerOrProvider: owner1Signer,
        });

        const OWNER_2_PRIVATE_KEY =
            await this.awsCOnfig.fetchVariableFromSecretsManager(
                'OWNER_2_PRIVATE_KEY',
            );

        const safeService = new SafeApiKit({
            txServiceUrl: TXN_SERVICE_URL,
            ethAdapter: ethAdapterOwner1,
        });

        const owner2Signer = new ethers.Wallet(OWNER_2_PRIVATE_KEY, provider);
        const ethAdapterOwner2 = new EthersAdapter({
            ethers,
            signerOrProvider: owner2Signer,
        });

        const SAFE_ADDRESS = await this.awsCOnfig.fetchVariableFromSecretsManager(
            'SAFE_ADDRESS',
        );

        const safeSdkOwner2 = await Safe.create({
            ethAdapter: ethAdapterOwner2,
            safeAddress: SAFE_ADDRESS,
        });

        const signature = await safeSdkOwner2.signTransactionHash(withdraw.txHash);
        await safeService.confirmTransaction(withdraw.txHash, signature.data);

        const safeSdk: Safe = await Safe.create({
            ethAdapter: ethAdapterOwner1,
            safeAddress: SAFE_ADDRESS,
        });

        const safeTransactionE = await safeService.getTransaction(withdraw.txHash);
        const executeTxResponse = await safeSdk.executeTransaction(
            safeTransactionE,
        );
        const receipt = await executeTxResponse.transactionResponse?.wait();`

@faisal-anwar825
Copy link

@dasanra here is the updated code. I have set the eth value to zero and also safeTxGas to zero. but still getting gas estimate error while executing transaction

@dasanra
Copy link
Collaborator

dasanra commented Apr 17, 2024

@faisal-anwar825 could you remove the safeTxGas from safeTransactionData?

Also, could you tell me which versions of the SDK are you using?

Could you tell me which line the code is failing? Does it fails when creating the transaction or already when sending it to the blockchain?

Could you leave in the queue, pending to be executed a transaction that is created with the script?
I can see several transactions sucessfully executed in the web app, how are you executing them?

@faisal-anwar825
Copy link

faisal-anwar825 commented Apr 18, 2024

"@safe-global/api-kit": "^1.3.1",
"@safe-global/protocol-kit": "^1.3.0",
these are the versions i am using. and transaction is being proposed but it fails on this line.
const executeTxResponse = await safeSdk.executeTransaction( safeTransactionE, );
Some transactions get executed while some throw this error

@dasanra
Copy link
Collaborator

dasanra commented Apr 18, 2024

Ok, those are old versions that are not supported anymore. There were some gas estimation issues that were solved in v2 specially affecting some specific RPC providers, which may be your case.

To go forward with this topic you will need to upgrade to the latest version:

"@safe-global/api-kit": "^2.3.0",
"@safe-global/protocol-kit": "^3.0.2"

From v2 you will need to use ethers v6, that is the supported version.

There are also some migration guides explaining bigger changes between the version you are using and the new one, just in case you are affected:

https://docs.safe.global/sdk/api-kit/reference/migrating-to-v2
https://docs.safe.global/sdk/protocol-kit/reference/migrating-to-v2

@faisal-anwar825
Copy link

faisal-anwar825 commented Apr 18, 2024

i have upgraded the versions to the latest
"@safe-global/api-kit": "^2.3.0", "@safe-global/protocol-kit": "^3.0.2",
Refactored the code as well
`.

      const provider = new ethers.JsonRpcProvider(
          token.blockchain.rpcUrl,
         );
      const OWNER_1_PRIVATE_KEY = await awsConfig.fetchVariableFromSecretsManager(
        'OWNER_1_PRIVATE_KEY',
      );

      if (typeof OWNER_1_PRIVATE_KEY !== 'string') {
        throw new Error('Error fetch keys from AWS.');
      }

      const owner1Signer = new ethers.Wallet(OWNER_1_PRIVATE_KEY, provider);

      const ethAdapterOwner1 = new EthersAdapter({
        ethers,
        signerOrProvider: owner1Signer,
      });

      const safeService = new SafeApiKit({
        chainId: ethers.toBigInt(token.blockchain.chainId),
        txServiceUrl: process.env.TXN_SERVICE_URL,
      });

      const value = ethers.parseEther(amount.toString()).toString();

      const checksumAddresss = ethers.getAddress(address);

      const safeTransactionData: SafeTransactionDataPartial = {
        to: checksumAddresss,
        data: '0x',
        value,
      };

      const safeSdk: Safe = await Safe.create({
        ethAdapter: ethAdapterOwner1,
        safeAddress: safeAddress,
      });

      // Create a Safe transaction with the provided parameters
      const safeTransaction = await safeSdk.createTransaction({
        transactions: [safeTransactionData],
      });
      console.log(`Owner 1 creates a transction..`);

      // Deterministic hash based on transaction parameters
      const safeTxHash = await safeSdk.getTransactionHash(safeTransaction);

      // Sign transaction to verify that the transaction is coming from owner 1
      const senderSignature = await safeSdk.signHash(safeTxHash);
      const checksumSafeAddresss = ethers.getAddress(safeAddress);
        await safeService.proposeTransaction({
          safeAddress: checksumSafeAddresss,
          safeTransactionData: safeTransaction.data,
          safeTxHash,
          senderAddress: await owner1Signer.getAddress(),
          senderSignature: senderSignature.data,
        });

`

but now it is giving the "Not found" error. nothing else just an error message saying not found. i have debugged the issue into the code of sdk. Error is coming from this file

node_modules/@safe-global/api-kit/src/utils/httpRequests.ts

when i propose transaction

'https://safe-transaction-sepolia.safe.global/v1/safes/0x159bD467fafcfA348D2b5f357b23aA1d70E814A0/multisig- transactions/'

this url is called from that file with this body

`

baseGas: '0'
contractTransactionHash: '0x9714fde10d7c957223aa3c518bf8c42877a1e85990d1930a5e2ef5717a1bfaab'
data: '0x'
gasPrice: '0'
gasToken: '0x0000000000000000000000000000000000000000'
nonce: 49
operation: 0
origin: undefined
refundReceiver: '0x0000000000000000000000000000000000000000'
safeTxGas: '0'
sender: '0xb55D2195F400A137Fa67EB520B13B6b951770b76'
signature: 
'0xa382e7b7559897293844a1676bbfee3f634b31
c64dd5e6917246c294de347c620a8f7b48a5a38619495277f21a7e1a9749821c54698341741cb9a58f73d4016720'
to: '0x18Ddae6FBf80672E77d2774030e9EaDaD4217F2C'
value: '3000000000000000'

`

http call is made successfully but when the response is converted to json at this piece of your SDK code in the mentioned file.

`

   try {
        jsonResponse = await response.json();
    }
    catch (error) {
        if (!response.ok) {
            throw new Error(response.statusText);
        }
    }

`

it goes into the catch block with error

'invalid json response body at https://safe-transaction-sepolia.safe.global/v1/safes/0x159bD467fafcfA348D2b5f357b23aA1d70E814A0/multisig-transactions/ reason: Unexpected token < in JSON at position 0'

I guess there is an issue with SDK's response.

response.ok is false and response.statusText is "Not Found"

@dasanra
Copy link
Collaborator

dasanra commented Apr 18, 2024

Yes, that is expected.

From api-kitv2 is not necessary to specify the transaction service URL if you use one of the public available ones.

You can instead just leave the chainId parameter only.
With this change we also removed a mandatory internal /api/element because we were forcing every custom API to contain that structure. If you still want to specify the transaction service URL you should change the one stored in awsConfig to be https://safe-transaction-sepolia.safe.global/api

This will fix the current error that is displayed in your app.

@faisal-anwar825
Copy link

Still same.
Apikit and protocol-kit versions are upgraded to latest. Ethers is updated to v6. Still some transactions get executed and some throw "Cannot estimate gas" error.

@Malikrehman00107
Copy link
Author

@dasanra sorry to bother you , Can you have a look at this, please?

Best

@Malikrehman00107
Copy link
Author

@germartinez can you please guide ? it seems daniel is away since few days

@Malikrehman00107
Copy link
Author

@dasanra can you please review and do necessary?

best

@Malikrehman00107
Copy link
Author

@yagopv

can you please check in with your colleagues this issue has been pending for the last 2 weeks.

Best

@dasanra attentive

@dasanra
Copy link
Collaborator

dasanra commented Apr 23, 2024

@faisal-anwar825 I see you are creating plenty of transactions currently with 2 signatures. But is not possible to execute those transactions because the previous transactions should be executed first

https://app.safe.global/transactions/queue?safe=sep:0x159bd467fafcfa348d2b5f357b23aa1d70e814a0

While all those transactions are in the queue it won't be possible for you to run the script completely, as the execute function will be locked because is not having the next nonce to execute.

Also I'm not really sure how you are creating those transactions, as the nonce is increasing but with the code you shared is not modifiying the nonce

@faisal-anwar825
Copy link

But how can i execute the previous transactions if they keep giving gas error randomly. Whats the solution of this? should i execute transactions in chronological order. In this case all newer transactions will remain pending if one transaction gets stuck.
Regarding nonce, that change was made recently in an attempt to somehow fix this issue. I am passing nonce to each transaction while creating it.
`

  const nonce = await safeService.getNextNonce(safeAddress)

  // Create a Safe transaction with the provided parameters
  const safeTransaction = await safeSdk.createTransaction({
    transactions: [safeTransactionData],
    options: {
      nonce
    }
  });

`

@dasanra
Copy link
Collaborator

dasanra commented Apr 23, 2024

Ok, if you call getNextNonce will just add more and more operations to the queue. You can manually set the 108 nonce for now and will overwrite the current pending 108. Once you get rid of that, you can execute all the pending directly in the web interface and then set the code as you currently have it.

// Create a Safe transaction with the provided parameters
  const safeTransaction = await safeSdk.createTransaction({
    transactions: [safeTransactionData],
    options: {
      nonce: 108
    }
  });

@Malikrehman00107
Copy link
Author

@faisal-anwar825 all good here ?

@Malikrehman00107
Copy link
Author

@dasanra we are still facing the issue with the nonce , every time transaction get stucked end up a mess in the backlog.

please propose a permanent and effective solution.

best regards

@dasanra
Copy link
Collaborator

dasanra commented May 6, 2024

@Malikrehman00107 from what I can see in the Safe you shared in the thread transactions are being executed sucessfully, so unless you provide more context we are not able to see any issue.

@dasanra
Copy link
Collaborator

dasanra commented May 10, 2024

After checking there are several transactions executed that seem automated we consider this issue as closed.

@dasanra dasanra closed this as completed May 10, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants