diff --git a/.github/coverage-config.yaml b/.github/coverage-config.yaml index 55c7435..425c49b 100644 --- a/.github/coverage-config.yaml +++ b/.github/coverage-config.yaml @@ -1,5 +1,6 @@ exclude: # Exclude files or packages matching their paths paths: - - \.pb\.go$ # excludes all protobuf generated files - - ^contracts # exclude package `contracts` \ No newline at end of file + - \.pb\.go$ # excludes all protobuf generated files + - ^contracts # exclude package `contracts` + - fork.go # exclude file `fork.go` \ No newline at end of file diff --git a/README.md b/README.md index bf89fbc..dde4716 100644 --- a/README.md +++ b/README.md @@ -11,12 +11,15 @@ - [x] Set code for address - [x] Start impersonate an address - [x] Stop impersonate an address +- [x] Impersonate a txn(require impersonate account) - [ ] Impersonate and make only 1 txn - [ ] Write Erc721 balance ## How to Use: ```go - forkRpcEndpoint, err := foundryutils.StartFork("https://rpc.ankr.com/eth", nil) + // if you want to fork on test code, you can use `StartFork` function + // otherwise, you can run anvil separately then call `SetupClient` + forkRpcEndpoint, err := foundryutils.StartFork("https://rpc.ankr.com/eth", foundryutils.ForkOpts{}) if err != nil { // do something with err } diff --git a/cheat/cheat.go b/cheat/cheat.go index f885b47..46038e4 100644 --- a/cheat/cheat.go +++ b/cheat/cheat.go @@ -1,6 +1,7 @@ package cheat import ( + "fmt" "math/big" "github.com/ethereum/go-ethereum/common" @@ -39,3 +40,22 @@ func StartImpersonateAccount(account common.Address) error { func StopImpersonateAccount(account common.Address) error { return client.GlobalClient.RpcClient.Call(nil, "anvil_stopImpersonatingAccount", account.Hex()) } + +// make an impersonate txn +func SendImpersonateTxn(from, to common.Address, gas uint64, value, gasPrice *big.Int, data []byte) error { + return client.GlobalClient.RpcClient.Call(nil, "eth_sendTransaction", + struct { + From string `json:"from"` + To string `json:"to"` + Value string `json:"value"` + Gas string `json:"gas"` + GasPrice string `json:"gasPrice"` + }{ + From: from.Hex(), + To: to.Hex(), + Value: hexutil.Encode(value.Bytes()), + Gas: fmt.Sprintf("0x%x", gas), + GasPrice: hexutil.Encode(gasPrice.Bytes()), + }, + ) +} diff --git a/cheat/cheat_test.go b/cheat/cheat_test.go index a87c72a..1a7925d 100644 --- a/cheat/cheat_test.go +++ b/cheat/cheat_test.go @@ -2,7 +2,6 @@ package cheat import ( "context" - "fmt" "io" "math/big" "os/exec" @@ -71,23 +70,8 @@ func (s *CheatSuite) TestStartImpersonateAccount() { s.Assert().NoError( StartImpersonateAccount(helper.DummyAccount), ) - // tx := types.NewTx(0, common.HexToAddress("0x08081999"), big.NewInt(1000000000000000000), 21000, big.NewInt(30000000000), nil) s.Assert().NoError( - client.GlobalClient.RpcClient.Call(nil, "eth_sendTransaction", - struct { - From string `json:"from"` - To string `json:"to"` - Value string `json:"value"` - Gas string `json:"gas"` - GasPrice string `json:"gasPrice"` - }{ - From: helper.DummyAccount.Hex(), - To: common.HexToAddress("0x08081999").Hex(), - Value: hexutil.Encode(big.NewInt(1000000000000000000).Bytes()), - Gas: fmt.Sprintf("0x%x", 21000), - GasPrice: hexutil.Encode(big.NewInt(10000000000000).Bytes()), - }, - ), + SendImpersonateTxn(helper.DummyAccount, common.HexToAddress("0x08081999"), 21000, big.NewInt(1000000000000000000), big.NewInt(10000000000000), nil), ) b, err := client.GlobalClient.EthClient.BalanceAt(context.Background(), common.HexToAddress("0x08081999"), nil) s.Assert().NoError(err) diff --git a/fork.go b/fork.go index 607d01e..bc4b704 100644 --- a/fork.go +++ b/fork.go @@ -12,37 +12,39 @@ var ( mu sync.Mutex ) +type ForkOpts struct { + Port string + BlockNumber *big.Int +} + // start fork a network for testing purposes -func StartFork(forkUrl string, port string, blockNumber *big.Int) (string, error) { +func StartFork(forkUrl string, opt ForkOpts) (string, error) { _, err := exec.LookPath("anvil") if err != nil { return "", err } - if port == "" { - port = "8545" - } - mu.Lock() if forkProcess != nil { - forkProcess.Process.Kill() - if err := forkProcess.Wait(); err != nil { - return "", fmt.Errorf("stop old fork process failed: %v", err.Error()) - } + return "", fmt.Errorf("fork process already exists") + } + if opt.Port == "" { + opt.Port = "8545" } args := []string{ "--fork-url", forkUrl, - "--port", port, + "--port", opt.Port, } - if blockNumber != nil { + if opt.BlockNumber != nil { args = append(args, []string{ - "--fork-block-number", blockNumber.String(), + "--fork-block-number", opt.BlockNumber.String(), }...) } - forkProcess = exec.Command("anvil", args...) + mu.Lock() + forkProcess = exec.Command("anvil", args...) // #nosec defer mu.Unlock() if err := forkProcess.Start(); err != nil { return "", err } - return fmt.Sprintf("http://localhost:%s", port), nil + return fmt.Sprintf("http://localhost:%s", opt.Port), nil } // stop current fork process @@ -50,7 +52,9 @@ func StopFork() error { mu.Lock() defer mu.Unlock() if forkProcess != nil { - forkProcess.Process.Kill() + if err := forkProcess.Process.Kill(); err != nil { + return fmt.Errorf("kill old fork process failed: %v", err.Error()) + } if err := forkProcess.Wait(); err != nil { return fmt.Errorf("stop old fork process failed: %v", err.Error()) }