From 428b6e8c2c6b2a77839cd9d8eebf8eae8cd5b6f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Duchesneau?= Date: Tue, 16 Jul 2024 17:05:21 -0400 Subject: [PATCH] add sei-mainnet, few tiny fixes --- ethfull/apicalls.go | 51 ++++- ethfull/chain_configs.go | 198 ++++++++++-------- ethfull/convo.go | 4 - ethfull/convo_test.go | 2 +- msgwrap.go | 2 +- pb/sf/codegen/conversation/v1/conversation.go | 2 +- 6 files changed, 160 insertions(+), 99 deletions(-) diff --git a/ethfull/apicalls.go b/ethfull/apicalls.go index 32ab092..a601c3f 100644 --- a/ethfull/apicalls.go +++ b/ethfull/apicalls.go @@ -14,6 +14,7 @@ import ( "github.com/streamingfast/dhttp" "github.com/streamingfast/eth-go" + "github.com/tidwall/gjson" ) var etherscanAPIKey = os.Getenv("SUBDEV_ETHERSCAN_API_KEY") @@ -29,6 +30,13 @@ func getContractABIFollowingProxy(ctx context.Context, contractAddress string, c return cachedABI, nil } + if chain.ApiEndpointDirect { + abi, abiContent, err := getContractABIDirect(ctx, contractAddress, chain.ApiEndpoint) + if err != nil { + return nil, err + } + return &ABI{abi, abiContent}, nil + } abi, abiContent, wait, err := getContractABI(ctx, contractAddress, chain.ApiEndpoint) if err != nil { return nil, err @@ -80,6 +88,39 @@ func getContractABIFollowingProxy(ctx context.Context, contractAddress string, c return &ABI{abi, abiContent}, nil } +func getContractABIDirect(ctx context.Context, address string, endpoint string) (*eth.ABI, string, error) { + url := fmt.Sprintf("%s/%s", endpoint, address) + fmt.Println("getting from url", url) + req, err := http.NewRequestWithContext(ctx, "GET", url, nil) + if err != nil { + return nil, "", fmt.Errorf("new request: %w", err) + } + + res, err := httpClient.Do(req) + if err != nil { + return nil, "", fmt.Errorf("getting contract abi: %w", err) + } + defer res.Body.Close() + + type Response struct { + Abi []byte `json:"abi"` + } + + data, err := io.ReadAll(res.Body) + if err != nil { + panic(err) + } + + abiContent := gjson.GetBytes(data, "abi").String() + + ethABI, err := eth.ParseABIFromBytes([]byte(abiContent)) + if err != nil { + return nil, "", fmt.Errorf("parsing abi %q: %w", abiContent, err) + } + return ethABI, abiContent, err + +} + func getContractABI(ctx context.Context, address string, endpoint string) (*eth.ABI, string, *time.Timer, error) { req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("%s/api?module=contract&action=getabi&address=%s&apiKey=%s", endpoint, address, etherscanAPIKey), nil) if err != nil { @@ -197,12 +238,12 @@ func getContractInitialBlock(ctx context.Context, chain *ChainConfig, contractAd req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("%s/api?module=account&action=txlist&address=%s&page=1&offset=1&sort=asc&apikey=%s", chain.ApiEndpoint, contractAddress, etherscanAPIKey), nil) if err != nil { - return 0, fmt.Errorf("new request: %w", err) + return chain.FirstStreamableBlock, fmt.Errorf("new request: %w", err) } res, err := httpClient.Do(req) if err != nil { - return 0, fmt.Errorf("failed request to etherscan: %w", err) + return chain.FirstStreamableBlock, fmt.Errorf("failed request to etherscan: %w", err) } defer res.Body.Close() @@ -216,16 +257,16 @@ func getContractInitialBlock(ctx context.Context, chain *ChainConfig, contractAd var response Response if err := json.NewDecoder(res.Body).Decode(&response); err != nil { - return 0, fmt.Errorf("unmarshaling: %w", err) + return chain.FirstStreamableBlock, fmt.Errorf("unmarshaling: %w", err) } if len(response.Result) == 0 { - return 0, fmt.Errorf("empty result from response %v", response) + return chain.FirstStreamableBlock, fmt.Errorf("empty result from response %v", response) } blockNum, err := strconv.ParseUint(response.Result[0].BlockNumber, 10, 64) if err != nil { - return 0, fmt.Errorf("parsing block number: %w", err) + return chain.FirstStreamableBlock, fmt.Errorf("parsing block number: %w", err) } return blockNum, nil diff --git a/ethfull/chain_configs.go b/ethfull/chain_configs.go index 6f62fe0..a1c9b36 100644 --- a/ethfull/chain_configs.go +++ b/ethfull/chain_configs.go @@ -9,13 +9,15 @@ import ( ) type ChainConfig struct { - ID string // Public - DisplayName string // Public - ExplorerLink string - ApiEndpoint string - FirehoseEndpoint string - Network string - SupportsCalls bool + ID string // Public + DisplayName string // Public + ExplorerLink string + ApiEndpoint string + ApiEndpointDirect bool + FirehoseEndpoint string + FirstStreamableBlock uint64 + Network string + SupportsCalls bool abiCache map[string]*ABI initialBlockCache map[string]uint64 @@ -25,104 +27,126 @@ var ChainConfigs []*ChainConfig var ChainConfigByID = map[string]*ChainConfig{ "mainnet": { - DisplayName: "Ethereum Mainnet", - ExplorerLink: "https://etherscan.io", - ApiEndpoint: "https://api.etherscan.io", - FirehoseEndpoint: "mainnet.eth.streamingfast.io:443", - Network: "mainnet", - abiCache: make(map[string]*ABI), - initialBlockCache: make(map[string]uint64), - SupportsCalls: true, + DisplayName: "Ethereum Mainnet", + ExplorerLink: "https://etherscan.io", + ApiEndpoint: "https://api.etherscan.io", + FirehoseEndpoint: "mainnet.eth.streamingfast.io:443", + FirstStreamableBlock: 0, + Network: "mainnet", + abiCache: make(map[string]*ABI), + initialBlockCache: make(map[string]uint64), + SupportsCalls: true, }, "bnb": { - DisplayName: "BNB", - ExplorerLink: "https://bscscan.com", - ApiEndpoint: "https://api.bscscan.com", - FirehoseEndpoint: "bnb.streamingfast.io:443", - Network: "bsc", - abiCache: make(map[string]*ABI), - initialBlockCache: make(map[string]uint64), - SupportsCalls: true, + DisplayName: "BNB", + ExplorerLink: "https://bscscan.com", + ApiEndpoint: "https://api.bscscan.com", + FirehoseEndpoint: "bnb.streamingfast.io:443", + FirstStreamableBlock: 0, + Network: "bsc", + abiCache: make(map[string]*ABI), + initialBlockCache: make(map[string]uint64), + SupportsCalls: true, }, "polygon": { - DisplayName: "Polygon", - ExplorerLink: "https://polygonscan.com", - ApiEndpoint: "https://api.polygonscan.com", - FirehoseEndpoint: "polygon.streamingfast.io:443", - Network: "polygon", - abiCache: make(map[string]*ABI), - initialBlockCache: make(map[string]uint64), - SupportsCalls: true, + DisplayName: "Polygon", + ExplorerLink: "https://polygonscan.com", + ApiEndpoint: "https://api.polygonscan.com", + FirehoseEndpoint: "polygon.streamingfast.io:443", + FirstStreamableBlock: 0, + Network: "polygon", + abiCache: make(map[string]*ABI), + initialBlockCache: make(map[string]uint64), + SupportsCalls: true, }, "amoy": { - DisplayName: "Polygon Amoy Testnet", - ExplorerLink: "https://www.okx.com/web3/explorer/amoy", - ApiEndpoint: "", - FirehoseEndpoint: "amoy.substreams.pinax.network:443", - Network: "amoy", - abiCache: make(map[string]*ABI), - initialBlockCache: make(map[string]uint64), - SupportsCalls: true, + DisplayName: "Polygon Amoy Testnet", + ExplorerLink: "https://www.okx.com/web3/explorer/amoy", + ApiEndpoint: "", + FirehoseEndpoint: "amoy.substreams.pinax.network:443", + FirstStreamableBlock: 0, + Network: "amoy", + abiCache: make(map[string]*ABI), + initialBlockCache: make(map[string]uint64), + SupportsCalls: true, }, "arbitrum": { - DisplayName: "Arbitrum", - ExplorerLink: "https://arbiscan.io", - ApiEndpoint: "https://api.arbiscan.io", - FirehoseEndpoint: "arb-one.streamingfast.io:443", - Network: "arbitrum", - abiCache: make(map[string]*ABI), - initialBlockCache: make(map[string]uint64), - SupportsCalls: true, + DisplayName: "Arbitrum", + ExplorerLink: "https://arbiscan.io", + ApiEndpoint: "https://api.arbiscan.io", + FirehoseEndpoint: "arb-one.streamingfast.io:443", + Network: "arbitrum", + FirstStreamableBlock: 0, + abiCache: make(map[string]*ABI), + initialBlockCache: make(map[string]uint64), + SupportsCalls: true, }, "holesky": { - DisplayName: "Holesky", - ExplorerLink: "https://holesky.etherscan.io/", - ApiEndpoint: "https://api-holesky.etherscan.io", - FirehoseEndpoint: "holesky.eth.streamingfast.io:443", - Network: "holesky", - abiCache: make(map[string]*ABI), - initialBlockCache: make(map[string]uint64), - SupportsCalls: true, + DisplayName: "Holesky", + ExplorerLink: "https://holesky.etherscan.io/", + ApiEndpoint: "https://api-holesky.etherscan.io", + FirehoseEndpoint: "holesky.eth.streamingfast.io:443", + FirstStreamableBlock: 0, + Network: "holesky", + abiCache: make(map[string]*ABI), + initialBlockCache: make(map[string]uint64), + SupportsCalls: true, }, "sepolia": { - DisplayName: "Sepolia Testnet", - ExplorerLink: "https://sepolia.etherscan.io", - ApiEndpoint: "https://api-sepolia.etherscan.io", - FirehoseEndpoint: "sepolia.streamingfast.io:443", - Network: "sepolia", - abiCache: make(map[string]*ABI), - initialBlockCache: make(map[string]uint64), - SupportsCalls: true, + DisplayName: "Sepolia Testnet", + ExplorerLink: "https://sepolia.etherscan.io", + ApiEndpoint: "https://api-sepolia.etherscan.io", + FirehoseEndpoint: "sepolia.streamingfast.io:443", + FirstStreamableBlock: 0, + Network: "sepolia", + abiCache: make(map[string]*ABI), + initialBlockCache: make(map[string]uint64), + SupportsCalls: true, }, "optimism": { - DisplayName: "Optimism Mainnet", - ExplorerLink: "https://optimistic.etherscan.io", - ApiEndpoint: "https://api-optimistic.etherscan.io", - FirehoseEndpoint: "opt-mainnet.streamingfast.io:443", - Network: "optimism", - abiCache: make(map[string]*ABI), - initialBlockCache: make(map[string]uint64), - SupportsCalls: false, + DisplayName: "Optimism Mainnet", + ExplorerLink: "https://optimistic.etherscan.io", + ApiEndpoint: "https://api-optimistic.etherscan.io", + FirehoseEndpoint: "opt-mainnet.streamingfast.io:443", + FirstStreamableBlock: 0, + Network: "optimism", + abiCache: make(map[string]*ABI), + initialBlockCache: make(map[string]uint64), + SupportsCalls: false, }, "avalanche": { - DisplayName: "Avalanche C-chain", - ExplorerLink: "https://subnets.avax.network/c-chain", - ApiEndpoint: "", - FirehoseEndpoint: "avalanche-mainnet.streamingfast.io:443", - Network: "avalanche", - abiCache: make(map[string]*ABI), - initialBlockCache: make(map[string]uint64), - SupportsCalls: false, + DisplayName: "Avalanche C-chain", + ExplorerLink: "https://subnets.avax.network/c-chain", + ApiEndpoint: "", + FirehoseEndpoint: "avalanche-mainnet.streamingfast.io:443", + FirstStreamableBlock: 0, + Network: "avalanche", + abiCache: make(map[string]*ABI), + initialBlockCache: make(map[string]uint64), + SupportsCalls: false, }, "chapel": { - DisplayName: "BNB Chapel Testnet", - ExplorerLink: "https://testnet.bscscan.com/", - ApiEndpoint: "", - FirehoseEndpoint: "chapel.substreams.pinax.network:443", - Network: "chapel", - abiCache: make(map[string]*ABI), - initialBlockCache: make(map[string]uint64), - SupportsCalls: true, + DisplayName: "BNB Chapel Testnet", + ExplorerLink: "https://testnet.bscscan.com/", + ApiEndpoint: "", + FirehoseEndpoint: "chapel.substreams.pinax.network:443", + FirstStreamableBlock: 0, + Network: "chapel", + abiCache: make(map[string]*ABI), + initialBlockCache: make(map[string]uint64), + SupportsCalls: true, + }, + "sei-mainnet": { + DisplayName: "SEI Mainnet (EVM)", + ExplorerLink: "https://testnet.bscscan.com/", + ApiEndpoint: "https://seitrace.com/pacific-1/api/v2/smart-contracts", + ApiEndpointDirect: true, + FirehoseEndpoint: "evm-mainnet.sei.streamingfast.io:443", + FirstStreamableBlock: 79123881, + Network: "sei-mainnet", + abiCache: make(map[string]*ABI), + initialBlockCache: make(map[string]uint64), + SupportsCalls: true, }, } diff --git a/ethfull/convo.go b/ethfull/convo.go index 7fa6bfa..3f67f20 100644 --- a/ethfull/convo.go +++ b/ethfull/convo.go @@ -673,10 +673,6 @@ message {{.Proto.MessageName}} {{.Proto.OutputModuleFieldName}} { return QuitInvalidContext } - if msg.Err != nil { - return cmd(AskContractInitialBlock{}) - } - return c.action(InputContractInitialBlock{}).TextInput("Please enter the contract initial block number", "Submit"). DefaultValue(fmt.Sprintf("%d", msg.InitialBlock)). Validation(`^\d+$`, "Please enter a valid block number"). diff --git a/ethfull/convo_test.go b/ethfull/convo_test.go index 63e7c94..18ce228 100644 --- a/ethfull/convo_test.go +++ b/ethfull/convo_test.go @@ -91,7 +91,7 @@ func TestConvoUpdate(t *testing.T) { assert.Equal(t, FetchContractInitialBlock{}, next()) next = conv.Update(ReturnFetchContractInitialBlock{Err: fmt.Errorf("failed")}) - assert.Equal(t, AskContractInitialBlock{}, next()) + assert.Contains(t, next().(*pbconvo.SystemOutput).Entry.(*pbconvo.SystemOutput_TextInput_).TextInput.String(), "Please enter the contract initial block number") next = conv.Update(InputContractInitialBlock{UserInput_TextInput: pbconvo.UserInput_TextInput{Value: "123"}}) assert.Equal(t, AskContractName{}, next()) diff --git a/msgwrap.go b/msgwrap.go index 5c77216..8c5c495 100644 --- a/msgwrap.go +++ b/msgwrap.go @@ -29,7 +29,7 @@ func (m *IncomingMessage) Humanize(seconds int) string { return ("---") } - return fmt.Sprintf("%d | %T %v -- %d", seconds, m.Msg, m.Msg) + return fmt.Sprintf("%d | %T %v", seconds, m.Msg, m.Msg) } type MsgWrapFactory struct { diff --git a/pb/sf/codegen/conversation/v1/conversation.go b/pb/sf/codegen/conversation/v1/conversation.go index 01d0d3a..0758e9c 100644 --- a/pb/sf/codegen/conversation/v1/conversation.go +++ b/pb/sf/codegen/conversation/v1/conversation.go @@ -64,7 +64,7 @@ func (i UserInput_Confirmation) Humanize(seconds int) string { func (i UserInput_DownloadedFiles) Humanize(seconds int) string { time := fmt.Sprintf("%4d ", seconds) - return fmt.Sprintf("[Downloaded files] %t", time) + return fmt.Sprintf("[Downloaded files] %s", time) } func (i UserInput_File) Humanize(seconds int) string {