Skip to content

Commit 731d465

Browse files
committed
Direct RPC call
1 parent f79c6c8 commit 731d465

14 files changed

Lines changed: 416 additions & 66 deletions

File tree

ecsigner/ecsigner.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,3 +81,8 @@ func Unmarshal(bt []byte) (signer.Signer, error) {
8181
ecsigner.SignerAddress = common.BytesToAddress(crypto.PubkeyToAddress(privkey.PublicKey).Bytes())
8282
return ecsigner, nil
8383
}
84+
85+
// KeyContainer function
86+
func (ecsigner *ECSigner) GetKey() *ecdsa.PrivateKey {
87+
return ecsigner.SignerKey
88+
}

rpccalls/call.go

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package rpccalls
2+
3+
import (
4+
"context"
5+
"crypto/ecdsa"
6+
"fmt"
7+
"math/big"
8+
9+
"github.com/ethereum/go-ethereum/common"
10+
"github.com/ethereum/go-ethereum/core/types"
11+
"github.com/ethereum/go-ethereum/ethclient"
12+
"github.com/san-lab/go4337/state"
13+
)
14+
15+
type KeyContainer interface {
16+
GetKey() *ecdsa.PrivateKey
17+
}
18+
19+
func CreateSignedTransaction(rpc *state.RPCEndpoint, from, to common.Address, value *big.Int, calldata []byte, gasLimit uint64, key *ecdsa.PrivateKey) (*types.Transaction, error) {
20+
client, err := ethclient.Dial(rpc.URL)
21+
if err != nil {
22+
return nil, err
23+
}
24+
25+
gasPrice, err := client.SuggestGasPrice(context.Background())
26+
if err != nil {
27+
return nil, fmt.Errorf("could not get gas price: %v", err)
28+
}
29+
30+
nonce, err := client.PendingNonceAt(context.Background(), from)
31+
if err != nil {
32+
return nil, fmt.Errorf("could not get nonce: %v", err)
33+
}
34+
35+
tx := types.NewTransaction(nonce, to, value, gasLimit, gasPrice, calldata)
36+
37+
return types.SignTx(tx, types.NewEIP155Signer(rpc.ChainId), key)
38+
39+
}
40+
41+
func SendTransaction(rpc *state.RPCEndpoint, signedTx *types.Transaction) (*common.Hash, error) {
42+
client, err := ethclient.Dial(rpc.URL)
43+
if err != nil {
44+
return nil, fmt.Errorf("could not connect to rpc: %v", err)
45+
}
46+
err = client.SendTransaction(context.Background(), signedTx)
47+
if err != nil {
48+
return nil, fmt.Errorf("could not send tx: %v", err)
49+
}
50+
h := signedTx.Hash()
51+
return &h, nil
52+
}
53+
54+
func CreateAndSendTransaction(rpc *state.RPCEndpoint, from, to common.Address, value *big.Int, calldata []byte, gasLimit uint64, key KeyContainer) (*common.Hash, error) {
55+
signedTx, err := CreateSignedTransaction(rpc, from, to, value, calldata, gasLimit, key.GetKey())
56+
if err != nil {
57+
return nil, fmt.Errorf("could not create signed tx: %v", err)
58+
}
59+
return SendTransaction(rpc, signedTx)
60+
}

state/state.go

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package state
33
import (
44
"encoding/json"
55
"fmt"
6+
"math/big"
67
"os"
78
"reflect"
89
"sort"
@@ -25,12 +26,13 @@ var PackedUserOpV7Type reflect.Type
2526
type StateStruct struct {
2627
AddressBooks map[string]*AddressBook
2728
//Default Gas Costs?
28-
Signers []signer.Signer `json:"-"`
29-
SignersRaw []string
30-
ABIArts map[string]*AbiArtifacts //ABI strings memorized
31-
UserOps map[string]*userop.UserOperation
32-
ChainID uint64
33-
AlchApiKey string
29+
Signers []signer.Signer `json:"-"`
30+
SignersRaw []string
31+
ABIArts map[string]*AbiArtifacts //ABI strings memorized
32+
UserOps map[string]*userop.UserOperation
33+
ChainID uint64
34+
AlchApiKey string
35+
RPCEndpoints map[string]*RPCEndpoint
3436
}
3537

3638
//type ABIs map[string]string
@@ -50,6 +52,13 @@ type AbiArtifacts struct {
5052
MethodCalls map[string]*MethodCall `json:"-"`
5153
}
5254

55+
type RPCEndpoint struct {
56+
Name string
57+
URL string
58+
ChainId *big.Int
59+
APIKey string
60+
}
61+
5362
func init() {
5463
stateMux.Lock()
5564
defer stateMux.Unlock()
@@ -70,6 +79,9 @@ func init() {
7079
if State.UserOps == nil {
7180
State.UserOps = make(map[string]*userop.UserOperation)
7281
}
82+
if State.RPCEndpoints == nil {
83+
State.RPCEndpoints = make(map[string]*RPCEndpoint)
84+
}
7385

7486
//Add the Entrypoint abis
7587
v6arts, err := ParseABI(EntrypointV6, entrypoint.EntryPointV6AbiJson)

ui/abiui.go

Lines changed: 6 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -612,32 +612,22 @@ func EncodeConstructorParamsUI(callBackItem *Item, mabi *abi.ABI) (ret bool) {
612612
switch sel {
613613
case Back.Label:
614614
return
615-
case encodeItem.Label:
615+
case encodeItem.Label, encodeWithBytecodeItem.Label:
616616
data, err := userop.EncodeWithParams(mabi, "", values...)
617617
if err != nil {
618-
fmt.Println("Errrrorrr!!!!!", err)
618+
fmt.Println("Error encoding parameters", err)
619619
continue
620620
}
621-
if callBackItem != nil {
622-
callBackItem.Value = data
623-
ret = true
624-
return //a bit controversial hack
621+
if sel == encodeWithBytecodeItem.Label {
622+
btc := DeployBytecodeItem.Value.([]byte)
623+
data = append(btc, data...)
625624
}
626-
fmt.Printf("Encoded call: 0x%x\n", data)
627-
case encodeWithBytecodeItem.Label:
628-
data, err := userop.EncodeWithParams(mabi, "", values...)
629-
if err != nil {
630-
fmt.Println("Error encoding with bytecode!", err)
631-
continue
632-
}
633-
btc := DeployBytecodeItem.Value.([]byte)
634-
data = append(btc, data...)
635625
if callBackItem != nil {
636626
callBackItem.Value = data
637627
ret = true
638628
return //a bit controversial hack
639629
}
640-
fmt.Printf("Encoded call with bytecode: 0x%x\n", data)
630+
fmt.Printf("Encoded call: 0x%x\n", data)
641631

642632
default:
643633
it, ok := GetItem(sel, items)

ui/addressbookui.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,7 @@ func AddressFromBookUI(label string) (*common.Address, bool) {
1515
selectToRemove := false
1616
abook, ok := state.GetAddressBook(label)
1717
if !ok {
18-
fmt.Println("Invalid address book: ", label)
19-
return nil, false
18+
state.State.AddressBooks[label] = &state.AddressBook{}
2019
}
2120
normalLabel := "Select a " + label
2221
removeLabel := "Select a " + label + " to remove"

ui/rootui.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ package ui
22

33
import (
44
"fmt"
5+
"os"
56

7+
"github.com/chzyer/readline"
68
"github.com/manifoldco/promptui"
79
"github.com/san-lab/go4337/entrypoint"
810
"github.com/san-lab/go4337/state"
@@ -11,6 +13,25 @@ import (
1113
func init() {
1214
EntryPointItem.Value = entrypoint.E7Address
1315
ApiKeyItem.Value = state.State.AlchApiKey
16+
//Get rid of the bloody bell
17+
readline.Stdout = &stderr{}
18+
}
19+
20+
type stderr struct{}
21+
22+
func (s *stderr) Write(b []byte) (int, error) {
23+
if len(b) == 1 && b[0] == 7 {
24+
return 0, nil
25+
}
26+
return os.Stderr.Write(b)
27+
}
28+
29+
func (s *stderr) Close() error {
30+
return os.Stderr.Close()
31+
}
32+
33+
func init() {
34+
readline.Stdout = &stderr{}
1435
}
1536

1637
var PaymasterItem = &Item{Label: "Paymaster", Details: "Manage Paymaster settings"}
@@ -20,6 +41,7 @@ var SignerItem = &Item{Label: "Signer", Details: "Manage Signer settings"}
2041
var EntryPointItem = &Item{Label: "Entrypoint", Details: "Set Entrypoint"}
2142
var ApiKeyItem = &Item{Label: "Alchemy API Key", Details: "Set Alchemy API Key"}
2243
var SettingsItem = &Item{Label: "Settings", Details: "Paymasters, Signers, ChainID, ..."}
44+
var RPCEndpointsItem = &Item{Label: "RPC Endpoints", Details: "Manage RPC Endpoints"}
2345

2446
func RootUI() {
2547
items := []*Item{
@@ -67,6 +89,7 @@ func SettingsUI() {
6789
ChainIDItem,
6890
EntryPointItem,
6991
ApiKeyItem,
92+
RPCEndpointsItem,
7093
Back,
7194
}
7295
prompt := promptui.Select{
@@ -94,6 +117,8 @@ func SettingsUI() {
94117
case ApiKeyItem.Label:
95118
InputNewStringUI(ApiKeyItem)
96119
state.State.AlchApiKey = ApiKeyItem.Value.(string)
120+
case RPCEndpointsItem.Label:
121+
RPCEndpointsUI(nil)
97122
case Back.Label:
98123
return
99124
default:

ui/rpcui.go

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
package ui
2+
3+
import (
4+
"fmt"
5+
"math/big"
6+
"strings"
7+
8+
"github.com/manifoldco/promptui"
9+
"github.com/san-lab/go4337/state"
10+
)
11+
12+
// Maintain a map ChainID -> RPC Endpoint
13+
func RPCEndpointsUI(it *Item) {
14+
deleting := false
15+
AddRPCEnpointItem := &Item{Label: "Add RPC Endpoint", Details: "Add a new RPC Endpoint"}
16+
RemoveRPCEnpointItem := &Item{Label: "Remove RPC Endpoint", Details: "Select an RPC Endpoint to remove"}
17+
18+
for {
19+
items := []*Item{}
20+
for _, rpc := range state.State.RPCEndpoints {
21+
items = append(items, &Item{Label: fmt.Sprintf("%s/%v", rpc.Name, rpc.ChainId)})
22+
}
23+
if !deleting {
24+
items = append(items, AddRPCEnpointItem, RemoveRPCEnpointItem)
25+
}
26+
items = append(items, Back)
27+
prompt := promptui.Select{Label: "RPC Endpoints", Items: items, Templates: ItemTemplate, Size: 10}
28+
_, sel, err := prompt.Run()
29+
if err != nil {
30+
fmt.Println(err)
31+
return
32+
}
33+
switch sel {
34+
case Back.Label:
35+
return
36+
case RemoveRPCEnpointItem.Label:
37+
deleting = true
38+
continue
39+
case AddRPCEnpointItem.Label:
40+
AddRPCEnpointUI()
41+
return
42+
default:
43+
name := strings.Split(sel, "/")[0]
44+
if deleting {
45+
prpt := promptui.Prompt{Label: fmt.Sprintf("Are you sure you want to delete RPC Endpoint %s (yes/no)?", sel), Default: "no"}
46+
y, err := prpt.Run()
47+
if err == nil && y == "yes" {
48+
49+
delete(state.State.RPCEndpoints, name)
50+
state.State.Save()
51+
}
52+
deleting = false
53+
} else {
54+
v := EditRPCEnpointUI(name)
55+
if it != nil {
56+
it.Value = v
57+
return
58+
}
59+
}
60+
61+
}
62+
}
63+
64+
}
65+
66+
func AddRPCEnpointUI() {
67+
tItem := &Item{Label: "RPC Endpoint Name", Details: "Enter a name for the RPC Endpoint"}
68+
err := InputNewStringUI(tItem)
69+
if err != nil {
70+
fmt.Println(err)
71+
return
72+
}
73+
name, ok := tItem.Value.(string)
74+
if !ok {
75+
fmt.Println("Invalid value")
76+
return
77+
}
78+
tItem.Label = "RPC Endpoint URL"
79+
tItem.Details = "Enter the URL for the RPC Endpoint"
80+
tItem.Value = ""
81+
err = InputNewStringUI(tItem)
82+
if err != nil {
83+
fmt.Println(err)
84+
return
85+
}
86+
url, ok := tItem.Value.(string)
87+
if !ok {
88+
fmt.Println("Invalid value")
89+
return
90+
}
91+
tItem.Label = "Chain ID"
92+
tItem.Details = "Enter the Chain ID for the RPC Endpoint"
93+
tItem.Value = nil
94+
err = InputBigInt(tItem)
95+
if err != nil {
96+
fmt.Println(err)
97+
return
98+
}
99+
chainId, ok := tItem.Value.(*big.Int)
100+
101+
state.State.RPCEndpoints[name] = &state.RPCEndpoint{Name: name, URL: url, ChainId: chainId}
102+
state.State.Save()
103+
}
104+
105+
// TODO Error handling
106+
func EditRPCEnpointUI(name string) *state.RPCEndpoint {
107+
return state.State.RPCEndpoints[name]
108+
}

0 commit comments

Comments
 (0)