mirror of
https://github.com/trezor/blockbook.git
synced 2026-02-19 16:31:19 +01:00
Show raw tx hex in UI (#1162)
* Fix Network configuration parameter * feat: allow for showing raw transaction hex for ETH transactions * chore: remove comments from JS code to avoid parsing issues in tests * temp: comment out failing tx template tests * chore: trim text from copyable before writing it to clipboard * chore: improve the design of Transaction hex * chore: add wrap to element showing raw hex data * fixup! chore: add wrap to element showing raw hex data * chore: remove redundant style, make HTML prettier * Revert "temp: comment out failing tx template tests" This reverts commit f104ebbf5111583b46996d7527a26c08cd9e29b6. * chore: put rawTx javascript functionality into main.js * chore: modify the expected HTML for changed tx template * feat: support the raw transaction hex also for BTC-like coins * chore: add on-hover effect for active button - make the background white * Minify javascript and styles --------- Co-authored-by: Martin Boehm <martin.boehm@1mbsoftware.net>
This commit is contained in:
@@ -207,6 +207,11 @@ func (w *Worker) GetTransaction(txid string, spendingTxs bool, specificJSON bool
|
||||
return tx, nil
|
||||
}
|
||||
|
||||
// GetRawTransaction gets raw transaction data in hex format from txid
|
||||
func (w *Worker) GetRawTransaction(txid string) (string, error) {
|
||||
return w.chain.EthereumTypeGetRawTransaction(txid)
|
||||
}
|
||||
|
||||
// getTransaction reads transaction data from txid
|
||||
func (w *Worker) getTransaction(txid string, spendingTxs bool, specificJSON bool, addresses map[string]struct{}) (*Tx, error) {
|
||||
bchainTx, height, err := w.txCache.GetTransaction(txid)
|
||||
|
||||
@@ -81,3 +81,7 @@ func (b *BaseChain) EthereumTypeGetStakingPoolsData(addrDesc AddressDescriptor)
|
||||
func (b *BaseChain) EthereumTypeRpcCall(data, to, from string) (string, error) {
|
||||
return "", errors.New("not supported")
|
||||
}
|
||||
|
||||
func (b *BaseChain) EthereumTypeGetRawTransaction(txid string) (string, error) {
|
||||
return "", errors.New("not supported")
|
||||
}
|
||||
|
||||
@@ -354,6 +354,11 @@ func (c *blockChainWithMetrics) EthereumTypeRpcCall(data, to, from string) (v st
|
||||
return c.b.EthereumTypeRpcCall(data, to, from)
|
||||
}
|
||||
|
||||
func (c *blockChainWithMetrics) EthereumTypeGetRawTransaction(txid string) (v string, err error) {
|
||||
defer func(s time.Time) { c.observeRPCLatency("EthereumTypeGetRawTransaction", s, err) }(time.Now())
|
||||
return c.b.EthereumTypeGetRawTransaction(txid)
|
||||
}
|
||||
|
||||
type mempoolWithMetrics struct {
|
||||
mempool bchain.Mempool
|
||||
m *common.Metrics
|
||||
|
||||
@@ -40,6 +40,7 @@ const (
|
||||
type Configuration struct {
|
||||
CoinName string `json:"coin_name"`
|
||||
CoinShortcut string `json:"coin_shortcut"`
|
||||
Network string `json:"network"`
|
||||
RPCURL string `json:"rpc_url"`
|
||||
RPCTimeout int `json:"rpc_timeout"`
|
||||
BlockAddressesToKeep int `json:"block_addresses_to_keep"`
|
||||
@@ -159,7 +160,12 @@ func (b *EthereumRPC) Initialize() error {
|
||||
return errors.Errorf("Unknown network id %v", id)
|
||||
}
|
||||
|
||||
err = b.initStakingPools(b.ChainConfig.CoinShortcut)
|
||||
networkConfig := b.ChainConfig.Network
|
||||
if networkConfig == "" {
|
||||
networkConfig = b.ChainConfig.CoinShortcut
|
||||
}
|
||||
|
||||
err = b.initStakingPools(networkConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -988,21 +994,31 @@ func (b *EthereumRPC) EthereumTypeEstimateGas(params map[string]interface{}) (ui
|
||||
|
||||
// SendRawTransaction sends raw transaction
|
||||
func (b *EthereumRPC) SendRawTransaction(hex string) (string, error) {
|
||||
return b.callRpcStringResult("eth_sendRawTransaction", hex)
|
||||
}
|
||||
|
||||
// EthereumTypeGetRawTransaction gets raw transaction in hex format
|
||||
func (b *EthereumRPC) EthereumTypeGetRawTransaction(txid string) (string, error) {
|
||||
return b.callRpcStringResult("eth_getRawTransactionByHash", txid)
|
||||
}
|
||||
|
||||
// Helper function for calling ETH RPC with parameters and getting string result
|
||||
func (b *EthereumRPC) callRpcStringResult(rpcMethod string, args ...interface{}) (string, error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), b.Timeout)
|
||||
defer cancel()
|
||||
var raw json.RawMessage
|
||||
err := b.RPC.CallContext(ctx, &raw, "eth_sendRawTransaction", hex)
|
||||
err := b.RPC.CallContext(ctx, &raw, rpcMethod, args...)
|
||||
if err != nil {
|
||||
return "", err
|
||||
} else if len(raw) == 0 {
|
||||
return "", errors.New("SendRawTransaction: failed")
|
||||
return "", errors.New(rpcMethod + " : failed")
|
||||
}
|
||||
var result string
|
||||
if err := json.Unmarshal(raw, &result); err != nil {
|
||||
return "", errors.Annotatef(err, "raw result %v", raw)
|
||||
}
|
||||
if result == "" {
|
||||
return "", errors.New("SendRawTransaction: failed, empty result")
|
||||
return "", errors.New(rpcMethod + " : failed, empty result")
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
@@ -11,9 +11,9 @@ import (
|
||||
"github.com/trezor/blockbook/bchain"
|
||||
)
|
||||
|
||||
func (b *EthereumRPC) initStakingPools(coinShortcut string) error {
|
||||
func (b *EthereumRPC) initStakingPools(network string) error {
|
||||
// for now only single staking pool
|
||||
envVar := strings.ToUpper(coinShortcut) + "_STAKING_POOL_CONTRACT"
|
||||
envVar := strings.ToUpper(network) + "_STAKING_POOL_CONTRACT"
|
||||
envValue := os.Getenv(envVar)
|
||||
if envValue != "" {
|
||||
parts := strings.Split(envValue, "/")
|
||||
|
||||
@@ -337,6 +337,7 @@ type BlockChain interface {
|
||||
EthereumTypeGetSupportedStakingPools() []string
|
||||
EthereumTypeGetStakingPoolsData(addrDesc AddressDescriptor) ([]StakingPoolData, error)
|
||||
EthereumTypeRpcCall(data, to, from string) (string, error)
|
||||
EthereumTypeGetRawTransaction(txid string) (string, error)
|
||||
GetTokenURI(contractDesc AddressDescriptor, tokenID *big.Int) (string, error)
|
||||
}
|
||||
|
||||
|
||||
@@ -507,7 +507,7 @@ func newInternalState(config *common.Config, d *db.RocksDB, enableSubNewTx bool)
|
||||
is.Host = name
|
||||
}
|
||||
|
||||
is.WsGetAccountInfoLimit, _ = strconv.Atoi(os.Getenv(strings.ToUpper(is.CoinShortcut) + "_WS_GETACCOUNTINFO_LIMIT"))
|
||||
is.WsGetAccountInfoLimit, _ = strconv.Atoi(os.Getenv(strings.ToUpper(is.GetNetwork()) + "_WS_GETACCOUNTINFO_LIMIT"))
|
||||
if is.WsGetAccountInfoLimit > 0 {
|
||||
glog.Info("WsGetAccountInfoLimit enabled with limit ", is.WsGetAccountInfoLimit)
|
||||
is.WsLimitExceedingIPs = make(map[string]int)
|
||||
|
||||
@@ -1,65 +1,66 @@
|
||||
{
|
||||
"coin": {
|
||||
"name": "Arbitrum",
|
||||
"shortcut": "ETH",
|
||||
"label": "Arbitrum",
|
||||
"alias": "arbitrum"
|
||||
},
|
||||
"ports": {
|
||||
"backend_rpc": 8205,
|
||||
"backend_p2p": 38405,
|
||||
"backend_http": 8305,
|
||||
"blockbook_internal": 9205,
|
||||
"blockbook_public": 9305
|
||||
},
|
||||
"ipc": {
|
||||
"rpc_url_template": "ws://127.0.0.1:{{.Ports.BackendRPC}}",
|
||||
"rpc_timeout": 25
|
||||
},
|
||||
"backend": {
|
||||
"package_name": "backend-arbitrum",
|
||||
"package_revision": "satoshilabs-1",
|
||||
"system_user": "arbitrum",
|
||||
"version": "3.2.1",
|
||||
"docker_image": "offchainlabs/nitro-node:v3.2.1-d81324d",
|
||||
"verification_type": "docker",
|
||||
"verification_source": "724ebdcca39cd0c28ffd025ecea8d1622a376f41344201b729afb60352cbc306",
|
||||
"extract_command": "docker cp extract:/home/user/target backend/target; docker cp extract:/home/user/nitro-legacy backend/nitro-legacy; docker cp extract:/usr/local/bin/nitro backend/nitro",
|
||||
"exclude_files": [],
|
||||
"exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/arbitrum_exec.sh 2>> {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'",
|
||||
"exec_script": "arbitrum.sh",
|
||||
"logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log",
|
||||
"postinst_script_template": "openssl rand -hex 32 > {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/jwtsecret",
|
||||
"service_type": "simple",
|
||||
"service_additional_params_template": "",
|
||||
"protect_memory": true,
|
||||
"mainnet": true,
|
||||
"server_config_file": "",
|
||||
"client_config_file": ""
|
||||
},
|
||||
"blockbook": {
|
||||
"package_name": "blockbook-arbitrum",
|
||||
"system_user": "blockbook-arbitrum",
|
||||
"internal_binding_template": ":{{.Ports.BlockbookInternal}}",
|
||||
"public_binding_template": ":{{.Ports.BlockbookPublic}}",
|
||||
"explorer_url": "",
|
||||
"additional_params": "",
|
||||
"block_chain": {
|
||||
"parse": true,
|
||||
"mempool_workers": 8,
|
||||
"mempool_sub_workers": 2,
|
||||
"block_addresses_to_keep": 300,
|
||||
"additional_params": {
|
||||
"mempoolTxTimeoutHours": 48,
|
||||
"queryBackendOnMempoolResync": false,
|
||||
"fiat_rates": "coingecko",
|
||||
"fiat_rates_vs_currencies": "AED,ARS,AUD,BDT,BHD,BMD,BRL,CAD,CHF,CLP,CNY,CZK,DKK,EUR,GBP,HKD,HUF,IDR,ILS,INR,JPY,KRW,KWD,LKR,MMK,MXN,MYR,NGN,NOK,NZD,PHP,PKR,PLN,RUB,SAR,SEK,SGD,THB,TRY,TWD,UAH,USD,VEF,VND,ZAR,BTC,ETH",
|
||||
"fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"ethereum\",\"platformIdentifier\": \"ethereum\",\"platformVsCurrency\": \"eth\",\"periodSeconds\": 900}"
|
||||
}
|
||||
"coin": {
|
||||
"name": "Arbitrum",
|
||||
"shortcut": "ETH",
|
||||
"network": "ARB",
|
||||
"label": "Arbitrum",
|
||||
"alias": "arbitrum"
|
||||
},
|
||||
"ports": {
|
||||
"backend_rpc": 8205,
|
||||
"backend_p2p": 38405,
|
||||
"backend_http": 8305,
|
||||
"blockbook_internal": 9205,
|
||||
"blockbook_public": 9305
|
||||
},
|
||||
"ipc": {
|
||||
"rpc_url_template": "ws://127.0.0.1:{{.Ports.BackendRPC}}",
|
||||
"rpc_timeout": 25
|
||||
},
|
||||
"backend": {
|
||||
"package_name": "backend-arbitrum",
|
||||
"package_revision": "satoshilabs-1",
|
||||
"system_user": "arbitrum",
|
||||
"version": "3.2.1",
|
||||
"docker_image": "offchainlabs/nitro-node:v3.2.1-d81324d",
|
||||
"verification_type": "docker",
|
||||
"verification_source": "724ebdcca39cd0c28ffd025ecea8d1622a376f41344201b729afb60352cbc306",
|
||||
"extract_command": "docker cp extract:/home/user/target backend/target; docker cp extract:/home/user/nitro-legacy backend/nitro-legacy; docker cp extract:/usr/local/bin/nitro backend/nitro",
|
||||
"exclude_files": [],
|
||||
"exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/arbitrum_exec.sh 2>> {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'",
|
||||
"exec_script": "arbitrum.sh",
|
||||
"logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log",
|
||||
"postinst_script_template": "openssl rand -hex 32 > {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/jwtsecret",
|
||||
"service_type": "simple",
|
||||
"service_additional_params_template": "",
|
||||
"protect_memory": true,
|
||||
"mainnet": true,
|
||||
"server_config_file": "",
|
||||
"client_config_file": ""
|
||||
},
|
||||
"blockbook": {
|
||||
"package_name": "blockbook-arbitrum",
|
||||
"system_user": "blockbook-arbitrum",
|
||||
"internal_binding_template": ":{{.Ports.BlockbookInternal}}",
|
||||
"public_binding_template": ":{{.Ports.BlockbookPublic}}",
|
||||
"explorer_url": "",
|
||||
"additional_params": "",
|
||||
"block_chain": {
|
||||
"parse": true,
|
||||
"mempool_workers": 8,
|
||||
"mempool_sub_workers": 2,
|
||||
"block_addresses_to_keep": 300,
|
||||
"additional_params": {
|
||||
"mempoolTxTimeoutHours": 48,
|
||||
"queryBackendOnMempoolResync": false,
|
||||
"fiat_rates": "coingecko",
|
||||
"fiat_rates_vs_currencies": "AED,ARS,AUD,BDT,BHD,BMD,BRL,CAD,CHF,CLP,CNY,CZK,DKK,EUR,GBP,HKD,HUF,IDR,ILS,INR,JPY,KRW,KWD,LKR,MMK,MXN,MYR,NGN,NOK,NZD,PHP,PKR,PLN,RUB,SAR,SEK,SGD,THB,TRY,TWD,UAH,USD,VEF,VND,ZAR,BTC,ETH",
|
||||
"fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"ethereum\",\"platformIdentifier\": \"ethereum\",\"platformVsCurrency\": \"eth\",\"periodSeconds\": 900}"
|
||||
}
|
||||
}
|
||||
},
|
||||
"meta": {
|
||||
"package_maintainer": "IT",
|
||||
"package_maintainer_email": "it@satoshilabs.com"
|
||||
}
|
||||
},
|
||||
"meta": {
|
||||
"package_maintainer": "IT",
|
||||
"package_maintainer_email": "it@satoshilabs.com"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,67 +1,68 @@
|
||||
{
|
||||
"coin": {
|
||||
"name": "Arbitrum Archive",
|
||||
"shortcut": "ETH",
|
||||
"label": "Arbitrum",
|
||||
"alias": "arbitrum_archive"
|
||||
},
|
||||
"ports": {
|
||||
"backend_rpc": 8306,
|
||||
"backend_p2p": 38406,
|
||||
"blockbook_internal": 9206,
|
||||
"blockbook_public": 9306
|
||||
},
|
||||
"ipc": {
|
||||
"rpc_url_template": "ws://127.0.0.1:{{.Ports.BackendRPC}}",
|
||||
"rpc_timeout": 25
|
||||
},
|
||||
"backend": {
|
||||
"package_name": "backend-arbitrum-archive",
|
||||
"package_revision": "satoshilabs-1",
|
||||
"system_user": "arbitrum",
|
||||
"version": "3.2.1",
|
||||
"docker_image": "offchainlabs/nitro-node:v3.2.1-d81324d",
|
||||
"verification_type": "docker",
|
||||
"verification_source": "724ebdcca39cd0c28ffd025ecea8d1622a376f41344201b729afb60352cbc306",
|
||||
"extract_command": "docker cp extract:/home/user/target backend/target; docker cp extract:/home/user/nitro-legacy backend/nitro-legacy; docker cp extract:/usr/local/bin/nitro backend/nitro",
|
||||
"exclude_files": [],
|
||||
"exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/arbitrum_archive_exec.sh 2>> {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'",
|
||||
"exec_script": "arbitrum_archive.sh",
|
||||
"logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log",
|
||||
"postinst_script_template": "openssl rand -hex 32 > {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/jwtsecret",
|
||||
"service_type": "simple",
|
||||
"service_additional_params_template": "",
|
||||
"protect_memory": true,
|
||||
"mainnet": true,
|
||||
"server_config_file": "",
|
||||
"client_config_file": ""
|
||||
},
|
||||
"blockbook": {
|
||||
"package_name": "blockbook-arbitrum-archive",
|
||||
"system_user": "blockbook-arbitrum",
|
||||
"internal_binding_template": ":{{.Ports.BlockbookInternal}}",
|
||||
"public_binding_template": ":{{.Ports.BlockbookPublic}}",
|
||||
"explorer_url": "",
|
||||
"additional_params": "-workers=16",
|
||||
"block_chain": {
|
||||
"parse": true,
|
||||
"mempool_workers": 8,
|
||||
"mempool_sub_workers": 2,
|
||||
"block_addresses_to_keep": 600,
|
||||
"additional_params": {
|
||||
"address_aliases": true,
|
||||
"mempoolTxTimeoutHours": 48,
|
||||
"processInternalTransactions": true,
|
||||
"queryBackendOnMempoolResync": false,
|
||||
"fiat_rates": "coingecko",
|
||||
"fiat_rates_vs_currencies": "AED,ARS,AUD,BDT,BHD,BMD,BRL,CAD,CHF,CLP,CNY,CZK,DKK,EUR,GBP,HKD,HUF,IDR,ILS,INR,JPY,KRW,KWD,LKR,MMK,MXN,MYR,NGN,NOK,NZD,PHP,PKR,PLN,RUB,SAR,SEK,SGD,THB,TRY,TWD,UAH,USD,VEF,VND,ZAR,BTC,ETH",
|
||||
"fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"ethereum\",\"platformIdentifier\": \"ethereum\",\"platformVsCurrency\": \"eth\",\"periodSeconds\": 900}",
|
||||
"fourByteSignatures": "https://www.4byte.directory/api/v1/signatures/"
|
||||
}
|
||||
"coin": {
|
||||
"name": "Arbitrum Archive",
|
||||
"shortcut": "ETH",
|
||||
"network": "ARB",
|
||||
"label": "Arbitrum",
|
||||
"alias": "arbitrum_archive"
|
||||
},
|
||||
"ports": {
|
||||
"backend_rpc": 8306,
|
||||
"backend_p2p": 38406,
|
||||
"blockbook_internal": 9206,
|
||||
"blockbook_public": 9306
|
||||
},
|
||||
"ipc": {
|
||||
"rpc_url_template": "ws://127.0.0.1:{{.Ports.BackendRPC}}",
|
||||
"rpc_timeout": 25
|
||||
},
|
||||
"backend": {
|
||||
"package_name": "backend-arbitrum-archive",
|
||||
"package_revision": "satoshilabs-1",
|
||||
"system_user": "arbitrum",
|
||||
"version": "3.2.1",
|
||||
"docker_image": "offchainlabs/nitro-node:v3.2.1-d81324d",
|
||||
"verification_type": "docker",
|
||||
"verification_source": "724ebdcca39cd0c28ffd025ecea8d1622a376f41344201b729afb60352cbc306",
|
||||
"extract_command": "docker cp extract:/home/user/target backend/target; docker cp extract:/home/user/nitro-legacy backend/nitro-legacy; docker cp extract:/usr/local/bin/nitro backend/nitro",
|
||||
"exclude_files": [],
|
||||
"exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/arbitrum_archive_exec.sh 2>> {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'",
|
||||
"exec_script": "arbitrum_archive.sh",
|
||||
"logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log",
|
||||
"postinst_script_template": "openssl rand -hex 32 > {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/jwtsecret",
|
||||
"service_type": "simple",
|
||||
"service_additional_params_template": "",
|
||||
"protect_memory": true,
|
||||
"mainnet": true,
|
||||
"server_config_file": "",
|
||||
"client_config_file": ""
|
||||
},
|
||||
"blockbook": {
|
||||
"package_name": "blockbook-arbitrum-archive",
|
||||
"system_user": "blockbook-arbitrum",
|
||||
"internal_binding_template": ":{{.Ports.BlockbookInternal}}",
|
||||
"public_binding_template": ":{{.Ports.BlockbookPublic}}",
|
||||
"explorer_url": "",
|
||||
"additional_params": "-workers=16",
|
||||
"block_chain": {
|
||||
"parse": true,
|
||||
"mempool_workers": 8,
|
||||
"mempool_sub_workers": 2,
|
||||
"block_addresses_to_keep": 600,
|
||||
"additional_params": {
|
||||
"address_aliases": true,
|
||||
"mempoolTxTimeoutHours": 48,
|
||||
"processInternalTransactions": true,
|
||||
"queryBackendOnMempoolResync": false,
|
||||
"fiat_rates": "coingecko",
|
||||
"fiat_rates_vs_currencies": "AED,ARS,AUD,BDT,BHD,BMD,BRL,CAD,CHF,CLP,CNY,CZK,DKK,EUR,GBP,HKD,HUF,IDR,ILS,INR,JPY,KRW,KWD,LKR,MMK,MXN,MYR,NGN,NOK,NZD,PHP,PKR,PLN,RUB,SAR,SEK,SGD,THB,TRY,TWD,UAH,USD,VEF,VND,ZAR,BTC,ETH",
|
||||
"fiat_rates_params": "{\"url\": \"https://api.coingecko.com/api/v3\", \"coin\": \"ethereum\",\"platformIdentifier\": \"ethereum\",\"platformVsCurrency\": \"eth\",\"periodSeconds\": 900}",
|
||||
"fourByteSignatures": "https://www.4byte.directory/api/v1/signatures/"
|
||||
}
|
||||
}
|
||||
},
|
||||
"meta": {
|
||||
"package_maintainer": "IT",
|
||||
"package_maintainer_email": "it@satoshilabs.com"
|
||||
}
|
||||
},
|
||||
"meta": {
|
||||
"package_maintainer": "IT",
|
||||
"package_maintainer_email": "it@satoshilabs.com"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"coin": {
|
||||
"name": "BNB Smart Chain",
|
||||
"shortcut": "BNB",
|
||||
"network": "BNB",
|
||||
"network": "BSC",
|
||||
"label": "BNB Smart Chain",
|
||||
"alias": "bsc"
|
||||
},
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"coin": {
|
||||
"name": "BNB Smart Chain Archive",
|
||||
"shortcut": "BNB",
|
||||
"network": "BNB",
|
||||
"network": "BSC",
|
||||
"label": "BNB Smart Chain",
|
||||
"alias": "bsc_archive"
|
||||
},
|
||||
|
||||
@@ -59,7 +59,7 @@ type marketChartPrices struct {
|
||||
}
|
||||
|
||||
// NewCoinGeckoDownloader creates a coingecko structure that implements the RatesDownloaderInterface
|
||||
func NewCoinGeckoDownloader(db *db.RocksDB, coinShortcut string, url string, coin string, platformIdentifier string, platformVsCurrency string, allowedVsCurrencies string, timeFormat string, metrics *common.Metrics, throttleDown bool) RatesDownloaderInterface {
|
||||
func NewCoinGeckoDownloader(db *db.RocksDB, network string, url string, coin string, platformIdentifier string, platformVsCurrency string, allowedVsCurrencies string, timeFormat string, metrics *common.Metrics, throttleDown bool) RatesDownloaderInterface {
|
||||
throttlingDelayMs := 0 // No delay by default
|
||||
if throttleDown {
|
||||
throttlingDelayMs = DefaultThrottleDelayMs
|
||||
@@ -67,7 +67,7 @@ func NewCoinGeckoDownloader(db *db.RocksDB, coinShortcut string, url string, coi
|
||||
|
||||
allowedVsCurrenciesMap := getAllowedVsCurrenciesMap(allowedVsCurrencies)
|
||||
|
||||
apiKey := os.Getenv(strings.ToUpper(coinShortcut) + "_COINGECKO_API_KEY")
|
||||
apiKey := os.Getenv(strings.ToUpper(network) + "_COINGECKO_API_KEY")
|
||||
if apiKey == "" {
|
||||
apiKey = os.Getenv("COINGECKO_API_KEY")
|
||||
}
|
||||
|
||||
@@ -186,6 +186,7 @@ func (s *PublicServer) ConnectFullPublicInterface() {
|
||||
serveMux.HandleFunc(path+"api/block-filters/", s.jsonHandler(s.apiBlockFilters, apiDefault))
|
||||
serveMux.HandleFunc(path+"api/tx-specific/", s.jsonHandler(s.apiTxSpecific, apiDefault))
|
||||
serveMux.HandleFunc(path+"api/tx/", s.jsonHandler(s.apiTx, apiDefault))
|
||||
serveMux.HandleFunc(path+"api/rawtx/", s.jsonHandler(s.apiRawTx, apiDefault))
|
||||
serveMux.HandleFunc(path+"api/address/", s.jsonHandler(s.apiAddress, apiDefault))
|
||||
serveMux.HandleFunc(path+"api/xpub/", s.jsonHandler(s.apiXpub, apiDefault))
|
||||
serveMux.HandleFunc(path+"api/utxo/", s.jsonHandler(s.apiUtxo, apiDefault))
|
||||
@@ -303,7 +304,7 @@ func (s *PublicServer) newTemplateData(r *http.Request) *TemplateData {
|
||||
t.MultiTokenName = bchain.EthereumTokenTypeMap[bchain.MultiToken]
|
||||
}
|
||||
if !s.debug {
|
||||
t.Minified = ".min.3"
|
||||
t.Minified = ".min.4"
|
||||
}
|
||||
if s.is.HasFiatRates {
|
||||
// get the secondary coin and if it should be shown either from query parameters "secondary" and "use_secondary"
|
||||
@@ -1288,6 +1289,19 @@ func (s *PublicServer) apiTx(r *http.Request, apiVersion int) (interface{}, erro
|
||||
return tx, err
|
||||
}
|
||||
|
||||
func (s *PublicServer) apiRawTx(r *http.Request, apiVersion int) (interface{}, error) {
|
||||
var txid string
|
||||
i := strings.LastIndexByte(r.URL.Path, '/')
|
||||
if i > 0 {
|
||||
txid = r.URL.Path[i+1:]
|
||||
}
|
||||
if len(txid) == 0 {
|
||||
return "", api.NewAPIError("Missing txid", true)
|
||||
}
|
||||
s.metrics.ExplorerViews.With(common.Labels{"action": "api-raw-tx"}).Inc()
|
||||
return s.api.GetRawTransaction(txid)
|
||||
}
|
||||
|
||||
func (s *PublicServer) apiTxSpecific(r *http.Request, apiVersion int) (interface{}, error) {
|
||||
var txid string
|
||||
i := strings.LastIndexByte(r.URL.Path, '/')
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -107,7 +107,7 @@ func NewWebsocketServer(db *db.RocksDB, chain bchain.BlockChain, mempool bchain.
|
||||
fiatRatesSubscriptions: make(map[string]map[*websocketChannel]string),
|
||||
fiatRatesTokenSubscriptions: make(map[*websocketChannel][]string),
|
||||
}
|
||||
envRpcCall := os.Getenv(strings.ToUpper(is.CoinShortcut) + "_ALLOWED_RPC_CALL_TO")
|
||||
envRpcCall := os.Getenv(strings.ToUpper(is.GetNetwork()) + "_ALLOWED_RPC_CALL_TO")
|
||||
if envRpcCall != "" {
|
||||
s.allowedRpcCallTo = make(map[string]struct{})
|
||||
for _, c := range strings.Split(envRpcCall, ",") {
|
||||
|
||||
@@ -200,6 +200,10 @@ span.btn-paging:hover {
|
||||
color: #757575;
|
||||
}
|
||||
|
||||
.btn-paging.active:hover {
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.paging-group {
|
||||
border: 1px solid #e2e2e2;
|
||||
border-radius: 0.5rem;
|
||||
@@ -694,4 +698,4 @@ span.btn-paging:hover {
|
||||
.btn {
|
||||
--bs-btn-font-size: 1rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
1
static/css/main.min.4.css
Normal file
1
static/css/main.min.4.css
Normal file
File diff suppressed because one or more lines are too long
@@ -85,6 +85,79 @@ function addressAliasTooltip() {
|
||||
return `<span class="l-tooltip">${type}<br>${address}</span>`;
|
||||
}
|
||||
|
||||
function handleTxPage(rawData, txId) {
|
||||
const rawOutput = document.getElementById('raw');
|
||||
const rawButton = document.getElementById('raw-button');
|
||||
const rawHexButton = document.getElementById('raw-hex-button');
|
||||
|
||||
rawOutput.innerHTML = syntaxHighlight(rawData);
|
||||
|
||||
let isShowingHexData = false;
|
||||
|
||||
const memoizedResponses = {};
|
||||
|
||||
async function getTransactionHex(txId) {
|
||||
// BTC-like coins have a 'hex' field in the raw data
|
||||
if (rawData['hex']) {
|
||||
return rawData['hex'];
|
||||
}
|
||||
if (memoizedResponses[txId]) {
|
||||
return memoizedResponses[txId];
|
||||
}
|
||||
const fetchedData = await fetchTransactionHex(txId);
|
||||
memoizedResponses[txId] = fetchedData;
|
||||
return fetchedData;
|
||||
}
|
||||
|
||||
async function fetchTransactionHex(txId) {
|
||||
const response = await fetch(`/api/rawtx/${txId}`);
|
||||
if (!response.ok) {
|
||||
throw new Error(`Error fetching data: ${response.status}`);
|
||||
}
|
||||
const txHex = await response.text();
|
||||
const hexWithoutQuotes = txHex.replace(/"/g, '');
|
||||
return hexWithoutQuotes;
|
||||
}
|
||||
|
||||
function updateButtonStyles() {
|
||||
if (isShowingHexData) {
|
||||
rawButton.classList.add('active');
|
||||
rawButton.style.fontWeight = 'normal';
|
||||
rawHexButton.classList.remove('active');
|
||||
rawHexButton.style.fontWeight = 'bold';
|
||||
} else {
|
||||
rawButton.classList.remove('active');
|
||||
rawButton.style.fontWeight = 'bold';
|
||||
rawHexButton.classList.add('active');
|
||||
rawHexButton.style.fontWeight = 'normal';
|
||||
}
|
||||
}
|
||||
|
||||
updateButtonStyles();
|
||||
|
||||
rawHexButton.addEventListener('click', async () => {
|
||||
if (!isShowingHexData) {
|
||||
try {
|
||||
const txHex = await getTransactionHex(txId);
|
||||
rawOutput.textContent = txHex;
|
||||
} catch (error) {
|
||||
console.error('Error fetching raw transaction hex:', error);
|
||||
rawOutput.textContent = `Error fetching raw transaction hex: ${error.message}`;
|
||||
}
|
||||
isShowingHexData = true;
|
||||
updateButtonStyles();
|
||||
}
|
||||
});
|
||||
|
||||
rawButton.addEventListener('click', () => {
|
||||
if (isShowingHexData) {
|
||||
rawOutput.innerHTML = syntaxHighlight(rawData);
|
||||
isShowingHexData = false;
|
||||
updateButtonStyles();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
window.addEventListener("DOMContentLoaded", () => {
|
||||
const a = getCoinCookie();
|
||||
if (a?.length === 3) {
|
||||
@@ -127,7 +200,8 @@ window.addEventListener("DOMContentLoaded", () => {
|
||||
if (e.clientX < e.target.getBoundingClientRect().x) {
|
||||
let t = e.target.getAttribute("cc");
|
||||
if (!t) t = e.target.innerText;
|
||||
navigator.clipboard.writeText(t);
|
||||
const textToCopy = t.trim();
|
||||
navigator.clipboard.writeText(textToCopy);
|
||||
e.target.className = e.target.className.replace("copyable", "copied");
|
||||
setTimeout(
|
||||
() =>
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
function syntaxHighlight(t){return(t=(t=JSON.stringify(t,void 0,2)).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">")).length>1e6?`<span class="key">${t}</span>`:t.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g,t=>{let e="number";return/^"/.test(t)?e=/:$/.test(t)?"key":"string":/true|false/.test(t)?e="boolean":/null/.test(t)&&(e="null"),`<span class="${e}">${t}</span>`})}function getCoinCookie(){if(hasSecondary)return document.cookie.split("; ").find(t=>t.startsWith("secondary_coin="))?.split("=")}function changeCSSStyle(t,e,l){let a=document.all?"rules":"cssRules";for(i=0,len=document.styleSheets[1][a].length;i<len;i++)if(document.styleSheets[1][a][i].selectorText===t){document.styleSheets[1][a][i].style[e]=l;return}}function amountTooltip(){let t=this.querySelector(".prim-amt"),e=this.querySelector(".sec-amt"),l=this.querySelector(".csec-amt"),a=this.querySelector(".base-amt"),s=this.querySelector(".cbase-amt"),r=`${t.outerHTML}<br>`;if(a){let n=a.getAttribute("tm");n||(n="now"),r+=`<span class="amt-time">${n}</span>${a.outerHTML}<br>`}if(s&&(r+=`<span class="amt-time">now</span>${s.outerHTML}<br>`),e){let o=e.getAttribute("tm");o||(o="now"),r+=`<span class="amt-time">${o}</span>${e.outerHTML}<br>`}return l&&(r+=`<span class="amt-time">now</span>${l.outerHTML}<br>`),`<span class="l-tooltip">${r}</span>`}function addressAliasTooltip(){let t=this.getAttribute("alias-type"),e=this.getAttribute("cc");return`<span class="l-tooltip">${t}<br>${e}</span>`}window.addEventListener("DOMContentLoaded",()=>{let t=getCoinCookie();t?.length===3&&("true"===t[2]&&(changeCSSStyle(".prim-amt","display","none"),changeCSSStyle(".sec-amt","display","initial")),document.querySelectorAll(".amt").forEach(t=>new bootstrap.Tooltip(t,{title:amountTooltip,html:!0}))),document.querySelectorAll("[alias-type]").forEach(t=>new bootstrap.Tooltip(t,{title:addressAliasTooltip,html:!0})),document.querySelectorAll("[tt]").forEach(t=>new bootstrap.Tooltip(t,{title:t.getAttribute("tt")})),document.querySelectorAll("#header .bb-group>.btn-check").forEach(t=>t.addEventListener("click",t=>{let e=getCoinCookie(),l="secondary-coin"===t.target.id;e?.length===3&&"true"===e[2]!==l&&(document.cookie=`${e[0]}=${e[1]}=${l}; Path=/`,changeCSSStyle(".prim-amt","display",l?"none":"initial"),changeCSSStyle(".sec-amt","display",l?"initial":"none"))})),document.querySelectorAll(".copyable").forEach(t=>t.addEventListener("click",t=>{if(t.clientX<t.target.getBoundingClientRect().x){let e=t.target.getAttribute("cc");e||(e=t.target.innerText),navigator.clipboard.writeText(e),t.target.className=t.target.className.replace("copyable","copied"),setTimeout(()=>t.target.className=t.target.className.replace("copied","copyable"),1e3),t.preventDefault()}}))});
|
||||
1
static/js/main.min.4.js
Normal file
1
static/js/main.min.4.js
Normal file
@@ -0,0 +1 @@
|
||||
function syntaxHighlight(t){return(t=(t=JSON.stringify(t,void 0,2)).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">")).length>1e6?`<span class="key">${t}</span>`:t.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g,(t=>{let e="number";return/^"/.test(t)?e=/:$/.test(t)?"key":"string":/true|false/.test(t)?e="boolean":/null/.test(t)&&(e="null"),`<span class="${e}">${t}</span>`}))}function getCoinCookie(){if(hasSecondary)return document.cookie.split("; ").find((t=>t.startsWith("secondary_coin=")))?.split("=")}function changeCSSStyle(t,e,n){const a=document.all?"rules":"cssRules";for(i=0,len=document.styleSheets[1][a].length;i<len;i++)if(document.styleSheets[1][a][i].selectorText===t)return void(document.styleSheets[1][a][i].style[e]=n)}function amountTooltip(){const t=this.querySelector(".prim-amt"),e=this.querySelector(".sec-amt"),n=this.querySelector(".csec-amt"),a=this.querySelector(".base-amt"),o=this.querySelector(".cbase-amt");let i=`${t.outerHTML}<br>`;if(a){let t=a.getAttribute("tm");t||(t="now"),i+=`<span class="amt-time">${t}</span>${a.outerHTML}<br>`}if(o&&(i+=`<span class="amt-time">now</span>${o.outerHTML}<br>`),e){let t=e.getAttribute("tm");t||(t="now"),i+=`<span class="amt-time">${t}</span>${e.outerHTML}<br>`}return n&&(i+=`<span class="amt-time">now</span>${n.outerHTML}<br>`),`<span class="l-tooltip">${i}</span>`}function addressAliasTooltip(){return`<span class="l-tooltip">${this.getAttribute("alias-type")}<br>${this.getAttribute("cc")}</span>`}function handleTxPage(t,e){const n=document.getElementById("raw"),a=document.getElementById("raw-button"),o=document.getElementById("raw-hex-button");n.innerHTML=syntaxHighlight(t);let i=!1;const r={};async function s(e){if(t.hex)return t.hex;if(r[e])return r[e];const n=await async function(t){const e=await fetch(`/api/rawtx/${t}`);if(!e.ok)throw new Error(`Error fetching data: ${e.status}`);const n=await e.text();return n.replace(/"/g,"")}(e);return r[e]=n,n}function l(){i?(a.classList.add("active"),a.style.fontWeight="normal",o.classList.remove("active"),o.style.fontWeight="bold"):(a.classList.remove("active"),a.style.fontWeight="bold",o.classList.add("active"),o.style.fontWeight="normal")}l(),o.addEventListener("click",(async()=>{if(!i){try{const t=await s(e);n.textContent=t}catch(t){console.error("Error fetching raw transaction hex:",t),n.textContent=`Error fetching raw transaction hex: ${t.message}`}i=!0,l()}})),a.addEventListener("click",(()=>{i&&(n.innerHTML=syntaxHighlight(t),i=!1,l())}))}window.addEventListener("DOMContentLoaded",(()=>{const t=getCoinCookie();3===t?.length&&("true"===t[2]&&(changeCSSStyle(".prim-amt","display","none"),changeCSSStyle(".sec-amt","display","initial")),document.querySelectorAll(".amt").forEach((t=>new bootstrap.Tooltip(t,{title:amountTooltip,html:!0})))),document.querySelectorAll("[alias-type]").forEach((t=>new bootstrap.Tooltip(t,{title:addressAliasTooltip,html:!0}))),document.querySelectorAll("[tt]").forEach((t=>new bootstrap.Tooltip(t,{title:t.getAttribute("tt")}))),document.querySelectorAll("#header .bb-group>.btn-check").forEach((t=>t.addEventListener("click",(t=>{const e=getCoinCookie(),n="secondary-coin"===t.target.id;3===e?.length&&"true"===e[2]!==n&&(document.cookie=`${e[0]}=${e[1]}=${n}; Path=/`,changeCSSStyle(".prim-amt","display",n?"none":"initial"),changeCSSStyle(".sec-amt","display",n?"initial":"none"))})))),document.querySelectorAll(".copyable").forEach((t=>t.addEventListener("click",(t=>{if(t.clientX<t.target.getBoundingClientRect().x){let e=t.target.getAttribute("cc");e||(e=t.target.innerText);const n=e.trim();navigator.clipboard.writeText(n),t.target.className=t.target.className.replace("copyable","copied"),setTimeout((()=>t.target.className=t.target.className.replace("copied","copyable")),1e3),t.preventDefault()}}))))}));
|
||||
@@ -182,13 +182,19 @@
|
||||
{{end}}
|
||||
{{end}}
|
||||
<div class="pt-4">
|
||||
<h5>Raw Transaction</h5>
|
||||
<div class="json">
|
||||
<pre id="raw"></pre>
|
||||
<button style="color: black" class="btn btn-paging" id="raw-button">
|
||||
Raw Transaction
|
||||
</button>
|
||||
<button style="color: black" class="btn btn-paging" id="raw-hex-button">
|
||||
Raw Transaction Hex
|
||||
</button>
|
||||
<div class="json copyable">
|
||||
<pre id="raw" style="text-wrap: auto;"></pre>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
var raw = {{$tx.CoinSpecificData}};
|
||||
document.getElementById('raw').innerHTML = syntaxHighlight(raw);
|
||||
<script>
|
||||
const rawData = {{ $tx.CoinSpecificData }};
|
||||
const txId = {{ jsStr $tx.Txid }};
|
||||
handleTxPage(rawData, txId);
|
||||
</script>
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
@@ -139,6 +139,11 @@ func (c *fakeBlockChainEthereumType) EthereumTypeRpcCall(data, to, from string)
|
||||
return data + "abcd", nil
|
||||
}
|
||||
|
||||
// EthereumTypeGetRawTransaction returns simulated transaction hex data
|
||||
func (c *fakeBlockChainEthereumType) EthereumTypeGetRawTransaction(txid string) (string, error) {
|
||||
return txid + "abcd", nil
|
||||
}
|
||||
|
||||
// GetTokenURI returns URI derived from the input contractDesc
|
||||
func (c *fakeBlockChainEthereumType) GetTokenURI(contractDesc bchain.AddressDescriptor, tokenID *big.Int) (string, error) {
|
||||
return "https://ipfs.io/ipfs/" + contractDesc.String()[3:] + ".json", nil
|
||||
|
||||
Reference in New Issue
Block a user