0

I have an issue:

I want to receive some BOKKY tokens to test it. When I use simple wallets I just send ethers and receive tokens, everything works fine.

I also have my own smart contract (multisig wallet) It works fine when I transfer ethers to other accounts, however when I try to receive tokens same way as I did with simple accounts (send ethers from my contract to token contract address ) my transaction fails with error:

Out of gas

The code of smart contract is here:

pragma solidity 0.5.0;

import "./ERC20.sol";

contract MultiSigWallet {

 // address private _owner;
  mapping(address => uint) private _owners;

  uint constant MIN_SIGNATURES = 2;
  uint private transactionIdx;
  address payable private _commissionAddress;
  mapping(uint => address) private tokens;
  uint private currentToken;

  ERC20 private erc20iface;

  struct Transaction {
    address from;
    address payable to;
    address _contract;
    uint amount;
    uint  signatureCount;
    uint commission;
    mapping(address => uint) signatures;
  }

  mapping(uint => Transaction) private _transactions;
  uint[] private _pendingTransactions;

//   modifier isOwner() {
//     require(msg.sender == _owner);
//     _;
//   }

  modifier validOwner() {
//  require(msg.sender == _owner || _owners[msg.sender] == 1);
    require(_owners[msg.sender] == 1);
    _;
  }

  event DepositFunds(address from, uint amount);
  event TransactionCreated(address from, address to, uint amount, uint transactionId);
  event TransactionCompleted(address from, address to, uint amount, uint transactionId);
  event TransactionSigned(address by, uint transactionId);
  event TransferFailed(address from, address to, uint amount, uint transactionId);

  constructor(address service, address payable commissionAddress, address token) public {
   // _owner = msg.sender;
    _commissionAddress = commissionAddress;
    _owners[msg.sender] = 1;
    _owners[service] = 1;

    currentToken = 0;
    tokens[currentToken] = token;
    currentToken++;
  }

  function () external payable
  {
    emit DepositFunds(msg.sender, msg.value);
  }

  function withdraw(uint amount, uint commission, address token)
    public
      {
    transferTo(msg.sender,  amount, commission, token);
  }

  function transferTo(address payable to, uint amount, uint commission, address token)
    validOwner
    public
      {
    // require(address(this).balance >= amount);
    require(amount > 0);

    uint transactionId = transactionIdx++;

    Transaction memory transaction;
    transaction.from = msg.sender;
    transaction.to = to;
    transaction._contract = token;
    transaction.commission = commission;
    transaction.amount = amount;
    transaction.signatureCount = 0;

    _transactions[transactionId] = transaction;
    _pendingTransactions.push(transactionId);

    emit TransactionCreated(msg.sender, to, amount, transactionId);

    signTransaction(transactionId);
  }

  function getPendingTransactions()
    view
    validOwner
    public
    returns(uint[] memory)
    {
      return _pendingTransactions;
    }

  function signTransaction(uint transactionId)
    validOwner
    public
   {
    Transaction storage transaction = _transactions[transactionId];
    // Transaction must exist
    require(0x0000000000000000000000000000000000000000 != transaction.from);
    // Creator cannot sign the transaction
    // require(msg.sender != transaction.from);
    // sender haven't sign it already
    require(transaction.signatures[msg.sender] != 1);

    transaction.signatures[msg.sender] = 1;
    transaction.signatureCount++;

    emit TransactionSigned(msg.sender, transactionId);

    if(transaction.signatureCount >= MIN_SIGNATURES) {

      if(transaction._contract != 0x0000000000000000000000000000000000000000) {
        // if(amount_ > ERC20Interface.allowance(transaction.from, address(this))) {
        //   emit TransferFailed(from_, to_, amount_, transactionId);
        //   revert();
        // }
        erc20iface =  ERC20(transaction._contract);
        erc20iface.transfer(transaction.to, transaction.amount);
        erc20iface.transfer(_commissionAddress, transaction.commission);
      } else {
        require(address(this).balance >= transaction.amount);
        (transaction.to).transfer(transaction.amount);
        _commissionAddress.transfer(transaction.commission);
      }

      emit TransactionCompleted(transaction.from, transaction.to, transaction.amount, transactionId);
      deleteTransaction(transactionId);
    }
  }

  function deleteTransaction(uint transactionId)
    validOwner
    public
    {
      uint replace = 0;
      assert(_pendingTransactions.length > 0);
      for(uint i = 0; i < _pendingTransactions.length; i++) {
        if(replace == 1) {
          _pendingTransactions[i-1] = _pendingTransactions[i];
        } else if (transactionId == _pendingTransactions[i]) {
          replace = 1;
        }
    }
      assert(replace == 1);
      delete _pendingTransactions[_pendingTransactions.length - 1];
      _pendingTransactions.length--;
      delete _transactions[transactionId];
    }

  function walletBalance()
    view
    public
    returns (uint) {
      return address(this).balance;
    }

}

Code for web3 (tx creation):

const createMsigTxETH = async options => {
  const { targetAddress, contractAddress, amount, commission, initiatorAddress } = options;

  const provider = new Web3.providers.HttpProvider(eth);
  const web3 = new Web3(provider);

  const amountETH = web3.utils.toWei(amount.toString(), 'ether');
  const commissionETH = web3.utils.toWei(commission.toString(), 'ether');
  const msigWalletContract = new web3.eth.Contract(MSIG_ABI, contractAddress);
  const nonce = await web3.eth.getTransactionCount(initiatorAddress);
  const TOKEN_ADDR = '0x0000000000000000000000000000000000000000';

  const txObj = {
    nonce: web3.utils.toHex(nonce),
    gasPrice: web3.utils.toHex(web3.utils.toWei('10', 'gwei')),
    gasLimit: web3.utils.toHex(GAS),
    data: msigWalletContract.methods
      .transferTo(targetAddress, amountETH, commissionETH, TOKEN_ADDR)
      .encodeABI(),
    to: contractAddress
  };

  const tx = new Tx(txObj);
  return tx;
};

Code for Signing tx:

const provider = new Web3.providers.HttpProvider(eth);
    const web3 = new Web3(provider);

    const msigWalletContract = new web3.eth.Contract(MSIG_ABI, contractAddress);
    const nonce = await msigWalletContract.methods
      .getPendingTransactions()
      .call({ from: serviceAddress });
    if (!nonce.length) throw Error('ERROR NONCE ');
    const txObj = {
      nonce: web3.utils.toHex(nonce[nonce.length - 1]),
      gasPrice: web3.utils.toHex(web3.utils.toWei('10', 'gwei')),
      gasLimit: web3.utils.toHex(GAS),
      data: msigWalletContract.methods.signTransaction(nonce[nonce.length - 1]).encodeABI(),
      to: contractAddress
    };

    const tx = new Tx(txObj);
    tx.sign(Buffer.from(servicePrivateKey, 'hex'));

    const serializedTx = tx.serialize();
    const raw = `0x${serializedTx.toString('hex')}`;

    try {
      return new Promise((resolve, reject) => {
        web3.eth.sendSignedTransaction(raw).on('transactionHash', txHash => {
          resolve(txHash);
        });
      });
    } catch (err) {
      throw new Error(err);
    }
  }

Can anyone suggest what it can be?

Thank you!

  • Do you have code to show? – Miguel Mota Mar 21 at 17:41
  • Although the gas price is for sure can be different for each and state. Can you show code, maybe you are using some loops or more function calls which cause more gas. – Abdullah Aziz Mar 22 at 0:40
  • @MiguelMota sure, here is the code (tx fails while signTransaction(..) is called) – newbie Mar 22 at 7:29
  • @AbdullahAziz added code – newbie Mar 22 at 7:29
  • can you explain a bit more, i could not get your point that when you call the function same way to receive ether? also please mention which function call is generating this error and which way call is success and which way is failed. – Abdullah Aziz Mar 22 at 7:48
1

You called signTransaction on your multisig wallet. It seems that this signature was the last one required, so your multisig wallet tried to execute transaction, i.e. send 1 ether to address 0x101848D5C5bBca18E6b4431eEdF6B95E9ADF82FA using transfer function. Function transfer allows at most 2300 gas to be consumed by target contract, but contract you were sending ether to tried to consume more, so transfer failed.

It looks like your multisig wallet does not support sending ether to smart contracts that need more than 2300 gas to process incoming ether transfers. You may have to modify code of your multisig wallet in order to support such transfers.

Note, that OpenZeppelin's multisig walled uses call instead of transfer to avoid such problems: https://github.com/OpenZeppelin/openzeppelin-solidity/blob/v1.2.0/contracts/MultisigWallet.sol#L63.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.