Rework tx link formatting

This commit is contained in:
Kirill Fedoseev 2022-05-30 20:07:31 +04:00
parent 9c45cb2918
commit 69e992f06e
10 changed files with 126 additions and 100 deletions

View File

@ -82,13 +82,7 @@ WHERE not exists(SELECT *
}
client, ok := clients[bt.ChainID]
if !ok {
var chainCfg *config.ChainConfig
for _, c := range cfg.Chains {
if c.ChainID == bt.ChainID {
chainCfg = c
break
}
}
chainCfg := cfg.GetChainConfig(bt.ChainID)
if chainCfg == nil {
logger.WithFields(fields).Fatal("can't find chain config")
}

View File

@ -42,6 +42,9 @@
},
"safe_logs_request": {
"type": "boolean"
},
"explorer_tx_link_format": {
"type": "string"
}
},
"required": [

View File

@ -7,6 +7,7 @@ chains:
chain_id: 1
block_time: 15s
block_index_interval: 60s
explorer_tx_link_format: 'https://etherscan.io/tx/%s'
bsc:
rpc:
host: https://bsc-dataseed2.defibit.io
@ -16,6 +17,7 @@ chains:
block_time: 3s
block_index_interval: 60s
safe_logs_request: true
explorer_tx_link_format: 'https://bscscan.com/tx/%s'
kovan:
rpc:
host: https://kovan.infura.io/v3/${INFURA_PROJECT_KEY}
@ -24,15 +26,17 @@ chains:
chain_id: 42
block_time: 5s
block_index_interval: 60s
explorer_tx_link_format: 'https://kovan.etherscan.io/tx/%s'
xdai:
rpc:
host: https://rpc.xdaichain.com/oe-only
host: https://rpc.ankr.com/gnosis
timeout: 20s
rps: 10
chain_id: 100
block_time: 5s
block_index_interval: 30s
safe_logs_request: true
explorer_tx_link_format: 'https://blockscout.com/xdai/mainnet/tx/%s'
poa:
rpc:
host: https://core.poanetwork.dev
@ -41,6 +45,7 @@ chains:
chain_id: 99
block_time: 5s
block_index_interval: 30s
explorer_tx_link_format: 'https://blockscout.com/poa/core/tx/%s'
sokol:
rpc:
host: https://sokol.poa.network
@ -49,6 +54,7 @@ chains:
chain_id: 77
block_time: 5s
block_index_interval: 30s
explorer_tx_link_format: 'https://blockscout.com/poa/sokol/tx/%s'
rinkeby:
rpc:
host: https://rinkeby.infura.io/v3/${INFURA_PROJECT_KEY}
@ -57,6 +63,7 @@ chains:
chain_id: 4
block_time: 15s
block_index_interval: 60s
explorer_tx_link_format: 'https://rinkeby.etherscan.io/tx/%s'
bridges:
xdai:
bridge_mode: ERC_TO_NATIVE
@ -158,7 +165,7 @@ bridges:
validator_contract_address: 0x6f00218e7D985FE1211f5d47B350708fF915A842
start_block: 14496719
required_block_confirmations: 12
max_block_range_size: 10000
max_block_range_size: 2000
foreign:
chain: bsc
address: 0x05185872898b6f94AA600177EF41B9334B1FA48B
@ -186,7 +193,7 @@ bridges:
validator_contract_address: 0xAC91dfb485ED2B96381686D3d299e3D041dB4051
start_block: 10030209
required_block_confirmations: 12
max_block_range_size: 20000
max_block_range_size: 2000
foreign:
chain: rinkeby
address: 0xD4075FB57fCf038bFc702c915Ef9592534bED5c1
@ -212,7 +219,7 @@ bridges:
validator_contract_address: 0x72C5E5f2C905f9b57FD433a58d6215AC8109991C
start_block: 17976494
required_block_confirmations: 12
max_block_range_size: 10000
max_block_range_size: 2000
foreign:
chain: poa
address: 0xB2218bdEbe8e90f80D04286772B0968ead666942

View File

@ -1,7 +1,6 @@
package config
import (
"bytes"
"errors"
"fmt"
"math"
@ -10,7 +9,6 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/sirupsen/logrus"
"gopkg.in/yaml.v3"
)
var (
@ -26,11 +24,12 @@ type RPCConfig struct {
}
type ChainConfig struct {
RPC *RPCConfig `yaml:"rpc"`
ChainID string `yaml:"chain_id"`
BlockTime time.Duration `yaml:"block_time"`
BlockIndexInterval time.Duration `yaml:"block_index_interval"`
SafeLogsRequest bool `yaml:"safe_logs_request"`
RPC *RPCConfig `yaml:"rpc"`
ChainID string `yaml:"chain_id"`
BlockTime time.Duration `yaml:"block_time"`
BlockIndexInterval time.Duration `yaml:"block_index_interval"`
SafeLogsRequest bool `yaml:"safe_logs_request"`
ExplorerTxLinkFormat string `yaml:"explorer_tx_link_format"`
}
type TokenConfig struct {
@ -94,11 +93,21 @@ type Config struct {
Presenter *PresenterConfig `yaml:"presenter"`
}
func parseYaml(out *Config, blob []byte) error {
dec := yaml.NewDecoder(bytes.NewReader(blob))
dec.KnownFields(true)
if err := dec.Decode(out); err != nil {
return fmt.Errorf("can't parse yaml: %w", err)
func (cfg *ChainConfig) FormatTxLink(txHash fmt.Stringer) string {
if cfg == nil || cfg.ExplorerTxLinkFormat == "" {
return txHash.String()
}
return fmt.Sprintf(cfg.ExplorerTxLinkFormat, txHash)
}
func (cfg *Config) GetChainConfig(chainID string) *ChainConfig {
if chainCfg, ok := cfg.Chains[chainID]; ok {
return chainCfg
}
for _, chainCfg := range cfg.Chains {
if chainCfg.ChainID == chainID {
return chainCfg
}
}
return nil
}

View File

@ -22,6 +22,7 @@ chains:
chain_id: 1
block_time: 15s
block_index_interval: 60s
explorer_tx_link_format: 'https://etherscan.io/tx/%s'
xdai:
rpc:
host: https://rpc.ankr.com/gnosis
@ -105,10 +106,11 @@ func TestReadConfigWithEnv(t *testing.T) {
Timeout: 30 * time.Second,
RPS: 10,
},
ChainID: "1",
BlockTime: 15 * time.Second,
BlockIndexInterval: 60 * time.Second,
SafeLogsRequest: false,
ChainID: "1",
BlockTime: 15 * time.Second,
BlockIndexInterval: 60 * time.Second,
SafeLogsRequest: false,
ExplorerTxLinkFormat: "https://etherscan.io/tx/%s",
}
xdaiChainCfg := &config.ChainConfig{
RPC: &config.RPCConfig{
@ -222,18 +224,18 @@ func TestBridgeSideConfig_ErcToNativeTokenAddresses(t *testing.T) {
cfg, err := config.ReadConfig([]byte(testCfg))
require.NoError(t, err)
tokenAddresses := cfg.Bridges["xdai"].Foreign.ErcToNativeTokenAddresses(7000000, 11000000)
require.Equal(t, tokenAddresses, []common.Address{
require.Equal(t, []common.Address{
common.HexToAddress("0x89d24A6b4CcB1B6fAA2625fE562bDD9a23260359"),
common.HexToAddress("0x6B175474E89094C44Da98b954EedeAC495271d0F"),
})
}, tokenAddresses)
tokenAddresses = cfg.Bridges["xdai"].Foreign.ErcToNativeTokenAddresses(7000000, 8000000)
require.Equal(t, tokenAddresses, []common.Address{
require.Equal(t, []common.Address{
common.HexToAddress("0x89d24A6b4CcB1B6fAA2625fE562bDD9a23260359"),
})
}, tokenAddresses)
tokenAddresses = cfg.Bridges["xdai"].Foreign.ErcToNativeTokenAddresses(10000000, 11000000)
require.Equal(t, tokenAddresses, []common.Address{
require.Equal(t, []common.Address{
common.HexToAddress("0x6B175474E89094C44Da98b954EedeAC495271d0F"),
})
}, tokenAddresses)
}
func TestBridgeSideConfig_ContractAddresses(t *testing.T) {
@ -241,10 +243,34 @@ func TestBridgeSideConfig_ContractAddresses(t *testing.T) {
cfg, err := config.ReadConfig([]byte(testCfg))
require.NoError(t, err)
tokenAddresses := cfg.Bridges["xdai"].Foreign.ContractAddresses(7000000, 11000000)
require.Equal(t, tokenAddresses, []common.Address{
require.Equal(t, []common.Address{
common.HexToAddress("0x89d24A6b4CcB1B6fAA2625fE562bDD9a23260359"),
common.HexToAddress("0x6B175474E89094C44Da98b954EedeAC495271d0F"),
common.HexToAddress("0x4aa42145Aa6Ebf72e164C9bBC74fbD3788045016"),
common.HexToAddress("0xe1579dEbdD2DF16Ebdb9db8694391fa74EeA201E"),
})
}, tokenAddresses)
}
func TestConfig_GetChainConfig(t *testing.T) {
t.Parallel()
cfg, err := config.ReadConfig([]byte(testCfg))
require.NoError(t, err)
require.NotNil(t, cfg.GetChainConfig("xdai"))
require.Equal(t, "100", cfg.GetChainConfig("xdai").ChainID)
require.NotNil(t, cfg.GetChainConfig("100"))
require.Equal(t, "100", cfg.GetChainConfig("100").ChainID)
require.Nil(t, cfg.GetChainConfig("123"))
}
func TestChainConfig_FormatTxLink(t *testing.T) {
t.Parallel()
cfg, err := config.ReadConfig([]byte(testCfg))
require.NoError(t, err)
txHash := "0x0000000000000000000000000000000000000000000000000000000000000000"
link := "https://etherscan.io/tx/0x0000000000000000000000000000000000000000000000000000000000000000"
require.Equal(t, link, cfg.GetChainConfig("mainnet").FormatTxLink(common.Hash{}))
require.Equal(t, link, cfg.GetChainConfig("1").FormatTxLink(common.Hash{}))
require.Equal(t, txHash, cfg.GetChainConfig("123").FormatTxLink(common.Hash{}))
}

17
config/yaml.go Normal file
View File

@ -0,0 +1,17 @@
package config
import (
"bytes"
"fmt"
"gopkg.in/yaml.v3"
)
func parseYaml(out interface{}, blob []byte) error {
dec := yaml.NewDecoder(bytes.NewReader(blob))
dec.KnownFields(true)
if err := dec.Decode(out); err != nil {
return fmt.Errorf("can't parse yaml: %w", err)
}
return nil
}

View File

@ -71,13 +71,7 @@ func GetChainConfigMiddleware(cfg *config.Config) func(http.Handler) http.Handle
}
}
var chainCfg *config.ChainConfig
for _, c := range cfg.Chains {
if c.ChainID == chainID {
chainCfg = c
break
}
}
chainCfg := cfg.GetChainConfig(chainID)
if chainCfg == nil {
render.JSON(w, r, http.StatusNotFound, fmt.Sprintf("chain with id %s not found", chainID))
return

View File

@ -529,6 +529,6 @@ func (p *Presenter) getTxInfo(ctx context.Context, logID uint) (*TxInfo, error)
return &TxInfo{
BlockNumber: log.BlockNumber,
Timestamp: bt.Timestamp,
Link: FormatLogTxLinkURL(log),
Link: p.cfg.GetChainConfig(log.ChainID).FormatTxLink(log.TransactionHash),
}, nil
}

View File

@ -101,3 +101,36 @@ type TxInfo struct {
Timestamp time.Time
Link string
}
func NewMessageInfo(msg *entity.Message) *MessageInfo {
return &MessageInfo{
BridgeID: msg.BridgeID,
MsgHash: msg.MsgHash,
MessageID: msg.MessageID,
Direction: msg.Direction,
Sender: msg.Sender,
Executor: msg.Executor,
DataType: msg.DataType,
}
}
func NewInformationRequestInfo(req *entity.InformationRequest) *InformationRequestInfo {
return &InformationRequestInfo{
BridgeID: req.BridgeID,
MessageID: req.MessageID,
Direction: req.Direction,
Sender: req.Sender,
Executor: req.Executor,
}
}
func NewErcToNativeMessageInfo(req *entity.ErcToNativeMessage) *ErcToNativeMessageInfo {
return &ErcToNativeMessageInfo{
BridgeID: req.BridgeID,
MsgHash: req.MsgHash,
Direction: req.Direction,
Sender: req.Sender,
Receiver: req.Receiver,
Value: req.Value,
}
}

View File

@ -1,57 +0,0 @@
package presenter
import (
"fmt"
"github.com/poanetwork/tokenbridge-monitor/entity"
)
var formats = map[string]string{
"1": "https://etherscan.io/tx/%s",
"4": "https://rinkeby.etherscan.io/tx/%s",
"42": "https://kovan.etherscan.io/tx/%s",
"56": "https://bscscan.com/tx/%s",
"77": "https://blockscout.com/poa/sokol/tx/%s",
"99": "https://blockscout.com/poa/core/tx/%s",
"100": "https://blockscout.com/xdai/mainnet/tx/%s",
}
func FormatLogTxLinkURL(log *entity.Log) string {
if format, ok := formats[log.ChainID]; ok {
return fmt.Sprintf(format, log.TransactionHash)
}
return log.TransactionHash.String()
}
func NewMessageInfo(msg *entity.Message) *MessageInfo {
return &MessageInfo{
BridgeID: msg.BridgeID,
MsgHash: msg.MsgHash,
MessageID: msg.MessageID,
Direction: msg.Direction,
Sender: msg.Sender,
Executor: msg.Executor,
DataType: msg.DataType,
}
}
func NewInformationRequestInfo(req *entity.InformationRequest) *InformationRequestInfo {
return &InformationRequestInfo{
BridgeID: req.BridgeID,
MessageID: req.MessageID,
Direction: req.Direction,
Sender: req.Sender,
Executor: req.Executor,
}
}
func NewErcToNativeMessageInfo(req *entity.ErcToNativeMessage) *ErcToNativeMessageInfo {
return &ErcToNativeMessageInfo{
BridgeID: req.BridgeID,
MsgHash: req.MsgHash,
Direction: req.Direction,
Sender: req.Sender,
Receiver: req.Receiver,
Value: req.Value,
}
}