Skip to content

Commit 9afd818

Browse files
committed
backend: implement block explorer selection
Add the block explorer prefix url to the configuration that will be used to open the transaction. The available options to select are statically defined and the frontend can learn them by calling the available-explorers endpoint.
1 parent c0c1d30 commit 9afd818

File tree

4 files changed

+151
-16
lines changed

4 files changed

+151
-16
lines changed

backend/backend.go

+30-9
Original file line numberDiff line numberDiff line change
@@ -73,13 +73,21 @@ var fixedURLWhitelist = []string{
7373
"https://shiftcrypto.support/",
7474
// Exchange rates.
7575
"https://www.coingecko.com/",
76-
// Block explorers.
76+
// Block explorers (btc).
7777
"https://blockstream.info/tx/",
7878
"https://blockstream.info/testnet/tx/",
79+
"https://mempool.space/tx/",
80+
"https://mempool.space/testnet/tx/",
81+
// Block explorers (ltc).
7982
"https://sochain.com/tx/LTCTEST/",
8083
"https://blockchair.com/litecoin/transaction/",
84+
// Block explorers (eth).
8185
"https://etherscan.io/tx/",
8286
"https://goerli.etherscan.io/tx/",
87+
"https://sepolia.etherscan.io/tx/",
88+
"https://ethplorer.io/tx/",
89+
"https://goerli.ethplorer.io/tx/",
90+
"https://sepolia.ethplorer.io/tx/",
8391
// Moonpay onramp
8492
"https://www.moonpay.com/",
8593
"https://support.moonpay.com/",
@@ -485,43 +493,51 @@ func (backend *Backend) Coin(code coinpkg.Code) (coinpkg.Coin, error) {
485493
servers := backend.defaultElectrumXServers(code)
486494
coin = btc.NewCoin(coinpkg.CodeRBTC, "Bitcoin Regtest", "RBTC", coinpkg.BtcUnitDefault, &chaincfg.RegressionNetParams, dbFolder, servers, "", backend.socksProxy)
487495
case code == coinpkg.CodeTBTC:
496+
blockExplorerPrefix := backend.config.AppConfig().Backend.TBTC.BlockExplorerTxPrefix
488497
servers := backend.defaultElectrumXServers(code)
489498
coin = btc.NewCoin(coinpkg.CodeTBTC, "Bitcoin Testnet", "TBTC", btcFormatUnit, &chaincfg.TestNet3Params, dbFolder, servers,
490-
"https://blockstream.info/testnet/tx/", backend.socksProxy)
499+
blockExplorerPrefix, backend.socksProxy)
491500
case code == coinpkg.CodeBTC:
501+
blockExplorerPrefix := backend.config.AppConfig().Backend.BTC.BlockExplorerTxPrefix
492502
servers := backend.defaultElectrumXServers(code)
493503
coin = btc.NewCoin(coinpkg.CodeBTC, "Bitcoin", "BTC", btcFormatUnit, &chaincfg.MainNetParams, dbFolder, servers,
494-
"https://blockstream.info/tx/", backend.socksProxy)
504+
blockExplorerPrefix, backend.socksProxy)
495505
case code == coinpkg.CodeTLTC:
506+
blockExplorerPrefix := backend.config.AppConfig().Backend.TLTC.BlockExplorerTxPrefix
496507
servers := backend.defaultElectrumXServers(code)
497508
coin = btc.NewCoin(coinpkg.CodeTLTC, "Litecoin Testnet", "TLTC", coinpkg.BtcUnitDefault, &ltc.TestNet4Params, dbFolder, servers,
498-
"https://sochain.com/tx/LTCTEST/", backend.socksProxy)
509+
blockExplorerPrefix, backend.socksProxy)
499510
case code == coinpkg.CodeLTC:
511+
blockExplorerPrefix := backend.config.AppConfig().Backend.LTC.BlockExplorerTxPrefix
500512
servers := backend.defaultElectrumXServers(code)
501513
coin = btc.NewCoin(coinpkg.CodeLTC, "Litecoin", "LTC", coinpkg.BtcUnitDefault, &ltc.MainNetParams, dbFolder, servers,
502-
"https://blockchair.com/litecoin/transaction/", backend.socksProxy)
514+
blockExplorerPrefix, backend.socksProxy)
503515
case code == coinpkg.CodeETH:
516+
blockExplorerPrefix := backend.config.AppConfig().Backend.ETH.BlockExplorerTxPrefix
504517
etherScan := etherscan.NewEtherScan("https://api.etherscan.io/api", backend.etherScanHTTPClient)
505518
coin = eth.NewCoin(etherScan, code, "Ethereum", "ETH", "ETH", params.MainnetChainConfig,
506-
"https://etherscan.io/tx/",
519+
blockExplorerPrefix,
507520
etherScan,
508521
nil)
509522
case code == coinpkg.CodeGOETH:
523+
blockExplorerPrefix := backend.config.AppConfig().Backend.GOETH.BlockExplorerTxPrefix
510524
etherScan := etherscan.NewEtherScan("https://api-goerli.etherscan.io/api", backend.etherScanHTTPClient)
511525
coin = eth.NewCoin(etherScan, code, "Ethereum Goerli", "GOETH", "GOETH", params.GoerliChainConfig,
512-
"https://goerli.etherscan.io/tx/",
526+
blockExplorerPrefix,
513527
etherScan,
514528
nil)
515529
case code == coinpkg.CodeSEPETH:
530+
blockExplorerPrefix := backend.config.AppConfig().Backend.SEPETH.BlockExplorerTxPrefix
516531
etherScan := etherscan.NewEtherScan("https://api-sepolia.etherscan.io/api", backend.etherScanHTTPClient)
517532
coin = eth.NewCoin(etherScan, code, "Ethereum Sepolia", "SEPETH", "SEPETH", params.SepoliaChainConfig,
518-
"https://sepolia.etherscan.io/tx/",
533+
blockExplorerPrefix,
519534
etherScan,
520535
nil)
521536
case erc20Token != nil:
537+
blockExplorerPrefix := backend.config.AppConfig().Backend.ETH.BlockExplorerTxPrefix
522538
etherScan := etherscan.NewEtherScan("https://api.etherscan.io/api", backend.etherScanHTTPClient)
523539
coin = eth.NewCoin(etherScan, erc20Token.code, erc20Token.name, erc20Token.unit, "ETH", params.MainnetChainConfig,
524-
"https://etherscan.io/tx/",
540+
blockExplorerPrefix,
525541
etherScan,
526542
erc20Token.token,
527543
)
@@ -1028,3 +1044,8 @@ func (backend *Backend) SetWatchonly(rootFingerprint []byte, watchonly bool) err
10281044
&t,
10291045
)
10301046
}
1047+
1048+
// AvailableExplorers returns a struct containing all available block explorers for each coin.
1049+
func (backend *Backend) AvailableExplorers() config.AvailableBlockExplorers {
1050+
return config.AvailableExplorers
1051+
}

backend/config/blockexplorer.go

+88
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
package config
2+
3+
// BlockExplorer defines a selectable block explorer.
4+
type BlockExplorer struct {
5+
Name string `json:"name"`
6+
Url string `json:"url"`
7+
}
8+
9+
// AvailableBlockExplorers defines all available block explorers for each coin.
10+
type AvailableBlockExplorers struct {
11+
Btc []BlockExplorer `json:"btc"`
12+
Tbtc []BlockExplorer `json:"tbtc"`
13+
Ltc []BlockExplorer `json:"ltc"`
14+
Tltc []BlockExplorer `json:"tltc"`
15+
Eth []BlockExplorer `json:"eth"`
16+
GoEth []BlockExplorer `json:"goeth"`
17+
SepEth []BlockExplorer `json:"sepeth"`
18+
}
19+
20+
// AvailableExplorers FIXME: Localize AvailableExplorers.
21+
var AvailableExplorers = AvailableBlockExplorers{
22+
Btc: []BlockExplorer{
23+
{
24+
Name: "blockstream.info",
25+
Url: "https://blockstream.info/tx/",
26+
},
27+
{
28+
Name: "mempool.space",
29+
Url: "https://mempool.space/tx",
30+
},
31+
},
32+
Tbtc: []BlockExplorer{
33+
{
34+
Name: "mempool.space",
35+
Url: "https://mempool.space/testnet/tx/",
36+
},
37+
{
38+
Name: "blockstream.info",
39+
Url: "https://blockstream.info/testnet/tx/",
40+
},
41+
},
42+
Ltc: []BlockExplorer{
43+
{
44+
Name: "sochain.com",
45+
Url: "https://sochain.com/tx/",
46+
},
47+
{
48+
Name: "blockchair.com",
49+
Url: "https://blockchair.com/litecoin/transaction",
50+
},
51+
},
52+
Tltc: []BlockExplorer{
53+
{
54+
Name: "sochain.com",
55+
Url: "https://sochain.com/tx/LTCTEST/",
56+
},
57+
},
58+
Eth: []BlockExplorer{
59+
{
60+
Name: "etherscan.io",
61+
Url: "https://etherscan.io/tx/",
62+
},
63+
{
64+
Name: "ethplorer.io",
65+
Url: "https://ethplorer.io/tx/",
66+
},
67+
},
68+
GoEth: []BlockExplorer{
69+
{
70+
Name: "etherscan.io",
71+
Url: "https://goerli.etherscan.io/tx/",
72+
},
73+
{
74+
Name: "ethplorer.io",
75+
Url: "https://goerli.ethplorer.io/tx/",
76+
},
77+
},
78+
SepEth: []BlockExplorer{
79+
{
80+
Name: "etherscan.io",
81+
Url: "https://sepolia.etherscan.io/tx/",
82+
},
83+
{
84+
Name: "ethplorer.io",
85+
Url: "https://sepolia.ethplorer.io/tx/",
86+
},
87+
},
88+
}

backend/config/config.go

+24-7
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@ func (s *ServerInfo) String() string {
4141

4242
// btcCoinConfig holds configurations specific to a btc-based coin.
4343
type btcCoinConfig struct {
44-
ElectrumServers []*ServerInfo `json:"electrumServers"`
44+
ElectrumServers []*ServerInfo `json:"electrumServers"`
45+
BlockExplorerTxPrefix string `json:"blockExplorerTxPrefix"`
4546
}
4647

4748
// ETHTransactionsSource where to get Ethereum transactions from. See the list of consts
@@ -59,6 +60,7 @@ const (
5960
// ethCoinConfig holds configurations for ethereum coins.
6061
type ethCoinConfig struct {
6162
DeprecatedActiveERC20Tokens []string `json:"activeERC20Tokens"`
63+
BlockExplorerTxPrefix string `json:"blockExplorerTxPrefix"`
6264
}
6365

6466
type proxyConfig struct {
@@ -76,12 +78,14 @@ type Backend struct {
7678

7779
Authentication bool `json:"authentication"`
7880

79-
BTC btcCoinConfig `json:"btc"`
80-
TBTC btcCoinConfig `json:"tbtc"`
81-
RBTC btcCoinConfig `json:"rbtc"`
82-
LTC btcCoinConfig `json:"ltc"`
83-
TLTC btcCoinConfig `json:"tltc"`
84-
ETH ethCoinConfig `json:"eth"`
81+
BTC btcCoinConfig `json:"btc"`
82+
TBTC btcCoinConfig `json:"tbtc"`
83+
RBTC btcCoinConfig `json:"rbtc"`
84+
LTC btcCoinConfig `json:"ltc"`
85+
TLTC btcCoinConfig `json:"tltc"`
86+
ETH ethCoinConfig `json:"eth"`
87+
GOETH ethCoinConfig `json:"goeth"`
88+
SEPETH ethCoinConfig `json:"sepeth"`
8589

8690
// Removed in v4.35 - don't reuse these two keys.
8791
TETH struct{} `json:"teth"`
@@ -168,6 +172,7 @@ func NewDefaultAppConfig() AppConfig {
168172
PEMCert: shiftRootCA,
169173
},
170174
},
175+
BlockExplorerTxPrefix: AvailableExplorers.Btc[0].Url,
171176
},
172177
TBTC: btcCoinConfig{
173178
ElectrumServers: []*ServerInfo{
@@ -182,6 +187,7 @@ func NewDefaultAppConfig() AppConfig {
182187
PEMCert: shiftRootCA,
183188
},
184189
},
190+
BlockExplorerTxPrefix: AvailableExplorers.Tbtc[0].Url,
185191
},
186192
RBTC: btcCoinConfig{
187193
ElectrumServers: []*ServerInfo{
@@ -210,6 +216,7 @@ func NewDefaultAppConfig() AppConfig {
210216
PEMCert: shiftRootCA,
211217
},
212218
},
219+
BlockExplorerTxPrefix: AvailableExplorers.Ltc[0].Url,
213220
},
214221
TLTC: btcCoinConfig{
215222
ElectrumServers: []*ServerInfo{
@@ -224,9 +231,19 @@ func NewDefaultAppConfig() AppConfig {
224231
PEMCert: shiftRootCA,
225232
},
226233
},
234+
BlockExplorerTxPrefix: AvailableExplorers.Tltc[0].Url,
227235
},
228236
ETH: ethCoinConfig{
229237
DeprecatedActiveERC20Tokens: []string{},
238+
BlockExplorerTxPrefix: AvailableExplorers.Eth[0].Url,
239+
},
240+
GOETH: ethCoinConfig{
241+
DeprecatedActiveERC20Tokens: []string{},
242+
BlockExplorerTxPrefix: AvailableExplorers.GoEth[0].Url,
243+
},
244+
SEPETH: ethCoinConfig{
245+
DeprecatedActiveERC20Tokens: []string{},
246+
BlockExplorerTxPrefix: AvailableExplorers.SepEth[0].Url,
230247
},
231248
// Copied from frontend/web/src/components/rates/rates.tsx.
232249
FiatList: []string{rates.USD.String(), rates.EUR.String(), rates.CHF.String()},

backend/handlers/handlers.go

+9
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ type Backend interface {
104104
AOPPCancel()
105105
AOPPApprove()
106106
AOPPChooseAccount(code accountsTypes.Code)
107+
AvailableExplorers() config.AvailableBlockExplorers
107108
GetAccountFromCode(code accountsTypes.Code) (accounts.Interface, error)
108109
HTTPClient() *http.Client
109110
LookupInsuredAccounts(accountCode accountsTypes.Code) ([]bitsurance.AccountDetails, error)
@@ -250,6 +251,7 @@ func NewHandlers(
250251
getAPIRouterNoError(apiRouter)("/set-watchonly", handlers.postSetWatchonly).Methods("POST")
251252
getAPIRouterNoError(apiRouter)("/on-auth-setting-changed", handlers.postOnAuthSettingChanged).Methods("POST")
252253
getAPIRouterNoError(apiRouter)("/accounts/eth-account-code", handlers.lookupEthAccountCode).Methods("POST")
254+
getAPIRouterNoError(apiRouter)("/available-explorers", handlers.getAvailableExplorers).Methods("GET")
253255

254256
devicesRouter := getAPIRouterNoError(apiRouter.PathPrefix("/devices").Subrouter())
255257
devicesRouter("/registered", handlers.getDevicesRegistered).Methods("GET")
@@ -1400,3 +1402,10 @@ func (handlers *Handlers) postOnAuthSettingChanged(r *http.Request) interface{}
14001402
handlers.backend.Config().AppConfig().Backend.Authentication)
14011403
return nil
14021404
}
1405+
1406+
// getAvailableExplorers returns a struct containing arrays with block explorers for each
1407+
// individual coin code.
1408+
func (handlers *Handlers) getAvailableExplorers(*http.Request) interface{} {
1409+
// TODO: maybe filter out testing coins if not testing and real if testing
1410+
return config.AvailableExplorers
1411+
}

0 commit comments

Comments
 (0)