3

I'd like to execute an erc20 contract method without unlocking a wallet, signing the transaction inside my code. Here is how the code should look like:

  public async send(sender: string, receiver: string, value: number, key: string)
    : PromiEvent<object> {
    return this.contract.methods.transfer(receiver, value)
      .send({ from: sender });
  }

the current code doesn't allow me to sign manually, it assumes the sender address is unlocked. How can I sign send manually?

1

3 Answers 3

3
  public async send(sender: string, receiver: string, value: number, key: string)
    // @ts-ignore: PromiEvent extends Promise
    : PromiEvent<TransactionReceipt> {
    const query = this.contract.methods.transfer(receiver, value);
    const encodedABI = query.encodeABI();
    const signedTx = await this.web3.eth.accounts.signTransaction(
      {
        data: encodedABI,
        from: sender,
        gas: 2000000,
        to: this.contract.options.address,
      },
      key,
      false,
    );
    // @ts-ignore: property exists
    return this.web3.eth.sendSignedTransaction(signedTx.rawTransaction);
  }
2

You can use truffle-hdwallet-provider on top of your HttpProvider. HDWallet Providers will then sign the transaction before broadcasting it out to the provider. It is just a simple wrapper on top of a Provider. Below is my index.js for getting any contract.

let HDWalletProvider = require('truffle-hdwallet-provider')
  , Web3 = require('web3')
  , path = require('path');

let settings = require('../settings');

function getWeb3Provider() {
  return new HDWalletProvider(settings.mnemonic, 'https://network.infura.io/apikey');
}

async function getContract(abi) {
  let provider = getWeb3Provider();

  let web3 = new Web3(provider);
  let artifact = require(path.join(settings.artifactsDir, abi));
  let contract = new web3.eth.Contract(artifact.abi, artifact.networks[settings.networkId].address);
  contract.setProvider(provider);
  return contract
}

module.exports = {
  getContract
};

You can then use your functions as is, they'll all be signed by the first private key generated by the mnemonic phase before broadcasting out. If you do not have the mnemonic phase, it is trivial to fork truffle-hdwallet-provider (it is a 60 line module) and have it take a private key instead of a mnemonic phase.

2
  • hm.. sounds like not the most straight forward solution, but maybe I can find something usefull in truffle code. I need to use a private key and not a mnemonic
    – jeff
    Jun 21, 2018 at 8:35
  • @jeff truffle-hdwallet-provider is a 60 line module and my personal opinion is that it is the neatest way. You don't have to then repeat the signing part of the transaction in every function... Having the provider deal with signing is extremely reusable and no repetition of code! You could have a generic function that deals with signing but you still have to call that function from all over the place whereas waller-providers seem more natural. Jun 21, 2018 at 17:06
0

This is my implementation using "@truffle/hdwallet-provider": "^2.0.3", "web3": "^1.6.1",


function getWeb3Provider() {
  return new HDWalletProvider({
    privateKeys: [NFT_MINTER_ACCOUNT_PRIVATE_KEY],
    providerOrUrl: BSC_RPC_ENDPOINT,
  });
}

const web3 = new Web3(BSC_RPC_ENDPOINT);
const contract = new web3.eth.Contract(
      jsonContractABI as unknown as AbiItem[],
      NFT_CONTRACT_ADDRESS
    );
contract.setProvider(getWeb3Provider());

then in send methods

contract.methods.safeMint(receiverAddress, itemUri).send({
      from: NFT_MINTER_ACCOUNT,
    });

in call methods

contract.methods.balanceOf(address).call();

Thank @manan-mehta

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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