Hi !
I'm using ethers in combination with web3 in order to achieve the following:
It seems fine with simple methods, like those that pass integers. But I got issues with more complex data. The code I use is this:
w1 = new ethers.Wallet(priv, provider);
iface = new ethers.utils.Interface(contract_abi);
txdata = iface.functions[method].encode(args);
signonce = await w1.getTransactionCount();
tx = {
"from" : addr,
"to" : contract_address,
"gasLimit" : 1500000,
"gasPrice" : 2,
"nonce" : signonce,
"data" : txdata,
"value" : 0,
};
web3 = new Web3( new Web3.providers.HttpProvider(rpcEndpoint));
account = await web3.eth.accounts.privateKeyToAccount(priv);
stx1 = await web3.eth.accounts.signTransaction(tx, priv);
web3.eth.sendSignedTransaction(stx1.rawTransaction)
This part works well. The problem is creating agrs to be encoded.
If a prameter is an int or a string, no problem.
var args = {"i":32};
But how do I encode bytes? arrays? addresses?
I get errors about the format/type/length of the parameter
Any help/advice/feedback would be greatly appreciated.
TIA
Here some extra attempts that are failing
args["_itemId"]
1.0857805267182605e+77
iface
Object { functions: {…}, events: {…}, abi: (57) […], deployFunction: {…}, … }
method
"transferEnable"
args
Object { _itemId: 1.0857805267182605e+77 }
iface.functions[method]
Object { inputs: (1) […], outputs: [], gas: undefined, payable: false, type: "transaction", signature: "transferEnable(bytes32)", sighash: "0x0eb8ed07", … }
iface.functions[method].encode({"_itemId": 1})
Error: invalid input argument (arg=undefined, reason="types/values length mismatch", value={"types":[{"name":"_itemId","type":"bytes32"}],"values":{"_itemId":1}}, version=4.0.11) ethers-v4.min.js:1:16093
Source map error: request failed with status 404
Resource URL: http://localhost/dist/ethers-v4.min.js
Source Map URL: ethers.min.js.map
iface.functions.transferEnable.encode({"_itemId": 1})
Error: invalid input argument (arg=undefined, reason="types/values length mismatch", value={"types":[{"name":"_itemId","type":"bytes32"}],"values":{"_itemId":1}}, version=4.0.11) ethers-v4.min.js:1:16093
iface.functions.transferEnable.encode({"_itemId": 0x1})
Error: invalid input argument (arg=undefined, reason="types/values length mismatch", value={"types":[{"name":"_itemId","type":"bytes32"}],"values":{"_itemId":1}}, version=4.0.11) ethers-v4.min.js:1:16093
iface.functions.transferEnable.encode({"_itemId": 0xf00d000000000000cabba51af28578cf7f88a2966f64759b7f6a0ed7e000664a})
Error: invalid input argument (arg=undefined, reason="types/values length mismatch", value={"types":[{"name":"_itemId","type":"bytes32"}],"values":{"_itemId":1.0857805267182605e+77}}, version=4.0.11) ethers-v4.min.js:1:16093
iface.functions.transferEnable.encode({"_itemId": '0xf00d000000000000cabba51af28578cf7f88a2966f64759b7f6a0ed7e000664a'})
Error: invalid input argument (arg=undefined, reason="types/values length mismatch", value={"types":[{"name":"_itemId","type":"bytes32"}],"values":{"_itemId":"0xf00d000000000000cabba51af28578cf7f88a2966f64759b7f6a0ed7e000664a"}}, version=4.0.11) ethers-v4.min.js:1:16093
iface.functions.transferEnable.encode({"_itemId": 'f00d000000000000cabba51af28578cf7f88a2966f64759b7f6a0ed7e000664a'})
Error: invalid input argument (arg=undefined, reason="types/values length mismatch", value={"types":[{"name":"_itemId","type":"bytes32"}],"values":{"_itemId":"f00d000000000000cabba51af28578cf7f88a2966f64759b7f6a0ed7e000664a"}}, version=4.0.11)
Sorry, my bad
Found the issue. Args must be in the form
args = Object.values({...object...});
Heya!
I also recommend you try out v5, which has a. simpler API for dealing with Interface and Contracts for offline signing.
w1 = new ethers.Wallet(priv, provider);
contract = new ethers.Contract(contract_address, contract_abi, w1);
overrides = {
nonce: w1.getTransactionCount(),
gasLimit: 1500000,
gasPrice: utils.parseUnits("2.0", "gwei"),
value: 0
};
unsignedTx = await contract.populateTransaction[method](...args, overrides);
signedTx = w1.signTransaction(unsignedTx);
provider.sendTransaction(signedTx);
This is all doable in v4 too, but v5 specifically added some API to make offline-signing easier. :)
Closing this now, but feel free to re-open if you feel it hasn't been addressed.
Thanks! :)
hi ,I'm trying to create rawHex of trasnction (offline mod)
let value = 0 // for test
let toAddress = '0x663b58B1620C545217d3df30122fa7ED1346fFf0'
let contractAddress = '0xF6fF95D53E08c9660dC7820fD5A775484f77183A' // token contract address
var numberOfDecimals = 8
var numberOfTokens = ethers.utils.parseUnits(value.toString(), numberOfDecimals)
let provider = ethers.getDefaultProvider('ropsten')
let wallet = new ethers.Wallet(this.privatekeyETH,provider)
const abi = [ "function transfer(address,uint)",
"function populateTransaction(address,uint)"]
const contract = new ethers.Contract(contractAddress, abi, wallet)
let transaction = {
gasLimit: Number(this.gasLimit),
gasPrice: ethers.BigNumber.from(this.gasPrice*1000000000), //1Gwei =1000,000,000
// nonce: this.Nonces
// to: toAddress,
// value: numberOfTokens,
}
let unsignedTx = await contract.functions.populateTransaction(toAddress,numberOfTokens,transaction)
await wallet.signTransaction(unsignedTx).then((tx) => {
console.log(tx)
// this.showLoading = ''
}).catch((e) => {
// this.showLoading = ''
console.log("errror = "+e);
})
The error I got is ###
```json
Error: invalid object key - v (argument="transaction:v", value=
{"nonce":45,
"gasPrice":{"_hex":"0xb2d05e00","_isBigNumber":true},
"gasLimit":{"_hex":"0x0493e0","_isBigNumber":true},
"to":"0xF6fF95D53E08c9660dC7820fD5A775484f77183A",
"value":{"_hex":"0x00","_isBigNumber":true},
"data":"0x2b1ad867000000000000000000000000663b58b1620c545217d3df30122fa7ed1346fff00000000000000000000000000000000000000000000000000000000000000000",
"chainId":3,
"v":42,
"r":"0xf2bf576f42430baa39df8d63d01b3308c875ae38e2eb66a64f42347ff6743c91",
"s":"0x5be3f94b6ed21d93f65c86d1f1fa8bb9a7ed69a4c78ec9879a70ab2684ab7372",
"hash":"0xac08e5f9d7c8796317ff96290effbf41e846eef077cbca3527c652bcc5b65ce9"
},
code=INVALID_ARGUMENT, version=properties/5.0.1)
@xzbnm
// This line returns the transaction object, which is already signed and sent
let unsignedTx = await contract.functions.populateTransaction(toAddress,numberOfTokens,transaction)
// Just replace that line with bellow one, it should probably work in your code
let unsignedTx = await contract.populateTransaction.populateTransaction(toAddress,numberOfTokens,transaction)
Edit: Hey, I got a feeling that you probably don't have a populateTransaction in a smart contract and you actually meant to create a populated tx for transfer method? If that's the case:
let unsignedTx = await contract.populateTransaction.transfer(toAddress,numberOfTokens,transaction)
```javascript
let unsignedTx = await contract.populateTransaction.transfer(toAddress,numberOfTokens,transaction)
````
This resolved my issue! Thanks!
Most helpful comment
@xzbnm
Edit: Hey, I got a feeling that you probably don't have a
populateTransactionin a smart contract and you actually meant to create a populated tx fortransfermethod? If that's the case: