Generate typescript documentation from golang types
This commit is contained in:
parent
9ecfdfe11d
commit
15c19ac1fc
11
.prettierrc
Normal file
11
.prettierrc
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"printWidth": 100,
|
||||||
|
"arrowParens": "avoid",
|
||||||
|
"bracketSpacing": true,
|
||||||
|
"singleQuote": true,
|
||||||
|
"semi": true,
|
||||||
|
"trailingComma": "all",
|
||||||
|
"tabWidth": 4,
|
||||||
|
"useTabs": false,
|
||||||
|
"bracketSameLine": false
|
||||||
|
}
|
||||||
@ -158,7 +158,7 @@ type MultiTokenValue struct {
|
|||||||
|
|
||||||
// Token contains info about tokens held by an address
|
// Token contains info about tokens held by an address
|
||||||
type Token struct {
|
type Token struct {
|
||||||
Type bchain.TokenTypeName `json:"type"`
|
Type bchain.TokenTypeName `json:"type" ts_type:"'XPUBAddress' | 'ERC20' | 'ERC721' | 'ERC1155'"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Path string `json:"path,omitempty"`
|
Path string `json:"path,omitempty"`
|
||||||
Contract string `json:"contract,omitempty"`
|
Contract string `json:"contract,omitempty"`
|
||||||
@ -263,7 +263,7 @@ type Tx struct {
|
|||||||
FeesSat *Amount `json:"fees,omitempty"`
|
FeesSat *Amount `json:"fees,omitempty"`
|
||||||
Hex string `json:"hex,omitempty"`
|
Hex string `json:"hex,omitempty"`
|
||||||
Rbf bool `json:"rbf,omitempty"`
|
Rbf bool `json:"rbf,omitempty"`
|
||||||
CoinSpecificData json.RawMessage `json:"coinSpecificData,omitempty"`
|
CoinSpecificData json.RawMessage `json:"coinSpecificData,omitempty" ts_type:"any"`
|
||||||
TokenTransfers []TokenTransfer `json:"tokenTransfers,omitempty"`
|
TokenTransfers []TokenTransfer `json:"tokenTransfers,omitempty"`
|
||||||
EthereumSpecific *EthereumSpecific `json:"ethereumSpecific,omitempty"`
|
EthereumSpecific *EthereumSpecific `json:"ethereumSpecific,omitempty"`
|
||||||
AddressAliases AddressAliasesMap `json:"addressAliases,omitempty"`
|
AddressAliases AddressAliasesMap `json:"addressAliases,omitempty"`
|
||||||
|
|||||||
412
blockbook-api.d.ts
vendored
Normal file
412
blockbook-api.d.ts
vendored
Normal file
@ -0,0 +1,412 @@
|
|||||||
|
/* Do not change, this code is generated from Golang structs */
|
||||||
|
|
||||||
|
export interface APIError {
|
||||||
|
Text: string;
|
||||||
|
Public: boolean;
|
||||||
|
}
|
||||||
|
export interface AddressAlias {
|
||||||
|
Type: string;
|
||||||
|
Alias: string;
|
||||||
|
}
|
||||||
|
export interface EthereumInternalTransfer {
|
||||||
|
type: number;
|
||||||
|
from: string;
|
||||||
|
to: string;
|
||||||
|
value?: string;
|
||||||
|
}
|
||||||
|
export interface EthereumParsedInputParam {
|
||||||
|
type: string;
|
||||||
|
values?: string[];
|
||||||
|
}
|
||||||
|
export interface EthereumParsedInputData {
|
||||||
|
methodId: string;
|
||||||
|
name: string;
|
||||||
|
function?: string;
|
||||||
|
params?: EthereumParsedInputParam[];
|
||||||
|
}
|
||||||
|
export interface EthereumSpecific {
|
||||||
|
type?: number;
|
||||||
|
createdContract?: string;
|
||||||
|
status: number;
|
||||||
|
error?: string;
|
||||||
|
nonce: number;
|
||||||
|
gasLimit?: string;
|
||||||
|
gasUsed?: string;
|
||||||
|
gasPrice?: string;
|
||||||
|
data?: string;
|
||||||
|
parsedData?: EthereumParsedInputData;
|
||||||
|
internalTransfers?: EthereumInternalTransfer[];
|
||||||
|
}
|
||||||
|
export interface MultiTokenValue {
|
||||||
|
id?: string;
|
||||||
|
value?: string;
|
||||||
|
}
|
||||||
|
export interface TokenTransfer {
|
||||||
|
type: string;
|
||||||
|
from: string;
|
||||||
|
to: string;
|
||||||
|
contract: string;
|
||||||
|
name: string;
|
||||||
|
symbol: string;
|
||||||
|
decimals: number;
|
||||||
|
value?: string;
|
||||||
|
multiTokenValues?: MultiTokenValue[];
|
||||||
|
}
|
||||||
|
export interface Vout {
|
||||||
|
value?: string;
|
||||||
|
n: number;
|
||||||
|
spent?: boolean;
|
||||||
|
spentTxId?: string;
|
||||||
|
spentIndex?: number;
|
||||||
|
spentHeight?: number;
|
||||||
|
hex?: string;
|
||||||
|
asm?: string;
|
||||||
|
addresses: string[];
|
||||||
|
isAddress: boolean;
|
||||||
|
isOwn?: boolean;
|
||||||
|
type?: string;
|
||||||
|
}
|
||||||
|
export interface Vin {
|
||||||
|
txid?: string;
|
||||||
|
vout?: number;
|
||||||
|
sequence?: number;
|
||||||
|
n: number;
|
||||||
|
addresses?: string[];
|
||||||
|
isAddress: boolean;
|
||||||
|
isOwn?: boolean;
|
||||||
|
value?: string;
|
||||||
|
hex?: string;
|
||||||
|
asm?: string;
|
||||||
|
coinbase?: string;
|
||||||
|
}
|
||||||
|
export interface Tx {
|
||||||
|
txid: string;
|
||||||
|
version?: number;
|
||||||
|
lockTime?: number;
|
||||||
|
vin: Vin[];
|
||||||
|
vout: Vout[];
|
||||||
|
blockHash?: string;
|
||||||
|
blockHeight: number;
|
||||||
|
confirmations: number;
|
||||||
|
confirmationETABlocks?: number;
|
||||||
|
confirmationETASeconds?: number;
|
||||||
|
blockTime: number;
|
||||||
|
size?: number;
|
||||||
|
vsize?: number;
|
||||||
|
value?: string;
|
||||||
|
valueIn?: string;
|
||||||
|
fees?: string;
|
||||||
|
hex?: string;
|
||||||
|
rbf?: boolean;
|
||||||
|
coinSpecificData?: any;
|
||||||
|
tokenTransfers?: TokenTransfer[];
|
||||||
|
ethereumSpecific?: EthereumSpecific;
|
||||||
|
addressAliases?: { [key: string]: AddressAlias };
|
||||||
|
}
|
||||||
|
export interface FeeStats {
|
||||||
|
txCount: number;
|
||||||
|
totalFeesSat?: string;
|
||||||
|
averageFeePerKb: number;
|
||||||
|
decilesFeePerKb: number[];
|
||||||
|
}
|
||||||
|
export interface ContractInfo {
|
||||||
|
type: string;
|
||||||
|
contract: string;
|
||||||
|
name: string;
|
||||||
|
symbol: string;
|
||||||
|
decimals: number;
|
||||||
|
createdInBlock?: number;
|
||||||
|
destructedInBlock?: number;
|
||||||
|
}
|
||||||
|
export interface Token {
|
||||||
|
type: 'XPUBAddress' | 'ERC20' | 'ERC721' | 'ERC1155';
|
||||||
|
name: string;
|
||||||
|
path?: string;
|
||||||
|
contract?: string;
|
||||||
|
transfers: number;
|
||||||
|
symbol?: string;
|
||||||
|
decimals?: number;
|
||||||
|
balance?: string;
|
||||||
|
baseValue?: number;
|
||||||
|
secondaryValue?: number;
|
||||||
|
ids?: string[];
|
||||||
|
multiTokenValues?: MultiTokenValue[];
|
||||||
|
totalReceived?: string;
|
||||||
|
totalSent?: string;
|
||||||
|
}
|
||||||
|
export interface Address {
|
||||||
|
page?: number;
|
||||||
|
totalPages?: number;
|
||||||
|
itemsOnPage?: number;
|
||||||
|
address: string;
|
||||||
|
balance?: string;
|
||||||
|
totalReceived?: string;
|
||||||
|
totalSent?: string;
|
||||||
|
unconfirmedBalance?: string;
|
||||||
|
unconfirmedTxs: number;
|
||||||
|
txs: number;
|
||||||
|
nonTokenTxs?: number;
|
||||||
|
internalTxs?: number;
|
||||||
|
transactions?: Tx[];
|
||||||
|
txids?: string[];
|
||||||
|
nonce?: string;
|
||||||
|
usedTokens?: number;
|
||||||
|
tokens?: Token[];
|
||||||
|
secondaryValue?: number;
|
||||||
|
tokensBaseValue?: number;
|
||||||
|
tokensSecondaryValue?: number;
|
||||||
|
totalBaseValue?: number;
|
||||||
|
totalSecondaryValue?: number;
|
||||||
|
contractInfo?: ContractInfo;
|
||||||
|
erc20Contract?: ContractInfo;
|
||||||
|
addressAliases?: { [key: string]: AddressAlias };
|
||||||
|
}
|
||||||
|
export interface Utxo {
|
||||||
|
txid: string;
|
||||||
|
vout: number;
|
||||||
|
value?: string;
|
||||||
|
height?: number;
|
||||||
|
confirmations: number;
|
||||||
|
address?: string;
|
||||||
|
path?: string;
|
||||||
|
lockTime?: number;
|
||||||
|
coinbase?: boolean;
|
||||||
|
}
|
||||||
|
export interface BalanceHistory {
|
||||||
|
time: number;
|
||||||
|
txs: number;
|
||||||
|
received?: string;
|
||||||
|
sent?: string;
|
||||||
|
sentToSelf?: string;
|
||||||
|
rates?: { [key: string]: number };
|
||||||
|
txid?: string;
|
||||||
|
}
|
||||||
|
export interface BlockInfo {
|
||||||
|
Hash: string;
|
||||||
|
Time: number;
|
||||||
|
Txs: number;
|
||||||
|
Size: number;
|
||||||
|
Height: number;
|
||||||
|
}
|
||||||
|
export interface Blocks {
|
||||||
|
page?: number;
|
||||||
|
totalPages?: number;
|
||||||
|
itemsOnPage?: number;
|
||||||
|
blocks: BlockInfo[];
|
||||||
|
}
|
||||||
|
export interface Block {
|
||||||
|
page?: number;
|
||||||
|
totalPages?: number;
|
||||||
|
itemsOnPage?: number;
|
||||||
|
hash: string;
|
||||||
|
previousBlockHash?: string;
|
||||||
|
nextBlockHash?: string;
|
||||||
|
height: number;
|
||||||
|
confirmations: number;
|
||||||
|
size: number;
|
||||||
|
time?: number;
|
||||||
|
version: string;
|
||||||
|
merkleRoot: string;
|
||||||
|
nonce: string;
|
||||||
|
bits: string;
|
||||||
|
difficulty: string;
|
||||||
|
tx?: string[];
|
||||||
|
txCount: number;
|
||||||
|
txs?: Tx[];
|
||||||
|
addressAliases?: { [key: string]: AddressAlias };
|
||||||
|
}
|
||||||
|
export interface BlockRaw {
|
||||||
|
hex: string;
|
||||||
|
}
|
||||||
|
export interface BackendInfo {
|
||||||
|
error?: string;
|
||||||
|
chain?: string;
|
||||||
|
blocks?: number;
|
||||||
|
headers?: number;
|
||||||
|
bestBlockHash?: string;
|
||||||
|
difficulty?: string;
|
||||||
|
sizeOnDisk?: number;
|
||||||
|
version?: string;
|
||||||
|
subversion?: string;
|
||||||
|
protocolVersion?: string;
|
||||||
|
timeOffset?: number;
|
||||||
|
warnings?: string;
|
||||||
|
consensus_version?: string;
|
||||||
|
consensus?: any;
|
||||||
|
}
|
||||||
|
export interface InternalStateColumn {
|
||||||
|
name: string;
|
||||||
|
version: number;
|
||||||
|
rows: number;
|
||||||
|
keyBytes: number;
|
||||||
|
valueBytes: number;
|
||||||
|
updated: string;
|
||||||
|
}
|
||||||
|
export interface BlockbookInfo {
|
||||||
|
coin: string;
|
||||||
|
host: string;
|
||||||
|
version: string;
|
||||||
|
gitCommit: string;
|
||||||
|
buildTime: string;
|
||||||
|
syncMode: boolean;
|
||||||
|
initialSync: boolean;
|
||||||
|
inSync: boolean;
|
||||||
|
bestHeight: number;
|
||||||
|
lastBlockTime: string;
|
||||||
|
inSyncMempool: boolean;
|
||||||
|
lastMempoolTime: string;
|
||||||
|
mempoolSize: number;
|
||||||
|
decimals: number;
|
||||||
|
dbSize: number;
|
||||||
|
hasFiatRates?: boolean;
|
||||||
|
hasTokenFiatRates?: boolean;
|
||||||
|
currentFiatRatesTime?: string;
|
||||||
|
historicalFiatRatesTime?: string;
|
||||||
|
historicalTokenFiatRatesTime?: string;
|
||||||
|
dbSizeFromColumns?: number;
|
||||||
|
dbColumns?: InternalStateColumn[];
|
||||||
|
about: string;
|
||||||
|
}
|
||||||
|
export interface SystemInfo {
|
||||||
|
blockbook?: BlockbookInfo;
|
||||||
|
backend?: BackendInfo;
|
||||||
|
}
|
||||||
|
export interface FiatTicker {
|
||||||
|
ts?: number;
|
||||||
|
rates: { [key: string]: number };
|
||||||
|
error?: string;
|
||||||
|
}
|
||||||
|
export interface FiatTickers {
|
||||||
|
tickers: FiatTicker[];
|
||||||
|
}
|
||||||
|
export interface AvailableVsCurrencies {
|
||||||
|
ts?: number;
|
||||||
|
available_currencies: string[];
|
||||||
|
error?: string;
|
||||||
|
}
|
||||||
|
export interface WsReq {
|
||||||
|
id: string;
|
||||||
|
method:
|
||||||
|
| 'getAccountInfo'
|
||||||
|
| 'getInfo'
|
||||||
|
| 'getBlockHash'
|
||||||
|
| 'getAccountUtxo'
|
||||||
|
| 'getBalanceHistory'
|
||||||
|
| 'getTransaction'
|
||||||
|
| 'getTransactionSpecific'
|
||||||
|
| 'estimateFee'
|
||||||
|
| 'sendTransaction'
|
||||||
|
| 'subscribeNewBlock'
|
||||||
|
| 'unsubscribeNewBlock'
|
||||||
|
| 'subscribeNewTransaction'
|
||||||
|
| 'unsubscribeNewTransaction'
|
||||||
|
| 'subscribeAddresses'
|
||||||
|
| 'unsubscribeAddresses'
|
||||||
|
| 'subscribeFiatRates'
|
||||||
|
| 'unsubscribeFiatRates'
|
||||||
|
| 'ping'
|
||||||
|
| 'getCurrentFiatRates'
|
||||||
|
| 'getFiatRatesForTimestamps'
|
||||||
|
| 'getFiatRatesTickersList';
|
||||||
|
params: any;
|
||||||
|
}
|
||||||
|
export interface WsRes {
|
||||||
|
id: string;
|
||||||
|
data: any;
|
||||||
|
}
|
||||||
|
export interface WsAccountInfoReq {
|
||||||
|
descriptor: string;
|
||||||
|
details?: 'basic' | 'tokens' | 'tokenBalances' | 'txids' | 'txslight' | 'txs';
|
||||||
|
tokens?: 'derived' | 'used' | 'nonzero';
|
||||||
|
pageSize?: number;
|
||||||
|
page?: number;
|
||||||
|
from?: number;
|
||||||
|
to?: number;
|
||||||
|
contractFilter?: string;
|
||||||
|
secondaryCurrency?: string;
|
||||||
|
gap?: number;
|
||||||
|
}
|
||||||
|
export interface WsBackendInfo {
|
||||||
|
version?: string;
|
||||||
|
subversion?: string;
|
||||||
|
consensus_version?: string;
|
||||||
|
consensus?: any;
|
||||||
|
}
|
||||||
|
export interface WsInfoRes {
|
||||||
|
name: string;
|
||||||
|
shortcut: string;
|
||||||
|
decimals: number;
|
||||||
|
version: string;
|
||||||
|
bestHeight: number;
|
||||||
|
bestHash: string;
|
||||||
|
block0Hash: string;
|
||||||
|
testnet: boolean;
|
||||||
|
backend: WsBackendInfo;
|
||||||
|
}
|
||||||
|
export interface WsBlockHashReq {
|
||||||
|
height: number;
|
||||||
|
}
|
||||||
|
export interface WsBlockHashRes {
|
||||||
|
hash: string;
|
||||||
|
}
|
||||||
|
export interface WsBlockReq {
|
||||||
|
id: string;
|
||||||
|
pageSize?: number;
|
||||||
|
page?: number;
|
||||||
|
}
|
||||||
|
export interface WsAccountUtxoReq {
|
||||||
|
descriptor: string;
|
||||||
|
}
|
||||||
|
export interface WsBalanceHistoryReq {
|
||||||
|
descriptor: string;
|
||||||
|
from?: number;
|
||||||
|
to?: number;
|
||||||
|
currencies?: string[];
|
||||||
|
gap?: number;
|
||||||
|
groupBy?: number;
|
||||||
|
}
|
||||||
|
export interface WsTransactionReq {
|
||||||
|
txid: string;
|
||||||
|
}
|
||||||
|
export interface WsTransactionSpecificReq {
|
||||||
|
txid: string;
|
||||||
|
}
|
||||||
|
export interface WsEstimateFeeReq {
|
||||||
|
blocks?: number[];
|
||||||
|
specific?: {
|
||||||
|
conservative?: boolean;
|
||||||
|
txsize?: number;
|
||||||
|
from?: string;
|
||||||
|
to?: string;
|
||||||
|
data?: string;
|
||||||
|
value?: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
export interface WsEstimateFeeRes {
|
||||||
|
feePerTx?: string;
|
||||||
|
feePerUnit?: string;
|
||||||
|
feeLimit?: string;
|
||||||
|
}
|
||||||
|
export interface WsSendTransactionReq {
|
||||||
|
hex: string;
|
||||||
|
}
|
||||||
|
export interface WsSubscribeAddressesReq {
|
||||||
|
addresses: string[];
|
||||||
|
}
|
||||||
|
export interface WsSubscribeFiatRatesReq {
|
||||||
|
currency?: string;
|
||||||
|
tokens?: string[];
|
||||||
|
}
|
||||||
|
export interface WsCurrentFiatRatesReq {
|
||||||
|
currencies?: string[];
|
||||||
|
token?: string;
|
||||||
|
}
|
||||||
|
export interface WsFiatRatesForTimestampsReq {
|
||||||
|
timestamps: number[];
|
||||||
|
currencies?: string[];
|
||||||
|
token?: string;
|
||||||
|
}
|
||||||
|
export interface WsFiatRatesTickersListReq {
|
||||||
|
timestamp?: number;
|
||||||
|
token?: string;
|
||||||
|
}
|
||||||
65
build/tools/typescriptify/typescriptify.go
Normal file
65
build/tools/typescriptify/typescriptify.go
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/tkrajina/typescriptify-golang-structs/typescriptify"
|
||||||
|
"github.com/trezor/blockbook/api"
|
||||||
|
"github.com/trezor/blockbook/server"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
t := typescriptify.New()
|
||||||
|
t.CreateInterface = true
|
||||||
|
t.Indent = " "
|
||||||
|
t.BackupDir = ""
|
||||||
|
|
||||||
|
t.ManageType(api.Amount{}, typescriptify.TypeOptions{TSType: "string"})
|
||||||
|
t.ManageType([]api.Amount{}, typescriptify.TypeOptions{TSType: "string[]"})
|
||||||
|
t.ManageType(big.Int{}, typescriptify.TypeOptions{TSType: "string"})
|
||||||
|
t.ManageType(time.Time{}, typescriptify.TypeOptions{TSType: "string", TSDoc: "Time in ISO 8601 YYYY-MM-DDTHH:mm:ss.sssZd"})
|
||||||
|
|
||||||
|
// API - REST and Websocket
|
||||||
|
t.Add(api.APIError{})
|
||||||
|
t.Add(api.Tx{})
|
||||||
|
t.Add(api.FeeStats{})
|
||||||
|
t.Add(api.Address{})
|
||||||
|
t.Add(api.Utxo{})
|
||||||
|
t.Add(api.BalanceHistory{})
|
||||||
|
t.Add(api.Blocks{})
|
||||||
|
t.Add(api.Block{})
|
||||||
|
t.Add(api.BlockRaw{})
|
||||||
|
t.Add(api.SystemInfo{})
|
||||||
|
t.Add(api.FiatTicker{})
|
||||||
|
t.Add(api.FiatTickers{})
|
||||||
|
t.Add(api.AvailableVsCurrencies{})
|
||||||
|
|
||||||
|
// Websocket specific
|
||||||
|
t.Add(server.WsReq{})
|
||||||
|
t.Add(server.WsRes{})
|
||||||
|
t.Add(server.WsAccountInfoReq{})
|
||||||
|
t.Add(server.WsInfoRes{})
|
||||||
|
t.Add(server.WsBlockHashReq{})
|
||||||
|
t.Add(server.WsBlockHashRes{})
|
||||||
|
t.Add(server.WsBlockReq{})
|
||||||
|
t.Add(server.WsAccountUtxoReq{})
|
||||||
|
t.Add(server.WsBalanceHistoryReq{})
|
||||||
|
t.Add(server.WsTransactionReq{})
|
||||||
|
t.Add(server.WsTransactionSpecificReq{})
|
||||||
|
t.Add(server.WsEstimateFeeReq{})
|
||||||
|
t.Add(server.WsEstimateFeeRes{})
|
||||||
|
t.Add(server.WsSendTransactionReq{})
|
||||||
|
t.Add(server.WsSubscribeAddressesReq{})
|
||||||
|
t.Add(server.WsSubscribeFiatRatesReq{})
|
||||||
|
t.Add(server.WsCurrentFiatRatesReq{})
|
||||||
|
t.Add(server.WsFiatRatesForTimestampsReq{})
|
||||||
|
t.Add(server.WsFiatRatesTickersListReq{})
|
||||||
|
|
||||||
|
err := t.ConvertToFile("blockbook-api.d.ts")
|
||||||
|
if err != nil {
|
||||||
|
panic(err.Error())
|
||||||
|
}
|
||||||
|
fmt.Println("OK")
|
||||||
|
}
|
||||||
2
go.mod
2
go.mod
@ -75,6 +75,8 @@ require (
|
|||||||
github.com/stretchr/testify v1.8.1 // indirect
|
github.com/stretchr/testify v1.8.1 // indirect
|
||||||
github.com/tklauser/go-sysconf v0.3.5 // indirect
|
github.com/tklauser/go-sysconf v0.3.5 // indirect
|
||||||
github.com/tklauser/numcpus v0.2.2 // indirect
|
github.com/tklauser/numcpus v0.2.2 // indirect
|
||||||
|
github.com/tkrajina/go-reflector v0.5.5 // indirect
|
||||||
|
github.com/tkrajina/typescriptify-golang-structs v0.1.10 // indirect
|
||||||
github.com/yusufpapurcu/wmi v1.2.2 // indirect
|
github.com/yusufpapurcu/wmi v1.2.2 // indirect
|
||||||
golang.org/x/exp v0.0.0-20220426173459-3bcf042a4bf5 // indirect
|
golang.org/x/exp v0.0.0-20220426173459-3bcf042a4bf5 // indirect
|
||||||
golang.org/x/sys v0.5.0 // indirect
|
golang.org/x/sys v0.5.0 // indirect
|
||||||
|
|||||||
4
go.sum
4
go.sum
@ -367,6 +367,10 @@ github.com/tklauser/go-sysconf v0.3.5 h1:uu3Xl4nkLzQfXNsWn15rPc/HQCJKObbt1dKJeWp
|
|||||||
github.com/tklauser/go-sysconf v0.3.5/go.mod h1:MkWzOF4RMCshBAMXuhXJs64Rte09mITnppBXY/rYEFI=
|
github.com/tklauser/go-sysconf v0.3.5/go.mod h1:MkWzOF4RMCshBAMXuhXJs64Rte09mITnppBXY/rYEFI=
|
||||||
github.com/tklauser/numcpus v0.2.2 h1:oyhllyrScuYI6g+h/zUvNXNp1wy7x8qQy3t/piefldA=
|
github.com/tklauser/numcpus v0.2.2 h1:oyhllyrScuYI6g+h/zUvNXNp1wy7x8qQy3t/piefldA=
|
||||||
github.com/tklauser/numcpus v0.2.2/go.mod h1:x3qojaO3uyYt0i56EW/VUYs7uBvdl2fkfZFu0T9wgjM=
|
github.com/tklauser/numcpus v0.2.2/go.mod h1:x3qojaO3uyYt0i56EW/VUYs7uBvdl2fkfZFu0T9wgjM=
|
||||||
|
github.com/tkrajina/go-reflector v0.5.5 h1:gwoQFNye30Kk7NrExj8zm3zFtrGPqOkzFMLuQZg1DtQ=
|
||||||
|
github.com/tkrajina/go-reflector v0.5.5/go.mod h1:ECbqLgccecY5kPmPmXg1MrHW585yMcDkVl6IvJe64T4=
|
||||||
|
github.com/tkrajina/typescriptify-golang-structs v0.1.10 h1:W/Ta9Kqo2lV+7bVXuQoUhZ0bDlnjwtPpKsy3A9M1nYg=
|
||||||
|
github.com/tkrajina/typescriptify-golang-structs v0.1.10/go.mod h1:sjU00nti/PMEOZb07KljFlR+lJ+RotsC0GBQMv9EKls=
|
||||||
github.com/tyler-smith/go-bip39 v1.0.2 h1:+t3w+KwLXO6154GNJY+qUtIxLTmFjfUmpguQT1OlOT8=
|
github.com/tyler-smith/go-bip39 v1.0.2 h1:+t3w+KwLXO6154GNJY+qUtIxLTmFjfUmpguQT1OlOT8=
|
||||||
github.com/urfave/cli/v2 v2.10.2 h1:x3p8awjp/2arX+Nl/G2040AZpOCHS/eMJJ1/a+mye4Y=
|
github.com/urfave/cli/v2 v2.10.2 h1:x3p8awjp/2arX+Nl/G2040AZpOCHS/eMJJ1/a+mye4Y=
|
||||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
|
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
|
||||||
|
|||||||
@ -1262,7 +1262,7 @@ func websocketTestsBitcoinType(t *testing.T, ts *httptest.Server) {
|
|||||||
"timestamps": []string{"yesterday"},
|
"timestamps": []string{"yesterday"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
want: `{"id":"23","data":{"error":{"message":"json: cannot unmarshal string into Go struct field .timestamps of type int64"}}}`,
|
want: `{"id":"23","data":{"error":{"message":"json: cannot unmarshal string into Go struct field WsFiatRatesForTimestampsReq.timestamps of type int64"}}}`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "websocket getFiatRatesForTimestamps empty currency",
|
name: "websocket getFiatRatesForTimestamps empty currency",
|
||||||
|
|||||||
@ -34,21 +34,10 @@ var (
|
|||||||
connectionCounter uint64
|
connectionCounter uint64
|
||||||
)
|
)
|
||||||
|
|
||||||
type websocketReq struct {
|
|
||||||
ID string `json:"id"`
|
|
||||||
Method string `json:"method"`
|
|
||||||
Params json.RawMessage `json:"params"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type websocketRes struct {
|
|
||||||
ID string `json:"id"`
|
|
||||||
Data interface{} `json:"data"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type websocketChannel struct {
|
type websocketChannel struct {
|
||||||
id uint64
|
id uint64
|
||||||
conn *websocket.Conn
|
conn *websocket.Conn
|
||||||
out chan *websocketRes
|
out chan *WsRes
|
||||||
ip string
|
ip string
|
||||||
requestHeader http.Header
|
requestHeader http.Header
|
||||||
alive bool
|
alive bool
|
||||||
@ -56,11 +45,6 @@ type websocketChannel struct {
|
|||||||
addrDescs []string // subscribed address descriptors as strings
|
addrDescs []string // subscribed address descriptors as strings
|
||||||
}
|
}
|
||||||
|
|
||||||
type fiatRatesSubscription struct {
|
|
||||||
Currency string `json:"currency"`
|
|
||||||
Tokens []string `json:"tokens"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// WebsocketServer is a handle to websocket server
|
// WebsocketServer is a handle to websocket server
|
||||||
type WebsocketServer struct {
|
type WebsocketServer struct {
|
||||||
upgrader *websocket.Upgrader
|
upgrader *websocket.Upgrader
|
||||||
@ -147,7 +131,7 @@ func (s *WebsocketServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
c := &websocketChannel{
|
c := &websocketChannel{
|
||||||
id: atomic.AddUint64(&connectionCounter, 1),
|
id: atomic.AddUint64(&connectionCounter, 1),
|
||||||
conn: conn,
|
conn: conn,
|
||||||
out: make(chan *websocketRes, outChannelSize),
|
out: make(chan *WsRes, outChannelSize),
|
||||||
ip: getIP(r),
|
ip: getIP(r),
|
||||||
requestHeader: r.Header,
|
requestHeader: r.Header,
|
||||||
alive: true,
|
alive: true,
|
||||||
@ -184,7 +168,7 @@ func (c *websocketChannel) CloseOut() bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *websocketChannel) DataOut(data *websocketRes) {
|
func (c *websocketChannel) DataOut(data *WsRes) {
|
||||||
c.aliveLock.Lock()
|
c.aliveLock.Lock()
|
||||||
defer c.aliveLock.Unlock()
|
defer c.aliveLock.Unlock()
|
||||||
if c.alive {
|
if c.alive {
|
||||||
@ -215,7 +199,7 @@ func (s *WebsocketServer) inputLoop(c *websocketChannel) {
|
|||||||
}
|
}
|
||||||
switch t {
|
switch t {
|
||||||
case websocket.TextMessage:
|
case websocket.TextMessage:
|
||||||
var req websocketReq
|
var req WsReq
|
||||||
err := json.Unmarshal(d, &req)
|
err := json.Unmarshal(d, &req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.Error("Error parsing message from ", c.id, ", ", string(d), ", ", err)
|
glog.Error("Error parsing message from ", c.id, ", ", string(d), ", ", err)
|
||||||
@ -229,7 +213,6 @@ func (s *WebsocketServer) inputLoop(c *websocketChannel) {
|
|||||||
return
|
return
|
||||||
case websocket.PingMessage:
|
case websocket.PingMessage:
|
||||||
c.conn.WriteControl(websocket.PongMessage, nil, time.Now().Add(defaultTimeout))
|
c.conn.WriteControl(websocket.PongMessage, nil, time.Now().Add(defaultTimeout))
|
||||||
break
|
|
||||||
case websocket.CloseMessage:
|
case websocket.CloseMessage:
|
||||||
s.closeChannel(c)
|
s.closeChannel(c)
|
||||||
return
|
return
|
||||||
@ -270,36 +253,30 @@ func (s *WebsocketServer) onDisconnect(c *websocketChannel) {
|
|||||||
s.metrics.WebsocketClients.Dec()
|
s.metrics.WebsocketClients.Dec()
|
||||||
}
|
}
|
||||||
|
|
||||||
var requestHandlers = map[string]func(*WebsocketServer, *websocketChannel, *websocketReq) (interface{}, error){
|
var requestHandlers = map[string]func(*WebsocketServer, *websocketChannel, *WsReq) (interface{}, error){
|
||||||
"getAccountInfo": func(s *WebsocketServer, c *websocketChannel, req *websocketReq) (rv interface{}, err error) {
|
"getAccountInfo": func(s *WebsocketServer, c *websocketChannel, req *WsReq) (rv interface{}, err error) {
|
||||||
r, err := unmarshalGetAccountInfoRequest(req.Params)
|
r, err := unmarshalGetAccountInfoRequest(req.Params)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
rv, err = s.getAccountInfo(r)
|
rv, err = s.getAccountInfo(r)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
},
|
},
|
||||||
"getInfo": func(s *WebsocketServer, c *websocketChannel, req *websocketReq) (rv interface{}, err error) {
|
"getInfo": func(s *WebsocketServer, c *websocketChannel, req *WsReq) (rv interface{}, err error) {
|
||||||
return s.getInfo()
|
return s.getInfo()
|
||||||
},
|
},
|
||||||
"getBlockHash": func(s *WebsocketServer, c *websocketChannel, req *websocketReq) (rv interface{}, err error) {
|
"getBlockHash": func(s *WebsocketServer, c *websocketChannel, req *WsReq) (rv interface{}, err error) {
|
||||||
r := struct {
|
r := WsBlockHashReq{}
|
||||||
Height int `json:"height"`
|
|
||||||
}{}
|
|
||||||
err = json.Unmarshal(req.Params, &r)
|
err = json.Unmarshal(req.Params, &r)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
rv, err = s.getBlockHash(r.Height)
|
rv, err = s.getBlockHash(r.Height)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
},
|
},
|
||||||
"getBlock": func(s *WebsocketServer, c *websocketChannel, req *websocketReq) (rv interface{}, err error) {
|
"getBlock": func(s *WebsocketServer, c *websocketChannel, req *WsReq) (rv interface{}, err error) {
|
||||||
if !s.is.ExtendedIndex {
|
if !s.is.ExtendedIndex {
|
||||||
return nil, errors.New("Not supported")
|
return nil, errors.New("Not supported")
|
||||||
}
|
}
|
||||||
r := struct {
|
r := WsBlockReq{}
|
||||||
Id string `json:"id"`
|
|
||||||
PageSize int `json:"pageSize"`
|
|
||||||
Page int `json:"page"`
|
|
||||||
}{}
|
|
||||||
err = json.Unmarshal(req.Params, &r)
|
err = json.Unmarshal(req.Params, &r)
|
||||||
if r.PageSize == 0 {
|
if r.PageSize == 0 {
|
||||||
r.PageSize = 1000000
|
r.PageSize = 1000000
|
||||||
@ -309,25 +286,16 @@ var requestHandlers = map[string]func(*WebsocketServer, *websocketChannel, *webs
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
},
|
},
|
||||||
"getAccountUtxo": func(s *WebsocketServer, c *websocketChannel, req *websocketReq) (rv interface{}, err error) {
|
"getAccountUtxo": func(s *WebsocketServer, c *websocketChannel, req *WsReq) (rv interface{}, err error) {
|
||||||
r := struct {
|
r := WsAccountUtxoReq{}
|
||||||
Descriptor string `json:"descriptor"`
|
|
||||||
}{}
|
|
||||||
err = json.Unmarshal(req.Params, &r)
|
err = json.Unmarshal(req.Params, &r)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
rv, err = s.getAccountUtxo(r.Descriptor)
|
rv, err = s.getAccountUtxo(r.Descriptor)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
},
|
},
|
||||||
"getBalanceHistory": func(s *WebsocketServer, c *websocketChannel, req *websocketReq) (rv interface{}, err error) {
|
"getBalanceHistory": func(s *WebsocketServer, c *websocketChannel, req *WsReq) (rv interface{}, err error) {
|
||||||
r := struct {
|
r := WsBalanceHistoryReq{}
|
||||||
Descriptor string `json:"descriptor"`
|
|
||||||
From int64 `json:"from"`
|
|
||||||
To int64 `json:"to"`
|
|
||||||
Currencies []string `json:"currencies"`
|
|
||||||
Gap int `json:"gap"`
|
|
||||||
GroupBy uint32 `json:"groupBy"`
|
|
||||||
}{}
|
|
||||||
err = json.Unmarshal(req.Params, &r)
|
err = json.Unmarshal(req.Params, &r)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
if r.From <= 0 {
|
if r.From <= 0 {
|
||||||
@ -346,63 +314,57 @@ var requestHandlers = map[string]func(*WebsocketServer, *websocketChannel, *webs
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
},
|
},
|
||||||
"getTransaction": func(s *WebsocketServer, c *websocketChannel, req *websocketReq) (rv interface{}, err error) {
|
"getTransaction": func(s *WebsocketServer, c *websocketChannel, req *WsReq) (rv interface{}, err error) {
|
||||||
r := struct {
|
r := WsTransactionReq{}
|
||||||
Txid string `json:"txid"`
|
|
||||||
}{}
|
|
||||||
err = json.Unmarshal(req.Params, &r)
|
err = json.Unmarshal(req.Params, &r)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
rv, err = s.getTransaction(r.Txid)
|
rv, err = s.getTransaction(r.Txid)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
},
|
},
|
||||||
"getTransactionSpecific": func(s *WebsocketServer, c *websocketChannel, req *websocketReq) (rv interface{}, err error) {
|
"getTransactionSpecific": func(s *WebsocketServer, c *websocketChannel, req *WsReq) (rv interface{}, err error) {
|
||||||
r := struct {
|
r := WsTransactionSpecificReq{}
|
||||||
Txid string `json:"txid"`
|
|
||||||
}{}
|
|
||||||
err = json.Unmarshal(req.Params, &r)
|
err = json.Unmarshal(req.Params, &r)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
rv, err = s.getTransactionSpecific(r.Txid)
|
rv, err = s.getTransactionSpecific(r.Txid)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
},
|
},
|
||||||
"estimateFee": func(s *WebsocketServer, c *websocketChannel, req *websocketReq) (rv interface{}, err error) {
|
"estimateFee": func(s *WebsocketServer, c *websocketChannel, req *WsReq) (rv interface{}, err error) {
|
||||||
return s.estimateFee(c, req.Params)
|
return s.estimateFee(c, req.Params)
|
||||||
},
|
},
|
||||||
"sendTransaction": func(s *WebsocketServer, c *websocketChannel, req *websocketReq) (rv interface{}, err error) {
|
"sendTransaction": func(s *WebsocketServer, c *websocketChannel, req *WsReq) (rv interface{}, err error) {
|
||||||
r := struct {
|
r := WsSendTransactionReq{}
|
||||||
Hex string `json:"hex"`
|
|
||||||
}{}
|
|
||||||
err = json.Unmarshal(req.Params, &r)
|
err = json.Unmarshal(req.Params, &r)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
rv, err = s.sendTransaction(r.Hex)
|
rv, err = s.sendTransaction(r.Hex)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
},
|
},
|
||||||
"subscribeNewBlock": func(s *WebsocketServer, c *websocketChannel, req *websocketReq) (rv interface{}, err error) {
|
"subscribeNewBlock": func(s *WebsocketServer, c *websocketChannel, req *WsReq) (rv interface{}, err error) {
|
||||||
return s.subscribeNewBlock(c, req)
|
return s.subscribeNewBlock(c, req)
|
||||||
},
|
},
|
||||||
"unsubscribeNewBlock": func(s *WebsocketServer, c *websocketChannel, req *websocketReq) (rv interface{}, err error) {
|
"unsubscribeNewBlock": func(s *WebsocketServer, c *websocketChannel, req *WsReq) (rv interface{}, err error) {
|
||||||
return s.unsubscribeNewBlock(c)
|
return s.unsubscribeNewBlock(c)
|
||||||
},
|
},
|
||||||
"subscribeNewTransaction": func(s *WebsocketServer, c *websocketChannel, req *websocketReq) (rv interface{}, err error) {
|
"subscribeNewTransaction": func(s *WebsocketServer, c *websocketChannel, req *WsReq) (rv interface{}, err error) {
|
||||||
return s.subscribeNewTransaction(c, req)
|
return s.subscribeNewTransaction(c, req)
|
||||||
},
|
},
|
||||||
"unsubscribeNewTransaction": func(s *WebsocketServer, c *websocketChannel, req *websocketReq) (rv interface{}, err error) {
|
"unsubscribeNewTransaction": func(s *WebsocketServer, c *websocketChannel, req *WsReq) (rv interface{}, err error) {
|
||||||
return s.unsubscribeNewTransaction(c)
|
return s.unsubscribeNewTransaction(c)
|
||||||
},
|
},
|
||||||
"subscribeAddresses": func(s *WebsocketServer, c *websocketChannel, req *websocketReq) (rv interface{}, err error) {
|
"subscribeAddresses": func(s *WebsocketServer, c *websocketChannel, req *WsReq) (rv interface{}, err error) {
|
||||||
ad, err := s.unmarshalAddresses(req.Params)
|
ad, err := s.unmarshalAddresses(req.Params)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
rv, err = s.subscribeAddresses(c, ad, req)
|
rv, err = s.subscribeAddresses(c, ad, req)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
},
|
},
|
||||||
"unsubscribeAddresses": func(s *WebsocketServer, c *websocketChannel, req *websocketReq) (rv interface{}, err error) {
|
"unsubscribeAddresses": func(s *WebsocketServer, c *websocketChannel, req *WsReq) (rv interface{}, err error) {
|
||||||
return s.unsubscribeAddresses(c)
|
return s.unsubscribeAddresses(c)
|
||||||
},
|
},
|
||||||
"subscribeFiatRates": func(s *WebsocketServer, c *websocketChannel, req *websocketReq) (rv interface{}, err error) {
|
"subscribeFiatRates": func(s *WebsocketServer, c *websocketChannel, req *WsReq) (rv interface{}, err error) {
|
||||||
var r fiatRatesSubscription
|
var r WsSubscribeFiatRatesReq
|
||||||
err = json.Unmarshal(req.Params, &r)
|
err = json.Unmarshal(req.Params, &r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -413,41 +375,31 @@ var requestHandlers = map[string]func(*WebsocketServer, *websocketChannel, *webs
|
|||||||
}
|
}
|
||||||
return s.subscribeFiatRates(c, &r, req)
|
return s.subscribeFiatRates(c, &r, req)
|
||||||
},
|
},
|
||||||
"unsubscribeFiatRates": func(s *WebsocketServer, c *websocketChannel, req *websocketReq) (rv interface{}, err error) {
|
"unsubscribeFiatRates": func(s *WebsocketServer, c *websocketChannel, req *WsReq) (rv interface{}, err error) {
|
||||||
return s.unsubscribeFiatRates(c)
|
return s.unsubscribeFiatRates(c)
|
||||||
},
|
},
|
||||||
"ping": func(s *WebsocketServer, c *websocketChannel, req *websocketReq) (rv interface{}, err error) {
|
"ping": func(s *WebsocketServer, c *websocketChannel, req *WsReq) (rv interface{}, err error) {
|
||||||
r := struct{}{}
|
r := struct{}{}
|
||||||
return r, nil
|
return r, nil
|
||||||
},
|
},
|
||||||
"getCurrentFiatRates": func(s *WebsocketServer, c *websocketChannel, req *websocketReq) (rv interface{}, err error) {
|
"getCurrentFiatRates": func(s *WebsocketServer, c *websocketChannel, req *WsReq) (rv interface{}, err error) {
|
||||||
r := struct {
|
r := WsCurrentFiatRatesReq{}
|
||||||
Currencies []string `json:"currencies"`
|
|
||||||
Token string `json:"token"`
|
|
||||||
}{}
|
|
||||||
err = json.Unmarshal(req.Params, &r)
|
err = json.Unmarshal(req.Params, &r)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
rv, err = s.getCurrentFiatRates(r.Currencies, r.Token)
|
rv, err = s.getCurrentFiatRates(r.Currencies, r.Token)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
},
|
},
|
||||||
"getFiatRatesForTimestamps": func(s *WebsocketServer, c *websocketChannel, req *websocketReq) (rv interface{}, err error) {
|
"getFiatRatesForTimestamps": func(s *WebsocketServer, c *websocketChannel, req *WsReq) (rv interface{}, err error) {
|
||||||
r := struct {
|
r := WsFiatRatesForTimestampsReq{}
|
||||||
Timestamps []int64 `json:"timestamps"`
|
|
||||||
Currencies []string `json:"currencies"`
|
|
||||||
Token string `json:"token"`
|
|
||||||
}{}
|
|
||||||
err = json.Unmarshal(req.Params, &r)
|
err = json.Unmarshal(req.Params, &r)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
rv, err = s.getFiatRatesForTimestamps(r.Timestamps, r.Currencies, r.Token)
|
rv, err = s.getFiatRatesForTimestamps(r.Timestamps, r.Currencies, r.Token)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
},
|
},
|
||||||
"getFiatRatesTickersList": func(s *WebsocketServer, c *websocketChannel, req *websocketReq) (rv interface{}, err error) {
|
"getFiatRatesTickersList": func(s *WebsocketServer, c *websocketChannel, req *WsReq) (rv interface{}, err error) {
|
||||||
r := struct {
|
r := WsFiatRatesTickersListReq{}
|
||||||
Timestamp int64 `json:"timestamp"`
|
|
||||||
Token string `json:"token"`
|
|
||||||
}{}
|
|
||||||
err = json.Unmarshal(req.Params, &r)
|
err = json.Unmarshal(req.Params, &r)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
rv, err = s.getAvailableVsCurrencies(r.Timestamp, r.Token)
|
rv, err = s.getAvailableVsCurrencies(r.Timestamp, r.Token)
|
||||||
@ -456,7 +408,7 @@ var requestHandlers = map[string]func(*WebsocketServer, *websocketChannel, *webs
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *WebsocketServer) onRequest(c *websocketChannel, req *websocketReq) {
|
func (s *WebsocketServer) onRequest(c *websocketChannel, req *WsReq) {
|
||||||
var err error
|
var err error
|
||||||
var data interface{}
|
var data interface{}
|
||||||
defer func() {
|
defer func() {
|
||||||
@ -469,7 +421,7 @@ func (s *WebsocketServer) onRequest(c *websocketChannel, req *websocketReq) {
|
|||||||
}
|
}
|
||||||
// nil data means no response
|
// nil data means no response
|
||||||
if data != nil {
|
if data != nil {
|
||||||
c.DataOut(&websocketRes{
|
c.DataOut(&WsRes{
|
||||||
ID: req.ID,
|
ID: req.ID,
|
||||||
Data: data,
|
Data: data,
|
||||||
})
|
})
|
||||||
@ -499,21 +451,8 @@ func (s *WebsocketServer) onRequest(c *websocketChannel, req *websocketReq) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type accountInfoReq struct {
|
func unmarshalGetAccountInfoRequest(params []byte) (*WsAccountInfoReq, error) {
|
||||||
Descriptor string `json:"descriptor"`
|
var r WsAccountInfoReq
|
||||||
Details string `json:"details"`
|
|
||||||
Tokens string `json:"tokens"`
|
|
||||||
PageSize int `json:"pageSize"`
|
|
||||||
Page int `json:"page"`
|
|
||||||
FromHeight int `json:"from"`
|
|
||||||
ToHeight int `json:"to"`
|
|
||||||
ContractFilter string `json:"contractFilter"`
|
|
||||||
SecondaryCurrency string `json:"secondaryCurrency"`
|
|
||||||
Gap int `json:"gap"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func unmarshalGetAccountInfoRequest(params []byte) (*accountInfoReq, error) {
|
|
||||||
var r accountInfoReq
|
|
||||||
err := json.Unmarshal(params, &r)
|
err := json.Unmarshal(params, &r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -521,7 +460,7 @@ func unmarshalGetAccountInfoRequest(params []byte) (*accountInfoReq, error) {
|
|||||||
return &r, nil
|
return &r, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *WebsocketServer) getAccountInfo(req *accountInfoReq) (res *api.Address, err error) {
|
func (s *WebsocketServer) getAccountInfo(req *WsAccountInfoReq) (res *api.Address, err error) {
|
||||||
var opt api.AccountDetails
|
var opt api.AccountDetails
|
||||||
switch req.Details {
|
switch req.Details {
|
||||||
case "tokens":
|
case "tokens":
|
||||||
@ -563,7 +502,7 @@ func (s *WebsocketServer) getAccountInfo(req *accountInfoReq) (res *api.Address,
|
|||||||
return a, nil
|
return a, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *WebsocketServer) getAccountUtxo(descriptor string) (interface{}, error) {
|
func (s *WebsocketServer) getAccountUtxo(descriptor string) (api.Utxos, error) {
|
||||||
utxo, err := s.api.GetXpubUtxo(descriptor, false, 0)
|
utxo, err := s.api.GetXpubUtxo(descriptor, false, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return s.api.GetAddressUtxo(descriptor, false)
|
return s.api.GetAddressUtxo(descriptor, false)
|
||||||
@ -571,7 +510,7 @@ func (s *WebsocketServer) getAccountUtxo(descriptor string) (interface{}, error)
|
|||||||
return utxo, nil
|
return utxo, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *WebsocketServer) getTransaction(txid string) (interface{}, error) {
|
func (s *WebsocketServer) getTransaction(txid string) (*api.Tx, error) {
|
||||||
return s.api.GetTransaction(txid, false, false)
|
return s.api.GetTransaction(txid, false, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -579,31 +518,14 @@ func (s *WebsocketServer) getTransactionSpecific(txid string) (interface{}, erro
|
|||||||
return s.chain.GetTransactionSpecific(&bchain.Tx{Txid: txid})
|
return s.chain.GetTransactionSpecific(&bchain.Tx{Txid: txid})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *WebsocketServer) getInfo() (interface{}, error) {
|
func (s *WebsocketServer) getInfo() (*WsInfoRes, error) {
|
||||||
vi := common.GetVersionInfo()
|
vi := common.GetVersionInfo()
|
||||||
bi := s.is.GetBackendInfo()
|
bi := s.is.GetBackendInfo()
|
||||||
height, hash, err := s.db.GetBestBlock()
|
height, hash, err := s.db.GetBestBlock()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
type backendInfo struct {
|
return &WsInfoRes{
|
||||||
Version string `json:"version,omitempty"`
|
|
||||||
Subversion string `json:"subversion,omitempty"`
|
|
||||||
ConsensusVersion string `json:"consensus_version,omitempty"`
|
|
||||||
Consensus interface{} `json:"consensus,omitempty"`
|
|
||||||
}
|
|
||||||
type info struct {
|
|
||||||
Name string `json:"name"`
|
|
||||||
Shortcut string `json:"shortcut"`
|
|
||||||
Decimals int `json:"decimals"`
|
|
||||||
Version string `json:"version"`
|
|
||||||
BestHeight int `json:"bestHeight"`
|
|
||||||
BestHash string `json:"bestHash"`
|
|
||||||
Block0Hash string `json:"block0Hash"`
|
|
||||||
Testnet bool `json:"testnet"`
|
|
||||||
Backend backendInfo `json:"backend"`
|
|
||||||
}
|
|
||||||
return &info{
|
|
||||||
Name: s.is.Coin,
|
Name: s.is.Coin,
|
||||||
Shortcut: s.is.CoinShortcut,
|
Shortcut: s.is.CoinShortcut,
|
||||||
Decimals: s.chainParser.AmountDecimals(),
|
Decimals: s.chainParser.AmountDecimals(),
|
||||||
@ -612,7 +534,7 @@ func (s *WebsocketServer) getInfo() (interface{}, error) {
|
|||||||
Version: vi.Version,
|
Version: vi.Version,
|
||||||
Block0Hash: s.block0hash,
|
Block0Hash: s.block0hash,
|
||||||
Testnet: s.chain.IsTestnet(),
|
Testnet: s.chain.IsTestnet(),
|
||||||
Backend: backendInfo{
|
Backend: WsBackendInfo{
|
||||||
Version: bi.Version,
|
Version: bi.Version,
|
||||||
Subversion: bi.Subversion,
|
Subversion: bi.Subversion,
|
||||||
ConsensusVersion: bi.ConsensusVersion,
|
ConsensusVersion: bi.ConsensusVersion,
|
||||||
@ -621,15 +543,12 @@ func (s *WebsocketServer) getInfo() (interface{}, error) {
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *WebsocketServer) getBlockHash(height int) (interface{}, error) {
|
func (s *WebsocketServer) getBlockHash(height int) (*WsBlockHashRes, error) {
|
||||||
h, err := s.db.GetBlockHash(uint32(height))
|
h, err := s.db.GetBlockHash(uint32(height))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
type hash struct {
|
return &WsBlockHashRes{
|
||||||
Hash string `json:"hash"`
|
|
||||||
}
|
|
||||||
return &hash{
|
|
||||||
Hash: h,
|
Hash: h,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
@ -643,21 +562,12 @@ func (s *WebsocketServer) getBlock(id string, page, pageSize int) (interface{},
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *WebsocketServer) estimateFee(c *websocketChannel, params []byte) (interface{}, error) {
|
func (s *WebsocketServer) estimateFee(c *websocketChannel, params []byte) (interface{}, error) {
|
||||||
type estimateFeeReq struct {
|
var r WsEstimateFeeReq
|
||||||
Blocks []int `json:"blocks"`
|
|
||||||
Specific map[string]interface{} `json:"specific"`
|
|
||||||
}
|
|
||||||
type estimateFeeRes struct {
|
|
||||||
FeePerTx string `json:"feePerTx,omitempty"`
|
|
||||||
FeePerUnit string `json:"feePerUnit,omitempty"`
|
|
||||||
FeeLimit string `json:"feeLimit,omitempty"`
|
|
||||||
}
|
|
||||||
var r estimateFeeReq
|
|
||||||
err := json.Unmarshal(params, &r)
|
err := json.Unmarshal(params, &r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
res := make([]estimateFeeRes, len(r.Blocks))
|
res := make([]WsEstimateFeeRes, len(r.Blocks))
|
||||||
if s.chainParser.GetChainType() == bchain.ChainEthereumType {
|
if s.chainParser.GetChainType() == bchain.ChainEthereumType {
|
||||||
gas, err := s.chain.EthereumTypeEstimateGas(r.Specific)
|
gas, err := s.chain.EthereumTypeEstimateGas(r.Specific)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -729,7 +639,7 @@ type subscriptionResponseMessage struct {
|
|||||||
Message string `json:"message"`
|
Message string `json:"message"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *WebsocketServer) subscribeNewBlock(c *websocketChannel, req *websocketReq) (res interface{}, err error) {
|
func (s *WebsocketServer) subscribeNewBlock(c *websocketChannel, req *WsReq) (res interface{}, err error) {
|
||||||
s.newBlockSubscriptionsLock.Lock()
|
s.newBlockSubscriptionsLock.Lock()
|
||||||
defer s.newBlockSubscriptionsLock.Unlock()
|
defer s.newBlockSubscriptionsLock.Unlock()
|
||||||
s.newBlockSubscriptions[c] = req.ID
|
s.newBlockSubscriptions[c] = req.ID
|
||||||
@ -745,7 +655,7 @@ func (s *WebsocketServer) unsubscribeNewBlock(c *websocketChannel) (res interfac
|
|||||||
return &subscriptionResponse{false}, nil
|
return &subscriptionResponse{false}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *WebsocketServer) subscribeNewTransaction(c *websocketChannel, req *websocketReq) (res interface{}, err error) {
|
func (s *WebsocketServer) subscribeNewTransaction(c *websocketChannel, req *WsReq) (res interface{}, err error) {
|
||||||
s.newTransactionSubscriptionsLock.Lock()
|
s.newTransactionSubscriptionsLock.Lock()
|
||||||
defer s.newTransactionSubscriptionsLock.Unlock()
|
defer s.newTransactionSubscriptionsLock.Unlock()
|
||||||
if !s.newTransactionEnabled {
|
if !s.newTransactionEnabled {
|
||||||
@ -768,9 +678,7 @@ func (s *WebsocketServer) unsubscribeNewTransaction(c *websocketChannel) (res in
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *WebsocketServer) unmarshalAddresses(params []byte) ([]string, error) {
|
func (s *WebsocketServer) unmarshalAddresses(params []byte) ([]string, error) {
|
||||||
r := struct {
|
r := WsSubscribeAddressesReq{}
|
||||||
Addresses []string `json:"addresses"`
|
|
||||||
}{}
|
|
||||||
err := json.Unmarshal(params, &r)
|
err := json.Unmarshal(params, &r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -804,7 +712,7 @@ func (s *WebsocketServer) doUnsubscribeAddresses(c *websocketChannel) {
|
|||||||
c.addrDescs = nil
|
c.addrDescs = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *WebsocketServer) subscribeAddresses(c *websocketChannel, addrDesc []string, req *websocketReq) (res interface{}, err error) {
|
func (s *WebsocketServer) subscribeAddresses(c *websocketChannel, addrDesc []string, req *WsReq) (res interface{}, err error) {
|
||||||
s.addressSubscriptionsLock.Lock()
|
s.addressSubscriptionsLock.Lock()
|
||||||
defer s.addressSubscriptionsLock.Unlock()
|
defer s.addressSubscriptionsLock.Unlock()
|
||||||
// unsubscribe all previous subscriptions
|
// unsubscribe all previous subscriptions
|
||||||
@ -847,7 +755,7 @@ func (s *WebsocketServer) doUnsubscribeFiatRates(c *websocketChannel) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// subscribeFiatRates subscribes all FiatRates subscriptions by this channel
|
// subscribeFiatRates subscribes all FiatRates subscriptions by this channel
|
||||||
func (s *WebsocketServer) subscribeFiatRates(c *websocketChannel, d *fiatRatesSubscription, req *websocketReq) (res interface{}, err error) {
|
func (s *WebsocketServer) subscribeFiatRates(c *websocketChannel, d *WsSubscribeFiatRatesReq, req *WsReq) (res interface{}, err error) {
|
||||||
s.fiatRatesSubscriptionsLock.Lock()
|
s.fiatRatesSubscriptionsLock.Lock()
|
||||||
defer s.fiatRatesSubscriptionsLock.Unlock()
|
defer s.fiatRatesSubscriptionsLock.Unlock()
|
||||||
// unsubscribe all previous subscriptions
|
// unsubscribe all previous subscriptions
|
||||||
@ -891,7 +799,7 @@ func (s *WebsocketServer) onNewBlockAsync(hash string, height uint32) {
|
|||||||
Hash: hash,
|
Hash: hash,
|
||||||
}
|
}
|
||||||
for c, id := range s.newBlockSubscriptions {
|
for c, id := range s.newBlockSubscriptions {
|
||||||
c.DataOut(&websocketRes{
|
c.DataOut(&WsRes{
|
||||||
ID: id,
|
ID: id,
|
||||||
Data: &data,
|
Data: &data,
|
||||||
})
|
})
|
||||||
@ -908,7 +816,7 @@ func (s *WebsocketServer) sendOnNewTx(tx *api.Tx) {
|
|||||||
s.newTransactionSubscriptionsLock.Lock()
|
s.newTransactionSubscriptionsLock.Lock()
|
||||||
defer s.newTransactionSubscriptionsLock.Unlock()
|
defer s.newTransactionSubscriptionsLock.Unlock()
|
||||||
for c, id := range s.newTransactionSubscriptions {
|
for c, id := range s.newTransactionSubscriptions {
|
||||||
c.DataOut(&websocketRes{
|
c.DataOut(&WsRes{
|
||||||
ID: id,
|
ID: id,
|
||||||
Data: &tx,
|
Data: &tx,
|
||||||
})
|
})
|
||||||
@ -936,7 +844,7 @@ func (s *WebsocketServer) sendOnNewTxAddr(stringAddressDescriptor string, tx *ap
|
|||||||
as, ok := s.addressSubscriptions[stringAddressDescriptor]
|
as, ok := s.addressSubscriptions[stringAddressDescriptor]
|
||||||
if ok {
|
if ok {
|
||||||
for c, id := range as {
|
for c, id := range as {
|
||||||
c.DataOut(&websocketRes{
|
c.DataOut(&WsRes{
|
||||||
ID: id,
|
ID: id,
|
||||||
Data: &data,
|
Data: &data,
|
||||||
})
|
})
|
||||||
@ -1038,12 +946,12 @@ func (s *WebsocketServer) broadcastTicker(currency string, rates map[string]floa
|
|||||||
dataWithTokens.TokenRates[token] = rate
|
dataWithTokens.TokenRates[token] = rate
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
c.DataOut(&websocketRes{
|
c.DataOut(&WsRes{
|
||||||
ID: id,
|
ID: id,
|
||||||
Data: &dataWithTokens,
|
Data: &dataWithTokens,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
c.DataOut(&websocketRes{
|
c.DataOut(&WsRes{
|
||||||
ID: id,
|
ID: id,
|
||||||
Data: &data,
|
Data: &data,
|
||||||
})
|
})
|
||||||
@ -1063,17 +971,17 @@ func (s *WebsocketServer) OnNewFiatRatesTicker(ticker *common.CurrencyRatesTicke
|
|||||||
s.broadcastTicker(allFiatRates, ticker.Rates, nil)
|
s.broadcastTicker(allFiatRates, ticker.Rates, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *WebsocketServer) getCurrentFiatRates(currencies []string, token string) (interface{}, error) {
|
func (s *WebsocketServer) getCurrentFiatRates(currencies []string, token string) (*api.FiatTicker, error) {
|
||||||
ret, err := s.api.GetCurrentFiatRates(currencies, strings.ToLower(token))
|
ret, err := s.api.GetCurrentFiatRates(currencies, strings.ToLower(token))
|
||||||
return ret, err
|
return ret, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *WebsocketServer) getFiatRatesForTimestamps(timestamps []int64, currencies []string, token string) (interface{}, error) {
|
func (s *WebsocketServer) getFiatRatesForTimestamps(timestamps []int64, currencies []string, token string) (*api.FiatTickers, error) {
|
||||||
ret, err := s.api.GetFiatRatesForTimestamps(timestamps, currencies, strings.ToLower(token))
|
ret, err := s.api.GetFiatRatesForTimestamps(timestamps, currencies, strings.ToLower(token))
|
||||||
return ret, err
|
return ret, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *WebsocketServer) getAvailableVsCurrencies(timestamp int64, token string) (interface{}, error) {
|
func (s *WebsocketServer) getAvailableVsCurrencies(timestamp int64, token string) (*api.AvailableVsCurrencies, error) {
|
||||||
ret, err := s.api.GetAvailableVsCurrencies(timestamp, strings.ToLower(token))
|
ret, err := s.api.GetAvailableVsCurrencies(timestamp, strings.ToLower(token))
|
||||||
return ret, err
|
return ret, err
|
||||||
}
|
}
|
||||||
|
|||||||
120
server/ws_types.go
Normal file
120
server/ws_types.go
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
package server
|
||||||
|
|
||||||
|
import "encoding/json"
|
||||||
|
|
||||||
|
type WsReq struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Method string `json:"method" ts_type:"'getAccountInfo' | 'getInfo' | 'getBlockHash' | 'getAccountUtxo' | 'getBalanceHistory' | 'getTransaction' | 'getTransactionSpecific' | 'estimateFee' | 'sendTransaction' | 'subscribeNewBlock' | 'unsubscribeNewBlock' | 'subscribeNewTransaction' | 'unsubscribeNewTransaction' | 'subscribeAddresses' | 'unsubscribeAddresses' | 'subscribeFiatRates' | 'unsubscribeFiatRates' | 'ping' | 'getCurrentFiatRates' | 'getFiatRatesForTimestamps' | 'getFiatRatesTickersList'"`
|
||||||
|
Params json.RawMessage `json:"params" ts_type:"any"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type WsRes struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Data interface{} `json:"data"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type WsAccountInfoReq struct {
|
||||||
|
Descriptor string `json:"descriptor"`
|
||||||
|
Details string `json:"details,omitempty" ts_type:"'basic' | 'tokens' | 'tokenBalances' | 'txids' | 'txslight' | 'txs'"`
|
||||||
|
Tokens string `json:"tokens,omitempty" ts_type:"'derived' | 'used' | 'nonzero'"`
|
||||||
|
PageSize int `json:"pageSize,omitempty"`
|
||||||
|
Page int `json:"page,omitempty"`
|
||||||
|
FromHeight int `json:"from,omitempty"`
|
||||||
|
ToHeight int `json:"to,omitempty"`
|
||||||
|
ContractFilter string `json:"contractFilter,omitempty"`
|
||||||
|
SecondaryCurrency string `json:"secondaryCurrency,omitempty"`
|
||||||
|
Gap int `json:"gap,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type WsBackendInfo struct {
|
||||||
|
Version string `json:"version,omitempty"`
|
||||||
|
Subversion string `json:"subversion,omitempty"`
|
||||||
|
ConsensusVersion string `json:"consensus_version,omitempty"`
|
||||||
|
Consensus interface{} `json:"consensus,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type WsInfoRes struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Shortcut string `json:"shortcut"`
|
||||||
|
Decimals int `json:"decimals"`
|
||||||
|
Version string `json:"version"`
|
||||||
|
BestHeight int `json:"bestHeight"`
|
||||||
|
BestHash string `json:"bestHash"`
|
||||||
|
Block0Hash string `json:"block0Hash"`
|
||||||
|
Testnet bool `json:"testnet"`
|
||||||
|
Backend WsBackendInfo `json:"backend"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type WsBlockHashReq struct {
|
||||||
|
Height int `json:"height"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type WsBlockHashRes struct {
|
||||||
|
Hash string `json:"hash"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type WsBlockReq struct {
|
||||||
|
Id string `json:"id"`
|
||||||
|
PageSize int `json:"pageSize,omitempty"`
|
||||||
|
Page int `json:"page,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type WsAccountUtxoReq struct {
|
||||||
|
Descriptor string `json:"descriptor"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type WsBalanceHistoryReq struct {
|
||||||
|
Descriptor string `json:"descriptor"`
|
||||||
|
From int64 `json:"from,omitempty"`
|
||||||
|
To int64 `json:"to,omitempty"`
|
||||||
|
Currencies []string `json:"currencies,omitempty"`
|
||||||
|
Gap int `json:"gap,omitempty"`
|
||||||
|
GroupBy uint32 `json:"groupBy,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type WsTransactionReq struct {
|
||||||
|
Txid string `json:"txid"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type WsTransactionSpecificReq struct {
|
||||||
|
Txid string `json:"txid"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type WsEstimateFeeReq struct {
|
||||||
|
Blocks []int `json:"blocks,omitempty"`
|
||||||
|
Specific map[string]interface{} `json:"specific,omitempty" ts_type:"{conservative?: boolean;txsize?: number;from?: string;to?: string;data?: string;value?: string;}"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type WsEstimateFeeRes struct {
|
||||||
|
FeePerTx string `json:"feePerTx,omitempty"`
|
||||||
|
FeePerUnit string `json:"feePerUnit,omitempty"`
|
||||||
|
FeeLimit string `json:"feeLimit,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type WsSendTransactionReq struct {
|
||||||
|
Hex string `json:"hex"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type WsSubscribeAddressesReq struct {
|
||||||
|
Addresses []string `json:"addresses"`
|
||||||
|
}
|
||||||
|
type WsSubscribeFiatRatesReq struct {
|
||||||
|
Currency string `json:"currency,omitempty"`
|
||||||
|
Tokens []string `json:"tokens,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type WsCurrentFiatRatesReq struct {
|
||||||
|
Currencies []string `json:"currencies,omitempty"`
|
||||||
|
Token string `json:"token,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type WsFiatRatesForTimestampsReq struct {
|
||||||
|
Timestamps []int64 `json:"timestamps"`
|
||||||
|
Currencies []string `json:"currencies,omitempty"`
|
||||||
|
Token string `json:"token,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type WsFiatRatesTickersListReq struct {
|
||||||
|
Timestamp int64 `json:"timestamp,omitempty"`
|
||||||
|
Token string `json:"token,omitempty"`
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user