package contracts import ( "context" "math/big" ethereum "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" ) type RPCClient interface { CallContext(ctx context.Context, result interface{}, method string, args ...interface{}) error } type ContractCaller struct { c RPCClient } func NewContractCaller(c RPCClient) *ContractCaller { return &ContractCaller{ c: c, } } // CodeAt returns the contract code of the given account. // The block number can be nil, in which case the code is taken from the latest known block. func (cc *ContractCaller) CodeAt(ctx context.Context, account common.Address, blockNumber *big.Int) ([]byte, error) { var result hexutil.Bytes err := cc.c.CallContext(ctx, &result, "eth_getCode", account, "latest") return result, err } // CallContract executes a message call transaction, which is directly executed in the VM // of the node, but never mined into the blockchain. // // blockNumber selects the block height at which the call runs. It can be nil, in which // case the code is taken from the latest known block. Note that state from very old // blocks might not be available. func (cc *ContractCaller) CallContract(ctx context.Context, msg ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) { var hex hexutil.Bytes err := cc.c.CallContext(ctx, &hex, "eth_call", toCallArg(msg), "latest") if err != nil { return nil, err } return hex, nil } func toCallArg(msg ethereum.CallMsg) interface{} { arg := map[string]interface{}{ "from": msg.From, "to": msg.To, } if len(msg.Data) > 0 { arg["data"] = hexutil.Bytes(msg.Data) } if msg.Value != nil { arg["value"] = (*hexutil.Big)(msg.Value) } if msg.Gas != 0 { arg["gas"] = hexutil.Uint64(msg.Gas) } if msg.GasPrice != nil { arg["gasPrice"] = (*hexutil.Big)(msg.GasPrice) } return arg }