diff --git a/indexer/indexer.gen.go b/indexer/indexer.gen.go index aa5a0892..ab07d201 100644 --- a/indexer/indexer.gen.go +++ b/indexer/indexer.gen.go @@ -1,6 +1,6 @@ -// sequence-indexer v0.4.0 4a22eceb25b79f11a96cfd7416e0e5e3150b8f77 +// sequence-indexer v0.4.0 3706bc20b567c4a87214824c1ef8d1ce1df96db2 // -- -// Code generated by webrpc-gen@v0.20.0 with golang generator. DO NOT EDIT. +// Code generated by webrpc-gen@v0.21.1 with golang generator. DO NOT EDIT. // // webrpc-gen -schema=indexer.ridl -target=golang -pkg=indexer -client -out=./clients/indexer.gen.go package indexer @@ -15,12 +15,17 @@ import ( "io" "net/http" "net/url" + "strings" "sync" "time" "github.com/0xsequence/go-sequence/lib/prototyp" ) +const WebrpcHeader = "Webrpc" + +const WebrpcHeaderValue = "webrpc@v0.21.1;gen-golang@v0.16.0;sequence-indexer@v0.4.0" + // WebRPC description and code-gen version func WebRPCVersion() string { return "v1" @@ -33,13 +38,109 @@ func WebRPCSchemaVersion() string { // Schema hash generated from your RIDL schema func WebRPCSchemaHash() string { - return "4a22eceb25b79f11a96cfd7416e0e5e3150b8f77" + return "3706bc20b567c4a87214824c1ef8d1ce1df96db2" +} + +type WebrpcGenVersions struct { + WebrpcGenVersion string + CodeGenName string + CodeGenVersion string + SchemaName string + SchemaVersion string +} + +func VersionFromHeader(h http.Header) (*WebrpcGenVersions, error) { + if h.Get(WebrpcHeader) == "" { + return nil, fmt.Errorf("header is empty or missing") + } + + versions, err := parseWebrpcGenVersions(h.Get(WebrpcHeader)) + if err != nil { + return nil, fmt.Errorf("webrpc header is invalid: %w", err) + } + + return versions, nil +} + +func parseWebrpcGenVersions(header string) (*WebrpcGenVersions, error) { + versions := strings.Split(header, ";") + if len(versions) < 3 { + return nil, fmt.Errorf("expected at least 3 parts while parsing webrpc header: %v", header) + } + + _, webrpcGenVersion, ok := strings.Cut(versions[0], "@") + if !ok { + return nil, fmt.Errorf("webrpc gen version could not be parsed from: %s", versions[0]) + } + + tmplTarget, tmplVersion, ok := strings.Cut(versions[1], "@") + if !ok { + return nil, fmt.Errorf("tmplTarget and tmplVersion could not be parsed from: %s", versions[1]) + } + + schemaName, schemaVersion, ok := strings.Cut(versions[2], "@") + if !ok { + return nil, fmt.Errorf("schema name and schema version could not be parsed from: %s", versions[2]) + } + + return &WebrpcGenVersions{ + WebrpcGenVersion: webrpcGenVersion, + CodeGenName: tmplTarget, + CodeGenVersion: tmplVersion, + SchemaName: schemaName, + SchemaVersion: schemaVersion, + }, nil } // // Common types // +type ResourceStatus uint32 + +const ( + ResourceStatus_NOT_AVAILABLE ResourceStatus = 0 + ResourceStatus_STALE ResourceStatus = 1 + ResourceStatus_AVAILABLE ResourceStatus = 2 +) + +var ResourceStatus_name = map[uint32]string{ + 0: "NOT_AVAILABLE", + 1: "STALE", + 2: "AVAILABLE", +} + +var ResourceStatus_value = map[string]uint32{ + "NOT_AVAILABLE": 0, + "STALE": 1, + "AVAILABLE": 2, +} + +func (x ResourceStatus) String() string { + return ResourceStatus_name[uint32(x)] +} + +func (x ResourceStatus) MarshalText() ([]byte, error) { + return []byte(ResourceStatus_name[uint32(x)]), nil +} + +func (x *ResourceStatus) UnmarshalText(b []byte) error { + *x = ResourceStatus(ResourceStatus_value[string(b)]) + return nil +} + +func (x *ResourceStatus) Is(values ...ResourceStatus) bool { + if x == nil { + return false + } + for _, v := range values { + if *x == v { + return true + } + } + return false +} + // ContractInfo is RPC type for responding to clients that represents // the contract-level metadata. type ContractInfo struct { @@ -55,18 +156,23 @@ type ContractInfo struct { Extensions *ContractInfoExtensions `json:"extensions" cbor:"-"` ContentHash uint64 `json:"-" cbor:"-"` UpdatedAt time.Time `json:"updatedAt" cbor:"-"` + NotFound bool `json:"notFound"` + QueuedAt *time.Time `json:"queuedAt"` + Status ResourceStatus `json:"status"` } type ContractInfoExtensions struct { - Link string `json:"link" cbor:"-"` - Description string `json:"description" cbor:"-"` - OgImage string `json:"ogImage" cbor:"-"` - OriginChainID uint64 `json:"originChainId" cbor:"-"` - OriginAddress string `json:"originAddress" cbor:"-"` - Blacklist bool `json:"blacklist,omitempty" cbor:"-"` - Verified bool `json:"verified" cbor:"-"` - VerifiedBy string `json:"verifiedBy,omitempty" cbor:"-"` - Featured bool `json:"featured,omitempty" cbor:"-"` + Link string `json:"link" cbor:"-"` + Description string `json:"description" cbor:"-"` + Categories []string `json:"categories,omitempty" cbor:"-"` + OgImage string `json:"ogImage" cbor:"-"` + OgName string `json:"ogName" cbor:"-"` + OriginChainID uint64 `json:"originChainId" cbor:"-"` + OriginAddress string `json:"originAddress" cbor:"-"` + Blacklist bool `json:"blacklist,omitempty" cbor:"-"` + Verified bool `json:"verified" cbor:"-"` + VerifiedBy string `json:"verifiedBy,omitempty" cbor:"-"` + Featured bool `json:"featured,omitempty" cbor:"-"` } // TokenMetadata based on 721/1155 standards, as well including some @@ -105,7 +211,10 @@ type TokenMetadata struct { Decimals *uint64 `json:"decimals,omitempty" cbor:"-"` UpdatedAt time.Time `json:"updatedAt" cbor:"-"` // Assets associated to this token metadata - Assets []*Asset `json:"assets,omitempty" cbor:"-"` + Assets []*Asset `json:"assets,omitempty" cbor:"-"` + Status ResourceStatus `json:"status"` + QueuedAt *time.Time `json:"queuedAt"` + LastFetched *time.Time `json:"lastFetched,omitempty"` } // Asset is a database type used by 'collections' to record static assets for @@ -622,6 +731,7 @@ type EtherBalance struct { type NativeTokenBalance struct { AccountAddress prototyp.Hash `json:"accountAddress" cbor:"-"` Balance prototyp.BigInt `json:"balance" cbor:"-"` + Error string `json:"error,omitempty" cbor:"-"` } type IndexState struct { @@ -714,20 +824,19 @@ type OrderbookOrder struct { // Pass this object to 'filter' and not to 'filters' parameter in GetOrderbookOrders() // 'filters' are deprecated // use 'filter' and these fields instead -// - userAddresses -// - excludeUserAddress' +// - 'userAddresses' or 'excludeUserAddresses' type OrderbookOrderFilter struct { - IsListing *bool `json:"isListing" cbor:"1,extension"` - // deprecated - UserAddresses []string `json:"userAddresses" cbor:"2,extension"` - TokenIDs []string `json:"tokenIds" cbor:"3,extension"` - // deprecated + IsListing *bool `json:"isListing" cbor:"1,extension"` + UserAddresses []string `json:"userAddresses" cbor:"2,extension"` + TokenIDs []string `json:"tokenIds" cbor:"3,extension"` ExcludeUserAddresses []string `json:"excludeUserAddresses" cbor:"4,extension"` AfterBlockNumber uint64 `json:"afterBlockNumber" cbor:"5,extension"` AfterCreatedAt int64 `json:"afterCreatedAt" cbor:"6,extension"` BeforeExpiry int64 `json:"beforeExpiry" cbor:"7,extension"` - UserAddress *string `json:"userAddress" cbor:"8,extension"` - ExcludeUserAddress *string `json:"excludeUserAddress" cbor:"9,extension"` + // deprecated + UserAddress *string `json:"userAddress" cbor:"8,extension"` + // deprecated + ExcludeUserAddress *string `json:"excludeUserAddress" cbor:"9,extension"` } // Token History @@ -831,6 +940,11 @@ type TransactionLog struct { Index uint64 `json:"index"` } +type TokenIDRange struct { + Start prototyp.BigInt `json:"start"` + End prototyp.BigInt `json:"end"` +} + // Page represents a results page. This can be used both to request a page and // to store the state of a page. type Page struct { @@ -895,10 +1009,11 @@ type MetadataOptions struct { } type TokenBalancesFilter struct { - AccountAddresses []prototyp.Hash `json:"accountAddresses"` - ContractStatus ContractVerificationStatus `json:"contractStatus"` - ContractWhitelist []prototyp.Hash `json:"contractWhitelist"` - ContractBlacklist []prototyp.Hash `json:"contractBlacklist"` + AccountAddresses []prototyp.Hash `json:"accountAddresses"` + ContractStatus ContractVerificationStatus `json:"contractStatus"` + ContractWhitelist []prototyp.Hash `json:"contractWhitelist"` + ContractBlacklist []prototyp.Hash `json:"contractBlacklist"` + OmitNativeBalances bool `json:"omitNativeBalances,omitempty"` } type TokenBalancesByContractFilter struct { @@ -907,32 +1022,56 @@ type TokenBalancesByContractFilter struct { ContractStatus ContractVerificationStatus `json:"contractStatus"` } +type GatewayEtherBalance struct { + ChainID uint64 `json:"chainId"` + Error error `json:"error,omitempty"` + Result *EtherBalance `json:"result"` +} + +type GatewayNativeTokenBalance struct { + ChainID uint64 `json:"chainId"` + Error error `json:"error,omitempty"` + Result *NativeTokenBalance `json:"result"` +} + +type GatewayNativeTokenBalances struct { + ChainID uint64 `json:"chainID"` + Error error `json:"error,omitempty"` + Balances []*NativeTokenBalance `json:"balances"` +} + +type GatewayTokenBalance struct { + ChainID uint64 `json:"chainId"` + Error error `json:"error,omitempty"` + Results []*TokenBalance `json:"results"` +} + var ( methods = map[string]method{ "/rpc/Indexer/Ping": { Name: "Ping", Service: "Indexer", - Annotations: map[string]string{}, + Annotations: map[string]string{"internal": ""}, }, "/rpc/Indexer/Version": { Name: "Version", Service: "Indexer", - Annotations: map[string]string{}, + Annotations: map[string]string{"internal": ""}, }, "/rpc/Indexer/RuntimeStatus": { Name: "RuntimeStatus", Service: "Indexer", - Annotations: map[string]string{}, + Annotations: map[string]string{"internal": ""}, }, "/rpc/Indexer/GetChainID": { Name: "GetChainID", Service: "Indexer", - Annotations: map[string]string{}, + Annotations: map[string]string{"internal": ""}, }, "/rpc/Indexer/GetEtherBalance": { Name: "GetEtherBalance", Service: "Indexer", - Annotations: map[string]string{}, + Annotations: map[string]string{"deprecated": "GetNativeTokenBalance"}, }, "/rpc/Indexer/GetNativeTokenBalance": { Name: "GetNativeTokenBalance", @@ -969,6 +1108,16 @@ var ( Service: "Indexer", Annotations: map[string]string{}, }, + "/rpc/Indexer/GetTokenIDs": { + Name: "GetTokenIDs", + Service: "Indexer", + Annotations: map[string]string{}, + }, + "/rpc/Indexer/GetTokenIDRanges": { + Name: "GetTokenIDRanges", + Service: "Indexer", + Annotations: map[string]string{}, + }, "/rpc/Indexer/GetBalanceUpdates": { Name: "GetBalanceUpdates", Service: "Indexer", @@ -979,31 +1128,36 @@ var ( Service: "Indexer", Annotations: map[string]string{}, }, - "/rpc/Indexer/SyncBalance": { - Name: "SyncBalance", + "/rpc/Indexer/FetchTransactionReceipt": { + Name: "FetchTransactionReceipt", Service: "Indexer", Annotations: map[string]string{}, }, - "/rpc/Indexer/FetchTransactionReceipt": { - Name: "FetchTransactionReceipt", + "/rpc/Indexer/FetchTransactionReceiptWithFilter": { + Name: "FetchTransactionReceiptWithFilter", Service: "Indexer", Annotations: map[string]string{}, }, - "/rpc/Indexer/GetOrderbookOrders": { - Name: "GetOrderbookOrders", + "/rpc/Indexer/SubscribeReceipts": { + Name: "SubscribeReceipts", Service: "Indexer", Annotations: map[string]string{}, }, - "/rpc/Indexer/GetTopOrders": { - Name: "GetTopOrders", + "/rpc/Indexer/SubscribeEvents": { + Name: "SubscribeEvents", Service: "Indexer", Annotations: map[string]string{}, }, - "/rpc/Indexer/FetchTransactionReceiptWithFilter": { - Name: "FetchTransactionReceiptWithFilter", + "/rpc/Indexer/SubscribeBalanceUpdates": { + Name: "SubscribeBalanceUpdates", Service: "Indexer", Annotations: map[string]string{}, }, + "/rpc/Indexer/SyncBalance": { + Name: "SyncBalance", + Service: "Indexer", + Annotations: map[string]string{"internal": ""}, + }, "/rpc/Indexer/GetAllWebhookListeners": { Name: "GetAllWebhookListeners", Service: "Indexer", @@ -1029,6 +1183,11 @@ var ( Service: "Indexer", Annotations: map[string]string{}, }, + "/rpc/Indexer/RemoveAllWebhookListeners": { + Name: "RemoveAllWebhookListeners", + Service: "Indexer", + Annotations: map[string]string{}, + }, "/rpc/Indexer/ToggleWebhookListener": { Name: "ToggleWebhookListener", Service: "Indexer", @@ -1044,21 +1203,61 @@ var ( Service: "Indexer", Annotations: map[string]string{}, }, - "/rpc/Indexer/SubscribeReceipts": { - Name: "SubscribeReceipts", + "/rpc/Indexer/GetOrderbookOrders": { + Name: "GetOrderbookOrders", Service: "Indexer", Annotations: map[string]string{}, }, - "/rpc/Indexer/SubscribeEvents": { - Name: "SubscribeEvents", + "/rpc/Indexer/GetTopOrders": { + Name: "GetTopOrders", Service: "Indexer", Annotations: map[string]string{}, }, - "/rpc/Indexer/SubscribeBalanceUpdates": { - Name: "SubscribeBalanceUpdates", - Service: "Indexer", + "/rpc/IndexerGateway/GetNativeTokenBalance": { + Name: "GetNativeTokenBalance", + Service: "IndexerGateway", + Annotations: map[string]string{}, + }, + "/rpc/IndexerGateway/GetTokenBalances": { + Name: "GetTokenBalances", + Service: "IndexerGateway", + Annotations: map[string]string{}, + }, + "/rpc/IndexerGateway/GetTokenBalancesSummary": { + Name: "GetTokenBalancesSummary", + Service: "IndexerGateway", + Annotations: map[string]string{}, + }, + "/rpc/IndexerGateway/GetTokenBalancesDetails": { + Name: "GetTokenBalancesDetails", + Service: "IndexerGateway", + Annotations: map[string]string{}, + }, + "/rpc/IndexerGateway/GetTokenBalancesByContract": { + Name: "GetTokenBalancesByContract", + Service: "IndexerGateway", Annotations: map[string]string{}, }, + "/rpc/IndexerGateway/GetBalanceUpdates": { + Name: "GetBalanceUpdates", + Service: "IndexerGateway", + Annotations: map[string]string{}, + }, + "/rpc/IndexerGateway/Ping": { + Name: "Ping", + Service: "IndexerGateway", + Annotations: map[string]string{"internal": ""}, + }, + "/rpc/IndexerGateway/Version": { + Name: "Version", + Service: "IndexerGateway", + Annotations: map[string]string{"internal": ""}, + }, + "/rpc/IndexerGateway/RuntimeStatus": { + Name: "RuntimeStatus", + Service: "IndexerGateway", + Annotations: map[string]string{"internal": ""}, + }, } ) @@ -1076,24 +1275,38 @@ var WebRPCServices = map[string][]string{ "GetTokenBalances", "GetTokenSupplies", "GetTokenSuppliesMap", + "GetTokenIDs", + "GetTokenIDRanges", "GetBalanceUpdates", "GetTransactionHistory", - "SyncBalance", "FetchTransactionReceipt", - "GetOrderbookOrders", - "GetTopOrders", "FetchTransactionReceiptWithFilter", + "SubscribeReceipts", + "SubscribeEvents", + "SubscribeBalanceUpdates", + "SyncBalance", "GetAllWebhookListeners", "GetWebhookListener", "AddWebhookListener", "UpdateWebhookListener", "RemoveWebhookListener", + "RemoveAllWebhookListeners", "ToggleWebhookListener", "PauseAllWebhookListeners", "ResumeAllWebhookListeners", - "SubscribeReceipts", - "SubscribeEvents", - "SubscribeBalanceUpdates", + "GetOrderbookOrders", + "GetTopOrders", + }, + "IndexerGateway": { + "GetNativeTokenBalance", + "GetTokenBalances", + "GetTokenBalancesSummary", + "GetTokenBalancesDetails", + "GetTokenBalancesByContract", + "GetBalanceUpdates", + "Ping", + "Version", + "RuntimeStatus", }, } @@ -1112,6 +1325,7 @@ type Indexer interface { GetChainID(ctx context.Context) (uint64, error) // Queries an ethereum node for the latest and confirm ETH balances // DEPRECATED: use GetNativeTokenBalance instead + // Deprecated: GetEtherBalance(ctx context.Context, accountAddress *string) (*EtherBalance, error) // GetNativeTokenBalance queries an ethereum node for the latest native token account balance. // The native token is the token of the chain the indexer is connected to, for example, ETH on Ethereum @@ -1126,7 +1340,7 @@ type Indexer interface { // // If `filter.contractStatus` is not provided, it will include verified only // tokens. - GetTokenBalancesSummary(ctx context.Context, filter *TokenBalancesFilter, omitMetadata *bool, page *Page) (*Page, []*TokenBalance, error) + GetTokenBalancesSummary(ctx context.Context, filter *TokenBalancesFilter, omitMetadata *bool, page *Page) (*Page, []*NativeTokenBalance, []*TokenBalance, error) // GetTokenBalancesDetails returns a detailed balance summary for a specific // accounts. The collection ERC721 & ERC1155 tokens are represented as // individual balances. @@ -1136,7 +1350,7 @@ type Indexer interface { // // If `filter.contractStatus` is not provided, it will include verified only // tokens. - GetTokenBalancesDetails(ctx context.Context, filter *TokenBalancesFilter, omitMetadata *bool, page *Page) (*Page, []*TokenBalance, error) + GetTokenBalancesDetails(ctx context.Context, filter *TokenBalancesFilter, omitMetadata *bool, page *Page) (*Page, []*NativeTokenBalance, []*TokenBalance, error) // GetTokenBalancesByContract returns a balances for a specific accounts and // contracts. The collection ERC721 & ERC1155 tokens are represented as // individual balances. @@ -1163,15 +1377,57 @@ type Indexer interface { // For an ERC-20 specify tokenIDs as an empty array or [0], for example, { '0xdef': [] } or { '0xdef': [0] } // For ERC-1155 pass the array of tokens are strings, ie. { '0xabc': ['1', '2', '3'] } GetTokenSuppliesMap(ctx context.Context, tokenMap map[string][]string, includeMetadata *bool, metadataOptions *MetadataOptions) (map[string][]*TokenSupply, error) - // Get balance update aggregate values -- useful for syncing balance details of a contract, ie. from Skyweaver + // GetTokenIDs returns the list of each individual token id for a token collection contract. + // This is useful for ERC-721 and ERC-1155 contracts to get the list of valid tokenIDs. + GetTokenIDs(ctx context.Context, contractAddress string, page *Page) (*Page, ContractType, []string, error) + // GetTokenIDRanges returns the range of tokenIDs for a token collection contract. + // This is useful for ERC-721 and ERC-1155 contracts to get the range of valid tokenIDs. It is similar to + // GetTokenIDs, but returns the range of tokenIDs instead of the list of tokenIDs, which is more efficient + // for large collections and very easy to the caller to expand the range into a list of tokenIDs. + // + // NOTE: this method will only return up to 15,000 ranges, if there are more ranges, it will return + // a boolean to indicate there are more ranges beyond the first 15,000. Therefore, if `moreRanges` is + // false then you have all the ranges, but if true, you need to make a follow up call to fetch the next + // page of ranges. + // + // As an example, if a NFT collection of 100,000 tokens uses ids from 1,2,3,...,100_000 then this endpoint + // will return just a single range from [1,100_000], but if there are gaps between the sequence, then + // those will be broken into separate range entries. + GetTokenIDRanges(ctx context.Context, contractAddress string) (ContractType, []*TokenIDRange, bool, error) + // Get balance update aggregate values -- useful for syncing balance details of a contract, ie. from Skyweaver. + // Also consider using SubscribeBalanceUpdates or SubscribeEvents as other alternatives. GetBalanceUpdates(ctx context.Context, contractAddress string, lastBlockNumber uint64, lastBlockHash *string, page *Page) (*Page, []*TokenBalance, error) // History of mined transactions for the account which includes a list of token transfers (sent/recieved) // and sent transactions from a Sequence wallet GetTransactionHistory(ctx context.Context, filter *TransactionHistoryFilter, page *Page, includeMetadata *bool, metadataOptions *MetadataOptions) (*Page, []*Transaction, error) - // Re-sync an incorrect token balance with the correct on-chain balance - SyncBalance(ctx context.Context, accountAddress string, contractAddress string, tokenID *string) error // Fetches a single receipt and then will stop the subscription FetchTransactionReceipt(ctx context.Context, txnHash string, maxBlockWait *int) (*TransactionReceipt, error) + // Fetches a single receipt with filter and then will stop the subscription + FetchTransactionReceiptWithFilter(ctx context.Context, filter *TransactionFilter, maxBlockWait *int) (*TransactionReceipt, error) + // Listen to transaction receipts on-chain based on the filter criteria + SubscribeReceipts(ctx context.Context, filter *TransactionFilter, stream SubscribeReceiptsStreamWriter) error + // SubscribeEvents listens to events on-chain based on the filter criteria + // + // TODO: some additional options can be passed such as block, reorg true, etc. + // or stay behind, etc. + SubscribeEvents(ctx context.Context, filter *EventFilter, stream SubscribeEventsStreamWriter) error + // SubscribeBalanceUpdates listens to balance updates for a specific contract address + SubscribeBalanceUpdates(ctx context.Context, contractAddress string, stream SubscribeBalanceUpdatesStreamWriter) error + // Re-sync an incorrect token balance with the correct on-chain balance + // NOTE: this method is almost never used, but we've marked it internal in case + // we ever want to use it again. This method was written a very long time ago in + // scenarios when the indexer had little bugs, but now its solid. + SyncBalance(ctx context.Context, accountAddress string, contractAddress string, tokenID *string) error + // Webhooks + GetAllWebhookListeners(ctx context.Context, projectId *uint64) ([]*WebhookListener, error) + GetWebhookListener(ctx context.Context, id uint64, projectId *uint64) (*WebhookListener, error) + AddWebhookListener(ctx context.Context, url string, filters *EventFilter, projectId *uint64) (bool, *WebhookListener, error) + UpdateWebhookListener(ctx context.Context, listener *WebhookListener, projectId *uint64) (bool, error) + RemoveWebhookListener(ctx context.Context, id uint64, projectId *uint64) (bool, error) + RemoveAllWebhookListeners(ctx context.Context, projectId *uint64) (bool, error) + ToggleWebhookListener(ctx context.Context, id uint64, projectId *uint64) (*WebhookListener, error) + PauseAllWebhookListeners(ctx context.Context, projectId *uint64) (bool, error) + ResumeAllWebhookListeners(ctx context.Context, projectId *uint64) (bool, error) // These parameters are depracated, please don't use them: // - filters // - beforeExpiryTimestamp @@ -1186,21 +1442,6 @@ type Indexer interface { // - excludeUserAddress' GetOrderbookOrders(ctx context.Context, page *Page, orderbookContractAddress string, collectionAddress string, currencyAddresses []string, filter *OrderbookOrderFilter, orderStatuses []OrderStatus, filters []*OrderbookOrderFilter, beforeExpiryTimestamp int64, blockNumberAfter int64, createdAtAfter int64) (*Page, []*OrderbookOrder, error) GetTopOrders(ctx context.Context, orderbookContractAddress string, collectionAddress string, currencyAddresses []string, tokenIDs []string, isListing bool, priceSort SortOrder, excludeUser *string) ([]*OrderbookOrder, error) - // Fetches a single receipt with filter and then will stop the subscription - FetchTransactionReceiptWithFilter(ctx context.Context, filter *TransactionFilter, maxBlockWait *int) (*TransactionReceipt, error) - GetAllWebhookListeners(ctx context.Context, projectId *uint64) ([]*WebhookListener, error) - GetWebhookListener(ctx context.Context, id uint64, projectId *uint64) (*WebhookListener, error) - AddWebhookListener(ctx context.Context, url string, filters *EventFilter, projectId *uint64) (bool, *WebhookListener, error) - UpdateWebhookListener(ctx context.Context, listener *WebhookListener, projectId *uint64) (bool, error) - RemoveWebhookListener(ctx context.Context, id uint64, projectId *uint64) (bool, error) - ToggleWebhookListener(ctx context.Context, id uint64, projectId *uint64) (*WebhookListener, error) - PauseAllWebhookListeners(ctx context.Context, projectId *uint64) (bool, error) - ResumeAllWebhookListeners(ctx context.Context, projectId *uint64) (bool, error) - SubscribeReceipts(ctx context.Context, filter *TransactionFilter, stream SubscribeReceiptsStreamWriter) error - // TODO: pass optional block ... - // can pass too, reorg true, etc. or stay behind, etc. - SubscribeEvents(ctx context.Context, filter *EventFilter, stream SubscribeEventsStreamWriter) error - SubscribeBalanceUpdates(ctx context.Context, contractAddress string, stream SubscribeBalanceUpdatesStreamWriter) error } type SubscribeReceiptsStreamWriter interface { @@ -1257,6 +1498,38 @@ func (w *subscribeBalanceUpdatesStreamWriter) Write(balance *TokenBalance) error return w.streamWriter.write(out) } +type IndexerGateway interface { + // GetNativeTokenBalance queries indexer nodes for the latest native token + // account balance. + GetNativeTokenBalance(ctx context.Context, chainIds []uint64, accountAddress *string) ([]*GatewayNativeTokenBalance, error) + // GetTokenBalances returns a balance summary/details for a specific account + // on all indexer nodes. By default if accountAddress is left empty, it will + // use the account from the jwt session. + GetTokenBalances(ctx context.Context, chainIds []uint64, accountAddress *string, contractAddress *string, tokenID *string, includeMetadata *bool, metadataOptions *MetadataOptions, includeCollectionTokens *bool, page *Page) (*Page, []*GatewayTokenBalance, error) + // GetTokenBalancesSummary returns a summary of token balances for the given + // accounts on all indexer nodes. The collection ERC721 & ERC1155 tokens are + // represented as a single aggregated balance. + GetTokenBalancesSummary(ctx context.Context, chainIds []uint64, filter *TokenBalancesFilter, omitMetadata *bool, page *Page) (*Page, []*GatewayNativeTokenBalances, []*GatewayTokenBalance, error) + // GetTokenBalancesDetails returns a detailed balance summary for the given + // accounts on all indexer nodes. The collection ERC721 & ERC1155 tokens are + // represented as individual balances. + GetTokenBalancesDetails(ctx context.Context, chainIds []uint64, filter *TokenBalancesFilter, omitMetadata *bool, page *Page) (*Page, []*GatewayNativeTokenBalances, []*GatewayTokenBalance, error) + // GetTokenBalancesByContract returns a balances for specific accounts and + // contracts on all indexer nodes. The collection ERC721 & ERC1155 tokens are + // represented as individual balances. + GetTokenBalancesByContract(ctx context.Context, chainIds []uint64, filter *TokenBalancesByContractFilter, omitMetadata *bool, page *Page) (*Page, []*GatewayTokenBalance, error) + // GetTokenBalances returns a balance summary/details for an specific account + // on all indexer nodes. By default if accountAddress is left empty, it will + // use the account from the jwt session. + GetBalanceUpdates(ctx context.Context, chainIds []uint64, contractAddress string, lastBlockNumber uint64, lastBlockHash *string, page *Page) (*Page, []*GatewayTokenBalance, error) + // Ping the indexer + Ping(ctx context.Context) (bool, error) + // Get the current version of the indexer + Version(ctx context.Context) (*Version, error) + // Get the current runtime health status of the indexer + RuntimeStatus(ctx context.Context) (*RuntimeStatus, error) +} + type streamWriter struct { mu sync.Mutex // Guards concurrent writes to w. w http.ResponseWriter @@ -1316,6 +1589,7 @@ type IndexerClient interface { GetChainID(ctx context.Context) (uint64, error) // Queries an ethereum node for the latest and confirm ETH balances // DEPRECATED: use GetNativeTokenBalance instead + // Deprecated: GetEtherBalance(ctx context.Context, accountAddress *string) (*EtherBalance, error) // GetNativeTokenBalance queries an ethereum node for the latest native token account balance. // The native token is the token of the chain the indexer is connected to, for example, ETH on Ethereum @@ -1330,7 +1604,7 @@ type IndexerClient interface { // // If `filter.contractStatus` is not provided, it will include verified only // tokens. - GetTokenBalancesSummary(ctx context.Context, filter *TokenBalancesFilter, omitMetadata *bool, page *Page) (*Page, []*TokenBalance, error) + GetTokenBalancesSummary(ctx context.Context, filter *TokenBalancesFilter, omitMetadata *bool, page *Page) (*Page, []*NativeTokenBalance, []*TokenBalance, error) // GetTokenBalancesDetails returns a detailed balance summary for a specific // accounts. The collection ERC721 & ERC1155 tokens are represented as // individual balances. @@ -1340,7 +1614,7 @@ type IndexerClient interface { // // If `filter.contractStatus` is not provided, it will include verified only // tokens. - GetTokenBalancesDetails(ctx context.Context, filter *TokenBalancesFilter, omitMetadata *bool, page *Page) (*Page, []*TokenBalance, error) + GetTokenBalancesDetails(ctx context.Context, filter *TokenBalancesFilter, omitMetadata *bool, page *Page) (*Page, []*NativeTokenBalance, []*TokenBalance, error) // GetTokenBalancesByContract returns a balances for a specific accounts and // contracts. The collection ERC721 & ERC1155 tokens are represented as // individual balances. @@ -1367,15 +1641,57 @@ type IndexerClient interface { // For an ERC-20 specify tokenIDs as an empty array or [0], for example, { '0xdef': [] } or { '0xdef': [0] } // For ERC-1155 pass the array of tokens are strings, ie. { '0xabc': ['1', '2', '3'] } GetTokenSuppliesMap(ctx context.Context, tokenMap map[string][]string, includeMetadata *bool, metadataOptions *MetadataOptions) (map[string][]*TokenSupply, error) - // Get balance update aggregate values -- useful for syncing balance details of a contract, ie. from Skyweaver + // GetTokenIDs returns the list of each individual token id for a token collection contract. + // This is useful for ERC-721 and ERC-1155 contracts to get the list of valid tokenIDs. + GetTokenIDs(ctx context.Context, contractAddress string, page *Page) (*Page, ContractType, []string, error) + // GetTokenIDRanges returns the range of tokenIDs for a token collection contract. + // This is useful for ERC-721 and ERC-1155 contracts to get the range of valid tokenIDs. It is similar to + // GetTokenIDs, but returns the range of tokenIDs instead of the list of tokenIDs, which is more efficient + // for large collections and very easy to the caller to expand the range into a list of tokenIDs. + // + // NOTE: this method will only return up to 15,000 ranges, if there are more ranges, it will return + // a boolean to indicate there are more ranges beyond the first 15,000. Therefore, if `moreRanges` is + // false then you have all the ranges, but if true, you need to make a follow up call to fetch the next + // page of ranges. + // + // As an example, if a NFT collection of 100,000 tokens uses ids from 1,2,3,...,100_000 then this endpoint + // will return just a single range from [1,100_000], but if there are gaps between the sequence, then + // those will be broken into separate range entries. + GetTokenIDRanges(ctx context.Context, contractAddress string) (ContractType, []*TokenIDRange, bool, error) + // Get balance update aggregate values -- useful for syncing balance details of a contract, ie. from Skyweaver. + // Also consider using SubscribeBalanceUpdates or SubscribeEvents as other alternatives. GetBalanceUpdates(ctx context.Context, contractAddress string, lastBlockNumber uint64, lastBlockHash *string, page *Page) (*Page, []*TokenBalance, error) // History of mined transactions for the account which includes a list of token transfers (sent/recieved) // and sent transactions from a Sequence wallet GetTransactionHistory(ctx context.Context, filter *TransactionHistoryFilter, page *Page, includeMetadata *bool, metadataOptions *MetadataOptions) (*Page, []*Transaction, error) - // Re-sync an incorrect token balance with the correct on-chain balance - SyncBalance(ctx context.Context, accountAddress string, contractAddress string, tokenID *string) error // Fetches a single receipt and then will stop the subscription FetchTransactionReceipt(ctx context.Context, txnHash string, maxBlockWait *int) (*TransactionReceipt, error) + // Fetches a single receipt with filter and then will stop the subscription + FetchTransactionReceiptWithFilter(ctx context.Context, filter *TransactionFilter, maxBlockWait *int) (*TransactionReceipt, error) + // Listen to transaction receipts on-chain based on the filter criteria + SubscribeReceipts(ctx context.Context, filter *TransactionFilter) (SubscribeReceiptsStreamReader, error) + // SubscribeEvents listens to events on-chain based on the filter criteria + // + // TODO: some additional options can be passed such as block, reorg true, etc. + // or stay behind, etc. + SubscribeEvents(ctx context.Context, filter *EventFilter) (SubscribeEventsStreamReader, error) + // SubscribeBalanceUpdates listens to balance updates for a specific contract address + SubscribeBalanceUpdates(ctx context.Context, contractAddress string) (SubscribeBalanceUpdatesStreamReader, error) + // Re-sync an incorrect token balance with the correct on-chain balance + // NOTE: this method is almost never used, but we've marked it internal in case + // we ever want to use it again. This method was written a very long time ago in + // scenarios when the indexer had little bugs, but now its solid. + SyncBalance(ctx context.Context, accountAddress string, contractAddress string, tokenID *string) error + // Webhooks + GetAllWebhookListeners(ctx context.Context, projectId *uint64) ([]*WebhookListener, error) + GetWebhookListener(ctx context.Context, id uint64, projectId *uint64) (*WebhookListener, error) + AddWebhookListener(ctx context.Context, url string, filters *EventFilter, projectId *uint64) (bool, *WebhookListener, error) + UpdateWebhookListener(ctx context.Context, listener *WebhookListener, projectId *uint64) (bool, error) + RemoveWebhookListener(ctx context.Context, id uint64, projectId *uint64) (bool, error) + RemoveAllWebhookListeners(ctx context.Context, projectId *uint64) (bool, error) + ToggleWebhookListener(ctx context.Context, id uint64, projectId *uint64) (*WebhookListener, error) + PauseAllWebhookListeners(ctx context.Context, projectId *uint64) (bool, error) + ResumeAllWebhookListeners(ctx context.Context, projectId *uint64) (bool, error) // These parameters are depracated, please don't use them: // - filters // - beforeExpiryTimestamp @@ -1390,21 +1706,6 @@ type IndexerClient interface { // - excludeUserAddress' GetOrderbookOrders(ctx context.Context, page *Page, orderbookContractAddress string, collectionAddress string, currencyAddresses []string, filter *OrderbookOrderFilter, orderStatuses []OrderStatus, filters []*OrderbookOrderFilter, beforeExpiryTimestamp int64, blockNumberAfter int64, createdAtAfter int64) (*Page, []*OrderbookOrder, error) GetTopOrders(ctx context.Context, orderbookContractAddress string, collectionAddress string, currencyAddresses []string, tokenIDs []string, isListing bool, priceSort SortOrder, excludeUser *string) ([]*OrderbookOrder, error) - // Fetches a single receipt with filter and then will stop the subscription - FetchTransactionReceiptWithFilter(ctx context.Context, filter *TransactionFilter, maxBlockWait *int) (*TransactionReceipt, error) - GetAllWebhookListeners(ctx context.Context, projectId *uint64) ([]*WebhookListener, error) - GetWebhookListener(ctx context.Context, id uint64, projectId *uint64) (*WebhookListener, error) - AddWebhookListener(ctx context.Context, url string, filters *EventFilter, projectId *uint64) (bool, *WebhookListener, error) - UpdateWebhookListener(ctx context.Context, listener *WebhookListener, projectId *uint64) (bool, error) - RemoveWebhookListener(ctx context.Context, id uint64, projectId *uint64) (bool, error) - ToggleWebhookListener(ctx context.Context, id uint64, projectId *uint64) (*WebhookListener, error) - PauseAllWebhookListeners(ctx context.Context, projectId *uint64) (bool, error) - ResumeAllWebhookListeners(ctx context.Context, projectId *uint64) (bool, error) - SubscribeReceipts(ctx context.Context, filter *TransactionFilter) (SubscribeReceiptsStreamReader, error) - // TODO: pass optional block ... - // can pass too, reorg true, etc. or stay behind, etc. - SubscribeEvents(ctx context.Context, filter *EventFilter) (SubscribeEventsStreamReader, error) - SubscribeBalanceUpdates(ctx context.Context, contractAddress string) (SubscribeBalanceUpdatesStreamReader, error) } type SubscribeReceiptsStreamReader interface { @@ -1419,20 +1720,53 @@ type SubscribeBalanceUpdatesStreamReader interface { Read() (balance *TokenBalance, err error) } +type IndexerGatewayClient interface { + // GetNativeTokenBalance queries indexer nodes for the latest native token + // account balance. + GetNativeTokenBalance(ctx context.Context, chainIds []uint64, accountAddress *string) ([]*GatewayNativeTokenBalance, error) + // GetTokenBalances returns a balance summary/details for a specific account + // on all indexer nodes. By default if accountAddress is left empty, it will + // use the account from the jwt session. + GetTokenBalances(ctx context.Context, chainIds []uint64, accountAddress *string, contractAddress *string, tokenID *string, includeMetadata *bool, metadataOptions *MetadataOptions, includeCollectionTokens *bool, page *Page) (*Page, []*GatewayTokenBalance, error) + // GetTokenBalancesSummary returns a summary of token balances for the given + // accounts on all indexer nodes. The collection ERC721 & ERC1155 tokens are + // represented as a single aggregated balance. + GetTokenBalancesSummary(ctx context.Context, chainIds []uint64, filter *TokenBalancesFilter, omitMetadata *bool, page *Page) (*Page, []*GatewayNativeTokenBalances, []*GatewayTokenBalance, error) + // GetTokenBalancesDetails returns a detailed balance summary for the given + // accounts on all indexer nodes. The collection ERC721 & ERC1155 tokens are + // represented as individual balances. + GetTokenBalancesDetails(ctx context.Context, chainIds []uint64, filter *TokenBalancesFilter, omitMetadata *bool, page *Page) (*Page, []*GatewayNativeTokenBalances, []*GatewayTokenBalance, error) + // GetTokenBalancesByContract returns a balances for specific accounts and + // contracts on all indexer nodes. The collection ERC721 & ERC1155 tokens are + // represented as individual balances. + GetTokenBalancesByContract(ctx context.Context, chainIds []uint64, filter *TokenBalancesByContractFilter, omitMetadata *bool, page *Page) (*Page, []*GatewayTokenBalance, error) + // GetTokenBalances returns a balance summary/details for an specific account + // on all indexer nodes. By default if accountAddress is left empty, it will + // use the account from the jwt session. + GetBalanceUpdates(ctx context.Context, chainIds []uint64, contractAddress string, lastBlockNumber uint64, lastBlockHash *string, page *Page) (*Page, []*GatewayTokenBalance, error) + // Ping the indexer + Ping(ctx context.Context) (bool, error) + // Get the current version of the indexer + Version(ctx context.Context) (*Version, error) + // Get the current runtime health status of the indexer + RuntimeStatus(ctx context.Context) (*RuntimeStatus, error) +} + // // Client // const IndexerPathPrefix = "/rpc/Indexer/" +const IndexerGatewayPathPrefix = "/rpc/IndexerGateway/" type indexerClient struct { client HTTPClient - urls [30]string + urls [33]string } func NewIndexerClient(addr string, client HTTPClient) IndexerClient { prefix := urlBase(addr) + IndexerPathPrefix - urls := [30]string{ + urls := [33]string{ prefix + "Ping", prefix + "Version", prefix + "RuntimeStatus", @@ -1445,24 +1779,27 @@ func NewIndexerClient(addr string, client HTTPClient) IndexerClient { prefix + "GetTokenBalances", prefix + "GetTokenSupplies", prefix + "GetTokenSuppliesMap", + prefix + "GetTokenIDs", + prefix + "GetTokenIDRanges", prefix + "GetBalanceUpdates", prefix + "GetTransactionHistory", - prefix + "SyncBalance", prefix + "FetchTransactionReceipt", - prefix + "GetOrderbookOrders", - prefix + "GetTopOrders", prefix + "FetchTransactionReceiptWithFilter", + prefix + "SubscribeReceipts", + prefix + "SubscribeEvents", + prefix + "SubscribeBalanceUpdates", + prefix + "SyncBalance", prefix + "GetAllWebhookListeners", prefix + "GetWebhookListener", prefix + "AddWebhookListener", prefix + "UpdateWebhookListener", prefix + "RemoveWebhookListener", + prefix + "RemoveAllWebhookListeners", prefix + "ToggleWebhookListener", prefix + "PauseAllWebhookListeners", prefix + "ResumeAllWebhookListeners", - prefix + "SubscribeReceipts", - prefix + "SubscribeEvents", - prefix + "SubscribeBalanceUpdates", + prefix + "GetOrderbookOrders", + prefix + "GetTopOrders", } return &indexerClient{ client: client, @@ -1479,7 +1816,7 @@ func (c *indexerClient) Ping(ctx context.Context) (bool, error) { if resp != nil { cerr := resp.Body.Close() if err == nil && cerr != nil { - err = ErrWebrpcRequestFailed.WithCause(fmt.Errorf("failed to close response body: %w", cerr)) + err = ErrWebrpcRequestFailed.WithCausef("failed to close response body: %w", cerr) } } @@ -1495,7 +1832,7 @@ func (c *indexerClient) Version(ctx context.Context) (*Version, error) { if resp != nil { cerr := resp.Body.Close() if err == nil && cerr != nil { - err = ErrWebrpcRequestFailed.WithCause(fmt.Errorf("failed to close response body: %w", cerr)) + err = ErrWebrpcRequestFailed.WithCausef("failed to close response body: %w", cerr) } } @@ -1511,7 +1848,7 @@ func (c *indexerClient) RuntimeStatus(ctx context.Context) (*RuntimeStatus, erro if resp != nil { cerr := resp.Body.Close() if err == nil && cerr != nil { - err = ErrWebrpcRequestFailed.WithCause(fmt.Errorf("failed to close response body: %w", cerr)) + err = ErrWebrpcRequestFailed.WithCausef("failed to close response body: %w", cerr) } } @@ -1527,7 +1864,7 @@ func (c *indexerClient) GetChainID(ctx context.Context) (uint64, error) { if resp != nil { cerr := resp.Body.Close() if err == nil && cerr != nil { - err = ErrWebrpcRequestFailed.WithCause(fmt.Errorf("failed to close response body: %w", cerr)) + err = ErrWebrpcRequestFailed.WithCausef("failed to close response body: %w", cerr) } } @@ -1546,7 +1883,7 @@ func (c *indexerClient) GetEtherBalance(ctx context.Context, accountAddress *str if resp != nil { cerr := resp.Body.Close() if err == nil && cerr != nil { - err = ErrWebrpcRequestFailed.WithCause(fmt.Errorf("failed to close response body: %w", cerr)) + err = ErrWebrpcRequestFailed.WithCausef("failed to close response body: %w", cerr) } } @@ -1565,55 +1902,57 @@ func (c *indexerClient) GetNativeTokenBalance(ctx context.Context, accountAddres if resp != nil { cerr := resp.Body.Close() if err == nil && cerr != nil { - err = ErrWebrpcRequestFailed.WithCause(fmt.Errorf("failed to close response body: %w", cerr)) + err = ErrWebrpcRequestFailed.WithCausef("failed to close response body: %w", cerr) } } return out.Ret0, err } -func (c *indexerClient) GetTokenBalancesSummary(ctx context.Context, filter *TokenBalancesFilter, omitMetadata *bool, page *Page) (*Page, []*TokenBalance, error) { +func (c *indexerClient) GetTokenBalancesSummary(ctx context.Context, filter *TokenBalancesFilter, omitMetadata *bool, page *Page) (*Page, []*NativeTokenBalance, []*TokenBalance, error) { in := struct { Arg0 *TokenBalancesFilter `json:"filter"` Arg1 *bool `json:"omitMetadata"` Arg2 *Page `json:"page"` }{filter, omitMetadata, page} out := struct { - Ret0 *Page `json:"page"` - Ret1 []*TokenBalance `json:"balances"` + Ret0 *Page `json:"page"` + Ret1 []*NativeTokenBalance `json:"nativeBalances"` + Ret2 []*TokenBalance `json:"balances"` }{} resp, err := doHTTPRequest(ctx, c.client, c.urls[6], in, &out) if resp != nil { cerr := resp.Body.Close() if err == nil && cerr != nil { - err = ErrWebrpcRequestFailed.WithCause(fmt.Errorf("failed to close response body: %w", cerr)) + err = ErrWebrpcRequestFailed.WithCausef("failed to close response body: %w", cerr) } } - return out.Ret0, out.Ret1, err + return out.Ret0, out.Ret1, out.Ret2, err } -func (c *indexerClient) GetTokenBalancesDetails(ctx context.Context, filter *TokenBalancesFilter, omitMetadata *bool, page *Page) (*Page, []*TokenBalance, error) { +func (c *indexerClient) GetTokenBalancesDetails(ctx context.Context, filter *TokenBalancesFilter, omitMetadata *bool, page *Page) (*Page, []*NativeTokenBalance, []*TokenBalance, error) { in := struct { Arg0 *TokenBalancesFilter `json:"filter"` Arg1 *bool `json:"omitMetadata"` Arg2 *Page `json:"page"` }{filter, omitMetadata, page} out := struct { - Ret0 *Page `json:"page"` - Ret1 []*TokenBalance `json:"balances"` + Ret0 *Page `json:"page"` + Ret1 []*NativeTokenBalance `json:"nativeBalances"` + Ret2 []*TokenBalance `json:"balances"` }{} resp, err := doHTTPRequest(ctx, c.client, c.urls[7], in, &out) if resp != nil { cerr := resp.Body.Close() if err == nil && cerr != nil { - err = ErrWebrpcRequestFailed.WithCause(fmt.Errorf("failed to close response body: %w", cerr)) + err = ErrWebrpcRequestFailed.WithCausef("failed to close response body: %w", cerr) } } - return out.Ret0, out.Ret1, err + return out.Ret0, out.Ret1, out.Ret2, err } func (c *indexerClient) GetTokenBalancesByContract(ctx context.Context, filter *TokenBalancesByContractFilter, omitMetadata *bool, page *Page) (*Page, []*TokenBalance, error) { @@ -1631,7 +1970,7 @@ func (c *indexerClient) GetTokenBalancesByContract(ctx context.Context, filter * if resp != nil { cerr := resp.Body.Close() if err == nil && cerr != nil { - err = ErrWebrpcRequestFailed.WithCause(fmt.Errorf("failed to close response body: %w", cerr)) + err = ErrWebrpcRequestFailed.WithCausef("failed to close response body: %w", cerr) } } @@ -1657,7 +1996,7 @@ func (c *indexerClient) GetTokenBalances(ctx context.Context, accountAddress *st if resp != nil { cerr := resp.Body.Close() if err == nil && cerr != nil { - err = ErrWebrpcRequestFailed.WithCause(fmt.Errorf("failed to close response body: %w", cerr)) + err = ErrWebrpcRequestFailed.WithCausef("failed to close response body: %w", cerr) } } @@ -1681,7 +2020,7 @@ func (c *indexerClient) GetTokenSupplies(ctx context.Context, contractAddress st if resp != nil { cerr := resp.Body.Close() if err == nil && cerr != nil { - err = ErrWebrpcRequestFailed.WithCause(fmt.Errorf("failed to close response body: %w", cerr)) + err = ErrWebrpcRequestFailed.WithCausef("failed to close response body: %w", cerr) } } @@ -1702,13 +2041,56 @@ func (c *indexerClient) GetTokenSuppliesMap(ctx context.Context, tokenMap map[st if resp != nil { cerr := resp.Body.Close() if err == nil && cerr != nil { - err = ErrWebrpcRequestFailed.WithCause(fmt.Errorf("failed to close response body: %w", cerr)) + err = ErrWebrpcRequestFailed.WithCausef("failed to close response body: %w", cerr) } } return out.Ret0, err } +func (c *indexerClient) GetTokenIDs(ctx context.Context, contractAddress string, page *Page) (*Page, ContractType, []string, error) { + in := struct { + Arg0 string `json:"contractAddress"` + Arg1 *Page `json:"page"` + }{contractAddress, page} + out := struct { + Ret0 *Page `json:"page"` + Ret1 ContractType `json:"contractType"` + Ret2 []string `json:"tokenIDs"` + }{} + + resp, err := doHTTPRequest(ctx, c.client, c.urls[12], in, &out) + if resp != nil { + cerr := resp.Body.Close() + if err == nil && cerr != nil { + err = ErrWebrpcRequestFailed.WithCausef("failed to close response body: %w", cerr) + } + } + + return out.Ret0, out.Ret1, out.Ret2, err +} + +func (c *indexerClient) GetTokenIDRanges(ctx context.Context, contractAddress string) (ContractType, []*TokenIDRange, bool, error) { + in := struct { + Arg0 string `json:"contractAddress"` + }{contractAddress} + out := struct { + Ret0 ContractType `json:"contractType"` + Ret1 []*TokenIDRange `json:"tokenIDRanges"` + Ret2 bool `json:"moreRanges"` + }{} + + resp, err := doHTTPRequest(ctx, c.client, c.urls[13], in, &out) + if resp != nil { + cerr := resp.Body.Close() + if err == nil && cerr != nil { + err = ErrWebrpcRequestFailed.WithCausef("failed to close response body: %w", cerr) + } + } + + return out.Ret0, out.Ret1, out.Ret2, err +} + func (c *indexerClient) GetBalanceUpdates(ctx context.Context, contractAddress string, lastBlockNumber uint64, lastBlockHash *string, page *Page) (*Page, []*TokenBalance, error) { in := struct { Arg0 string `json:"contractAddress"` @@ -1721,11 +2103,11 @@ func (c *indexerClient) GetBalanceUpdates(ctx context.Context, contractAddress s Ret1 []*TokenBalance `json:"balances"` }{} - resp, err := doHTTPRequest(ctx, c.client, c.urls[12], in, &out) + resp, err := doHTTPRequest(ctx, c.client, c.urls[14], in, &out) if resp != nil { cerr := resp.Body.Close() if err == nil && cerr != nil { - err = ErrWebrpcRequestFailed.WithCause(fmt.Errorf("failed to close response body: %w", cerr)) + err = ErrWebrpcRequestFailed.WithCausef("failed to close response body: %w", cerr) } } @@ -1744,127 +2126,190 @@ func (c *indexerClient) GetTransactionHistory(ctx context.Context, filter *Trans Ret1 []*Transaction `json:"transactions"` }{} - resp, err := doHTTPRequest(ctx, c.client, c.urls[13], in, &out) + resp, err := doHTTPRequest(ctx, c.client, c.urls[15], in, &out) if resp != nil { cerr := resp.Body.Close() if err == nil && cerr != nil { - err = ErrWebrpcRequestFailed.WithCause(fmt.Errorf("failed to close response body: %w", cerr)) + err = ErrWebrpcRequestFailed.WithCausef("failed to close response body: %w", cerr) } } return out.Ret0, out.Ret1, err } -func (c *indexerClient) SyncBalance(ctx context.Context, accountAddress string, contractAddress string, tokenID *string) error { +func (c *indexerClient) FetchTransactionReceipt(ctx context.Context, txnHash string, maxBlockWait *int) (*TransactionReceipt, error) { in := struct { - Arg0 string `json:"accountAddress"` - Arg1 string `json:"contractAddress"` - Arg2 *string `json:"tokenID"` - }{accountAddress, contractAddress, tokenID} + Arg0 string `json:"txnHash"` + Arg1 *int `json:"maxBlockWait"` + }{txnHash, maxBlockWait} + out := struct { + Ret0 *TransactionReceipt `json:"receipt"` + }{} - resp, err := doHTTPRequest(ctx, c.client, c.urls[14], in, nil) + resp, err := doHTTPRequest(ctx, c.client, c.urls[16], in, &out) if resp != nil { cerr := resp.Body.Close() if err == nil && cerr != nil { - err = ErrWebrpcRequestFailed.WithCause(fmt.Errorf("failed to close response body: %w", cerr)) + err = ErrWebrpcRequestFailed.WithCausef("failed to close response body: %w", cerr) } } - return err + return out.Ret0, err } -func (c *indexerClient) FetchTransactionReceipt(ctx context.Context, txnHash string, maxBlockWait *int) (*TransactionReceipt, error) { +func (c *indexerClient) FetchTransactionReceiptWithFilter(ctx context.Context, filter *TransactionFilter, maxBlockWait *int) (*TransactionReceipt, error) { in := struct { - Arg0 string `json:"txnHash"` - Arg1 *int `json:"maxBlockWait"` - }{txnHash, maxBlockWait} + Arg0 *TransactionFilter `json:"filter"` + Arg1 *int `json:"maxBlockWait"` + }{filter, maxBlockWait} out := struct { Ret0 *TransactionReceipt `json:"receipt"` }{} - resp, err := doHTTPRequest(ctx, c.client, c.urls[15], in, &out) + resp, err := doHTTPRequest(ctx, c.client, c.urls[17], in, &out) if resp != nil { cerr := resp.Body.Close() if err == nil && cerr != nil { - err = ErrWebrpcRequestFailed.WithCause(fmt.Errorf("failed to close response body: %w", cerr)) + err = ErrWebrpcRequestFailed.WithCausef("failed to close response body: %w", cerr) } } return out.Ret0, err } -func (c *indexerClient) GetOrderbookOrders(ctx context.Context, page *Page, orderbookContractAddress string, collectionAddress string, currencyAddresses []string, filter *OrderbookOrderFilter, orderStatuses []OrderStatus, filters []*OrderbookOrderFilter, beforeExpiryTimestamp int64, blockNumberAfter int64, createdAtAfter int64) (*Page, []*OrderbookOrder, error) { +func (c *indexerClient) SubscribeReceipts(ctx context.Context, filter *TransactionFilter) (SubscribeReceiptsStreamReader, error) { in := struct { - Arg0 *Page `json:"page"` - Arg1 string `json:"orderbookContractAddress"` - Arg2 string `json:"collectionAddress"` - Arg3 []string `json:"currencyAddresses"` - Arg4 *OrderbookOrderFilter `json:"filter"` - Arg5 []OrderStatus `json:"orderStatuses"` - Arg6 []*OrderbookOrderFilter `json:"filters"` - Arg7 int64 `json:"beforeExpiryTimestamp"` - Arg8 int64 `json:"blockNumberAfter"` - Arg9 int64 `json:"createdAtAfter"` - }{page, orderbookContractAddress, collectionAddress, currencyAddresses, filter, orderStatuses, filters, beforeExpiryTimestamp, blockNumberAfter, createdAtAfter} + Arg0 *TransactionFilter `json:"filter"` + }{filter} + + resp, err := doHTTPRequest(ctx, c.client, c.urls[18], in, nil) + if err != nil { + if resp != nil { + resp.Body.Close() + } + return nil, err + } + + buf := bufio.NewReader(resp.Body) + return &subscribeReceiptsStreamReader{streamReader{ctx: ctx, c: resp.Body, r: buf}}, nil +} + +type subscribeReceiptsStreamReader struct { + streamReader +} + +func (r *subscribeReceiptsStreamReader) Read() (*TransactionReceipt, error) { out := struct { - Ret0 *Page `json:"page"` - Ret1 []*OrderbookOrder `json:"orders"` + Ret0 *TransactionReceipt `json:"receipt"` + WebRPCError *WebRPCError `json:"webrpcError"` }{} - resp, err := doHTTPRequest(ctx, c.client, c.urls[16], in, &out) - if resp != nil { - cerr := resp.Body.Close() - if err == nil && cerr != nil { - err = ErrWebrpcRequestFailed.WithCause(fmt.Errorf("failed to close response body: %w", cerr)) - } + err := r.streamReader.read(&out) + if err != nil { + return out.Ret0, err } - return out.Ret0, out.Ret1, err + if out.WebRPCError != nil { + return out.Ret0, out.WebRPCError + } + + return out.Ret0, nil } -func (c *indexerClient) GetTopOrders(ctx context.Context, orderbookContractAddress string, collectionAddress string, currencyAddresses []string, tokenIDs []string, isListing bool, priceSort SortOrder, excludeUser *string) ([]*OrderbookOrder, error) { +func (c *indexerClient) SubscribeEvents(ctx context.Context, filter *EventFilter) (SubscribeEventsStreamReader, error) { in := struct { - Arg0 string `json:"orderbookContractAddress"` - Arg1 string `json:"collectionAddress"` - Arg2 []string `json:"currencyAddresses"` - Arg3 []string `json:"tokenIDs"` - Arg4 bool `json:"isListing"` - Arg5 SortOrder `json:"priceSort"` - Arg6 *string `json:"excludeUser"` - }{orderbookContractAddress, collectionAddress, currencyAddresses, tokenIDs, isListing, priceSort, excludeUser} + Arg0 *EventFilter `json:"filter"` + }{filter} + + resp, err := doHTTPRequest(ctx, c.client, c.urls[19], in, nil) + if err != nil { + if resp != nil { + resp.Body.Close() + } + return nil, err + } + + buf := bufio.NewReader(resp.Body) + return &subscribeEventsStreamReader{streamReader{ctx: ctx, c: resp.Body, r: buf}}, nil +} + +type subscribeEventsStreamReader struct { + streamReader +} + +func (r *subscribeEventsStreamReader) Read() (*EventLog, error) { out := struct { - Ret0 []*OrderbookOrder `json:"orders"` + Ret0 *EventLog `json:"log"` + WebRPCError *WebRPCError `json:"webrpcError"` }{} - resp, err := doHTTPRequest(ctx, c.client, c.urls[17], in, &out) - if resp != nil { - cerr := resp.Body.Close() - if err == nil && cerr != nil { - err = ErrWebrpcRequestFailed.WithCause(fmt.Errorf("failed to close response body: %w", cerr)) - } + err := r.streamReader.read(&out) + if err != nil { + return out.Ret0, err } - return out.Ret0, err + if out.WebRPCError != nil { + return out.Ret0, out.WebRPCError + } + + return out.Ret0, nil } -func (c *indexerClient) FetchTransactionReceiptWithFilter(ctx context.Context, filter *TransactionFilter, maxBlockWait *int) (*TransactionReceipt, error) { +func (c *indexerClient) SubscribeBalanceUpdates(ctx context.Context, contractAddress string) (SubscribeBalanceUpdatesStreamReader, error) { in := struct { - Arg0 *TransactionFilter `json:"filter"` - Arg1 *int `json:"maxBlockWait"` - }{filter, maxBlockWait} + Arg0 string `json:"contractAddress"` + }{contractAddress} + + resp, err := doHTTPRequest(ctx, c.client, c.urls[20], in, nil) + if err != nil { + if resp != nil { + resp.Body.Close() + } + return nil, err + } + + buf := bufio.NewReader(resp.Body) + return &subscribeBalanceUpdatesStreamReader{streamReader{ctx: ctx, c: resp.Body, r: buf}}, nil +} + +type subscribeBalanceUpdatesStreamReader struct { + streamReader +} + +func (r *subscribeBalanceUpdatesStreamReader) Read() (*TokenBalance, error) { out := struct { - Ret0 *TransactionReceipt `json:"receipt"` + Ret0 *TokenBalance `json:"balance"` + WebRPCError *WebRPCError `json:"webrpcError"` }{} - resp, err := doHTTPRequest(ctx, c.client, c.urls[18], in, &out) + err := r.streamReader.read(&out) + if err != nil { + return out.Ret0, err + } + + if out.WebRPCError != nil { + return out.Ret0, out.WebRPCError + } + + return out.Ret0, nil +} + +func (c *indexerClient) SyncBalance(ctx context.Context, accountAddress string, contractAddress string, tokenID *string) error { + in := struct { + Arg0 string `json:"accountAddress"` + Arg1 string `json:"contractAddress"` + Arg2 *string `json:"tokenID"` + }{accountAddress, contractAddress, tokenID} + + resp, err := doHTTPRequest(ctx, c.client, c.urls[21], in, nil) if resp != nil { cerr := resp.Body.Close() if err == nil && cerr != nil { - err = ErrWebrpcRequestFailed.WithCause(fmt.Errorf("failed to close response body: %w", cerr)) + err = ErrWebrpcRequestFailed.WithCausef("failed to close response body: %w", cerr) } } - return out.Ret0, err + return err } func (c *indexerClient) GetAllWebhookListeners(ctx context.Context, projectId *uint64) ([]*WebhookListener, error) { @@ -1875,11 +2320,11 @@ func (c *indexerClient) GetAllWebhookListeners(ctx context.Context, projectId *u Ret0 []*WebhookListener `json:"listeners"` }{} - resp, err := doHTTPRequest(ctx, c.client, c.urls[19], in, &out) + resp, err := doHTTPRequest(ctx, c.client, c.urls[22], in, &out) if resp != nil { cerr := resp.Body.Close() if err == nil && cerr != nil { - err = ErrWebrpcRequestFailed.WithCause(fmt.Errorf("failed to close response body: %w", cerr)) + err = ErrWebrpcRequestFailed.WithCausef("failed to close response body: %w", cerr) } } @@ -1895,11 +2340,11 @@ func (c *indexerClient) GetWebhookListener(ctx context.Context, id uint64, proje Ret0 *WebhookListener `json:"listener"` }{} - resp, err := doHTTPRequest(ctx, c.client, c.urls[20], in, &out) + resp, err := doHTTPRequest(ctx, c.client, c.urls[23], in, &out) if resp != nil { cerr := resp.Body.Close() if err == nil && cerr != nil { - err = ErrWebrpcRequestFailed.WithCause(fmt.Errorf("failed to close response body: %w", cerr)) + err = ErrWebrpcRequestFailed.WithCausef("failed to close response body: %w", cerr) } } @@ -1917,11 +2362,11 @@ func (c *indexerClient) AddWebhookListener(ctx context.Context, url string, filt Ret1 *WebhookListener `json:"listener"` }{} - resp, err := doHTTPRequest(ctx, c.client, c.urls[21], in, &out) + resp, err := doHTTPRequest(ctx, c.client, c.urls[24], in, &out) if resp != nil { cerr := resp.Body.Close() if err == nil && cerr != nil { - err = ErrWebrpcRequestFailed.WithCause(fmt.Errorf("failed to close response body: %w", cerr)) + err = ErrWebrpcRequestFailed.WithCausef("failed to close response body: %w", cerr) } } @@ -1937,11 +2382,11 @@ func (c *indexerClient) UpdateWebhookListener(ctx context.Context, listener *Web Ret0 bool `json:"status"` }{} - resp, err := doHTTPRequest(ctx, c.client, c.urls[22], in, &out) + resp, err := doHTTPRequest(ctx, c.client, c.urls[25], in, &out) if resp != nil { cerr := resp.Body.Close() if err == nil && cerr != nil { - err = ErrWebrpcRequestFailed.WithCause(fmt.Errorf("failed to close response body: %w", cerr)) + err = ErrWebrpcRequestFailed.WithCausef("failed to close response body: %w", cerr) } } @@ -1957,11 +2402,30 @@ func (c *indexerClient) RemoveWebhookListener(ctx context.Context, id uint64, pr Ret0 bool `json:"status"` }{} - resp, err := doHTTPRequest(ctx, c.client, c.urls[23], in, &out) + resp, err := doHTTPRequest(ctx, c.client, c.urls[26], in, &out) if resp != nil { cerr := resp.Body.Close() if err == nil && cerr != nil { - err = ErrWebrpcRequestFailed.WithCause(fmt.Errorf("failed to close response body: %w", cerr)) + err = ErrWebrpcRequestFailed.WithCausef("failed to close response body: %w", cerr) + } + } + + return out.Ret0, err +} + +func (c *indexerClient) RemoveAllWebhookListeners(ctx context.Context, projectId *uint64) (bool, error) { + in := struct { + Arg0 *uint64 `json:"projectId"` + }{projectId} + out := struct { + Ret0 bool `json:"status"` + }{} + + resp, err := doHTTPRequest(ctx, c.client, c.urls[27], in, &out) + if resp != nil { + cerr := resp.Body.Close() + if err == nil && cerr != nil { + err = ErrWebrpcRequestFailed.WithCausef("failed to close response body: %w", cerr) } } @@ -1977,11 +2441,11 @@ func (c *indexerClient) ToggleWebhookListener(ctx context.Context, id uint64, pr Ret0 *WebhookListener `json:"webhookListener"` }{} - resp, err := doHTTPRequest(ctx, c.client, c.urls[24], in, &out) + resp, err := doHTTPRequest(ctx, c.client, c.urls[28], in, &out) if resp != nil { cerr := resp.Body.Close() if err == nil && cerr != nil { - err = ErrWebrpcRequestFailed.WithCause(fmt.Errorf("failed to close response body: %w", cerr)) + err = ErrWebrpcRequestFailed.WithCausef("failed to close response body: %w", cerr) } } @@ -1996,11 +2460,11 @@ func (c *indexerClient) PauseAllWebhookListeners(ctx context.Context, projectId Ret0 bool `json:"status"` }{} - resp, err := doHTTPRequest(ctx, c.client, c.urls[25], in, &out) + resp, err := doHTTPRequest(ctx, c.client, c.urls[29], in, &out) if resp != nil { cerr := resp.Body.Close() if err == nil && cerr != nil { - err = ErrWebrpcRequestFailed.WithCause(fmt.Errorf("failed to close response body: %w", cerr)) + err = ErrWebrpcRequestFailed.WithCausef("failed to close response body: %w", cerr) } } @@ -2015,132 +2479,283 @@ func (c *indexerClient) ResumeAllWebhookListeners(ctx context.Context, projectId Ret0 bool `json:"status"` }{} - resp, err := doHTTPRequest(ctx, c.client, c.urls[26], in, &out) + resp, err := doHTTPRequest(ctx, c.client, c.urls[30], in, &out) if resp != nil { cerr := resp.Body.Close() if err == nil && cerr != nil { - err = ErrWebrpcRequestFailed.WithCause(fmt.Errorf("failed to close response body: %w", cerr)) + err = ErrWebrpcRequestFailed.WithCausef("failed to close response body: %w", cerr) } } return out.Ret0, err } -func (c *indexerClient) SubscribeReceipts(ctx context.Context, filter *TransactionFilter) (SubscribeReceiptsStreamReader, error) { +func (c *indexerClient) GetOrderbookOrders(ctx context.Context, page *Page, orderbookContractAddress string, collectionAddress string, currencyAddresses []string, filter *OrderbookOrderFilter, orderStatuses []OrderStatus, filters []*OrderbookOrderFilter, beforeExpiryTimestamp int64, blockNumberAfter int64, createdAtAfter int64) (*Page, []*OrderbookOrder, error) { in := struct { - Arg0 *TransactionFilter `json:"filter"` - }{filter} + Arg0 *Page `json:"page"` + Arg1 string `json:"orderbookContractAddress"` + Arg2 string `json:"collectionAddress"` + Arg3 []string `json:"currencyAddresses"` + Arg4 *OrderbookOrderFilter `json:"filter"` + Arg5 []OrderStatus `json:"orderStatuses"` + Arg6 []*OrderbookOrderFilter `json:"filters"` + Arg7 int64 `json:"beforeExpiryTimestamp"` + Arg8 int64 `json:"blockNumberAfter"` + Arg9 int64 `json:"createdAtAfter"` + }{page, orderbookContractAddress, collectionAddress, currencyAddresses, filter, orderStatuses, filters, beforeExpiryTimestamp, blockNumberAfter, createdAtAfter} + out := struct { + Ret0 *Page `json:"page"` + Ret1 []*OrderbookOrder `json:"orders"` + }{} - resp, err := doHTTPRequest(ctx, c.client, c.urls[27], in, nil) - if err != nil { - if resp != nil { - resp.Body.Close() + resp, err := doHTTPRequest(ctx, c.client, c.urls[31], in, &out) + if resp != nil { + cerr := resp.Body.Close() + if err == nil && cerr != nil { + err = ErrWebrpcRequestFailed.WithCausef("failed to close response body: %w", cerr) } - return nil, err } - buf := bufio.NewReader(resp.Body) - return &subscribeReceiptsStreamReader{streamReader{ctx: ctx, c: resp.Body, r: buf}}, nil -} - -type subscribeReceiptsStreamReader struct { - streamReader + return out.Ret0, out.Ret1, err } -func (r *subscribeReceiptsStreamReader) Read() (*TransactionReceipt, error) { +func (c *indexerClient) GetTopOrders(ctx context.Context, orderbookContractAddress string, collectionAddress string, currencyAddresses []string, tokenIDs []string, isListing bool, priceSort SortOrder, excludeUser *string) ([]*OrderbookOrder, error) { + in := struct { + Arg0 string `json:"orderbookContractAddress"` + Arg1 string `json:"collectionAddress"` + Arg2 []string `json:"currencyAddresses"` + Arg3 []string `json:"tokenIDs"` + Arg4 bool `json:"isListing"` + Arg5 SortOrder `json:"priceSort"` + Arg6 *string `json:"excludeUser"` + }{orderbookContractAddress, collectionAddress, currencyAddresses, tokenIDs, isListing, priceSort, excludeUser} out := struct { - Ret0 *TransactionReceipt `json:"receipt"` - WebRPCError *WebRPCError `json:"webrpcError"` + Ret0 []*OrderbookOrder `json:"orders"` }{} - err := r.streamReader.read(&out) - if err != nil { - return out.Ret0, err + resp, err := doHTTPRequest(ctx, c.client, c.urls[32], in, &out) + if resp != nil { + cerr := resp.Body.Close() + if err == nil && cerr != nil { + err = ErrWebrpcRequestFailed.WithCausef("failed to close response body: %w", cerr) + } } - if out.WebRPCError != nil { - return out.Ret0, out.WebRPCError + return out.Ret0, err +} + +type indexerGatewayClient struct { + client HTTPClient + urls [9]string +} + +func NewIndexerGatewayClient(addr string, client HTTPClient) IndexerGatewayClient { + prefix := urlBase(addr) + IndexerGatewayPathPrefix + urls := [9]string{ + prefix + "GetNativeTokenBalance", + prefix + "GetTokenBalances", + prefix + "GetTokenBalancesSummary", + prefix + "GetTokenBalancesDetails", + prefix + "GetTokenBalancesByContract", + prefix + "GetBalanceUpdates", + prefix + "Ping", + prefix + "Version", + prefix + "RuntimeStatus", + } + return &indexerGatewayClient{ + client: client, + urls: urls, } +} - return out.Ret0, nil +func (c *indexerGatewayClient) GetNativeTokenBalance(ctx context.Context, chainIds []uint64, accountAddress *string) ([]*GatewayNativeTokenBalance, error) { + in := struct { + Arg0 []uint64 `json:"chainIds"` + Arg1 *string `json:"accountAddress"` + }{chainIds, accountAddress} + out := struct { + Ret0 []*GatewayNativeTokenBalance `json:"balances"` + }{} + + resp, err := doHTTPRequest(ctx, c.client, c.urls[0], in, &out) + if resp != nil { + cerr := resp.Body.Close() + if err == nil && cerr != nil { + err = ErrWebrpcRequestFailed.WithCausef("failed to close response body: %w", cerr) + } + } + + return out.Ret0, err } -func (c *indexerClient) SubscribeEvents(ctx context.Context, filter *EventFilter) (SubscribeEventsStreamReader, error) { +func (c *indexerGatewayClient) GetTokenBalances(ctx context.Context, chainIds []uint64, accountAddress *string, contractAddress *string, tokenID *string, includeMetadata *bool, metadataOptions *MetadataOptions, includeCollectionTokens *bool, page *Page) (*Page, []*GatewayTokenBalance, error) { in := struct { - Arg0 *EventFilter `json:"filter"` - }{filter} + Arg0 []uint64 `json:"chainIds"` + Arg1 *string `json:"accountAddress"` + Arg2 *string `json:"contractAddress"` + Arg3 *string `json:"tokenID"` + Arg4 *bool `json:"includeMetadata"` + Arg5 *MetadataOptions `json:"metadataOptions"` + Arg6 *bool `json:"includeCollectionTokens"` + Arg7 *Page `json:"page"` + }{chainIds, accountAddress, contractAddress, tokenID, includeMetadata, metadataOptions, includeCollectionTokens, page} + out := struct { + Ret0 *Page `json:"page"` + Ret1 []*GatewayTokenBalance `json:"balances"` + }{} - resp, err := doHTTPRequest(ctx, c.client, c.urls[28], in, nil) - if err != nil { - if resp != nil { - resp.Body.Close() + resp, err := doHTTPRequest(ctx, c.client, c.urls[1], in, &out) + if resp != nil { + cerr := resp.Body.Close() + if err == nil && cerr != nil { + err = ErrWebrpcRequestFailed.WithCausef("failed to close response body: %w", cerr) } - return nil, err } - buf := bufio.NewReader(resp.Body) - return &subscribeEventsStreamReader{streamReader{ctx: ctx, c: resp.Body, r: buf}}, nil + return out.Ret0, out.Ret1, err } -type subscribeEventsStreamReader struct { - streamReader +func (c *indexerGatewayClient) GetTokenBalancesSummary(ctx context.Context, chainIds []uint64, filter *TokenBalancesFilter, omitMetadata *bool, page *Page) (*Page, []*GatewayNativeTokenBalances, []*GatewayTokenBalance, error) { + in := struct { + Arg0 []uint64 `json:"chainIds"` + Arg1 *TokenBalancesFilter `json:"filter"` + Arg2 *bool `json:"omitMetadata"` + Arg3 *Page `json:"page"` + }{chainIds, filter, omitMetadata, page} + out := struct { + Ret0 *Page `json:"page"` + Ret1 []*GatewayNativeTokenBalances `json:"nativeBalances"` + Ret2 []*GatewayTokenBalance `json:"balances"` + }{} + + resp, err := doHTTPRequest(ctx, c.client, c.urls[2], in, &out) + if resp != nil { + cerr := resp.Body.Close() + if err == nil && cerr != nil { + err = ErrWebrpcRequestFailed.WithCausef("failed to close response body: %w", cerr) + } + } + + return out.Ret0, out.Ret1, out.Ret2, err } -func (r *subscribeEventsStreamReader) Read() (*EventLog, error) { +func (c *indexerGatewayClient) GetTokenBalancesDetails(ctx context.Context, chainIds []uint64, filter *TokenBalancesFilter, omitMetadata *bool, page *Page) (*Page, []*GatewayNativeTokenBalances, []*GatewayTokenBalance, error) { + in := struct { + Arg0 []uint64 `json:"chainIds"` + Arg1 *TokenBalancesFilter `json:"filter"` + Arg2 *bool `json:"omitMetadata"` + Arg3 *Page `json:"page"` + }{chainIds, filter, omitMetadata, page} out := struct { - Ret0 *EventLog `json:"log"` - WebRPCError *WebRPCError `json:"webrpcError"` + Ret0 *Page `json:"page"` + Ret1 []*GatewayNativeTokenBalances `json:"nativeBalances"` + Ret2 []*GatewayTokenBalance `json:"balances"` }{} - err := r.streamReader.read(&out) - if err != nil { - return out.Ret0, err + resp, err := doHTTPRequest(ctx, c.client, c.urls[3], in, &out) + if resp != nil { + cerr := resp.Body.Close() + if err == nil && cerr != nil { + err = ErrWebrpcRequestFailed.WithCausef("failed to close response body: %w", cerr) + } } - if out.WebRPCError != nil { - return out.Ret0, out.WebRPCError + return out.Ret0, out.Ret1, out.Ret2, err +} + +func (c *indexerGatewayClient) GetTokenBalancesByContract(ctx context.Context, chainIds []uint64, filter *TokenBalancesByContractFilter, omitMetadata *bool, page *Page) (*Page, []*GatewayTokenBalance, error) { + in := struct { + Arg0 []uint64 `json:"chainIds"` + Arg1 *TokenBalancesByContractFilter `json:"filter"` + Arg2 *bool `json:"omitMetadata"` + Arg3 *Page `json:"page"` + }{chainIds, filter, omitMetadata, page} + out := struct { + Ret0 *Page `json:"page"` + Ret1 []*GatewayTokenBalance `json:"balances"` + }{} + + resp, err := doHTTPRequest(ctx, c.client, c.urls[4], in, &out) + if resp != nil { + cerr := resp.Body.Close() + if err == nil && cerr != nil { + err = ErrWebrpcRequestFailed.WithCausef("failed to close response body: %w", cerr) + } } - return out.Ret0, nil + return out.Ret0, out.Ret1, err } -func (c *indexerClient) SubscribeBalanceUpdates(ctx context.Context, contractAddress string) (SubscribeBalanceUpdatesStreamReader, error) { +func (c *indexerGatewayClient) GetBalanceUpdates(ctx context.Context, chainIds []uint64, contractAddress string, lastBlockNumber uint64, lastBlockHash *string, page *Page) (*Page, []*GatewayTokenBalance, error) { in := struct { - Arg0 string `json:"contractAddress"` - }{contractAddress} + Arg0 []uint64 `json:"chainIds"` + Arg1 string `json:"contractAddress"` + Arg2 uint64 `json:"lastBlockNumber"` + Arg3 *string `json:"lastBlockHash"` + Arg4 *Page `json:"page"` + }{chainIds, contractAddress, lastBlockNumber, lastBlockHash, page} + out := struct { + Ret0 *Page `json:"page"` + Ret1 []*GatewayTokenBalance `json:"balances"` + }{} - resp, err := doHTTPRequest(ctx, c.client, c.urls[29], in, nil) - if err != nil { - if resp != nil { - resp.Body.Close() + resp, err := doHTTPRequest(ctx, c.client, c.urls[5], in, &out) + if resp != nil { + cerr := resp.Body.Close() + if err == nil && cerr != nil { + err = ErrWebrpcRequestFailed.WithCausef("failed to close response body: %w", cerr) } - return nil, err } - buf := bufio.NewReader(resp.Body) - return &subscribeBalanceUpdatesStreamReader{streamReader{ctx: ctx, c: resp.Body, r: buf}}, nil + return out.Ret0, out.Ret1, err } -type subscribeBalanceUpdatesStreamReader struct { - streamReader +func (c *indexerGatewayClient) Ping(ctx context.Context) (bool, error) { + out := struct { + Ret0 bool `json:"status"` + }{} + + resp, err := doHTTPRequest(ctx, c.client, c.urls[6], nil, &out) + if resp != nil { + cerr := resp.Body.Close() + if err == nil && cerr != nil { + err = ErrWebrpcRequestFailed.WithCausef("failed to close response body: %w", cerr) + } + } + + return out.Ret0, err } -func (r *subscribeBalanceUpdatesStreamReader) Read() (*TokenBalance, error) { +func (c *indexerGatewayClient) Version(ctx context.Context) (*Version, error) { out := struct { - Ret0 *TokenBalance `json:"balance"` - WebRPCError *WebRPCError `json:"webrpcError"` + Ret0 *Version `json:"version"` }{} - err := r.streamReader.read(&out) - if err != nil { - return out.Ret0, err + resp, err := doHTTPRequest(ctx, c.client, c.urls[7], nil, &out) + if resp != nil { + cerr := resp.Body.Close() + if err == nil && cerr != nil { + err = ErrWebrpcRequestFailed.WithCausef("failed to close response body: %w", cerr) + } } - if out.WebRPCError != nil { - return out.Ret0, out.WebRPCError + return out.Ret0, err +} + +func (c *indexerGatewayClient) RuntimeStatus(ctx context.Context) (*RuntimeStatus, error) { + out := struct { + Ret0 *RuntimeStatus `json:"status"` + }{} + + resp, err := doHTTPRequest(ctx, c.client, c.urls[8], nil, &out) + if resp != nil { + cerr := resp.Body.Close() + if err == nil && cerr != nil { + err = ErrWebrpcRequestFailed.WithCausef("failed to close response body: %w", cerr) + } } - return out.Ret0, nil + return out.Ret0, err } type streamReader struct { @@ -2186,7 +2801,7 @@ func (r *streamReader) handleReadError(err error) error { if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) { return ErrWebrpcClientDisconnected.WithCause(err) } - return ErrWebrpcBadResponse.WithCause(fmt.Errorf("reading stream: %w", err)) + return ErrWebrpcBadResponse.WithCausef("reading stream: %w", err) } // HTTPClient is the interface used by generated clients to send HTTP requests. @@ -2219,6 +2834,7 @@ func newRequest(ctx context.Context, url string, reqBody io.Reader, contentType } req.Header.Set("Accept", contentType) req.Header.Set("Content-Type", contentType) + req.Header.Set(WebrpcHeader, WebrpcHeaderValue) if headers, ok := HTTPRequestHeaders(ctx); ok { for k := range headers { for _, v := range headers[k] { @@ -2233,15 +2849,15 @@ func newRequest(ctx context.Context, url string, reqBody io.Reader, contentType func doHTTPRequest(ctx context.Context, client HTTPClient, url string, in, out interface{}) (*http.Response, error) { reqBody, err := json.Marshal(in) if err != nil { - return nil, ErrWebrpcRequestFailed.WithCause(fmt.Errorf("failed to marshal JSON body: %w", err)) + return nil, ErrWebrpcRequestFailed.WithCausef("failed to marshal JSON body: %w", err) } if err = ctx.Err(); err != nil { - return nil, ErrWebrpcRequestFailed.WithCause(fmt.Errorf("aborted because context was done: %w", err)) + return nil, ErrWebrpcRequestFailed.WithCausef("aborted because context was done: %w", err) } req, err := newRequest(ctx, url, bytes.NewBuffer(reqBody), "application/json") if err != nil { - return nil, ErrWebrpcRequestFailed.WithCause(fmt.Errorf("could not build request: %w", err)) + return nil, ErrWebrpcRequestFailed.WithCausef("could not build request: %w", err) } resp, err := client.Do(req) @@ -2252,12 +2868,12 @@ func doHTTPRequest(ctx context.Context, client HTTPClient, url string, in, out i if resp.StatusCode != 200 { respBody, err := io.ReadAll(resp.Body) if err != nil { - return nil, ErrWebrpcBadResponse.WithCause(fmt.Errorf("failed to read server error response body: %w", err)) + return nil, ErrWebrpcBadResponse.WithCausef("failed to read server error response body: %w", err) } var rpcErr WebRPCError if err := json.Unmarshal(respBody, &rpcErr); err != nil { - return nil, ErrWebrpcBadResponse.WithCause(fmt.Errorf("failed to unmarshal server error: %w", err)) + return nil, ErrWebrpcBadResponse.WithCausef("failed to unmarshal server error: %w", err) } if rpcErr.Cause != "" { rpcErr.cause = errors.New(rpcErr.Cause) @@ -2268,12 +2884,12 @@ func doHTTPRequest(ctx context.Context, client HTTPClient, url string, in, out i if out != nil { respBody, err := io.ReadAll(resp.Body) if err != nil { - return nil, ErrWebrpcBadResponse.WithCause(fmt.Errorf("failed to read response body: %w", err)) + return nil, ErrWebrpcBadResponse.WithCausef("failed to read response body: %w", err) } err = json.Unmarshal(respBody, &out) if err != nil { - return nil, ErrWebrpcBadResponse.WithCause(fmt.Errorf("failed to unmarshal JSON response body: %w", err)) + return nil, ErrWebrpcBadResponse.WithCausef("failed to unmarshal JSON response body: %w", err) } } @@ -2441,12 +3057,24 @@ var ( ErrMethodNotFound = WebRPCError{Code: 1003, Name: "MethodNotFound", Message: "Method not found", HTTPStatus: 404} ErrRequestConflict = WebRPCError{Code: 1004, Name: "RequestConflict", Message: "Conflict with target resource", HTTPStatus: 409} ErrAborted = WebRPCError{Code: 1005, Name: "Aborted", Message: "Request aborted", HTTPStatus: 400} - ErrTimeout = WebRPCError{Code: 2000, Name: "Timeout", Message: "Request timed out", HTTPStatus: 408} + ErrGeoblocked = WebRPCError{Code: 1006, Name: "Geoblocked", Message: "Geoblocked region", HTTPStatus: 451} + ErrRateLimited = WebRPCError{Code: 1007, Name: "RateLimited", Message: "Rate-limited. Please slow down.", HTTPStatus: 429} + ErrProjectNotFound = WebRPCError{Code: 1100, Name: "ProjectNotFound", Message: "Project not found", HTTPStatus: 401} + ErrAccessKeyNotFound = WebRPCError{Code: 1101, Name: "AccessKeyNotFound", Message: "Access key not found", HTTPStatus: 401} + ErrAccessKeyMismatch = WebRPCError{Code: 1102, Name: "AccessKeyMismatch", Message: "Access key mismatch", HTTPStatus: 409} + ErrInvalidOrigin = WebRPCError{Code: 1103, Name: "InvalidOrigin", Message: "Invalid origin for Access Key", HTTPStatus: 403} + ErrInvalidService = WebRPCError{Code: 1104, Name: "InvalidService", Message: "Service not enabled for Access key", HTTPStatus: 403} + ErrUnauthorizedUser = WebRPCError{Code: 1105, Name: "UnauthorizedUser", Message: "Unauthorized user", HTTPStatus: 403} + ErrQuotaExceeded = WebRPCError{Code: 1200, Name: "QuotaExceeded", Message: "Quota exceeded", HTTPStatus: 429} + ErrRateLimit = WebRPCError{Code: 1201, Name: "RateLimit", Message: "Rate limit exceeded", HTTPStatus: 429} + ErrNoDefaultKey = WebRPCError{Code: 1300, Name: "NoDefaultKey", Message: "No default access key found", HTTPStatus: 403} + ErrMaxAccessKeys = WebRPCError{Code: 1301, Name: "MaxAccessKeys", Message: "Access keys limit reached", HTTPStatus: 403} + ErrAtLeastOneKey = WebRPCError{Code: 1302, Name: "AtLeastOneKey", Message: "You need at least one Access Key", HTTPStatus: 403} + ErrTimeout = WebRPCError{Code: 1900, Name: "Timeout", Message: "Request timed out", HTTPStatus: 408} ErrInvalidArgument = WebRPCError{Code: 2001, Name: "InvalidArgument", Message: "Invalid argument", HTTPStatus: 400} ErrUnavailable = WebRPCError{Code: 2002, Name: "Unavailable", Message: "Unavailable resource", HTTPStatus: 400} ErrQueryFailed = WebRPCError{Code: 2003, Name: "QueryFailed", Message: "Query failed", HTTPStatus: 400} ErrResourceExhausted = WebRPCError{Code: 2004, Name: "ResourceExhausted", Message: "Resource exhausted", HTTPStatus: 400} ErrNotFound = WebRPCError{Code: 3000, Name: "NotFound", Message: "Resource not found", HTTPStatus: 400} - ErrProjectNotFound = WebRPCError{Code: 3002, Name: "ProjectNotFound", Message: "Project not found", HTTPStatus: 400} ErrMetadataCallFailed = WebRPCError{Code: 3003, Name: "MetadataCallFailed", Message: "Metadata service call failed", HTTPStatus: 400} )