2021-09-09 14:28:54 +00:00
|
|
|
package network
|
|
|
|
|
2024-10-03 19:59:44 +00:00
|
|
|
//go:generate mockgen -package=mock_network -source=network.go -destination=mock/network.go
|
|
|
|
|
2021-09-09 14:28:54 +00:00
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"database/sql"
|
2022-05-16 07:58:36 +00:00
|
|
|
"fmt"
|
2021-09-09 14:28:54 +00:00
|
|
|
|
2023-10-03 13:27:42 +00:00
|
|
|
"github.com/status-im/status-go/multiaccounts/accounts"
|
2022-01-12 20:04:43 +00:00
|
|
|
"github.com/status-im/status-go/params"
|
|
|
|
)
|
2021-09-09 14:28:54 +00:00
|
|
|
|
2024-01-29 10:15:28 +00:00
|
|
|
var SepoliaChainIDs = []uint64{11155111, 421614, 11155420}
|
2023-10-03 13:27:42 +00:00
|
|
|
|
2024-01-29 10:15:28 +00:00
|
|
|
var GoerliChainIDs = []uint64{5, 421613, 420}
|
2023-10-03 13:27:42 +00:00
|
|
|
|
2023-07-13 14:03:49 +00:00
|
|
|
type CombinedNetwork struct {
|
|
|
|
Prod *params.Network
|
|
|
|
Test *params.Network
|
|
|
|
}
|
|
|
|
|
2023-09-15 08:06:44 +00:00
|
|
|
const baseQuery = "SELECT chain_id, chain_name, rpc_url, original_rpc_url, fallback_url, original_fallback_url, block_explorer_url, icon_url, native_currency_name, native_currency_symbol, native_currency_decimals, is_test, layer, enabled, chain_color, short_name, related_chain_id FROM networks"
|
2021-09-09 14:28:54 +00:00
|
|
|
|
|
|
|
func newNetworksQuery() *networksQuery {
|
|
|
|
buf := bytes.NewBuffer(nil)
|
|
|
|
buf.WriteString(baseQuery)
|
|
|
|
return &networksQuery{buf: buf}
|
|
|
|
}
|
|
|
|
|
|
|
|
type networksQuery struct {
|
|
|
|
buf *bytes.Buffer
|
|
|
|
args []interface{}
|
|
|
|
added bool
|
|
|
|
}
|
|
|
|
|
|
|
|
func (nq *networksQuery) andOrWhere() {
|
|
|
|
if nq.added {
|
|
|
|
nq.buf.WriteString(" AND")
|
|
|
|
} else {
|
|
|
|
nq.buf.WriteString(" WHERE")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (nq *networksQuery) filterEnabled(enabled bool) *networksQuery {
|
|
|
|
nq.andOrWhere()
|
|
|
|
nq.added = true
|
|
|
|
nq.buf.WriteString(" enabled = ?")
|
|
|
|
nq.args = append(nq.args, enabled)
|
|
|
|
return nq
|
|
|
|
}
|
|
|
|
|
|
|
|
func (nq *networksQuery) filterChainID(chainID uint64) *networksQuery {
|
|
|
|
nq.andOrWhere()
|
|
|
|
nq.added = true
|
|
|
|
nq.buf.WriteString(" chain_id = ?")
|
|
|
|
nq.args = append(nq.args, chainID)
|
|
|
|
return nq
|
|
|
|
}
|
|
|
|
|
2023-09-15 08:06:44 +00:00
|
|
|
func (nq *networksQuery) exec(db *sql.DB) ([]*params.Network, error) {
|
2021-09-09 14:28:54 +00:00
|
|
|
rows, err := db.Query(nq.buf.String(), nq.args...)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2022-01-12 20:04:43 +00:00
|
|
|
var res []*params.Network
|
2021-09-09 14:28:54 +00:00
|
|
|
defer rows.Close()
|
|
|
|
for rows.Next() {
|
2022-01-12 20:04:43 +00:00
|
|
|
network := params.Network{}
|
2021-09-09 14:28:54 +00:00
|
|
|
err := rows.Scan(
|
2023-09-15 08:06:44 +00:00
|
|
|
&network.ChainID, &network.ChainName, &network.RPCURL, &network.OriginalRPCURL, &network.FallbackURL, &network.OriginalFallbackURL,
|
|
|
|
&network.BlockExplorerURL, &network.IconURL, &network.NativeCurrencyName, &network.NativeCurrencySymbol,
|
2022-06-16 18:42:21 +00:00
|
|
|
&network.NativeCurrencyDecimals, &network.IsTest, &network.Layer, &network.Enabled, &network.ChainColor, &network.ShortName,
|
2023-07-13 14:03:49 +00:00
|
|
|
&network.RelatedChainID,
|
2021-09-09 14:28:54 +00:00
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2023-08-08 09:05:17 +00:00
|
|
|
|
2021-09-09 14:28:54 +00:00
|
|
|
res = append(res, &network)
|
|
|
|
}
|
|
|
|
|
|
|
|
return res, err
|
|
|
|
}
|
|
|
|
|
2024-06-27 21:27:09 +00:00
|
|
|
type ManagerInterface interface {
|
|
|
|
Get(onlyEnabled bool) ([]*params.Network, error)
|
|
|
|
GetAll() ([]*params.Network, error)
|
|
|
|
Find(chainID uint64) *params.Network
|
|
|
|
GetConfiguredNetworks() []params.Network
|
|
|
|
GetTestNetworksEnabled() (bool, error)
|
|
|
|
}
|
|
|
|
|
2021-09-09 14:28:54 +00:00
|
|
|
type Manager struct {
|
2023-08-08 09:05:17 +00:00
|
|
|
db *sql.DB
|
|
|
|
configuredNetworks []params.Network
|
2023-10-03 13:27:42 +00:00
|
|
|
accountsDB *accounts.Database
|
2021-09-09 14:28:54 +00:00
|
|
|
}
|
|
|
|
|
2021-09-22 17:49:20 +00:00
|
|
|
func NewManager(db *sql.DB) *Manager {
|
2023-10-03 13:27:42 +00:00
|
|
|
accountsDB, err := accounts.NewDB(db)
|
|
|
|
if err != nil {
|
|
|
|
return nil
|
|
|
|
}
|
2021-09-09 14:28:54 +00:00
|
|
|
return &Manager{
|
2023-10-03 13:27:42 +00:00
|
|
|
db: db,
|
|
|
|
accountsDB: accountsDB,
|
2021-09-09 14:28:54 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-16 07:58:36 +00:00
|
|
|
func find(chainID uint64, networks []params.Network) int {
|
|
|
|
for i := range networks {
|
|
|
|
if networks[i].ChainID == chainID {
|
|
|
|
return i
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return -1
|
|
|
|
}
|
|
|
|
|
2022-01-12 20:04:43 +00:00
|
|
|
func (nm *Manager) Init(networks []params.Network) error {
|
2021-09-22 17:49:20 +00:00
|
|
|
if networks == nil {
|
|
|
|
return nil
|
|
|
|
}
|
2023-08-08 09:05:17 +00:00
|
|
|
nm.configuredNetworks = networks
|
2021-09-22 17:49:20 +00:00
|
|
|
|
2022-05-16 07:58:36 +00:00
|
|
|
var errors string
|
2021-09-09 14:28:54 +00:00
|
|
|
currentNetworks, _ := nm.Get(false)
|
2022-05-16 07:58:36 +00:00
|
|
|
|
|
|
|
// Delete networks which are not supported any more
|
|
|
|
for i := range currentNetworks {
|
|
|
|
if find(currentNetworks[i].ChainID, networks) == -1 {
|
|
|
|
err := nm.Delete(currentNetworks[i].ChainID)
|
|
|
|
if err != nil {
|
|
|
|
errors += fmt.Sprintf("error deleting network with ChainID: %d, %s", currentNetworks[i].ChainID, err.Error())
|
|
|
|
}
|
|
|
|
}
|
2021-09-09 14:28:54 +00:00
|
|
|
}
|
|
|
|
|
2023-08-08 09:05:17 +00:00
|
|
|
// Add new networks and update related chain id for the old ones
|
2021-09-09 14:28:54 +00:00
|
|
|
for i := range networks {
|
2022-05-16 07:58:36 +00:00
|
|
|
found := false
|
2024-01-16 13:47:57 +00:00
|
|
|
networks[i].OriginalRPCURL = networks[i].RPCURL
|
|
|
|
networks[i].OriginalFallbackURL = networks[i].FallbackURL
|
2023-11-07 11:14:31 +00:00
|
|
|
|
2022-05-16 07:58:36 +00:00
|
|
|
for j := range currentNetworks {
|
|
|
|
if currentNetworks[j].ChainID == networks[i].ChainID {
|
|
|
|
found = true
|
2023-07-13 14:03:49 +00:00
|
|
|
if currentNetworks[j].RelatedChainID != networks[i].RelatedChainID {
|
|
|
|
// Update fallback_url if it's different
|
|
|
|
err := nm.UpdateRelatedChainID(currentNetworks[j].ChainID, networks[i].RelatedChainID)
|
|
|
|
if err != nil {
|
|
|
|
errors += fmt.Sprintf("error updating network fallback_url for ChainID: %d, %s", currentNetworks[j].ChainID, err.Error())
|
|
|
|
}
|
|
|
|
}
|
2024-01-16 13:47:57 +00:00
|
|
|
|
|
|
|
if networks[i].OriginalRPCURL != currentNetworks[j].OriginalRPCURL && currentNetworks[j].RPCURL == currentNetworks[j].OriginalRPCURL {
|
|
|
|
err := nm.updateRPCURL(networks[i].ChainID, networks[i].OriginalRPCURL)
|
|
|
|
if err != nil {
|
|
|
|
errors += fmt.Sprintf("error updating rpc url for ChainID: %d, %s", currentNetworks[j].ChainID, err.Error())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if networks[i].OriginalFallbackURL != currentNetworks[j].OriginalFallbackURL && currentNetworks[j].FallbackURL == currentNetworks[j].OriginalFallbackURL {
|
|
|
|
err := nm.updateFallbackURL(networks[i].ChainID, networks[i].OriginalFallbackURL)
|
|
|
|
if err != nil {
|
|
|
|
errors += fmt.Sprintf("error updating rpc url for ChainID: %d, %s", currentNetworks[j].ChainID, err.Error())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
err := nm.updateOriginalURLs(networks[i].ChainID, networks[i].OriginalRPCURL, networks[i].OriginalFallbackURL)
|
2023-09-15 08:06:44 +00:00
|
|
|
if err != nil {
|
|
|
|
errors += fmt.Sprintf("error updating network original url for ChainID: %d, %s", currentNetworks[j].ChainID, err.Error())
|
|
|
|
}
|
2024-07-31 06:21:11 +00:00
|
|
|
|
2022-05-16 07:58:36 +00:00
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if !found {
|
2023-11-07 11:14:31 +00:00
|
|
|
// Insert new network
|
2022-05-16 07:58:36 +00:00
|
|
|
err := nm.Upsert(&networks[i])
|
|
|
|
if err != nil {
|
|
|
|
errors += fmt.Sprintf("error inserting network with ChainID: %d, %s", networks[i].ChainID, err.Error())
|
|
|
|
}
|
2021-09-09 14:28:54 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-16 07:58:36 +00:00
|
|
|
if len(errors) > 0 {
|
|
|
|
return fmt.Errorf(errors)
|
|
|
|
}
|
|
|
|
|
2021-09-09 14:28:54 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2022-01-12 20:04:43 +00:00
|
|
|
func (nm *Manager) Upsert(network *params.Network) error {
|
2021-09-09 14:28:54 +00:00
|
|
|
_, err := nm.db.Exec(
|
2023-09-15 08:06:44 +00:00
|
|
|
"INSERT OR REPLACE INTO networks (chain_id, chain_name, rpc_url, original_rpc_url, fallback_url, original_fallback_url, block_explorer_url, icon_url, native_currency_name, native_currency_symbol, native_currency_decimals, is_test, layer, enabled, chain_color, short_name, related_chain_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
|
|
|
network.ChainID, network.ChainName, network.RPCURL, network.OriginalRPCURL, network.FallbackURL, network.OriginalFallbackURL, network.BlockExplorerURL, network.IconURL,
|
2021-09-09 14:28:54 +00:00
|
|
|
network.NativeCurrencyName, network.NativeCurrencySymbol, network.NativeCurrencyDecimals,
|
2022-06-16 18:42:21 +00:00
|
|
|
network.IsTest, network.Layer, network.Enabled, network.ChainColor, network.ShortName,
|
2023-07-13 14:03:49 +00:00
|
|
|
network.RelatedChainID,
|
2021-09-09 14:28:54 +00:00
|
|
|
)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
func (nm *Manager) Delete(chainID uint64) error {
|
|
|
|
_, err := nm.db.Exec("DELETE FROM networks WHERE chain_id = ?", chainID)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2023-07-13 14:03:49 +00:00
|
|
|
func (nm *Manager) UpdateRelatedChainID(chainID uint64, relatedChainID uint64) error {
|
|
|
|
_, err := nm.db.Exec(`UPDATE networks SET related_chain_id = ? WHERE chain_id = ?`, relatedChainID, chainID)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2024-01-16 13:47:57 +00:00
|
|
|
func (nm *Manager) updateRPCURL(chainID uint64, rpcURL string) error {
|
|
|
|
_, err := nm.db.Exec(`UPDATE networks SET rpc_url = ? WHERE chain_id = ?`, rpcURL, chainID)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
func (nm *Manager) updateFallbackURL(chainID uint64, fallbackURL string) error {
|
|
|
|
_, err := nm.db.Exec(`UPDATE networks SET fallback_url = ? WHERE chain_id = ?`, fallbackURL, chainID)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
func (nm *Manager) updateOriginalURLs(chainID uint64, originalRPCURL, OriginalFallbackURL string) error {
|
|
|
|
_, err := nm.db.Exec(`UPDATE networks SET original_rpc_url = ?, original_fallback_url = ? WHERE chain_id = ?`, originalRPCURL, OriginalFallbackURL, chainID)
|
2023-09-15 08:06:44 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2022-01-12 20:04:43 +00:00
|
|
|
func (nm *Manager) Find(chainID uint64) *params.Network {
|
2023-09-15 08:06:44 +00:00
|
|
|
networks, err := newNetworksQuery().filterChainID(chainID).exec(nm.db)
|
2021-09-09 14:28:54 +00:00
|
|
|
if len(networks) != 1 || err != nil {
|
|
|
|
return nil
|
|
|
|
}
|
2024-07-31 06:21:11 +00:00
|
|
|
setDefaultRPCURL(networks, nm.configuredNetworks)
|
2021-09-09 14:28:54 +00:00
|
|
|
return networks[0]
|
|
|
|
}
|
|
|
|
|
2023-10-03 13:27:42 +00:00
|
|
|
func (nm *Manager) GetAll() ([]*params.Network, error) {
|
|
|
|
query := newNetworksQuery()
|
2024-07-31 06:21:11 +00:00
|
|
|
networks, err := query.exec(nm.db)
|
|
|
|
setDefaultRPCURL(networks, nm.configuredNetworks)
|
|
|
|
return networks, err
|
2023-10-03 13:27:42 +00:00
|
|
|
}
|
|
|
|
|
2022-01-12 20:04:43 +00:00
|
|
|
func (nm *Manager) Get(onlyEnabled bool) ([]*params.Network, error) {
|
2024-02-20 09:30:47 +00:00
|
|
|
isGoerliEnabled, err := nm.accountsDB.GetIsGoerliEnabled()
|
2023-10-03 13:27:42 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2021-09-09 14:28:54 +00:00
|
|
|
query := newNetworksQuery()
|
|
|
|
if onlyEnabled {
|
|
|
|
query.filterEnabled(true)
|
|
|
|
}
|
|
|
|
|
2023-10-03 13:27:42 +00:00
|
|
|
networks, err := query.exec(nm.db)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
var results []*params.Network
|
|
|
|
for _, network := range networks {
|
2024-02-20 09:30:47 +00:00
|
|
|
if isGoerliEnabled {
|
2023-10-10 12:30:12 +00:00
|
|
|
found := false
|
|
|
|
for _, chainID := range SepoliaChainIDs {
|
|
|
|
if network.ChainID == chainID {
|
|
|
|
found = true
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if found {
|
|
|
|
continue
|
|
|
|
}
|
2023-10-03 13:27:42 +00:00
|
|
|
}
|
|
|
|
|
2024-02-20 09:30:47 +00:00
|
|
|
if !isGoerliEnabled {
|
2023-10-03 13:27:42 +00:00
|
|
|
found := false
|
|
|
|
|
|
|
|
for _, chainID := range GoerliChainIDs {
|
|
|
|
if network.ChainID == chainID {
|
|
|
|
found = true
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if found {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
2024-07-31 06:21:11 +00:00
|
|
|
|
|
|
|
configuredNetwork, err := findNetwork(nm.configuredNetworks, network.ChainID)
|
|
|
|
if err != nil {
|
|
|
|
addDefaultRPCURL(network, configuredNetwork)
|
|
|
|
}
|
|
|
|
|
2023-10-03 13:27:42 +00:00
|
|
|
results = append(results, network)
|
|
|
|
}
|
|
|
|
|
|
|
|
return results, nil
|
2021-09-09 14:28:54 +00:00
|
|
|
}
|
2022-09-13 09:30:52 +00:00
|
|
|
|
2023-07-13 14:03:49 +00:00
|
|
|
func (nm *Manager) GetCombinedNetworks() ([]*CombinedNetwork, error) {
|
2023-10-03 13:27:42 +00:00
|
|
|
networks, err := nm.Get(false)
|
2023-07-13 14:03:49 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
var combinedNetworks []*CombinedNetwork
|
|
|
|
for _, network := range networks {
|
|
|
|
found := false
|
|
|
|
for _, n := range combinedNetworks {
|
2023-10-03 13:27:42 +00:00
|
|
|
if (n.Test != nil && (network.ChainID == n.Test.RelatedChainID || n.Test.ChainID == network.RelatedChainID)) || (n.Prod != nil && (network.ChainID == n.Prod.RelatedChainID || n.Prod.ChainID == network.RelatedChainID)) {
|
2023-07-13 14:03:49 +00:00
|
|
|
found = true
|
|
|
|
if network.IsTest {
|
|
|
|
n.Test = network
|
|
|
|
break
|
|
|
|
} else {
|
|
|
|
n.Prod = network
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if found {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
newCombined := &CombinedNetwork{}
|
|
|
|
if network.IsTest {
|
|
|
|
newCombined.Test = network
|
|
|
|
} else {
|
|
|
|
newCombined.Prod = network
|
|
|
|
}
|
|
|
|
combinedNetworks = append(combinedNetworks, newCombined)
|
|
|
|
}
|
|
|
|
|
|
|
|
return combinedNetworks, nil
|
|
|
|
}
|
|
|
|
|
2022-09-13 09:30:52 +00:00
|
|
|
func (nm *Manager) GetConfiguredNetworks() []params.Network {
|
2023-08-08 09:05:17 +00:00
|
|
|
return nm.configuredNetworks
|
2022-09-13 09:30:52 +00:00
|
|
|
}
|
2024-02-22 15:17:35 +00:00
|
|
|
|
|
|
|
func (nm *Manager) GetTestNetworksEnabled() (result bool, err error) {
|
|
|
|
return nm.accountsDB.GetTestNetworksEnabled()
|
|
|
|
}
|
2024-06-06 19:57:29 +00:00
|
|
|
|
|
|
|
// Returns all networks for active mode (test/prod) and in case of test mode,
|
|
|
|
// returns either Goerli or Sepolia networks based on the value of isGoerliEnabled
|
|
|
|
func (nm *Manager) GetActiveNetworks() ([]*params.Network, error) {
|
|
|
|
areTestNetworksEnabled, err := nm.GetTestNetworksEnabled()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
networks, err := nm.Get(false)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
availableNetworks := make([]*params.Network, 0)
|
|
|
|
for _, network := range networks {
|
|
|
|
if network.IsTest != areTestNetworksEnabled {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
availableNetworks = append(availableNetworks, network)
|
|
|
|
}
|
|
|
|
|
|
|
|
return availableNetworks, nil
|
|
|
|
}
|
2024-07-31 06:21:11 +00:00
|
|
|
|
|
|
|
func findNetwork(networks []params.Network, chainID uint64) (params.Network, error) {
|
|
|
|
for _, network := range networks {
|
|
|
|
if network.ChainID == chainID {
|
|
|
|
return network, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return params.Network{}, fmt.Errorf("network not found")
|
|
|
|
}
|
|
|
|
|
|
|
|
func addDefaultRPCURL(target *params.Network, source params.Network) {
|
|
|
|
target.DefaultRPCURL = source.DefaultRPCURL
|
|
|
|
target.DefaultFallbackURL = source.DefaultFallbackURL
|
2024-10-02 08:20:03 +00:00
|
|
|
target.DefaultFallbackURL2 = source.DefaultFallbackURL2
|
2024-07-31 06:21:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func setDefaultRPCURL(target []*params.Network, source []params.Network) {
|
|
|
|
for i := range target {
|
|
|
|
for j := range source {
|
|
|
|
if target[i].ChainID == source[j].ChainID {
|
|
|
|
addDefaultRPCURL(target[i], source[j])
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|