Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ccip test utils #681

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -703,8 +703,8 @@ SkipRequestIfAnotherRequestTriggeredWithin = '40m'
MsgType = 'WithoutToken'
# these are all the valid network pairs
NetworkPairs = [
'ETHEREUM_MAINNET,OPTIMISM_MAINNET',
'ETHEREUM_MAINNET,AVALANCHE_MAINNET',
# 'ETHEREUM_MAINNET,OPTIMISM_MAINNET', Running on secondary toml
# 'ETHEREUM_MAINNET,AVALANCHE_MAINNET', Running on secondary toml
'ETHEREUM_MAINNET,POLYGON_MAINNET',
'ETHEREUM_MAINNET,BSC_MAINNET',
'ETHEREUM_MAINNET,ARBITRUM_MAINNET',
Expand All @@ -719,7 +719,7 @@ NetworkPairs = [
'BASE_MAINNET,BSC_MAINNET',
'POLYGON_MAINNET,ARBITRUM_MAINNET', # added as batch 1
'ARBITRUM_MAINNET,BSC_MAINNET', # added as batch 1
'ARBITRUM_MAINNET,OPTIMISM_MAINNET', # added as batch 1
# 'ARBITRUM_MAINNET,OPTIMISM_MAINNET', Running on secondary toml
'AVALANCHE_MAINNET,OPTIMISM_MAINNET', # added as batch 2
'AVALANCHE_MAINNET,ARBITRUM_MAINNET', # added as batch 2
'BASE_MAINNET,POLYGON_MAINNET', # added as batch 2
Expand Down
50 changes: 50 additions & 0 deletions integration-tests/ccip-tests/utils/ccip-explorer/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# CCIP Explorer Search
This tool facilitates querying CCIP transactions and generating a CSV report and console output that includes details such as network names, transaction hashes, fee tokens, and URLs to view transactions on various blockchain explorers.

## Command Line Flags

* -sender: Sender's address
* -receiver: Receiver's address
* -source: Source network shortcode
* -dest: Destination network shortcode
* -messageId: Unique identifier for the message (probably should not be an option)
* -feeToken: Fee token address

Additional flags for pagination:
* -first
* -offset

## Example Command

```bash
go run . -sender=<address> -receiver=<address> -source=ETH -dest=POLY -first=5
```

## Output
After executing the script, it will generate a transactions.csv file in the current directory. This file includes details on each transaction queried, such as source and destination networks, fee token, and URLs for transaction verification on network-specific explorers.

Transactions will also be printed in the terminal with markdown formatted links which can be inserted into markdown or confluence docs.

## Configuration

### Network Mappings

Predefined network name mappings are used to identify networks. These mappings are hard-coded within the script:

```toml
var networkNameMapping = map[string]string{
"POLY": "polygon-mainnet",
"BSC": "binance_smart_chain-mainnet",
"ETH": "ethereum-mainnet",
// Add more mappings here...
}
```
### networks.toml Configuration

The networks.toml file has network-specific configurations such as explorer URLs and token address mappings. Here's an example structure of the networks.toml:

```toml
[networks."polygon-mainnet"]
url = "https://polygonscan.com/tx/"
tokens = { "0x0d500b1d8e8ef31e21c99d1db9a6444d3adf1270" = "WMATIC", "0xb0897686c545045afc77cf20ec7a532e3120e0f1" = "LINK" }
```
222 changes: 222 additions & 0 deletions integration-tests/ccip-tests/utils/ccip-explorer/api.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
package main

import (
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"

)

type ApiTransactionsResponse struct {
Data struct {
AllCcipTransactionsFlats struct {
Nodes []struct {
TransactionHash string `json:"transactionHash"`
FeeToken string `json:"feeToken"`
DestTransactionHash string `json:"destTransactionHash"`
SourceNetworkName string `json:"sourceNetworkName"`
DestNetworkName string `json:"destNetworkName"`
MessageID string `json:"messageId"`
BlockTimestamp string `json:"blockTimestamp"`
} `json:"nodes"`
} `json:"allCcipTransactionsFlats"`
} `json:"data"`
}

type ChainlinkResponse struct {
Data struct {
AllCcipMessages struct {
Nodes []Node `json:"nodes"`
} `json:"allCcipMessages"`
} `json:"data"`
}

type Node struct {
Arm string `json:"arm"`
BlessBlockNumber int `json:"blessBlockNumber"`
BlessBlockTimestamp string `json:"blessBlockTimestamp"`
BlessLogIndex int `json:"blessLogIndex"`
BlockTimestamp string `json:"blockTimestamp"`
CommitBlockNumber int `json:"commitBlockNumber"`
BlessTransactionHash string `json:"blessTransactionHash"`
CommitBlockTimestamp string `json:"commitBlockTimestamp"`
CommitLogIndex int `json:"commitLogIndex"`
CommitStore string `json:"commitStore"`
CommitTransactionHash string `json:"commitTransactionHash"`
Data string `json:"data"`
DestChainId string `json:"destChainId"`
DestRouterAddress string `json:"destRouterAddress"`
DestNetworkName string `json:"destNetworkName"`
TokenAmounts []TokenAmount `json:"tokenAmounts"`
Votes int `json:"votes"`
Strict bool `json:"strict"`
SourceChainId string `json:"sourceChainId"`
State int `json:"state"`
SourceNetworkName string `json:"sourceNetworkName"`
SequenceNumber int `json:"sequenceNumber"`
Sender string `json:"sender"`
SendTransactionHash string `json:"sendTransactionHash"`
SendTimestamp string `json:"sendTimestamp"`
SendLogIndex int `json:"sendLogIndex"`
SendBlock int `json:"sendBlock"`
SendFinalized string `json:"sendFinalized"`
RouterAddress string `json:"routerAddress"`
Receiver string `json:"receiver"`
Root string `json:"root"`
ReceiptTransactionHash string `json:"receiptTransactionHash"`
ReceiptTimestamp string `json:"receiptTimestamp"`
ReceiptLogIndex int `json:"receiptLogIndex"`
ReceiptBlock int `json:"receiptBlock"`
ReceiptFinalized string `json:"receiptFinalized"`
OnrampAddress string `json:"onrampAddress"`
OfframpAddress string `json:"offrampAddress"`
Nonce int `json:"nonce"`
Min string `json:"min"`
MessageId string `json:"messageId"`
Max string `json:"max"`
Info Info `json:"info"`
GasLimit string `json:"gasLimit"`
FeeTokenAmount string `json:"feeTokenAmount"`
FeeToken string `json:"feeToken"`
}

// TokenAmount represents each object within the "tokenAmounts" array
type TokenAmount struct {
Token string `json:"token"`
Amount string `json:"amount"`
}

// Info represents the "info" object within a Node
type Info struct {
Data string `json:"data"`
Nonce int `json:"nonce"`
Sender string `json:"sender"`
Strict bool `json:"strict"`
FeeToken string `json:"feeToken"`
GasLimit int `json:"gasLimit"`
Receiver string `json:"receiver"`
MessageId string `json:"messageId"`
TokenAmounts []TokenAmount `json:"tokenAmounts"`
FeeTokenAmount int64 `json:"feeTokenAmount"`
SequenceNumber int `json:"sequenceNumber"`
SourceTokenData []string `json:"sourceTokenData"`
SourceChainSelector int64 `json:"sourceChainSelector"`
}

func GenerateQueryString(sender string, receiver string, sourceNetworkName string,
destNetworkName string, messageId string, feeToken string,
first int, offset int) string {
baseURL := "https://ccip.chain.link/api/query/LATEST_TRANSACTIONS_QUERY"
queryParams := url.Values{}
//queryParams.Add("query", "LATEST_TRANSACTIONS_QUERY")

// Construct the condition map dynamically based on provided arguments
// Construct the condition map dynamically based on provided arguments
condition := make(map[string]interface{})

// Add sender to the condition map if it's not empty
if sender != "" {
condition["sender"] = sender
}

// Add receiver to the condition map if it's not empty
if receiver != "" {
condition["receiver"] = receiver
}

// Add sourceNetworkName to the condition map if it's not empty
if sourceNetworkName != "" {
condition["sourceNetworkName"] = sourceNetworkName
}

// Add destNetworkName to the condition map if it's not empty
if destNetworkName != "" {
condition["destNetworkName"] = destNetworkName
}

// Add messageId to the condition map if it's not empty
if messageId != "" {
condition["messageId"] = messageId
}

// Add feeToken to the condition map if it's not empty
if feeToken != "" {
condition["feeToken"] = feeToken
}

// Convert the condition map and other variables into a JSON string
variables := map[string]interface{}{
"first": first,
"offset": offset,
"condition": condition,
}
variablesJSON, err := json.Marshal(variables)
if err != nil {
fmt.Println("Error marshalling variables to JSON:", err)
return ""
}

queryParams.Add("variables", string(variablesJSON))

return baseURL + "?" + queryParams.Encode()
}

func QueryTransactionsAPI(url string) (*ApiTransactionsResponse, error) {

apiResponse := ApiTransactionsResponse{}
method := "GET"
client := &http.Client{}

req, err := http.NewRequest(method, url, nil)
if err != nil {
return nil, fmt.Errorf("error making request to API: %w", err)
}

res, err := client.Do(req)
if err != nil {
fmt.Println(err)
return nil, err
}
defer res.Body.Close()

body, err := io.ReadAll(res.Body)
if err != nil {
return nil, fmt.Errorf("error reading response body: %w", err)
}

if err := json.Unmarshal(body, &apiResponse); err != nil {
return nil, fmt.Errorf("error unmarshalling response: %w", err)
}
return &apiResponse, nil
}

func QueryMessageAPI(url string) (*ChainlinkResponse, error) {

apiResponse := ChainlinkResponse{}
method := "GET"
client := &http.Client{}

req, err := http.NewRequest(method, url, nil)
if err != nil {
return nil, fmt.Errorf("error making request to API: %w", err)
}

res, err := client.Do(req)
if err != nil {
fmt.Println(err)
return nil, err
}
defer res.Body.Close()

body, err := io.ReadAll(res.Body)
if err != nil {
return nil, fmt.Errorf("error reading response body: %w", err)
}

if err := json.Unmarshal(body, &apiResponse); err != nil {
return nil, fmt.Errorf("error unmarshalling response: %w", err)
}
return &apiResponse, nil
}
20 changes: 20 additions & 0 deletions integration-tests/ccip-tests/utils/ccip-explorer/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package main

import (
"github.com/BurntSushi/toml"
)

type Config struct {
Networks map[string]struct {
URL string `toml:"url"`
Tokens map[string]string `toml:"tokens"`
} `toml:"networks"`
}

func LoadConfig(path string) (Config, error) {
var config Config
if _, err := toml.DecodeFile(path, &config); err != nil {
return Config{}, err
}
return config, nil
}
56 changes: 56 additions & 0 deletions integration-tests/ccip-tests/utils/ccip-explorer/flags.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package main

import (
"flag"
"strings"
)

type CommandLineArgs struct {
Sender string
First int
Offset int
Receiver string
Source string
Dest string
FeeToken string
MessageId string
}

// Helper function to translate network names
func translateNetworkName(shorthand string) string {
if fullName, exists := networkNameMapping[shorthand]; exists {
return fullName
}
return shorthand // Return the input if no mapping found
}

func ParseFlags() CommandLineArgs {
args := CommandLineArgs{}
// Define temporary variables to hold the pointer values
sender := flag.String("sender", "", "Sender address")
first := flag.Int("first", 100, "First N results")
offset := flag.Int("offset", 0, "Offset for results")
receiver := flag.String("receiver", "", "Receiver address")
source := flag.String("source", "", "Source network name")
dest := flag.String("dest", "", "Destination network name")
feeToken := flag.String("feeToken", "", "Fee Token")
messageId := flag.String("messageId", "", "Message ID")

flag.Parse()

// Translate shorthand network names to full names
fullSource := translateNetworkName(strings.ToUpper(*source))
fullDest := translateNetworkName(strings.ToUpper(*dest))

// Assign and convert to lowercase where applicable
args.Sender = strings.ToLower(*sender)
args.First = *first
args.Offset = *offset
args.Receiver = strings.ToLower(*receiver)
args.Source = strings.ToLower(fullSource)
args.Dest = strings.ToLower(fullDest)
args.FeeToken = strings.ToLower(*feeToken)
args.MessageId = strings.ToLower(*messageId)

return args
}