All tutorials show how to impersonate an account using hardhat and etherjs. In my case I create a local mainnet fork and run the tests and create TX using golang so interested to know how to impersonate without using etherjs
3 Answers
What is mentioned in the hardhard reference is the parameters for a json rpc call. This is independant of ethers and can be performed with any http client in any language.
Example using cUrl
curl --location --request POST 'localhost:8545' \
--header 'Content-Type: application/json' \
--data-raw '{
"jsonrpc": "2.0",
"id": "1",
"method": "hardhat_impersonateAccount",
"params": ["0x364d6D0333432C3Ac016Ca832fb8594A8cE43Ca6"]
}'
Example using native go (autogenerated by postman :D ):
package main
import (
"fmt"
"strings"
"net/http"
"io/ioutil"
)
func main() {
url := "localhost:8545"
method := "POST"
payload := strings.NewReader({ "jsonrpc": "2.0", "id": "1", "method": "hardhat_impersonateAccount", "params": ["0x364d6D0333432C3Ac016Ca832fb8594A8cE43Ca6"] })
client := &http.Client {
}
req, err := http.NewRequest(method, url, payload)
if err != nil {
fmt.Println(err)
return
}
req.Header.Add("Content-Type", "application/json")
res, err := client.Do(req)
if err != nil {
fmt.Println(err)
return
}
defer res.Body.Close()
body, err := ioutil.ReadAll(res.Body)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(string(body))
}
Probably a better approach for go would be to use one of the wbe3 libraries (e.g. web3go -> https://medium.com/coinmonks/web3-go-part-1-31c68c68e20e)
- 4,934
- 2
- 7
- 25
Based on your comment, I think there are two things you're asking:
- How do I impersonate an account?
- How do I send a transaction from an impersonated account?
The tl;dr is what Richard said in their answer and in the comments: use the hardhat_impersonateAccount RPC method and then send a transaction with eth_sendTransaction, setting the from field to the address impersonated account. I'll try to expand on what this means.
When you send a transaction from a wallet, what's going on under the hood is this:
- The wallet uses your private key to sign a transaction.
- The signed transaction can be sent to a node using
eth_sendRawTransaction
That will work with basically any node.
But you also have the eth_sendTransaction, where you don't send a signed transaction. Instead, you send an object that says "send a transaction from this address, to this other address, with this value and data", etc.
Obviously this won't work in any node! If it did, anyone could send transactions from any address.
But there are some scenarios where this method can be used. For example, in a development node like the Hardhat network, some addresses are "unlocked". These are the addresses that npx hardhat node shows you when you start a node. An eth_sendTransaction call that uses one of those addresses as the from field will be accepted. What happens under the hood is that the Hardhat node has access to the private key of the address, so it can sign it before mining it.
You could also use the private keys of those addresses and send the same transaction by signing it and sending it with eth_sendRawTransaction. There's basically no difference between these two approaches.
Now, as I mentioned before, when you impersonate an account with hardhat_impersonateAccount you can use eth_sendTransaction to send a transaction from the impersonated account. But what happens under the hood is different. Hardhat will see that the from address is impersonated and accept the transaction even if it's not correctly signed. This means that the block which includes that transaction is technically violating the consensus rules.
And, unlike what happens with the unlocked accounts, there's no equivalent way of doing this with eth_sendRawTransaction, because that would mean you have the private key of the address (and, if you do, there's no point in impersonating the address in the first place).
- 2,862
- 17
- 27
I've faced the same issue and found that solution:
// Instantiate a contract:
const TetherToken = artifacts.require("TetherToken");
tetherToken = await TetherToken.new(1000, 'TetherToken', 'USDT', 20);
// Get methods signatures:
console.log(tetherToken.methods);
// Call method "approve" with two parameters from account 0x3c44cdddb6a900fa2b585dd299e03d12fa4293bc:
await tetherToken.methods["approve(address,uint256)"](someAddress, 5, { from: "0x3c44cdddb6a900fa2b585dd299e03d12fa4293bc" });
eth_sendTransactionrpc call. it is not possible to get a real signature. – Richard Jan 08 '22 at 17:14