Ethers.js: How do I send contract transactions?

Created on 19 Mar 2019  路  22Comments  路  Source: ethers-io/ethers.js

I want to send a contract transaction through infura provider, but I need a signer to do so. The problem is that the infura provider doesn't support getSigner(). How can I send contract transactions through infura?

const TokenArtifacts = require('../build/contracts/Token.json');
const abi = TokenArtifacts.abi;
const address = TokenArtifacts.networks[3].address;

var ethers = require('ethers'); // Require the ethers library
var secrets = require("./../secrets.json");

var mnemonic = secrets.mnemonic; // x x x x x x x x x sight glance vintage";
var path = "m/44'/60'/0'/0/0";

var provider = new ethers.providers.InfuraProvider('ropsten');
var wallet = ethers.Wallet.fromMnemonic(mnemonic, path);
wallet.provider = provider; // Set the provider for the wallet

var newAddress = "0x15c72944b325a3E1c7a4DBdc6F883bD5948d3D9f";

var contract = new ethers.Contract(address, abi, provider);
var transaction = contract.transfer(newAddress, 10);
// var transaction = contract.totalSupply();

// Send the transaction
var sendTransactionPromise = wallet.sendTransaction(transaction);

sendTransactionPromise.then(function(tx) {
    console.log(tx);
});
discussion

Most helpful comment

The wallet is the signer, sorry, the third parameter to Contract should have been wallet.

var contract = new ethers.Contract(address, abi, wallet);

All 22 comments

Right, since INFURA does not have your private key, you need to supply it yourself. I think you are close. I'll just make a few comments and changes to your code:

const TokenArtifacts = require('../build/contracts/Token.json');
const abi = TokenArtifacts.abi;
const address = TokenArtifacts.networks[3].address;

var ethers = require('ethers'); // Require the ethers library
var secrets = require("./../secrets.json");

var mnemonic = secrets.mnemonic; // x x x x x x x x x sight glance vintage";
// *** Since you are using the default path, you do not need to specify it
//var path = "m/44'/60'/0'/0/0";

var provider = new ethers.providers.InfuraProvider('ropsten');
var wallet = ethers.Wallet.fromMnemonic(mnemonic);
// *** You cannot "set" a provider like this; all ethers.js objects are immutable, for safety and consistency reasons
//wallet.provider = provider; // Set the provider for the wallet
// *** You can use the connect method to create a new instance of a Wallet though:
wallet = wallet.connect(provider);

var newAddress = "0x15c72944b325a3E1c7a4DBdc6F883bD5948d3D9f";

// ** You want to connect you *signer* to the contract, not the *provider*
//var contract = new ethers.Contract(address, abi, provider);
var contract = new ethers.Contract(address, abi, signer);

// *** This returns a promise that calls sendTransaction on the wallet for you:
//var transaction = contract.transfer(newAddress, 10);
var sendTransactionPromise = contract.transfer(newAddress, 10);

// *** So there is no need for this;
// Send the transaction
// var sendTransactionPromise = wallet.sendTransaction(transaction);

sendTransactionPromise.then(function(tx) {
    console.log(tx);
});

Let me know if you still have any issues. :)

Thanks for the feedback.

Note that you haven't defined signer above. Adding and using signer produces the following error message 'Error: INFURA does not support signing (operation="getSigner")'. I'll post the full code snippet.

const TokenArtifacts = require('../build/contracts/Token.json');
const abi = TokenArtifacts.abi;
const address = TokenArtifacts.networks[3].address;

var ethers = require('ethers'); // Require the ethers library
var secrets = require("./../secrets.json");

var mnemonic = secrets.mnemonic; // "x x x x x x x x x x x x";
var path = "m/44'/60'/0'/0/0";

var provider = new ethers.providers.InfuraProvider('ropsten');
var wallet = ethers.Wallet.fromMnemonic(mnemonic, path);
var signer = provider.getSigner();

wallet = wallet.connect(provider); // Set the provider for the wallet

var newAddress = "0x15c72944b325a3E1c7a4DBdc6F883bD5948d3D9f";

var contract = new ethers.Contract(address, abi, signer);
var transaction = contract.transfer(newAddress, 10);
// var transaction = contract.totalSupply();

// Send the transaction
var sendTransactionPromise = wallet.sendTransaction(transaction);

sendTransactionPromise.then(function(tx) {
    console.log(tx);
});

I'm wondering what the workaround is.

The wallet is the signer, sorry, the third parameter to Contract should have been wallet.

var contract = new ethers.Contract(address, abi, wallet);

I think we are almost there.

I get the following error:

TypeError: wallet.connect is not a function

It is on the following line:

wallet = wallet.connect(provider); // Set the provider for the wallet

That鈥檚 odd. What鈥檚 version are you using?

Can you do a console.log of the wallet before calling connect.

Wallet {
privateKey: '0xaaaaabd5cae0c3aa546df2aa3272717c558031068d3cd895883e1828b02c87e9',
provider: [Getter/Setter],
defaultGasLimit: [Getter/Setter],
address: '0xF58E01Ac4134468F9Ad846034fb9247c6C131d8C',
sign: [Function],
mnemonic: 'x x x x x x x x x x x x',
path: 'm/44\'/60\'/0\'/0/0'
}

Ah, you are definitely using v3. Can you upgrade to v4? :)

Version 3.0.25 of ethers.js.

Sure

I am almost there. Now I just get "UnhandledPromiseRejectionWarning: Error: replacement fee too low". Any suggestions for code to include to make the transaction actually work?

Try incrementing the transaction nonce

https://github.com/ethers-io/ethers.js/issues/319

That means there is already another transaction with that nonce sent to the network, but not mined.

You must either:

  • replace the transaction; same nonce with a gas price that is 50% + 1 wei more than the original transaction
  • increase the nonce and wait for the old transaction to mine

If the old transaction have a very low gas price, you will have to replace it.

Now there is no error message, but you can't see the transaction on https://ropsten.etherscan.io/address/0xf92298d72afe68300ea065c0edadbb1a29804faa.

My previous comment might not be valid. I am now able to see the successful contract transactions at the url https://ropsten.etherscan.io/token/0xf92298d72afe68300ea065c0edadbb1a29804faa?a=0x15c72944b325a3e1c7a4dbdc6f883bd5948d3d9f, but getting a different issue which I'll research on my own. (UnhandledPromiseRejectionWarning: Error: replacement fee too low). Thanks for everything and let me know if there is some way I can help.

@dblockunity The "replacement fee too low" error indicates you have a transaction with the same nonce already. It sounds like you may be setting your gas price too low.

You can check current gas prices here: https://ethgasstation.info

Once you have sent a transaction to the network, you must either increase the nonce for the next transaction, or if you are trying to replace it, set the gasPrice to 50% + wei (or more), to bribe the network into choosing the new transaction over the old one, with the same nonce.

Makes sense.

I think this issue was resolved. Closing now. If you still have problems though, please feel free to re-open.

Thanks! :)

hi, i am new to ether.js i want to send ether from my react-native wallet to another metamask address, but it throws me error, i want to know if that is the right syntax to send transaction or not. I am attaching the error log

let provider = new ethers.providers.InfuraProvider("rinkeby",projectID);
let wallet = new ethers.Wallet(await AsyncStorage.getItem('walletPrivateKey'), provider);
console.log("*wlletAdress:",wallet.address);
let tx={
// Required unless deploying a contract (in which case omit)
to: '0x79980eB91462dd932E490F6927b008465c4aa8E6', // the target address or ENS name

// These are optional/meaningless for call and estimateGas
nonce: 0, // the transaction nonce
gasLimit: 0, // the maximum gas this transaction may spend
gasPrice: 0, // the price (in wei) per unit of gas

// These are always optional (but for call, data is usually specified)
data: "0x", // extra data for the transaction, or input for call
value: 0, // the amount (in wei) this transaction is sending
chainId: 4 // the network ID; usually added by a signer
}
console.log(tx);
const signedTransaction = wallet.sign(tx);
var txPromise = await wallet.sendTransaction(signedTransaction);
if(txPromise)console.log(txPromise);

WARN Possible Unhandled Promise Rejection (id: 0):
Error: invalid object key - _U (argument="transaction", value={"_U":0,"_V":0,"_W":null,"_X":null,"nonce":{"_U":0,"_V":0,"_W":null,"_X":null}}, key="_U", version=4.0.48)

i am using ether.js 4.0.48

@msohaib008 It's been quite a while since I've had to deal with ethers v4. Can you try upgrading to v5? I don't think wallet.sign would be the call you want to use, but if you switch to v5, I can provide more guidance as it is fresher in my mind. :)

okay i will try to upgrade to 5.0. thanks @ricmoo .
hope that it will not break my app

Was this page helpful?
0 / 5 - 0 ratings