diff --git a/trails-api/api.gen.go b/trails-api/api.gen.go new file mode 100644 index 00000000..59538c98 --- /dev/null +++ b/trails-api/api.gen.go @@ -0,0 +1,1124 @@ +// trails-api v0.4.0 28a9163b0bcbdf5cecfc8f2b668550cc1ccadaf3 +// -- +// Code generated by webrpc-gen@v0.25.3 with golang generator. DO NOT EDIT. +// +// webrpc-gen -schema=api.ridl -target=golang -pkg=api -client -out=./clients/api.gen.go +package api + +import ( + "bytes" + "context" + "encoding/json" + "errors" + "fmt" + "io" + "net/http" + "net/url" + "strings" + "time" + + "github.com/0xsequence/go-sequence/lib/prototyp" +) + +const WebrpcHeader = "Webrpc" + +const WebrpcHeaderValue = "webrpc@v0.25.3;gen-golang@v0.18.4;trails-api@v0.4.0" + +// WebRPC description and code-gen version +func WebRPCVersion() string { + return "v1" +} + +// Schema version of your RIDL schema +func WebRPCSchemaVersion() string { + return "v0.4.0" +} + +// Schema hash generated from your RIDL schema +func WebRPCSchemaHash() string { + return "28a9163b0bcbdf5cecfc8f2b668550cc1ccadaf3" +} + +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 TradeType string + +const ( + TradeType_EXACT_INPUT TradeType = "EXACT_INPUT" + TradeType_EXACT_OUTPUT TradeType = "EXACT_OUTPUT" +) + +func (x TradeType) MarshalText() ([]byte, error) { + return []byte(x), nil +} + +func (x *TradeType) UnmarshalText(b []byte) error { + *x = TradeType(string(b)) + return nil +} + +func (x *TradeType) Is(values ...TradeType) bool { + if x == nil { + return false + } + for _, v := range values { + if *x == v { + return true + } + } + return false +} + +type SortOrder uint32 + +const ( + SortOrder_DESC SortOrder = 0 + SortOrder_ASC SortOrder = 1 +) + +var SortOrder_name = map[uint32]string{ + 0: "DESC", + 1: "ASC", +} + +var SortOrder_value = map[string]uint32{ + "DESC": 0, + "ASC": 1, +} + +func (x SortOrder) String() string { + return SortOrder_name[uint32(x)] +} + +func (x SortOrder) MarshalText() ([]byte, error) { + return []byte(SortOrder_name[uint32(x)]), nil +} + +func (x *SortOrder) UnmarshalText(b []byte) error { + *x = SortOrder(SortOrder_value[string(b)]) + return nil +} + +func (x *SortOrder) Is(values ...SortOrder) bool { + if x == nil { + return false + } + for _, v := range values { + if *x == v { + return true + } + } + return false +} + +type Version struct { + WebrpcVersion string `json:"webrpcVersion"` + SchemaVersion string `json:"schemaVersion"` + SchemaHash string `json:"schemaHash"` + AppVersion string `json:"appVersion"` +} + +type RuntimeStatus struct { + // overall status, true/false + HealthOK bool `json:"healthOK"` + StartTime time.Time `json:"startTime"` + Uptime uint64 `json:"uptime"` + Ver string `json:"ver"` + Branch string `json:"branch"` + CommitHash string `json:"commitHash"` + Checks *RuntimeChecks `json:"checks"` + NumTxnsRelayed map[string]*NumTxnsRelayed `json:"numTxnsRelayed"` +} + +type NumTxnsRelayed struct { + ChainID uint64 `json:"chainID"` + Prev uint64 `json:"prev"` + Current uint64 `json:"current"` + Period uint64 `json:"period"` +} + +type RuntimeChecks struct { +} + +// From: `0xsequence/relayer`: https://github.com/0xsequence/relayer/blob/2c695b820970c68c57bfe71810f469412cdec675/proto/relayer.ridl#L502 +// TODO: rename this to MetaTxnRaw (eventually), we can leave it for now to not break compat +// or name it, MetaTxnArgs ..? +type MetaTxn struct { + ID string `json:"id"` + ChainId prototyp.BigInt `json:"chainId"` + WalletAddress string `json:"walletAddress" db:"wallet_address"` + // TODO (later): rename this to `to: string` + Contract string `json:"contract" db:"to_address"` + // TODO: rename to 'execdata' + Input string `json:"input" db:"tx_data"` +} + +type Call struct { + To prototyp.Hash `json:"to"` + Value prototyp.BigInt `json:"value"` + Data prototyp.Hash `json:"data"` + GasLimit prototyp.BigInt `json:"gasLimit"` + DelegateCall *bool `json:"delegateCall"` + OnlyFallback *bool `json:"onlyFallback"` + BehaviorOnError *uint8 `json:"behaviorOnError"` +} + +type IntentCallsPayload struct { + ChainId prototyp.BigInt `json:"chainId"` + Space prototyp.BigInt `json:"space"` + Nonce prototyp.BigInt `json:"nonce"` + Calls []*Call `json:"calls"` +} + +// IntentConfig +type IntentConfig struct { + ID uint64 `json:"id" db:"id,omitempty"` + ConfigHash prototyp.Hash `json:"configHash" db:"config_hash"` + OriginIntentAddress prototyp.Hash `json:"originIntentAddress" db:"origin_intent_address"` + DestinationIntentAddress prototyp.Hash `json:"destinationIntentAddress" db:"destination_intent_address"` + MainSigner prototyp.Hash `json:"mainSigner" db:"main_signer"` + Calls prototyp.JSONString `json:"calls" db:"calls"` + Preconditions prototyp.JSONString `json:"preconditions" db:"preconditions"` + ExecutionStatus *string `json:"executionStatus" db:"execution_status,omitempty"` + MetaTxnID *string `json:"metaTxnId" db:"meta_txn_id,omitempty"` + TxnHash *string `json:"txnHash" db:"txn_hash,omitempty"` + UpdatedAt *time.Time `json:"updatedAt,omitempty" db:"updated_at,omitempty"` + CreatedAt *time.Time `json:"createdAt,omitempty" db:"created_at,omitempty"` +} + +type AddressOverrides struct { + TrailsLiFiSapientSignerAddress *string `json:"trailsLiFiSapientSignerAddress"` + TrailsRelaySapientSignerAddress *string `json:"trailsRelaySapientSignerAddress"` + TrailsCCTPV2SapientSignerAddress *string `json:"trailsCCTPV2SapientSignerAddress"` +} + +type TakerFee struct { + Address prototyp.Hash `json:"address"` + Bps uint64 `json:"bps"` +} + +type IntentPrecondition struct { + Type string `json:"type"` + ChainId prototyp.BigInt `json:"chainId"` + Data interface{} `json:"data"` +} + +type Token struct { + ChainId uint64 `json:"chainId"` + ContractAddress prototyp.Hash `json:"contractAddress"` + TokenId *string `json:"tokenId"` +} + +type Price struct { + Value float64 `json:"value"` + Currency string `json:"currency"` +} + +type TokenPrice struct { + Token *Token `json:"token"` + Price *Price `json:"price"` + Price24hChange *Price `json:"price24hChange"` + Price24hVol *Price `json:"price24hVol"` + FloorPrice *Price `json:"floorPrice,omitempty"` + BuyPrice *Price `json:"buyPrice,omitempty"` + SellPrice *Price `json:"sellPrice,omitempty"` + UpdatedAt *time.Time `json:"updatedAt"` +} + +type ExchangeRate struct { + Name string `json:"name"` + Symbol string `json:"symbol"` + Value float64 `json:"value"` + VsCurrency string `json:"vsCurrency"` + CurrencyType string `json:"currencyType"` +} + +// TOOD: refactor, we should be returning a cursor always.. +// see indexer/indexer.ridl Page object.. +// +// --- +// +// 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 { + // Common for both numbered pages and cursor: Number of items per page + // TODO: REMOVE.. + PageSize *uint32 `json:"pageSize"` + // Numbered pages: Page number, this is multiplied by the value of the parameter. + // TODO: REMOVE.. + Page *uint32 `json:"page"` + // Number of total items on this query. + // TODO: REMOVE.. + TotalRecords *uint64 `json:"totalRecords"` + // Cursor: column to compare before/after to + Column *string `json:"column"` + // Cursor: return column < before - include to get previous page + Before *interface{} `json:"before"` + // Cursor: return column > after - include to get next page + After *interface{} `json:"after"` + // Sorting filter + Sort []*SortBy `json:"sort"` + // Indicates if there are more results available + More *bool `json:"more,omitempty"` +} + +type SortBy struct { + Column string `json:"column"` + Order SortOrder `json:"order"` +} + +type CCTPTransfer struct { + ID string `json:"id"` + SourceTxHash string `json:"sourceTxHash"` + SourceChainID uint64 `json:"sourceChainId"` + DestinationChainID uint64 `json:"destinationChainId"` + Message string `json:"message"` + Attestation string `json:"attestation"` + Status string `json:"status"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` +} + +type CrossChainFee struct { + ProviderFee prototyp.BigInt `json:"providerFee"` + TrailsSwapFee prototyp.BigInt `json:"trailsSwapFee"` + ProviderFeeUSD float64 `json:"providerFeeUSD"` + TrailsSwapFeeUSD float64 `json:"trailsSwapFeeUSD"` + TotalFeeAmount prototyp.BigInt `json:"totalFeeAmount"` + TotalFeeUSD float64 `json:"totalFeeUSD"` +} + +type MetaTxnFeeDetail struct { + MetaTxnID string `json:"metaTxnID"` + EstimatedGasLimit prototyp.BigInt `json:"estimatedGasLimit"` + FeeNative prototyp.BigInt `json:"feeNative"` +} + +type ChainExecuteQuote struct { + ChainId prototyp.BigInt `json:"chainId"` + TotalGasLimit prototyp.BigInt `json:"totalGasLimit"` + GasPrice prototyp.BigInt `json:"gasPrice"` + TotalFeeAmount prototyp.BigInt `json:"totalFeeAmount"` + NativeTokenSymbol string `json:"nativeTokenSymbol"` + NativeTokenPrice float64 `json:"nativeTokenPrice"` + MetaTxnFeeDetails []*MetaTxnFeeDetail `json:"metaTxnFeeDetails"` + TotalFeeUSD float64 `json:"totalFeeUSD"` +} + +type ExecuteQuote struct { + ChainQuotes []*ChainExecuteQuote `json:"chainQuotes"` +} + +type TrailsFee struct { + ExecuteQuote *ExecuteQuote `json:"executeQuote"` + CrossChainFee *CrossChainFee `json:"crossChainFee"` + TakerFeeAmount prototyp.BigInt `json:"takerFeeAmount"` + TakerFeeUSD *float64 `json:"takerFeeUSD"` + TrailsFixedFeeUSD float64 `json:"trailsFixedFeeUSD"` + FeeToken prototyp.Hash `json:"feeToken"` + OriginTokenTotalAmount prototyp.BigInt `json:"originTokenTotalAmount"` + TotalFeeAmount prototyp.BigInt `json:"totalFeeAmount"` + TotalFeeUSD float64 `json:"totalFeeUSD"` + QuoteProvider *string `json:"quoteProvider"` +} + +type IntentQuote struct { + FromAmount prototyp.BigInt `json:"fromAmount"` + FromAmountMin prototyp.BigInt `json:"fromAmountMin"` + ToAmount prototyp.BigInt `json:"toAmount"` + ToAmountMin prototyp.BigInt `json:"toAmountMin"` + PriceImpact float64 `json:"priceImpact"` + PriceImpactUsd string `json:"priceImpactUsd"` + MaxSlippage float64 `json:"maxSlippage"` + QuoteProvider string `json:"quoteProvider"` + QuoteProviderRequestId string `json:"quoteProviderRequestId"` + QuoteProviderFeeUsd string `json:"quoteProviderFeeUsd"` + FeeQuotes map[string]string `json:"feeQuotes"` +} + +var methods = map[string]method{ + "/rpc/API/Ping": { + Name: "Ping", + Service: "API", + Annotations: map[string]string{}, + }, + "/rpc/API/Version": { + Name: "Version", + Service: "API", + Annotations: map[string]string{}, + }, + "/rpc/API/RuntimeStatus": { + Name: "RuntimeStatus", + Service: "API", + Annotations: map[string]string{}, + }, + "/rpc/API/Clock": { + Name: "Clock", + Service: "API", + Annotations: map[string]string{}, + }, + "/rpc/API/GetIntentCallsPayloads": { + Name: "GetIntentCallsPayloads", + Service: "API", + Annotations: map[string]string{}, + }, + "/rpc/API/CommitIntentConfig": { + Name: "CommitIntentConfig", + Service: "API", + Annotations: map[string]string{}, + }, + "/rpc/API/GetIntentConfig": { + Name: "GetIntentConfig", + Service: "API", + Annotations: map[string]string{}, + }, + "/rpc/API/GetCCTPTransfer": { + Name: "GetCCTPTransfer", + Service: "API", + Annotations: map[string]string{"public": ""}, + }, + "/rpc/API/QueueCCTPTransfer": { + Name: "QueueCCTPTransfer", + Service: "API", + Annotations: map[string]string{"public": ""}, + }, + "/rpc/API/QueueIntentConfigExecution": { + Name: "QueueIntentConfigExecution", + Service: "API", + Annotations: map[string]string{"public": ""}, + }, + "/rpc/API/GetIntentConfigExecutionStatus": { + Name: "GetIntentConfigExecutionStatus", + Service: "API", + Annotations: map[string]string{"public": ""}, + }, + "/rpc/API/ListIntentConfigs": { + Name: "ListIntentConfigs", + Service: "API", + Annotations: map[string]string{"public": ""}, + }, + "/rpc/API/QueueMetaTxnReceipt": { + Name: "QueueMetaTxnReceipt", + Service: "API", + Annotations: map[string]string{"public": ""}, + }, +} + +func WebrpcMethods() map[string]method { + res := make(map[string]method, len(methods)) + for k, v := range methods { + res[k] = v + } + + return res +} + +var WebRPCServices = map[string][]string{ + "API": { + "Ping", + "Version", + "RuntimeStatus", + "Clock", + "GetIntentCallsPayloads", + "CommitIntentConfig", + "GetIntentConfig", + "GetCCTPTransfer", + "QueueCCTPTransfer", + "QueueIntentConfigExecution", + "GetIntentConfigExecutionStatus", + "ListIntentConfigs", + "QueueMetaTxnReceipt", + }, +} + +// +// Server types +// + +type API interface { + // + // Runtime + // + Ping(ctx context.Context) (bool, error) + Version(ctx context.Context) (*Version, error) + RuntimeStatus(ctx context.Context) (*RuntimeStatus, error) + Clock(ctx context.Context) (time.Time, error) + // + // Chain abstraction + // + GetIntentCallsPayloads(ctx context.Context, userAddress string, destinationChainId uint64, destinationTokenAddress string, destinationTokenAmount string, destinationToAddress string, originChainId uint64, originTokenAddress string, originTokenAmount string, destinationCallData *string, destinationCallValue *string, provider *string, addressOverrides *AddressOverrides, destinationSalt *string, takerFee *TakerFee, slippageTolerance *float64, tradeType *TradeType) ([]*IntentCallsPayload, []*IntentPrecondition, []*MetaTxn, *TrailsFee, *IntentQuote, map[string]string, string, string, error) + CommitIntentConfig(ctx context.Context, originIntentAddress string, destinationIntentAddress string, mainSigner string, calls []*IntentCallsPayload, preconditions []*IntentPrecondition, addressOverrides *AddressOverrides) (*IntentConfig, error) + GetIntentConfig(ctx context.Context, intentAddress string) (*IntentConfig, error) + // + // CCTP + // + GetCCTPTransfer(ctx context.Context, id string) (*CCTPTransfer, error) + QueueCCTPTransfer(ctx context.Context, sourceTxHash *string, metaTxHash *string, sourceChainId uint64, destinationChainId uint64) (*CCTPTransfer, error) + // + // Intent Machine Worker + // + QueueIntentConfigExecution(ctx context.Context, intentConfigId uint64) (bool, error) + GetIntentConfigExecutionStatus(ctx context.Context, intentConfigId uint64) (string, error) + ListIntentConfigs(ctx context.Context, page *Page, executionStatus *string) (*Page, []*IntentConfig, error) + QueueMetaTxnReceipt(ctx context.Context, metaTxID string) (bool, error) +} + +// +// Client types +// + +type APIClient interface { + // + // Runtime + // + Ping(ctx context.Context) (bool, error) + Version(ctx context.Context) (*Version, error) + RuntimeStatus(ctx context.Context) (*RuntimeStatus, error) + Clock(ctx context.Context) (time.Time, error) + // + // Chain abstraction + // + GetIntentCallsPayloads(ctx context.Context, userAddress string, destinationChainId uint64, destinationTokenAddress string, destinationTokenAmount string, destinationToAddress string, originChainId uint64, originTokenAddress string, originTokenAmount string, destinationCallData *string, destinationCallValue *string, provider *string, addressOverrides *AddressOverrides, destinationSalt *string, takerFee *TakerFee, slippageTolerance *float64, tradeType *TradeType) ([]*IntentCallsPayload, []*IntentPrecondition, []*MetaTxn, *TrailsFee, *IntentQuote, map[string]string, string, string, error) + CommitIntentConfig(ctx context.Context, originIntentAddress string, destinationIntentAddress string, mainSigner string, calls []*IntentCallsPayload, preconditions []*IntentPrecondition, addressOverrides *AddressOverrides) (*IntentConfig, error) + GetIntentConfig(ctx context.Context, intentAddress string) (*IntentConfig, error) + // + // CCTP + // + GetCCTPTransfer(ctx context.Context, id string) (*CCTPTransfer, error) + QueueCCTPTransfer(ctx context.Context, sourceTxHash *string, metaTxHash *string, sourceChainId uint64, destinationChainId uint64) (*CCTPTransfer, error) + // + // Intent Machine Worker + // + QueueIntentConfigExecution(ctx context.Context, intentConfigId uint64) (bool, error) + GetIntentConfigExecutionStatus(ctx context.Context, intentConfigId uint64) (string, error) + ListIntentConfigs(ctx context.Context, page *Page, executionStatus *string) (*Page, []*IntentConfig, error) + QueueMetaTxnReceipt(ctx context.Context, metaTxID string) (bool, error) +} + +// +// Client +// + +const APIPathPrefix = "/rpc/API/" + +type aPIClient struct { + client HTTPClient + urls [13]string +} + +func NewAPIClient(addr string, client HTTPClient) APIClient { + prefix := urlBase(addr) + APIPathPrefix + urls := [13]string{ + prefix + "Ping", + prefix + "Version", + prefix + "RuntimeStatus", + prefix + "Clock", + prefix + "GetIntentCallsPayloads", + prefix + "CommitIntentConfig", + prefix + "GetIntentConfig", + prefix + "GetCCTPTransfer", + prefix + "QueueCCTPTransfer", + prefix + "QueueIntentConfigExecution", + prefix + "GetIntentConfigExecutionStatus", + prefix + "ListIntentConfigs", + prefix + "QueueMetaTxnReceipt", + } + return &aPIClient{ + client: client, + urls: urls, + } +} + +func (c *aPIClient) Ping(ctx context.Context) (bool, error) { + out := struct { + Ret0 bool `json:"status"` + }{} + + resp, err := doHTTPRequest(ctx, c.client, c.urls[0], 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 (c *aPIClient) Version(ctx context.Context) (*Version, error) { + out := struct { + Ret0 *Version `json:"version"` + }{} + + resp, err := doHTTPRequest(ctx, c.client, c.urls[1], 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 (c *aPIClient) RuntimeStatus(ctx context.Context) (*RuntimeStatus, error) { + out := struct { + Ret0 *RuntimeStatus `json:"status"` + }{} + + resp, err := doHTTPRequest(ctx, c.client, c.urls[2], 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 (c *aPIClient) Clock(ctx context.Context) (time.Time, error) { + out := struct { + Ret0 time.Time `json:"serverTime"` + }{} + + resp, err := doHTTPRequest(ctx, c.client, c.urls[3], 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 (c *aPIClient) GetIntentCallsPayloads(ctx context.Context, userAddress string, destinationChainId uint64, destinationTokenAddress string, destinationTokenAmount string, destinationToAddress string, originChainId uint64, originTokenAddress string, originTokenAmount string, destinationCallData *string, destinationCallValue *string, provider *string, addressOverrides *AddressOverrides, destinationSalt *string, takerFee *TakerFee, slippageTolerance *float64, tradeType *TradeType) ([]*IntentCallsPayload, []*IntentPrecondition, []*MetaTxn, *TrailsFee, *IntentQuote, map[string]string, string, string, error) { + in := struct { + Arg0 string `json:"userAddress"` + Arg1 uint64 `json:"destinationChainId"` + Arg2 string `json:"destinationTokenAddress"` + Arg3 string `json:"destinationTokenAmount"` + Arg4 string `json:"destinationToAddress"` + Arg5 uint64 `json:"originChainId"` + Arg6 string `json:"originTokenAddress"` + Arg7 string `json:"originTokenAmount"` + Arg8 *string `json:"destinationCallData"` + Arg9 *string `json:"destinationCallValue"` + Arg10 *string `json:"provider"` + Arg11 *AddressOverrides `json:"addressOverrides"` + Arg12 *string `json:"destinationSalt"` + Arg13 *TakerFee `json:"takerFee"` + Arg14 *float64 `json:"slippageTolerance"` + Arg15 *TradeType `json:"tradeType"` + }{userAddress, destinationChainId, destinationTokenAddress, destinationTokenAmount, destinationToAddress, originChainId, originTokenAddress, originTokenAmount, destinationCallData, destinationCallValue, provider, addressOverrides, destinationSalt, takerFee, slippageTolerance, tradeType} + out := struct { + Ret0 []*IntentCallsPayload `json:"calls"` + Ret1 []*IntentPrecondition `json:"preconditions"` + Ret2 []*MetaTxn `json:"metaTxns"` + Ret3 *TrailsFee `json:"trailsFee"` + Ret4 *IntentQuote `json:"quote"` + Ret5 map[string]string `json:"feeQuotes"` + Ret6 string `json:"originIntentAddress"` + Ret7 string `json:"destinationIntentAddress"` + }{} + + 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, out.Ret1, out.Ret2, out.Ret3, out.Ret4, out.Ret5, out.Ret6, out.Ret7, err +} + +func (c *aPIClient) CommitIntentConfig(ctx context.Context, originIntentAddress string, destinationIntentAddress string, mainSigner string, calls []*IntentCallsPayload, preconditions []*IntentPrecondition, addressOverrides *AddressOverrides) (*IntentConfig, error) { + in := struct { + Arg0 string `json:"originIntentAddress"` + Arg1 string `json:"destinationIntentAddress"` + Arg2 string `json:"mainSigner"` + Arg3 []*IntentCallsPayload `json:"calls"` + Arg4 []*IntentPrecondition `json:"preconditions"` + Arg5 *AddressOverrides `json:"addressOverrides"` + }{originIntentAddress, destinationIntentAddress, mainSigner, calls, preconditions, addressOverrides} + out := struct { + Ret0 *IntentConfig `json:"config"` + }{} + + 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 out.Ret0, err +} + +func (c *aPIClient) GetIntentConfig(ctx context.Context, intentAddress string) (*IntentConfig, error) { + in := struct { + Arg0 string `json:"intentAddress"` + }{intentAddress} + out := struct { + Ret0 *IntentConfig `json:"config"` + }{} + + 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.WithCausef("failed to close response body: %w", cerr) + } + } + + return out.Ret0, err +} + +func (c *aPIClient) GetCCTPTransfer(ctx context.Context, id string) (*CCTPTransfer, error) { + in := struct { + Arg0 string `json:"id"` + }{id} + out := struct { + Ret0 *CCTPTransfer `json:"transfer"` + }{} + + 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.WithCausef("failed to close response body: %w", cerr) + } + } + + return out.Ret0, err +} + +func (c *aPIClient) QueueCCTPTransfer(ctx context.Context, sourceTxHash *string, metaTxHash *string, sourceChainId uint64, destinationChainId uint64) (*CCTPTransfer, error) { + in := struct { + Arg0 *string `json:"sourceTxHash"` + Arg1 *string `json:"metaTxHash"` + Arg2 uint64 `json:"sourceChainId"` + Arg3 uint64 `json:"destinationChainId"` + }{sourceTxHash, metaTxHash, sourceChainId, destinationChainId} + out := struct { + Ret0 *CCTPTransfer `json:"transfer"` + }{} + + resp, err := doHTTPRequest(ctx, c.client, c.urls[8], 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 *aPIClient) QueueIntentConfigExecution(ctx context.Context, intentConfigId uint64) (bool, error) { + in := struct { + Arg0 uint64 `json:"intentConfigId"` + }{intentConfigId} + out := struct { + Ret0 bool `json:"status"` + }{} + + resp, err := doHTTPRequest(ctx, c.client, c.urls[9], 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 *aPIClient) GetIntentConfigExecutionStatus(ctx context.Context, intentConfigId uint64) (string, error) { + in := struct { + Arg0 uint64 `json:"intentConfigId"` + }{intentConfigId} + out := struct { + Ret0 string `json:"executionStatus"` + }{} + + resp, err := doHTTPRequest(ctx, c.client, c.urls[10], 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 *aPIClient) ListIntentConfigs(ctx context.Context, page *Page, executionStatus *string) (*Page, []*IntentConfig, error) { + in := struct { + Arg0 *Page `json:"page"` + Arg1 *string `json:"executionStatus"` + }{page, executionStatus} + out := struct { + Ret0 *Page `json:"page"` + Ret1 []*IntentConfig `json:"intentConfigs"` + }{} + + resp, err := doHTTPRequest(ctx, c.client, c.urls[11], 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, err +} + +func (c *aPIClient) QueueMetaTxnReceipt(ctx context.Context, metaTxID string) (bool, error) { + in := struct { + Arg0 string `json:"metaTxID"` + }{metaTxID} + out := struct { + Ret0 bool `json:"status"` + }{} + + 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, err +} + +// HTTPClient is the interface used by generated clients to send HTTP requests. +// It is fulfilled by *(net/http).Client, which is sufficient for most users. +// Users can provide their own implementation for special retry policies. +type HTTPClient interface { + Do(req *http.Request) (*http.Response, error) +} + +// urlBase helps ensure that addr specifies a scheme. If it is unparsable +// as a URL, it returns addr unchanged. +func urlBase(addr string) string { + // If the addr specifies a scheme, use it. If not, default to + // http. If url.Parse fails on it, return it unchanged. + url, err := url.Parse(addr) + if err != nil { + return addr + } + if url.Scheme == "" { + url.Scheme = "http" + } + return url.String() +} + +// newRequest makes an http.Request from a client, adding common headers. +func newRequest(ctx context.Context, url string, reqBody io.Reader, contentType string) (*http.Request, error) { + req, err := http.NewRequestWithContext(ctx, "POST", url, reqBody) + if err != nil { + return nil, err + } + 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] { + req.Header.Add(k, v) + } + } + } + return req, nil +} + +// doHTTPRequest is common code to make a request to the remote service. +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.WithCausef("failed to marshal JSON body: %w", err) + } + if err = ctx.Err(); err != nil { + 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.WithCausef("could not build request: %w", err) + } + + resp, err := client.Do(req) + if err != nil { + return nil, ErrWebrpcRequestFailed.WithCause(err) + } + + if resp.StatusCode != 200 { + respBody, err := io.ReadAll(resp.Body) + if err != nil { + 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.WithCausef("failed to unmarshal server error: %w", err) + } + if rpcErr.Cause != "" { + rpcErr.cause = errors.New(rpcErr.Cause) + } + return nil, rpcErr + } + + if out != nil { + respBody, err := io.ReadAll(resp.Body) + if err != nil { + return nil, ErrWebrpcBadResponse.WithCausef("failed to read response body: %w", err) + } + + err = json.Unmarshal(respBody, &out) + if err != nil { + return nil, ErrWebrpcBadResponse.WithCausef("failed to unmarshal JSON response body: %w", err) + } + } + + return resp, nil +} + +func WithHTTPRequestHeaders(ctx context.Context, h http.Header) (context.Context, error) { + if _, ok := h["Accept"]; ok { + return nil, errors.New("provided header cannot set Accept") + } + if _, ok := h["Content-Type"]; ok { + return nil, errors.New("provided header cannot set Content-Type") + } + + copied := make(http.Header, len(h)) + for k, vv := range h { + if vv == nil { + copied[k] = nil + continue + } + copied[k] = make([]string, len(vv)) + copy(copied[k], vv) + } + + return context.WithValue(ctx, HTTPClientRequestHeadersCtxKey, copied), nil +} + +func HTTPRequestHeaders(ctx context.Context) (http.Header, bool) { + h, ok := ctx.Value(HTTPClientRequestHeadersCtxKey).(http.Header) + return h, ok +} + +// +// Helpers +// + +type method struct { + Name string + Service string + Annotations map[string]string +} + +type contextKey struct { + name string +} + +func (k *contextKey) String() string { + return "webrpc context value " + k.name +} + +var ( + HTTPClientRequestHeadersCtxKey = &contextKey{"HTTPClientRequestHeaders"} + HTTPRequestCtxKey = &contextKey{"HTTPRequest"} + + ServiceNameCtxKey = &contextKey{"ServiceName"} + + MethodNameCtxKey = &contextKey{"MethodName"} +) + +func ServiceNameFromContext(ctx context.Context) string { + service, _ := ctx.Value(ServiceNameCtxKey).(string) + return service +} + +func MethodNameFromContext(ctx context.Context) string { + method, _ := ctx.Value(MethodNameCtxKey).(string) + return method +} + +func RequestFromContext(ctx context.Context) *http.Request { + r, _ := ctx.Value(HTTPRequestCtxKey).(*http.Request) + return r +} + +func MethodCtx(ctx context.Context) (method, bool) { + req := RequestFromContext(ctx) + if req == nil { + return method{}, false + } + + m, ok := methods[req.URL.Path] + if !ok { + return method{}, false + } + + return m, true +} + +// +// Errors +// + +type WebRPCError struct { + Name string `json:"error"` + Code int `json:"code"` + Message string `json:"msg"` + Cause string `json:"cause,omitempty"` + HTTPStatus int `json:"status"` + cause error +} + +var _ error = WebRPCError{} + +func (e WebRPCError) Error() string { + if e.cause != nil { + return fmt.Sprintf("%s %d: %s: %v", e.Name, e.Code, e.Message, e.cause) + } + return fmt.Sprintf("%s %d: %s", e.Name, e.Code, e.Message) +} + +func (e WebRPCError) Is(target error) bool { + if target == nil { + return false + } + if rpcErr, ok := target.(WebRPCError); ok { + return rpcErr.Code == e.Code + } + return errors.Is(e.cause, target) +} + +func (e WebRPCError) Unwrap() error { + return e.cause +} + +func (e WebRPCError) WithCause(cause error) WebRPCError { + err := e + err.cause = cause + err.Cause = cause.Error() + return err +} + +func (e WebRPCError) WithCausef(format string, args ...interface{}) WebRPCError { + cause := fmt.Errorf(format, args...) + err := e + err.cause = cause + err.Cause = cause.Error() + return err +} + +// Deprecated: Use .WithCause() method on WebRPCError. +func ErrorWithCause(rpcErr WebRPCError, cause error) WebRPCError { + return rpcErr.WithCause(cause) +} + +// Webrpc errors +var ( + ErrWebrpcEndpoint = WebRPCError{Code: 0, Name: "WebrpcEndpoint", Message: "endpoint error", HTTPStatus: 400} + ErrWebrpcRequestFailed = WebRPCError{Code: -1, Name: "WebrpcRequestFailed", Message: "request failed", HTTPStatus: 400} + ErrWebrpcBadRoute = WebRPCError{Code: -2, Name: "WebrpcBadRoute", Message: "bad route", HTTPStatus: 404} + ErrWebrpcBadMethod = WebRPCError{Code: -3, Name: "WebrpcBadMethod", Message: "bad method", HTTPStatus: 405} + ErrWebrpcBadRequest = WebRPCError{Code: -4, Name: "WebrpcBadRequest", Message: "bad request", HTTPStatus: 400} + ErrWebrpcBadResponse = WebRPCError{Code: -5, Name: "WebrpcBadResponse", Message: "bad response", HTTPStatus: 500} + ErrWebrpcServerPanic = WebRPCError{Code: -6, Name: "WebrpcServerPanic", Message: "server panic", HTTPStatus: 500} + ErrWebrpcInternalError = WebRPCError{Code: -7, Name: "WebrpcInternalError", Message: "internal error", HTTPStatus: 500} + ErrWebrpcClientDisconnected = WebRPCError{Code: -8, Name: "WebrpcClientDisconnected", Message: "client disconnected", HTTPStatus: 400} + ErrWebrpcStreamLost = WebRPCError{Code: -9, Name: "WebrpcStreamLost", Message: "stream lost", HTTPStatus: 400} + ErrWebrpcStreamFinished = WebRPCError{Code: -10, Name: "WebrpcStreamFinished", Message: "stream finished", HTTPStatus: 200} +) + +// Schema errors +var ( + ErrUnauthorized = WebRPCError{Code: 1000, Name: "Unauthorized", Message: "Unauthorized access", HTTPStatus: 401} + ErrPermissionDenied = WebRPCError{Code: 1001, Name: "PermissionDenied", Message: "Permission denied", HTTPStatus: 403} + ErrSessionExpired = WebRPCError{Code: 1002, Name: "SessionExpired", Message: "Session expired", HTTPStatus: 403} + 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} + 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: 1008, 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 request exceeded", HTTPStatus: 429} + ErrQuotaRateLimit = WebRPCError{Code: 1201, Name: "QuotaRateLimit", Message: "Quota 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: 2000, 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} + ErrNotFound = WebRPCError{Code: 8000, Name: "NotFound", Message: "Resource not found", HTTPStatus: 400} + ErrUnsupportedNetwork = WebRPCError{Code: 8008, Name: "UnsupportedNetwork", Message: "Unsupported network", HTTPStatus: 422} +) diff --git a/trails-api/trails-api.gen.go b/trails-api/trails-api.gen.go new file mode 100644 index 00000000..23c6e3fb --- /dev/null +++ b/trails-api/trails-api.gen.go @@ -0,0 +1,2164 @@ +// trails-api v1-25.12.24-babcedf7 ec3a111bf8949a383b62140bc50c9d8dc773ff6c +// -- +// Code generated by webrpc-gen@v0.31.3 with golang generator. DO NOT EDIT. +// +// webrpc-gen -schema=trails-api.ridl -target=golang -pkg=trailsapi -client -service=Trails -methodTreeShake -ignore=@onramp -out=./clients/trails-api.gen.go +package trailsapi + +import ( + "bytes" + "context" + "database/sql/driver" + "encoding/json" + "errors" + "fmt" + "io" + "math/big" + "net/http" + "net/url" + "strings" + "time" + + "github.com/0xsequence/go-sequence/lib/prototyp" +) + +// WebRPC description and code-gen version +func WebRPCVersion() string { + return "v1" +} + +// Schema version of your RIDL schema +func WebRPCSchemaVersion() string { + return "v1-25.12.24-babcedf7" +} + +// Schema hash generated from your RIDL schema +func WebRPCSchemaHash() string { + return "ec3a111bf8949a383b62140bc50c9d8dc773ff6c" +} + +// +// Client interface +// + +type TrailsClient interface { + Ping(ctx context.Context) (string, error) + RuntimeStatus(ctx context.Context) (*RuntimeStatus, error) + Clock(ctx context.Context) (time.Time, error) + QuoteIntent(ctx context.Context, quoteIntentRequest QuoteIntentRequest) (*QuoteIntentResponse, error) + CommitIntent(ctx context.Context, commitIntentRequest CommitIntentRequest) (*CommitIntentResponse, error) + ExecuteIntent(ctx context.Context, executeIntentRequest ExecuteIntentRequest) (*ExecuteIntentResponse, error) + WaitIntentReceipt(ctx context.Context, waitIntentReceiptRequest WaitIntentReceiptRequest) (*WaitIntentReceiptResponse, error) + GetIntentReceipt(ctx context.Context, getIntentReceiptRequest GetIntentReceiptRequest) (*GetIntentReceiptResponse, error) + GetIntent(ctx context.Context, getIntentRequest GetIntentRequest) (*GetIntentResponse, error) + SearchIntents(ctx context.Context, searchIntentsRequest SearchIntentsRequest) (*SearchIntentsResponse, error) + GetIntentHistory(ctx context.Context, getIntentHistoryRequest GetIntentHistoryRequest) (*GetIntentHistoryResponse, error) + AbortIntent(ctx context.Context, abortIntentRequest AbortIntentRequest) (*AbortIntentResponse, error) + // GetChains will return the list of supported chains by Trails. + GetChains(ctx context.Context, getChainsRequest GetChainsRequest) (*GetChainsResponse, error) + // GetExactOutputRoutes will return a list of origin tokens, when given a destination chain and token, + // that can be used to pay/send from an origin chain the exact output amount on the + // destination chain. + // + // The request will include the destination chain and token desired. Optionally, the + // user can specify an origin chain and token to filter results to only that specific + // origin token. Additionally, an optional owner address can be provided to filter + // results to only tokens the owner has a balance on (requires indexer gateway to be + // configured). + // + // The response is a list of origin tokens and their chains which can be used to fulfill + // the exact output request. These are tokens the user can send FROM to achieve the desired + // destination token amount. + // + // aka, the 'pay' routes + GetExactOutputRoutes(ctx context.Context, getExactOutputRoutesRequest GetExactOutputRoutesRequest) (*GetExactOutputRoutesResponse, error) + // GetExactInputRoutes will return a list of destination tokens, when given an origin chain and token, + // that can be used to send/swap to a destination chain and token. + // + // The request will include the origin chain and token used for input. Optionally, the + // user can specify a destination chain and token to further filter the results. + // + // The response is a list of destination tokens and their chains which can be reached from + // the origin token and chain. These are tokens the user can send TO from the given origin token. + // + // aka, the 'swap' routes + GetExactInputRoutes(ctx context.Context, getExactInputRoutesRequest GetExactInputRoutesRequest) (*GetExactInputRoutesResponse, error) + // GetTokenList will return a list of tokens based on the provided filters. + // NOTE: there are many filters and options on GetTokenListRequest + GetTokenList(ctx context.Context, getTokenListRequest GetTokenListRequest) (*GetTokenListResponse, error) + // GetTokenPrices will return the live prices for a list of tokens. + GetTokenPrices(ctx context.Context, getTokenPricesRequest GetTokenPricesRequest) (*GetTokenPricesResponse, error) + // GetTrailsContracts returns Trails contract addresses used by the Trails Intents stack. + GetTrailsContracts(ctx context.Context) (*TrailsContracts, error) + // Deprecated: please use GetIntentHistory instead + GetIntentTransactionHistory(ctx context.Context, getIntentTransactionHistoryRequest GetIntentTransactionHistoryRequest) (*GetIntentTransactionHistoryResponse, error) +} + +// +// Schema types +// + +type TradeType string + +const ( + TradeType_EXACT_INPUT TradeType = "EXACT_INPUT" + TradeType_EXACT_OUTPUT TradeType = "EXACT_OUTPUT" +) + +func (x TradeType) MarshalText() ([]byte, error) { + return []byte(x), nil +} + +func (x *TradeType) UnmarshalText(b []byte) error { + *x = TradeType(string(b)) + return nil +} + +func (x *TradeType) Is(values ...TradeType) bool { + if x == nil { + return false + } + for _, v := range values { + if *x == v { + return true + } + } + return false +} + +type RouteProvider string + +const ( + RouteProvider_AUTO RouteProvider = "AUTO" + RouteProvider_CCTP RouteProvider = "CCTP" + RouteProvider_LIFI RouteProvider = "LIFI" + RouteProvider_RELAY RouteProvider = "RELAY" + RouteProvider_SUSHI RouteProvider = "SUSHI" + RouteProvider_ZEROX RouteProvider = "ZEROX" +) + +func (x RouteProvider) MarshalText() ([]byte, error) { + return []byte(x), nil +} + +func (x *RouteProvider) UnmarshalText(b []byte) error { + *x = RouteProvider(string(b)) + return nil +} + +func (x *RouteProvider) Is(values ...RouteProvider) bool { + if x == nil { + return false + } + for _, v := range values { + if *x == v { + return true + } + } + return false +} + +type IntentStatus uint8 + +const ( + // intent has been quoted + IntentStatus_QUOTED IntentStatus = 0 + // intent has been committed by user + IntentStatus_COMMITTED IntentStatus = 1 + // intent is in the process of being executed + IntentStatus_EXECUTING IntentStatus = 2 + // done, intent execution failed + IntentStatus_FAILED IntentStatus = 3 + // done, intent execution succeeded + IntentStatus_SUCCEEDED IntentStatus = 4 + // done, intent execution was aborted by user + IntentStatus_ABORTED IntentStatus = 5 + // done, intent execution was refunded + IntentStatus_REFUNDED IntentStatus = 6 +) + +var IntentStatus_name = map[uint8]string{ + 0: "QUOTED", + 1: "COMMITTED", + 2: "EXECUTING", + 3: "FAILED", + 4: "SUCCEEDED", + 5: "ABORTED", + 6: "REFUNDED", +} + +var IntentStatus_value = map[string]uint8{ + "QUOTED": 0, + "COMMITTED": 1, + "EXECUTING": 2, + "FAILED": 3, + "SUCCEEDED": 4, + "ABORTED": 5, + "REFUNDED": 6, +} + +func (x IntentStatus) String() string { + return IntentStatus_name[uint8(x)] +} + +func (x IntentStatus) MarshalText() ([]byte, error) { + return []byte(IntentStatus_name[uint8(x)]), nil +} + +func (x *IntentStatus) UnmarshalText(b []byte) error { + *x = IntentStatus(IntentStatus_value[string(b)]) + return nil +} + +func (x *IntentStatus) Is(values ...IntentStatus) bool { + if x == nil { + return false + } + for _, v := range values { + if *x == v { + return true + } + } + return false +} + +type TransactionType uint8 + +const ( + TransactionType_UNKNOWN TransactionType = 0 + // Initial token transfer from owner to origin intent address on origin chain, funding the intent execution. + TransactionType_DEPOSIT TransactionType = 1 + // Origin chain transaction executing intent's origin calls (swaps, bridge preparation, token transfers). + TransactionType_ORIGIN TransactionType = 2 + // Destination chain transaction executing intent's destination calls (receiving bridged tokens, final swaps/transfers). + TransactionType_DESTINATION TransactionType = 3 + // Route provider transaction, see TransactionContext for more details. + TransactionType_ROUTE TransactionType = 4 +) + +var TransactionType_name = map[uint8]string{ + 0: "UNKNOWN", + 1: "DEPOSIT", + 2: "ORIGIN", + 3: "DESTINATION", + 4: "ROUTE", +} + +var TransactionType_value = map[string]uint8{ + "UNKNOWN": 0, + "DEPOSIT": 1, + "ORIGIN": 2, + "DESTINATION": 3, + "ROUTE": 4, +} + +func (x TransactionType) String() string { + return TransactionType_name[uint8(x)] +} + +func (x TransactionType) MarshalText() ([]byte, error) { + return []byte(TransactionType_name[uint8(x)]), nil +} + +func (x *TransactionType) UnmarshalText(b []byte) error { + *x = TransactionType(TransactionType_value[string(b)]) + return nil +} + +func (x *TransactionType) Is(values ...TransactionType) bool { + if x == nil { + return false + } + for _, v := range values { + if *x == v { + return true + } + } + return false +} + +type TransactionContext uint8 + +const ( + TransactionContext_NONE TransactionContext = 0 + // Relays Circle's attested CCTP burn message to destination chain, verifying attestation and minting USDC. + TransactionContext_CCTPV2_MESSAGE TransactionContext = 1 +) + +var TransactionContext_name = map[uint8]string{ + 0: "NONE", + 1: "CCTPV2_MESSAGE", +} + +var TransactionContext_value = map[string]uint8{ + "NONE": 0, + "CCTPV2_MESSAGE": 1, +} + +func (x TransactionContext) String() string { + return TransactionContext_name[uint8(x)] +} + +func (x TransactionContext) MarshalText() ([]byte, error) { + return []byte(TransactionContext_name[uint8(x)]), nil +} + +func (x *TransactionContext) UnmarshalText(b []byte) error { + *x = TransactionContext(TransactionContext_value[string(b)]) + return nil +} + +func (x *TransactionContext) Is(values ...TransactionContext) bool { + if x == nil { + return false + } + for _, v := range values { + if *x == v { + return true + } + } + return false +} + +type TransactionStatus uint8 + +const ( + TransactionStatus_UNKNOWN TransactionStatus = 0 + // on hold, ie. waiting for bridge (off-chain burn attestation) + TransactionStatus_ON_HOLD TransactionStatus = 1 + // ready to be dequeued & sent by the worker + TransactionStatus_PENDING TransactionStatus = 2 + // worker: sending to Relayer + TransactionStatus_RELAYING TransactionStatus = 3 + // sent to the blockchain, whether direct, or via Relayer + TransactionStatus_SENT TransactionStatus = 4 + // encountered error during sending (will be retried) + TransactionStatus_ERRORED TransactionStatus = 5 + // worker: waiting for receipt + TransactionStatus_MINING TransactionStatus = 6 + // terminal state: txn is mined onchain with success status + TransactionStatus_SUCCEEDED TransactionStatus = 7 + // terminal state: txn is mined onchain with failure status + TransactionStatus_FAILED TransactionStatus = 8 + // terminal state: aborted at one of the stages, and never completed onchain txn + TransactionStatus_ABORTED TransactionStatus = 9 + // terminal state: txn mined but call reverted (refund triggered) + TransactionStatus_REVERTED TransactionStatus = 10 +) + +var TransactionStatus_name = map[uint8]string{ + 0: "UNKNOWN", + 1: "ON_HOLD", + 2: "PENDING", + 3: "RELAYING", + 4: "SENT", + 5: "ERRORED", + 6: "MINING", + 7: "SUCCEEDED", + 8: "FAILED", + 9: "ABORTED", + 10: "REVERTED", +} + +var TransactionStatus_value = map[string]uint8{ + "UNKNOWN": 0, + "ON_HOLD": 1, + "PENDING": 2, + "RELAYING": 3, + "SENT": 4, + "ERRORED": 5, + "MINING": 6, + "SUCCEEDED": 7, + "FAILED": 8, + "ABORTED": 9, + "REVERTED": 10, +} + +func (x TransactionStatus) String() string { + return TransactionStatus_name[uint8(x)] +} + +func (x TransactionStatus) MarshalText() ([]byte, error) { + return []byte(TransactionStatus_name[uint8(x)]), nil +} + +func (x *TransactionStatus) UnmarshalText(b []byte) error { + *x = TransactionStatus(TransactionStatus_value[string(b)]) + return nil +} + +func (x *TransactionStatus) Is(values ...TransactionStatus) bool { + if x == nil { + return false + } + for _, v := range values { + if *x == v { + return true + } + } + return false +} + +type ChainGasUsageStatus string + +const ( + ChainGasUsageStatus_NORMAL ChainGasUsageStatus = "NORMAL" + ChainGasUsageStatus_BUSY ChainGasUsageStatus = "BUSY" + ChainGasUsageStatus_VERY_BUSY ChainGasUsageStatus = "VERY_BUSY" +) + +func (x ChainGasUsageStatus) MarshalText() ([]byte, error) { + return []byte(x), nil +} + +func (x *ChainGasUsageStatus) UnmarshalText(b []byte) error { + *x = ChainGasUsageStatus(string(b)) + return nil +} + +func (x *ChainGasUsageStatus) Is(values ...ChainGasUsageStatus) bool { + if x == nil { + return false + } + for _, v := range values { + if *x == v { + return true + } + } + return false +} + +type CCTPTransferStatus uint8 + +const ( + CCTPTransferStatus_UNKNOWN CCTPTransferStatus = 0 + CCTPTransferStatus_ON_HOLD CCTPTransferStatus = 1 + CCTPTransferStatus_PENDING CCTPTransferStatus = 2 + CCTPTransferStatus_FETCHING CCTPTransferStatus = 3 + CCTPTransferStatus_COMPLETE CCTPTransferStatus = 4 + CCTPTransferStatus_FAILED CCTPTransferStatus = 5 +) + +var CCTPTransferStatus_name = map[uint8]string{ + 0: "UNKNOWN", + 1: "ON_HOLD", + 2: "PENDING", + 3: "FETCHING", + 4: "COMPLETE", + 5: "FAILED", +} + +var CCTPTransferStatus_value = map[string]uint8{ + "UNKNOWN": 0, + "ON_HOLD": 1, + "PENDING": 2, + "FETCHING": 3, + "COMPLETE": 4, + "FAILED": 5, +} + +func (x CCTPTransferStatus) String() string { + return CCTPTransferStatus_name[uint8(x)] +} + +func (x CCTPTransferStatus) MarshalText() ([]byte, error) { + return []byte(CCTPTransferStatus_name[uint8(x)]), nil +} + +func (x *CCTPTransferStatus) UnmarshalText(b []byte) error { + *x = CCTPTransferStatus(CCTPTransferStatus_value[string(b)]) + return nil +} + +func (x *CCTPTransferStatus) Is(values ...CCTPTransferStatus) bool { + if x == nil { + return false + } + for _, v := range values { + if *x == v { + return true + } + } + return false +} + +// QuoteIntentRequest represents a user's request to get a quote for a +// Trails same-chain or cross-chain intent. +type QuoteIntentRequest struct { + // intent owner, aka the user address processing the quote + OwnerAddress prototyp.Hash `json:"ownerAddress"` + // origin chain and token details + OriginChainID uint64 `json:"originChainId"` + OriginTokenAddress prototyp.Hash `json:"originTokenAddress"` + // destination chain, token, and execution details + DestinationChainID uint64 `json:"destinationChainId"` + DestinationTokenAddress prototyp.Hash `json:"destinationTokenAddress"` + DestinationToAddress prototyp.Hash `json:"destinationToAddress"` + DestinationCallData *string `json:"destinationCallData"` + DestinationCallValue *prototyp.BigInt `json:"destinationCallValue"` + // originTokenAmount is used with EXACT_INPUT txns + OriginTokenAmount *prototyp.BigInt `json:"originTokenAmount"` + // destinationTokenAmount is used with EXACT_OUTPUT txns + DestinationTokenAmount *prototyp.BigInt `json:"destinationTokenAmount"` + // tradeType indicates whether the trade is an exact input or exact output trade. + // EXACT_OUTPUT is the default if unspecified. + TradeType *TradeType `json:"tradeType,omitempty"` + // onlyNativeGasFee indicates that only the native gas fee (ie. eth) + // option will be returned, even if the user has no balance. + // this option is useful for smart wallets which have their own gas abstraction. + OnlyNativeGasFee bool `json:"onlyNativeGasFee,omitempty"` + // options for additional auxiliary params + Options *QuoteIntentRequestOptions `json:"options,omitempty"` +} + +type QuoteIntentRequestOptions struct { + SwapProvider RouteProvider `json:"swapProvider,omitempty"` + BridgeProvider RouteProvider `json:"bridgeProvider,omitempty"` + // slippageTolerance is a decimal amount, for example + // 0.001 is 0.1% slippage tolerance. If not specified (null), + // AUTO mode is used where optimal slippage is calculated dynamically based on transaction USD value + routing provider. + SlippageTolerance *float64 `json:"slippageTolerance,omitempty"` + TrailsAddressOverrides *TrailsAddressOverrides `json:"trailsAddressOverrides,omitempty"` +} + +// Fully formed/quoted/solved, executable intent. +type Intent struct { + // id is an internal-only sequential primary key. + ID uint64 `json:"-" db:"id,omitempty"` + // Trails/Sequence project id intent originated from + ProjectID uint64 `json:"-" db:"project_id"` + // intentId is a deterministic public intent identifier + // based on the hash of the origin and destination intent addresses. + IntentID prototyp.Hash `json:"intentId" db:"intent_id"` + // intent status of different stages of the intent to keep track. + // note: we also track status in IntentReceipt for execution status. + Status IntentStatus `json:"status" db:"status,omitempty"` + QuoteRequest *QuoteIntentRequest `json:"quoteRequest" db:"quote_request"` + // intent owner / main signer + OwnerAddress prototyp.Hash `json:"ownerAddress" db:"owner_address"` + OriginChainID uint64 `json:"originChainId" db:"orig_chain_id"` + DestinationChainID uint64 `json:"destinationChainId" db:"dest_chain_id"` + OriginIntentAddress prototyp.Hash `json:"originIntentAddress" db:"orig_intent_address"` + DestinationIntentAddress *prototyp.Hash `json:"destinationIntentAddress,omitempty" db:"dest_intent_address,omitempty"` + Salt prototyp.BigInt `json:"salt" db:"salt"` + DepositTransaction *DepositTransaction `json:"depositTransaction" db:"deposit_txn"` + OriginCalls *IntentCalls `json:"originCalls" db:"orig_calls"` + DestinationCalls *IntentCalls `json:"destinationCalls" db:"dest_calls,omitempty"` + OriginPrecondition *TransactionPrecondition `json:"originPrecondition" db:"orig_precondition"` + DestinationPrecondition *TransactionPrecondition `json:"destinationPrecondition" db:"dest_precondition,omitempty"` + OriginMetaTxn *MetaTxn `json:"originMetaTxn" db:"orig_meta_txn"` + DestinationMetaTxn *MetaTxn `json:"destinationMetaTxn" db:"dest_meta_txn,omitempty"` + Quote *IntentProviderQuote `json:"quote" db:"quote"` + Fees *IntentFees `json:"fees" db:"fees"` + TrailsVersion string `json:"trailsVersion" db:"trails_version"` + TrailsContracts *TrailsContracts `json:"trailsContracts" db:"trails_contracts"` + ExpiresAt time.Time `json:"expiresAt" db:"expires_at"` + UpdatedAt *time.Time `json:"updatedAt,omitempty" db:"updated_at,omitempty"` + CreatedAt *time.Time `json:"createdAt,omitempty" db:"created_at,omitempty"` +} + +// DepositTransaction represents a deposit transfer transaction of the token amount +// to the origin intent address. +type DepositTransaction struct { + // toAddress is the origin intent address where the deposit is sent to. + ToAddress prototyp.Hash `json:"toAddress"` + // tokenAddress is the token being deposited. + TokenAddress prototyp.Hash `json:"tokenAddress"` + // decimals is the number of decimals for the token. If the value is null, it + // means we were unable to retrieve the decimals for the token. + Decimals *uint8 `json:"decimals"` + // amount is the token amount being deposited. + Amount prototyp.BigInt `json:"amount"` +} + +type IntentCalls struct { + ChainID uint64 `json:"chainId"` + Space prototyp.BigInt `json:"space"` + Nonce prototyp.BigInt `json:"nonce"` + Calls []*TransactionCall `json:"calls"` +} + +type TransactionCall struct { + To prototyp.Hash `json:"to"` + Value prototyp.BigInt `json:"value"` + Data prototyp.Hash `json:"data"` + GasLimit prototyp.BigInt `json:"gasLimit"` + DelegateCall *bool `json:"delegateCall"` + OnlyFallback *bool `json:"onlyFallback"` + BehaviorOnError *uint8 `json:"behaviorOnError"` +} + +// TransactionPrecondition preconditions based on https://eips.ethereum.org/EIPS/eip-7795 +// NOTE: make sure this struct type matches the relayer TransactionPrecondition type. +type TransactionPrecondition struct { + // value must be 'tokenMinBalance' or '' + Type string `json:"type"` + ChainID uint64 `json:"chainId"` + OwnerAddress prototyp.Hash `json:"ownerAddress"` + TokenAddress prototyp.Hash `json:"tokenAddress"` + MinAmount prototyp.BigInt `json:"minAmount"` +} + +// MetaTxn is a meta-transaction to be submitted to Sequence Relayer. +type MetaTxn struct { + ID string `json:"id"` + ChainID uint64 `json:"chainId"` + // fromAddress ...? + WalletAddress string `json:"walletAddress"` + // TODO: name it.. toAddress ? + Contract string `json:"contract"` + Input string `json:"input"` +} + +type IntentHistory struct { + // intent deterministic id + IntentID prototyp.Hash `json:"intentId"` + // intent status from the 'intents' table + Status IntentStatus `json:"status"` + // intent creation and expiration timestamps + ExpiresAt time.Time `json:"expiresAt" db:"expires_at"` + UpdatedAt *time.Time `json:"updatedAt,omitempty"` + CreatedAt *time.Time `json:"createdAt,omitempty"` + // intent receipt from the 'intent_receipts' table + // + // NOTE: we will always include a receipt, even if we don't have one + // in the db, so we can return the receipt.summary + Receipt *IntentReceipt `json:"receipt"` +} + +type IntentReceiptSummary struct { + IntentID prototyp.Hash `json:"intentId"` + Status IntentStatus `json:"status"` + OwnerAddress prototyp.Hash `json:"ownerAddress"` + OriginChainID uint64 `json:"originChainId"` + DestinationChainID uint64 `json:"destinationChainId"` + TradeType TradeType `json:"tradeType"` + RouteProviders []RouteProvider `json:"routeProviders"` + OriginIntentAddress prototyp.Hash `json:"originIntentAddress"` + OriginTokenAddress prototyp.Hash `json:"originTokenAddress"` + OriginTokenAmount prototyp.BigInt `json:"originTokenAmount"` + OriginTokenMetadata *TokenMetadata `json:"originTokenMetadata"` + // NOTE: we will always set this value, even if its to the origin intent address as the same value. + DestinationIntentAddress prototyp.Hash `json:"destinationIntentAddress"` + // NOTE: for destinationTokenAddress, destinationTokenAmount, destinationToAddress, and destinationTokenMetadata + // are assigned at execution time for EXACT_INPUT and also if destinationHasCallData is true, as we only + // know the values once the destination txn has been executed. + DestinationTokenAddress *prototyp.Hash `json:"destinationTokenAddress"` + DestinationTokenAmount *prototyp.BigInt `json:"destinationTokenAmount"` + DestinationToAddress *prototyp.Hash `json:"destinationToAddress"` + DestinationTokenMetadata *TokenMetadata `json:"destinationTokenMetadata"` + DestinationHasCallData bool `json:"destinationHasCallData"` + DestinationHasCallValue bool `json:"destinationHasCallValue"` + // memo is just any additional context / note we wanted to share + // when computing the summary. + Memo *string `json:"memo"` + // createdAt is the time the intent quote was created + CreatedAt time.Time `json:"createdAt"` + // expiresAt is the time the intent quote expires + ExpiresAt time.Time `json:"expiresAt"` + // startedAt is the time the intent execution started + StartedAt *time.Time `json:"startedAt"` + // finishedAt is the time the intent execution finished + FinishedAt *time.Time `json:"finishedAt"` +} + +// IntentReceipt represents an intent that went through trails, and fully executed +// as a set of transactions, and is now complete. This method is used to fetch the +// status of an intent, and also used by the GetIntentTransactionHistory endpoint. +type IntentReceipt struct { + // id is an internal-only sequential primary key. + ID uint64 `json:"-" db:"id,omitempty"` + // Trails/Sequence project id intent originated from + ProjectID uint64 `json:"-" db:"project_id"` + // intent deterministic id + IntentID prototyp.Hash `json:"intentId" db:"intent_id"` + // intent execution status + Status IntentStatus `json:"status" db:"status,omitempty"` + // intent owner / main signer + OwnerAddress prototyp.Hash `json:"ownerAddress" db:"owner_address"` + // origin chain id where the intent originated from + OriginChainID uint64 `json:"originChainId" db:"orig_chain_id"` + // destination chain id where the intent is targetting as final execution + DestinationChainID uint64 `json:"destinationChainId" db:"dest_chain_id"` + // deposit transaction transaction is the user-initiated transfer into + // the origin intent address on the origin chain. + // + // also note, that if a user is using the 'gasless' deposit method, + // then they supply us with an 'intentEntry' signature, and then + // we relay the deposit as a meta-transaction on their behalf to + // the intent entrypoint contract. + // + // there will also be a deposit transaction to the origin intent address. + DepositTransactionID uint64 `json:"-" db:"deposit_txn_id"` + DepositTransaction *IntentTransaction `json:"depositTransaction" db:"-"` + // origin intent transaction db reference and runtime type + OriginTransactionID uint64 `json:"-" db:"orig_txn_id"` + OriginTransaction *IntentTransaction `json:"originTransaction" db:"-"` + // destination intent transaction db reference and runtime type + // note: this may be null if the intent did not require a destination txn, + // ie. simple swap on origin chain only with no destination calldata. + DestinationTransactionID *uint64 `json:"-" db:"dest_txn_id,omitempty"` + DestinationTransaction *IntentTransaction `json:"destinationTransaction" db:"-"` + Summary *IntentReceiptSummary `json:"summary" db:"summary"` + // timestamp when the intent receipt was updated, usually from status change + UpdatedAt *time.Time `json:"updatedAt,omitempty" db:"updated_at,omitempty"` + // timestamp of when the intent receipt was created + CreatedAt *time.Time `json:"createdAt,omitempty" db:"created_at,omitempty"` +} + +// IntentTransaction represents a single transaction within an intent's execution flow. +type IntentTransaction struct { + // Auto-incrementing id used for table pk + ID uint64 `json:"-" db:"id,omitempty"` + // we don't return it in the json, no need. its internal. + // Intent id this transaction is associated with + IntentID prototyp.Hash `json:"intentId" db:"intent_id"` + // Status of this specific transaction + Status TransactionStatus `json:"status" db:"status"` + StatusReason *string `json:"statusReason" db:"status_reason"` + // Chain where this transaction executes + ChainID uint64 `json:"chainId" db:"chain_id"` + Type TransactionType `json:"type" db:"type"` + Context TransactionContext `json:"context" db:"context"` + FromAddress prototyp.Hash `json:"fromAddress" db:"from_address"` + // ie. originIntentAddress, TrailsRouter, or TrailsIntentEntrypoint + ToAddress prototyp.Hash `json:"toAddress" db:"to_address"` + // Token being transferred in this transaction + TokenAddress prototyp.Hash `json:"tokenAddress" db:"token_address"` + // Amount transferred in this transaction + TokenAmount prototyp.BigInt `json:"tokenAmount" db:"token_amount"` + Calldata prototyp.Hash `json:"-" db:"calldata,omitempty"` + // for user-sent deposit txn, this will be null + MetaTxnID prototyp.Hash `json:"metaTxnId,omitempty" db:"meta_txn_id,omitempty"` + MetaTxnFeeQuote *string `json:"metaTxnFeeQuote" db:"fee_quote,omitempty"` + Precondition *TransactionPrecondition `json:"precondition" db:"precondition,omitempty"` + // Gasless deposit metadata (nullable) + DepositIntentEntry *DepositIntentEntry `json:"depositIntentEntry" db:"deposit_intent_entry,omitempty"` + // Transaction hash received from the relayer after it's mined. + TxnHash prototyp.Hash `json:"txnHash,omitempty" db:"txn_hash,omitempty"` + // On-chain block timestamp when txn was mined + TxnMinedAt *time.Time `json:"txnMinedAt" db:"txn_mined_at,omitempty"` + UpdatedAt *time.Time `json:"updatedAt,omitempty" db:"updated_at,omitempty"` + CreatedAt *time.Time `json:"createdAt,omitempty" db:"created_at,omitempty"` +} + +type PriceImpact struct { + PriceImpact float64 `json:"priceImpact"` + PriceImpactUSD float64 `json:"priceImpactUsd"` +} + +type PriceImpactDetails struct { + // Price impact that results from gas fees on origin and destination chains + ExecutionPriceImpact *PriceImpact `json:"executionPriceImpact"` + // Price impact that results from slippage and pure market movement + MarketPriceImpact *PriceImpact `json:"marketPriceImpact"` + // Price impact that results from provider fees, excluding trails (swap + bridge) + ProviderFeesPriceImpact *PriceImpact `json:"providerFeesPriceImpact"` + // Price impact that results from trails fees + TrailsFeesPriceImpact *PriceImpact `json:"trailsFeesPriceImpact"` + // Price impact that results from net result of all fees, currently used in the final price impact shown to user + NetPriceImpact *PriceImpact `json:"netPriceImpact"` +} + +// IntentProviderQuote represents the quotes from the underlining providers for both +// the swap and/or bridge external providers. If we do both a swap and a bridge, then +// the total amount is the sum of both providers. +type IntentProviderQuote struct { + RouteProviders []RouteProvider `json:"routeProviders"` + RouteProvidersRequestIDs []string `json:"routeProvidersRequestIds"` + RouteProvidersFeeUSD []float64 `json:"routeProvidersFeeUsd"` + FromAmount prototyp.BigInt `json:"fromAmount"` + FromAmountMin prototyp.BigInt `json:"fromAmountMin"` + FromAmountUSD float64 `json:"fromAmountUsd"` + FromAmountMinUSD float64 `json:"fromAmountMinUsd"` + ToAmount prototyp.BigInt `json:"toAmount"` + ToAmountMin prototyp.BigInt `json:"toAmountMin"` + ToAmountUSD float64 `json:"toAmountUsd"` + ToAmountMinUSD float64 `json:"toAmountMinUsd"` + MaxSlippage float64 `json:"maxSlippage"` + // This is the net price impact (same value as priceImpactDetails.netPriceImpact) + PriceImpact float64 `json:"priceImpact"` + PriceImpactUSD float64 `json:"priceImpactUsd"` + PriceImpactDetails *PriceImpactDetails `json:"priceImpactDetails"` +} + +type IntentFees struct { + // gas fees on origin and destination chains + OriginGas *IntentTransactionGasFee `json:"originGas"` + DestinationGas *IntentTransactionGasFee `json:"destinationGas"` + // provider fees (swap + bridge + trails) + Provider *IntentProviderFees `json:"provider"` + // fee token details + FeeTokenAddress prototyp.Hash `json:"feeTokenAddress"` + // @deprecated: kept for backwards compatibility here, set to `collectorFeeTotal` + FeeTokenAmount prototyp.BigInt `json:"feeTokenAmount"` + // @deprecated: kept for backwards compatibility here, set to `collectorFeeUsd` + // fee token usd = gas + trails fee (amount trails' fee collector gets transferred) + FeeTokenUSD float64 `json:"feeTokenUsd"` + // @deprecated: kept for backwards compatibility here, set to 0 + // fee token amount = provider + trails + origin amount total + FeeTokenTotal prototyp.BigInt `json:"feeTokenTotal"` + // gas fee component (origin + destination gas or orchestraion fee) + GasFeeTotal prototyp.BigInt `json:"gasFeeTotal"` + GasFeeUSD float64 `json:"gasFeeUsd"` + // trails fee component + TrailsFeeTotal prototyp.BigInt `json:"trailsFeeTotal"` + TrailsFeeUSD float64 `json:"trailsFeeUsd"` + // gas + trails fee component (amount trails collects as a fee) + CollectorFeeTotal prototyp.BigInt `json:"collectorFeeTotal"` + CollectedFeeUSD float64 `json:"collectorFeeUsd"` + // provider fee component + ProviderFeeTotal prototyp.BigInt `json:"providerFeeTotal"` + ProviderFeeUSD float64 `json:"providerFeeUsd"` + // display fees (gas + trails + provider) - what user sees + TotalFeeAmount prototyp.BigInt `json:"totalFeeAmount"` + TotalFeeUSD float64 `json:"totalFeeUsd"` +} + +// ProviderFees is the swap+bridge provider fee, trails fee summary and totals +// @deprecated: please use IntentFees instead +type IntentProviderFees struct { + // quote provider fee component (swap and/or bridge) + QuoteProvider string `json:"quoteProvider"` + QuoteProviderFee prototyp.BigInt `json:"quoteProviderFee"` + QuoteProviderFeeUSD float64 `json:"quoteProviderFeeUsd"` + // trails provider fee component, denominated in the origin token + TrailsFee prototyp.BigInt `json:"trailsFee"` + TrailsFeeUSD float64 `json:"trailsFeeUsd"` + // provider + trails (non-gas) fee totals + QuoteProviderWithTrailsFee prototyp.BigInt `json:"quoteProviderWithTrailsFee"` + ProviderWithTrailsFeeUSD float64 `json:"providerWithTrailsFeeUsd"` + // total provider fees, aka, swap + bridge + trails fees + // @deprecated: kept for backwards compatibility here, sync w/ providerWithTrailsFee + TotalFeeAmount prototyp.BigInt `json:"totalFeeAmount"` + TotalFeeUSD float64 `json:"totalFeeUsd"` +} + +// DEPRECATED: please use GetIntentHistory / IntentReceiptSummary instead +// IntentSummary represents a summary view of an intent for listing purposes. +// NOTE: this type is deprecated, please use GetIntentHistory method instead +type IntentSummary struct { + ID uint64 `json:"-"` + // intent deterministic id + IntentID prototyp.Hash `json:"intentId"` + // intent status + Status IntentStatus `json:"status"` + // intent owner / main signer + OwnerAddress prototyp.Hash `json:"ownerAddress"` + // origin and destination chain ids + OriginChainID uint64 `json:"originChainId"` + // - originChainMetadata: ChainMetadata + DestinationChainID uint64 `json:"destinationChainId"` + // origin and destination intent addresses + OriginIntentAddress prototyp.Hash `json:"originIntentAddress"` + DestinationIntentAddress prototyp.Hash `json:"destinationIntentAddress"` + // deposit transfer transaction hash and status, which is the init + // transfer from the owner / main signer into the origin intent + // address on the origin chain id + DepositTransactionHash *prototyp.Hash `json:"depositTransactionHash"` + DepositTransactionStatus TransactionStatus `json:"depositTransactionStatus"` + // origin and destination execution transaction hashes and statuses + OriginTransactionHash *prototyp.Hash `json:"originTransactionHash"` + OriginTransactionStatus TransactionStatus `json:"originTransactionStatus"` + DestinationTransactionHash *prototyp.Hash `json:"destinationTransactionHash"` + DestinationTransactionStatus TransactionStatus `json:"destinationTransactionStatus"` + OriginTokenAddress prototyp.Hash `json:"originTokenAddress"` + OriginTokenAmount prototyp.BigInt `json:"originTokenAmount"` + OriginTokenMetadata *TokenMetadata `json:"originTokenMetadata"` + DestinationTokenAddress prototyp.Hash `json:"destinationTokenAddress"` + DestinationTokenAmount prototyp.BigInt `json:"destinationTokenAmount"` + DestinationTokenMetadata *TokenMetadata `json:"destinationTokenMetadata"` + DestinationToAddress prototyp.Hash `json:"destinationToAddress,omitempty"` + ExpiresAt time.Time `json:"expiresAt" db:"expires_at"` + // timestamp when the intent receipt was updated, usually from status change + UpdatedAt *time.Time `json:"updatedAt,omitempty"` + // timestamp of when the intent receipt was created + CreatedAt time.Time `json:"createdAt"` +} + +type IntentTransactionGasFee struct { + ChainID uint64 `json:"chainId"` + TotalGasLimit prototyp.BigInt `json:"totalGasLimit"` + GasPrice prototyp.BigInt `json:"gasPrice"` + NativeTokenSymbol string `json:"nativeTokenSymbol"` + NativeTokenPriceUSD *float64 `json:"nativeTokenPriceUsd"` + ChainGasUsageStatus ChainGasUsageStatus `json:"chainGasUsageStatus"` + TotalFeeAmount prototyp.BigInt `json:"totalFeeAmount"` + TotalFeeUSD float64 `json:"totalFeeUsd"` + // sequence relayer gas fee quotes + // AKA, meta transaction gas fee quotes for pre-sponsored + MetaTxnFeeDetails *MetaTxnFeeDetails `json:"metaTxnFeeDetails"` + // metaTxnGasQuote is like a voucher for gas sponsorship from the relayer + MetaTxnGasQuote string `json:"metaTxnGasQuote"` +} + +type MetaTxnFeeDetails struct { + MetaTxnID string `json:"metaTxnId"` + EstimatedGasLimit prototyp.BigInt `json:"estimatedGasLimit"` + FeeNative prototyp.BigInt `json:"feeNative"` +} + +type ChainMetadata struct { + ChainID uint64 `json:"chainId"` + Name string `json:"name"` + LogoURI string `json:"logoUri,omitempty"` + Testnet *bool `json:"testnet,omitempty"` +} + +type TokenMetadata struct { + ChainID uint64 `json:"chainId"` + TokenAddress prototyp.Hash `json:"tokenAddress"` + Name string `json:"name"` + Symbol string `json:"symbol"` + Decimals *uint8 `json:"decimals,omitempty"` + LogoURI string `json:"logoUri,omitempty"` +} + +type Token struct { + ChainID uint64 `json:"chainId"` + TokenAddress prototyp.Hash `json:"tokenAddress"` + TokenSymbol *string `json:"tokenSymbol,omitempty"` +} + +type TokenPrice struct { + Token *Token `json:"token"` + PriceUSD *float64 `json:"priceUsd"` + // - price?: float64 + // - currency: string + UpdatedAt time.Time `json:"updatedAt"` +} + +type CCTPTransfer struct { + ID uint64 `json:"id" db:"id,omitempty"` + IntentID prototyp.Hash `json:"intentId" db:"intent_id"` + OriginChainID uint64 `json:"originChainId" db:"orig_chain_id"` + DestinationChainID uint64 `json:"destinationChainId" db:"dest_chain_id"` + OriginTxnID uint64 `json:"originTxnId" db:"orig_txn_id"` + OriginTxnHash prototyp.Hash `json:"originTxnHash" db:"orig_txn_hash"` + DestinationTxnID uint64 `json:"destinationTxnId" db:"dest_txn_id"` + Message prototyp.Hash `json:"message" db:"message,omitempty"` + Attestation prototyp.Hash `json:"attestation" db:"attestation,omitempty"` + Status CCTPTransferStatus `json:"status" db:"status"` + StatusReason *string `json:"statusReason" db:"status_reason,omitempty"` + CreatedAt time.Time `json:"createdAt" db:"created_at,omitempty"` + UpdatedAt time.Time `json:"updatedAt" db:"updated_at,omitempty"` +} + +type GasFeeOptions struct { + GasEstimate *GasEstimate `json:"gasEstimate"` + FeeOptions []*FeeOption `json:"feeOptions"` + ExpiresAt time.Time `json:"expiresAt"` + FeeCollectorAddress prototyp.Hash `json:"feeCollectorAddress"` +} + +type GasEstimate struct { + TotalGas uint64 `json:"totalGas"` + GasPrice string `json:"gasPrice"` + NativeCost string `json:"nativeCost"` + NativeCostUSD float64 `json:"nativeCostUsd"` +} + +type FeeOption struct { + // if value is 0x000...000 or 0xeee...eeee, it is native token + TokenAddress prototyp.Hash `json:"tokenAddress"` + TokenSymbol string `json:"tokenSymbol"` + TokenDecimals uint8 `json:"tokenDecimals"` + Amount prototyp.BigInt `json:"amount"` + AmountUSD float64 `json:"amountUsd"` + FeeCollectorAddress prototyp.Hash `json:"feeCollectorAddress"` + // - isNative: bool + // - supportsPermit: bool + Is2612 bool `json:"is2612"` +} + +// DepositSignature contains all gasless deposit signature parameters for ExecuteIntent +type DepositSignature struct { + // Required: EIP-712 TrailsIntent signature (hex) + IntentSignature prototyp.Hash `json:"intentSignature"` + // Optional: EIP-2612 permit signature (hex, if token approval needed) + PermitSignature *prototyp.Hash `json:"permitSignature"` + // Optional: Permit expiration timestamp + PermitDeadline *uint64 `json:"permitDeadline"` + // Optional: Permit amount (usually max uint256) + PermitAmount prototyp.BigInt `json:"permitAmount"` + // Required: Selected fee option from gasFeeOptions + SelectedGasFeeOption *FeeOption `json:"selectedGasFeeOption"` + // Required: User nonce from Intent Entrypoint contract (fetched fresh at execute time) + UserNonce uint64 `json:"userNonce"` + // Required: Intent deadline timestamp + Deadline uint64 `json:"deadline"` +} + +// DepositIntentEntry contains gasless deposit metadata stored in JSONB +type DepositIntentEntry struct { + // EIP-712 TrailsIntent signature (hex) + IntentSignature prototyp.Hash `json:"intentSignature"` + // EIP-2612 permit signature (hex, if approval needed) + PermitSignature *prototyp.Hash `json:"permitSignature"` + // Permit expiration timestamp + PermitDeadline *uint64 `json:"permitDeadline"` + // Permit amount (usually max uint256) + PermitAmount prototyp.BigInt `json:"permitAmount"` + // Fee amount in deposit token + FeeAmount prototyp.BigInt `json:"feeAmount"` + // Fee token address (same as deposit token) + FeeToken prototyp.Hash `json:"feeToken"` + // Address that receives the fee + FeeCollector prototyp.Hash `json:"feeCollector"` + // User nonce from Intent Entrypoint contract (provided by client) + UserNonce uint64 `json:"userNonce"` + // Intent deadline timestamp + Deadline uint64 `json:"deadline"` +} + +type TrailsAddressOverrides struct { + SequenceWalletFactoryAddress *string `json:"sequenceWalletFactoryAddress"` + SequenceWalletMainModuleAddress *string `json:"sequenceWalletMainModuleAddress"` + SequenceWalletMainModuleUpgradableAddress *string `json:"sequenceWalletMainModuleUpgradableAddress"` + SequenceWalletGuestModuleAddress *string `json:"sequenceWalletGuestModuleAddress"` + SequenceWalletUtilsAddress *string `json:"sequenceWalletUtilsAddress"` +} + +type TrailsContracts struct { + TrailsIntentEntrypointAddress prototyp.Hash `json:"trailsIntentEntrypointAddress"` + TrailsRouterAddress prototyp.Hash `json:"trailsRouterAddress"` + TrailsRouterShimAddress prototyp.Hash `json:"trailsRouterShimAddress"` +} + +type ChainInfo struct { + // id is the chain id + ID uint64 `json:"id"` + // id is the chain full name + Name string `json:"name"` + // tokenName/Symbol/Decimals are the native currency details + TokenName string `json:"tokenName"` + TokenSymbol string `json:"tokenSymbol"` + TokenDecimals uint8 `json:"tokenDecimals"` + // is testnet flag informs whether the chain is a testnet + IsTestnet bool `json:"isTestnet"` + // supportsBridging indicates whether the chain supports bridging operations + SupportsBridging bool `json:"supportsBridging"` + // additional metadata + LogoURI string `json:"logoUri"` + BlockExplorerURL string `json:"blockExplorerUrl"` +} + +type TokenInfo struct { + ChainID uint64 `json:"chainId"` + Address prototyp.Hash `json:"address"` + Name string `json:"name"` + Symbol string `json:"symbol"` + Decimals uint8 `json:"decimals"` + // supportsBridging indicates whether the chain supports bridging operations + SupportsBridging bool `json:"supportsBridging,omitempty"` + // additional metadata + LogoURI string `json:"logoUri"` + Featured bool `json:"featured"` + FeatureIndex int `json:"-"` +} + +type MeldQuote struct { + TransactionType string `json:"transactionType"` + SourceAmount float64 `json:"sourceAmount"` + SourceAmountWithoutFees float64 `json:"sourceAmountWithoutFees"` + FiatAmountWithoutFees float64 `json:"fiatAmountWithoutFees"` + DestinationAmountWithoutFees float64 `json:"destinationAmountWithoutFees,omitempty"` + SourceCurrencyCode string `json:"sourceCurrencyCode"` + CountryCode string `json:"countryCode"` + TotalFee float64 `json:"totalFee"` + NetworkFee float64 `json:"networkFee"` + TransactionFee float64 `json:"transactionFee"` + DestinationAmount float64 `json:"destinationAmount"` + DestinationCurrencyCode string `json:"destinationCurrencyCode"` + ExchangeRate float64 `json:"exchangeRate"` + PaymentMethodType string `json:"paymentMethodType"` + CustomerScore float64 `json:"customerScore"` + ServiceProvider string `json:"serviceProvider"` + InstitutionName string `json:"institutionName,omitempty"` + LowKyc bool `json:"lowKyc,omitempty"` + PartnerFee float64 `json:"partnerFee"` +} + +type SortOrder uint32 + +const ( + SortOrder_DESC SortOrder = 0 + SortOrder_ASC SortOrder = 1 +) + +var SortOrder_name = map[uint32]string{ + 0: "DESC", + 1: "ASC", +} + +var SortOrder_value = map[string]uint32{ + "DESC": 0, + "ASC": 1, +} + +func (x SortOrder) String() string { + return SortOrder_name[uint32(x)] +} + +func (x SortOrder) MarshalText() ([]byte, error) { + return []byte(SortOrder_name[uint32(x)]), nil +} + +func (x *SortOrder) UnmarshalText(b []byte) error { + *x = SortOrder(SortOrder_value[string(b)]) + return nil +} + +func (x *SortOrder) Is(values ...SortOrder) bool { + if x == nil { + return false + } + for _, v := range values { + if *x == v { + return true + } + } + return false +} + +type RuntimeStatus struct { + // overall status, true/false + HealthOK bool `json:"healthOK"` + Version string `json:"version"` + Branch string `json:"branch"` + CommitHash string `json:"commitHash"` + StartTime time.Time `json:"startTime"` + Uptime string `json:"uptime"` + Hostname string `json:"hostname"` + Runnables interface{} `json:"runnables"` + Services []*ServiceStatus `json:"services"` +} + +type ServiceStatus struct { + Name string `json:"name"` + Healthy bool `json:"healthy"` + Error string `json:"error,omitempty"` + Latency string `json:"latency"` +} + +// QuoteIntent accepts an intent request from the user/app and returns +// a fully formed/quoted/solved and executable Intent. +// +// NOTE: we use the IntentRequest directly for the QuoteIntent request type. +// And do not create a QuoteIntentRequest additional type, as its unnecessary. +type QuoteIntentResponse struct { + Intent *Intent `json:"intent"` + GasFeeOptions *GasFeeOptions `json:"gasFeeOptions"` +} + +// CommitIntent accepts a fully formed Intent and commits it for execution. CommitIntent +// is called by clients to confirm they wish to commit to the intent and will execute it. +// We return the intent ID for tracking. +type CommitIntentRequest struct { + Intent *Intent `json:"intent"` +} + +type CommitIntentResponse struct { + IntentID prototyp.Hash `json:"intentId"` +} + +// ExecuteIntent relays the intent execution request to the intent execution engine. +// Clients provide the transfer transaction hash that executed the transfer to the +// intent origin address. +type ExecuteIntentRequest struct { + IntentID prototyp.Hash `json:"intentId"` + // Traditional deposit: provide transaction hash + DepositTransactionHash prototyp.Hash `json:"depositTransactionHash"` + // Gasless deposit: provide signatures (mutually exclusive with depositTransactionHash) + DepositSignature *DepositSignature `json:"depositSignature"` +} + +type ExecuteIntentResponse struct { + IntentID prototyp.Hash `json:"intentId"` + IntentStatus IntentStatus `json:"intentStatus"` +} + +// GetIntentReceipt returns the current receipt/status of an intent immediately. +// If you are waiting for a intent to complete, use WaitIntentReceipt. +type GetIntentReceiptRequest struct { + IntentID prototyp.Hash `json:"intentId"` +} + +type GetIntentReceiptResponse struct { + IntentReceipt *IntentReceipt `json:"intentReceipt"` +} + +// WaitIntentReceipt will block until the intent reaches a terminal status or timeout. +type WaitIntentReceiptRequest struct { + IntentID prototyp.Hash `json:"intentId"` + // lastReceiptStates is the previously returned status array from a prior + // WaitIntentReceipt call. This allows the server to only return + // updates since the last call, allowing for more efficient polling. + LastReceiptStates []TransactionStatus `json:"lastReceiptStates"` +} + +type WaitIntentReceiptResponse struct { + IntentReceipt *IntentReceipt `json:"intentReceipt"` + // receiptStates is an array of key statuses within the intentReceipt object + // for all sub intent transactions.this can be passed back to subsequent + // WaitIntentReceipt calls to as a form of a stateless cursor to only + // get updates since last call. + ReceiptStates []TransactionStatus `json:"receiptStates"` + // done flag informs a client whether the wait completed successfully (true) or timed out (false). + // as the clients will keep polling until done is true. NOTE: done does not indicate success, + // it just indicates the execution is done, to check status see intentReceipt.status. + Done bool `json:"done"` +} + +// GetIntent queries the database for solved and committed 'intents' +type GetIntentRequest struct { + IntentID prototyp.Hash `json:"intentId"` +} + +type GetIntentResponse struct { + Intent *Intent `json:"intent"` +} + +// SearchIntents searches past intents based on filters. +// We do not return 'solved' status intents here, only committed/executed/failed/succeeded. +type SearchIntentsRequest struct { + ByIntentID prototyp.Hash `json:"byIntentId"` + ByProjectID uint64 `json:"byProjectId"` + ByTransactionHash prototyp.Hash `json:"byTransactionHash"` + ByOwnerAddress prototyp.Hash `json:"byOwnerAddress"` + ByOriginIntentAddress prototyp.Hash `json:"byOriginIntentAddress"` + ByDestinationIntentAddress prototyp.Hash `json:"byDestinationIntentAddress"` + ByQueryString string `json:"byQueryString"` +} + +type SearchIntentsResponse struct { + // NOTE: we intentionally do not return a page here, and limit it to 10 latest results + Intents []*Intent `json:"intents"` +} + +// GetIntentHistory returns the intent history list, with receipt and summary info. +type GetIntentHistoryRequest struct { + // cursor paging parameters + Page *Page `json:"page"` + // optional project id scope filter + ByProjectID uint64 `json:"byProjectId"` + ByOwnerAddress prototyp.Hash `json:"byOwnerAddress"` +} + +type GetIntentHistoryResponse struct { + Intents []*IntentHistory `json:"intents"` + NextPage *Page `json:"nextPage"` +} + +type AbortIntentRequest struct { + // intentId is the ID of the intent which has been aborted + IntentID prototyp.Hash `json:"intentId"` + // chainId is required to specify which chain the transaction was aborted + ChainID uint64 `json:"chainId"` + // abortTransactionHash is the transaction hash of the abort transaction + // submitted by the user which we validate before updating status + AbortTransactionHash prototyp.Hash `json:"abortTransactionHash"` +} + +type AbortIntentResponse struct { + IntentID prototyp.Hash `json:"intentId"` + Status IntentStatus `json:"status"` +} + +type GetTokenPricesRequest struct { + Tokens []*Token `json:"tokens"` +} + +type GetTokenPricesResponse struct { + TokenPrices []*TokenPrice `json:"tokenPrices"` +} + +type GetChainsRequest struct { + // TODO: use enum eventually.. + RouteProvider *string `json:"routeProvider"` +} + +type GetChainsResponse struct { + Chains []*ChainInfo `json:"chains"` +} + +type GetExactOutputRoutesRequest struct { + DestinationChainID uint64 `json:"destinationChainId"` + DestinationTokenAddress prototyp.Hash `json:"destinationTokenAddress"` + // optionally passed to filter results to only tokens from this origin chain + OriginChainID *uint64 `json:"originChainId"` + // optionally passed to filter results to only this specific origin token + OriginTokenAddress prototyp.Hash `json:"originTokenAddress"` + // owner address is used to filter tokens the owner has a balance on. + // + // if unspecified, then we filter origin tokens based on popular options + // as is useful for a qr code deposit flow. + OwnerAddress prototyp.Hash `json:"ownerAddress"` +} + +type GetExactOutputRoutesResponse struct { + Tokens []*TokenInfo `json:"tokens"` +} + +type GetExactInputRoutesRequest struct { + OriginChainID uint64 `json:"originChainId"` + OriginTokenAddress prototyp.Hash `json:"originTokenAddress"` + // optionally passed to filter results + DestinationChainID *uint64 `json:"destinationChainId"` + DestinationTokenAddress prototyp.Hash `json:"destinationTokenAddress"` +} + +type GetExactInputRoutesResponse struct { + Tokens []*TokenInfo `json:"tokens"` +} + +type GetTokenListRequest struct { + // chainIds is required to specify which chains to get featured tokens for + ChainIDs []uint64 `json:"chainIds"` + // optional search query string to search token name/symbol/address. + // when searching lists, all of the lists will be used in the search automatically. + SearchQuery string `json:"searchQuery"` + // optional limit to return per chain, ie. like the top N number of tokens + // sorted by featured then alphabetically by name. + Limit int32 `json:"limit"` + // optional token address to filter results to a specific token, useful for checking + // if tokenAddress filter is used, the entire token list is searched for the token + TokenAddress prototyp.Hash `json:"tokenAddress"` + // optional filter to include all tokens in response, not just featured + // by default, only featured tokens are returned. + IncludeAllListed bool `json:"includeAllListed"` + // optional filter to include external tokens in response, in addition to listed/featured + // by default, external tokens are not returned. + IncludeExternal bool `json:"includeExternal"` + // optional filter to exlude specific token addresses from the response + ExcludeTokens []prototyp.Hash `json:"excludeTokens"` +} + +type GetTokenListResponse struct { + Tokens []*TokenInfo `json:"tokens"` +} + +// 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 { + // Cursor: column to compare before/after to + Column *string `json:"column,omitempty"` + // Cursor: return column < before - include to get previous page + Before *uint64 `json:"before,omitempty"` + // Cursor: return column > after - include to get next page + After *uint64 `json:"after,omitempty"` + // Sorting filter + Sort []*SortBy `json:"sort,omitempty"` + // Number of items per page + PageSize *uint32 `json:"pageSize,omitempty"` + // Indicates if there are more results available + More *bool `json:"more,omitempty"` +} + +type SortBy struct { + Column string `json:"column"` + Order SortOrder `json:"order"` +} + +// DEPRECATED: please use GetIntentHistory instead +type GetIntentTransactionHistoryRequest struct { + // cursor paging parameters + Page *Page `json:"page"` + // optional project id scope filter + ByProjectID uint64 `json:"byProjectId"` + ByOwnerAddress prototyp.Hash `json:"byOwnerAddress"` +} + +// DEPRECATED: please use GetIntentHistory instead +type GetIntentTransactionHistoryResponse struct { + Intents []*IntentSummary `json:"intents"` + NextPage *Page `json:"nextPage"` +} + +// +// Client +// + +const TrailsPathPrefix = "/rpc/Trails/" + +type trailsClient struct { + client HTTPClient + urls [19]string +} + +func NewTrailsClient(addr string, client HTTPClient) TrailsClient { + prefix := urlBase(addr) + TrailsPathPrefix + urls := [19]string{ + prefix + "Ping", + prefix + "RuntimeStatus", + prefix + "Clock", + prefix + "QuoteIntent", + prefix + "CommitIntent", + prefix + "ExecuteIntent", + prefix + "WaitIntentReceipt", + prefix + "GetIntentReceipt", + prefix + "GetIntent", + prefix + "SearchIntents", + prefix + "GetIntentHistory", + prefix + "AbortIntent", + prefix + "GetChains", + prefix + "GetExactOutputRoutes", + prefix + "GetExactInputRoutes", + prefix + "GetTokenList", + prefix + "GetTokenPrices", + prefix + "GetTrailsContracts", + prefix + "GetIntentTransactionHistory", + } + return &trailsClient{ + client: client, + urls: urls, + } +} + +func (c *trailsClient) Ping(ctx context.Context) (string, error) { + out := struct { + Ret0 string `json:"version"` + }{} + + resp, err := doHTTPRequest(ctx, c.client, c.urls[0], 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 (c *trailsClient) RuntimeStatus(ctx context.Context) (*RuntimeStatus, error) { + out := struct { + Ret0 *RuntimeStatus `json:"status"` + }{} + + resp, err := doHTTPRequest(ctx, c.client, c.urls[1], 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 (c *trailsClient) Clock(ctx context.Context) (time.Time, error) { + out := struct { + Ret0 time.Time `json:"serverTime"` + }{} + + resp, err := doHTTPRequest(ctx, c.client, c.urls[2], 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 (c *trailsClient) QuoteIntent(ctx context.Context, quoteIntentRequest QuoteIntentRequest) (*QuoteIntentResponse, error) { + out := struct { + Ret0 *QuoteIntentResponse + }{} + + resp, err := doHTTPRequest(ctx, c.client, c.urls[3], quoteIntentRequest, &out.Ret0) + 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 *trailsClient) CommitIntent(ctx context.Context, commitIntentRequest CommitIntentRequest) (*CommitIntentResponse, error) { + out := struct { + Ret0 *CommitIntentResponse + }{} + + resp, err := doHTTPRequest(ctx, c.client, c.urls[4], commitIntentRequest, &out.Ret0) + 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 *trailsClient) ExecuteIntent(ctx context.Context, executeIntentRequest ExecuteIntentRequest) (*ExecuteIntentResponse, error) { + out := struct { + Ret0 *ExecuteIntentResponse + }{} + + resp, err := doHTTPRequest(ctx, c.client, c.urls[5], executeIntentRequest, &out.Ret0) + 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 *trailsClient) WaitIntentReceipt(ctx context.Context, waitIntentReceiptRequest WaitIntentReceiptRequest) (*WaitIntentReceiptResponse, error) { + out := struct { + Ret0 *WaitIntentReceiptResponse + }{} + + resp, err := doHTTPRequest(ctx, c.client, c.urls[6], waitIntentReceiptRequest, &out.Ret0) + 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 *trailsClient) GetIntentReceipt(ctx context.Context, getIntentReceiptRequest GetIntentReceiptRequest) (*GetIntentReceiptResponse, error) { + out := struct { + Ret0 *GetIntentReceiptResponse + }{} + + resp, err := doHTTPRequest(ctx, c.client, c.urls[7], getIntentReceiptRequest, &out.Ret0) + 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 *trailsClient) GetIntent(ctx context.Context, getIntentRequest GetIntentRequest) (*GetIntentResponse, error) { + out := struct { + Ret0 *GetIntentResponse + }{} + + resp, err := doHTTPRequest(ctx, c.client, c.urls[8], getIntentRequest, &out.Ret0) + 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 *trailsClient) SearchIntents(ctx context.Context, searchIntentsRequest SearchIntentsRequest) (*SearchIntentsResponse, error) { + out := struct { + Ret0 *SearchIntentsResponse + }{} + + resp, err := doHTTPRequest(ctx, c.client, c.urls[9], searchIntentsRequest, &out.Ret0) + 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 *trailsClient) GetIntentHistory(ctx context.Context, getIntentHistoryRequest GetIntentHistoryRequest) (*GetIntentHistoryResponse, error) { + out := struct { + Ret0 *GetIntentHistoryResponse + }{} + + resp, err := doHTTPRequest(ctx, c.client, c.urls[10], getIntentHistoryRequest, &out.Ret0) + 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 *trailsClient) AbortIntent(ctx context.Context, abortIntentRequest AbortIntentRequest) (*AbortIntentResponse, error) { + out := struct { + Ret0 *AbortIntentResponse + }{} + + resp, err := doHTTPRequest(ctx, c.client, c.urls[11], abortIntentRequest, &out.Ret0) + 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 *trailsClient) GetChains(ctx context.Context, getChainsRequest GetChainsRequest) (*GetChainsResponse, error) { + out := struct { + Ret0 *GetChainsResponse + }{} + + resp, err := doHTTPRequest(ctx, c.client, c.urls[12], getChainsRequest, &out.Ret0) + 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 *trailsClient) GetExactOutputRoutes(ctx context.Context, getExactOutputRoutesRequest GetExactOutputRoutesRequest) (*GetExactOutputRoutesResponse, error) { + out := struct { + Ret0 *GetExactOutputRoutesResponse + }{} + + resp, err := doHTTPRequest(ctx, c.client, c.urls[13], getExactOutputRoutesRequest, &out.Ret0) + 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 *trailsClient) GetExactInputRoutes(ctx context.Context, getExactInputRoutesRequest GetExactInputRoutesRequest) (*GetExactInputRoutesResponse, error) { + out := struct { + Ret0 *GetExactInputRoutesResponse + }{} + + resp, err := doHTTPRequest(ctx, c.client, c.urls[14], getExactInputRoutesRequest, &out.Ret0) + 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 *trailsClient) GetTokenList(ctx context.Context, getTokenListRequest GetTokenListRequest) (*GetTokenListResponse, error) { + out := struct { + Ret0 *GetTokenListResponse + }{} + + resp, err := doHTTPRequest(ctx, c.client, c.urls[15], getTokenListRequest, &out.Ret0) + 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 *trailsClient) GetTokenPrices(ctx context.Context, getTokenPricesRequest GetTokenPricesRequest) (*GetTokenPricesResponse, error) { + out := struct { + Ret0 *GetTokenPricesResponse + }{} + + resp, err := doHTTPRequest(ctx, c.client, c.urls[16], getTokenPricesRequest, &out.Ret0) + 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 *trailsClient) GetTrailsContracts(ctx context.Context) (*TrailsContracts, error) { + out := struct { + Ret0 *TrailsContracts `json:"TrailsContracts"` + }{} + + resp, err := doHTTPRequest(ctx, c.client, c.urls[17], 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 (c *trailsClient) GetIntentTransactionHistory(ctx context.Context, getIntentTransactionHistoryRequest GetIntentTransactionHistoryRequest) (*GetIntentTransactionHistoryResponse, error) { + out := struct { + Ret0 *GetIntentTransactionHistoryResponse + }{} + + resp, err := doHTTPRequest(ctx, c.client, c.urls[18], getIntentTransactionHistoryRequest, &out.Ret0) + 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 +} + +// +// Client helpers +// + +// HTTPClient is the interface used by generated clients to send HTTP requests. +// It is fulfilled by *(net/http).Client, which is sufficient for most users. +// Users can provide their own implementation for special retry policies. +type HTTPClient interface { + Do(req *http.Request) (*http.Response, error) +} + +// urlBase helps ensure that addr specifies a scheme. If it is unparsable +// as a URL, it returns addr unchanged. +func urlBase(addr string) string { + // If the addr specifies a scheme, use it. If not, default to + // http. If url.Parse fails on it, return it unchanged. + url, err := url.Parse(addr) + if err != nil { + return addr + } + if url.Scheme == "" { + url.Scheme = "http" + } + return url.String() +} + +// newRequest makes an http.Request from a client, adding common headers. +func newRequest(ctx context.Context, url string, reqBody io.Reader, contentType string) (*http.Request, error) { + req, err := http.NewRequestWithContext(ctx, "POST", url, reqBody) + if err != nil { + return nil, err + } + 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] { + req.Header.Add(k, v) + } + } + } + return req, nil +} + +// doHTTPRequest is common code to make a request to the remote service. +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.WithCausef("failed to marshal JSON body: %w", err) + } + if err = ctx.Err(); err != nil { + 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.WithCausef("could not build request: %w", err) + } + + resp, err := client.Do(req) + if err != nil { + return nil, ErrWebrpcRequestFailed.WithCause(err) + } + + if resp.StatusCode != 200 { + respBody, err := io.ReadAll(resp.Body) + if err != nil { + 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.WithCausef("failed to unmarshal server error: %w", err) + } + if rpcErr.Cause != "" { + rpcErr.cause = errors.New(rpcErr.Cause) + } + return nil, rpcErr + } + + if out != nil { + respBody, err := io.ReadAll(resp.Body) + if err != nil { + return nil, ErrWebrpcBadResponse.WithCausef("failed to read response body: %w", err) + } + + err = json.Unmarshal(respBody, &out) + if err != nil { + return nil, ErrWebrpcBadResponse.WithCausef("failed to unmarshal JSON response body: %w", err) + } + } + + return resp, nil +} + +func WithHTTPRequestHeaders(ctx context.Context, h http.Header) (context.Context, error) { + if _, ok := h["Accept"]; ok { + return nil, errors.New("provided header cannot set Accept") + } + if _, ok := h["Content-Type"]; ok { + return nil, errors.New("provided header cannot set Content-Type") + } + + copied := make(http.Header, len(h)) + for k, vv := range h { + if vv == nil { + copied[k] = nil + continue + } + copied[k] = make([]string, len(vv)) + copy(copied[k], vv) + } + + return context.WithValue(ctx, HTTPClientRequestHeadersCtxKey, copied), nil +} + +func HTTPRequestHeaders(ctx context.Context) (http.Header, bool) { + h, ok := ctx.Value(HTTPClientRequestHeadersCtxKey).(http.Header) + return h, ok +} + +// +// Webrpc helpers +// + +type contextKey struct { + name string +} + +func (k *contextKey) String() string { + return "webrpc context value " + k.name +} + +var ( + HTTPClientRequestHeadersCtxKey = &contextKey{"HTTPClientRequestHeaders"} +) + +// PtrTo is a useful helper when constructing values for optional fields. +func PtrTo[T any](v T) *T { return &v } + +// +// BigInt helpers +// + +// BigInt is an alias of big.Int with custom JSON (decimal string) encoding. +type BigInt big.Int + +func NewBigInt(v int64) BigInt { var bi big.Int; bi.SetInt64(v); return BigInt(bi) } + +// AsInt exposes the underlying *big.Int. +func (b *BigInt) AsInt() *big.Int { return (*big.Int)(b) } + +// String returns the decimal string representation of the BigInt. +func (b BigInt) String() string { return b.AsInt().String() } + +// MarshalText implements encoding.TextMarshaler. +func (b BigInt) MarshalText() ([]byte, error) { + return []byte(fmt.Sprintf("\"%s\"", b.String())), nil +} + +// UnmarshalText implements encoding.TextUnmarshaler. +func (b *BigInt) UnmarshalText(text []byte) error { + if len(text) == 0 { + return nil + } + if len(text) == 4 && text[0] == 'n' && string(text) == "null" { + return nil + } + for _, c := range text { + if c == ' ' || c == '\t' || c == '\n' || c == '\r' { + return fmt.Errorf("BigInt.UnmarshalText: unexpected whitespace in %q", text) + } + } + var digits []byte + if text[0] == '-' || (text[0] >= '0' && text[0] <= '9') { + digits = text + } else { + if len(text) < 2 || text[0] != '"' || text[len(text)-1] != '"' { + return fmt.Errorf("BigInt.UnmarshalText: unsupported format %q", text) + } + digits = text[1 : len(text)-1] + } + i, ok := big.NewInt(0).SetString(string(digits), 10) + if !ok { + return fmt.Errorf("BigInt.UnmarshalText: failed to parse %q", text) + } + *b = BigInt(*i) + return nil +} + +// MarshalJSON implements json.Marshaler +func (b BigInt) MarshalJSON() ([]byte, error) { + return b.MarshalText() +} + +// UnmarshalJSON implements json.Unmarshaler +func (b *BigInt) UnmarshalJSON(text []byte) error { + if string(text) == "null" { + return nil + } + return b.UnmarshalText(text) +} + +// MarshalBinary implements encoding.BinaryMarshaler. The first byte is the sign byte +// to represent positive or negative numbers. +func (b BigInt) MarshalBinary() ([]byte, error) { + bytes := b.AsInt().Bytes() + out := make([]byte, len(bytes)+1) + copy(out[1:], bytes) + if b.AsInt().Sign() < 0 { + // Prepend a sign byte (0xFF for negative) + out[0] = 0xFF + } else { + // For zero or positive numbers, prepend 0x00 + out[0] = 0x00 + } + return out, nil +} + +// UnmarshalBinary implements encoding.BinaryUnmarshaler. The first byte is the sign byte +// to represent positive or negative numbers. +func (b *BigInt) UnmarshalBinary(buff []byte) error { + if len(buff) == 0 { + *b = BigInt(*big.NewInt(0)) + return nil + } + // Extract the sign byte + signByte := buff[0] + i := new(big.Int) + if len(buff) > 1 { + i.SetBytes(buff[1:]) + } + // Apply sign if negative + if signByte == 0xFF { + i.Neg(i) + } + *b = BigInt(*i) + return nil +} + +func (b BigInt) Value() (driver.Value, error) { + return b.String(), nil +} + +func (b *BigInt) Scan(src interface{}) error { + if src == nil { + return nil + } + + var svalue string + switch v := src.(type) { + case string: + svalue = v + case []byte: + svalue = string(v) + default: + return fmt.Errorf("BigInt.Scan: unexpected type %T", src) + } + + // pgx driver returns NeX where N is digits and X is exponent + parts := strings.SplitN(svalue, "e", 2) + + var ok bool + i := &big.Int{} + i, ok = i.SetString(parts[0], 10) + if !ok { + return fmt.Errorf("BigInt.Scan: failed to scan value %q", svalue) + } + + if len(parts) >= 2 { + exp := big.NewInt(0) + exp, ok = exp.SetString(parts[1], 10) + if !ok { + return fmt.Errorf("BigInt.Scan failed to scan exp component %q", svalue) + } + i = i.Mul(i, big.NewInt(1).Exp(big.NewInt(10), exp, nil)) + } + + *b = BigInt(*i) + return nil +} + +// +// Errors +// + +type WebRPCError struct { + Name string `json:"error"` + Code int `json:"code"` + Message string `json:"msg"` + Cause string `json:"cause,omitempty"` + HTTPStatus int `json:"status"` + cause error +} + +var _ error = WebRPCError{} + +func (e WebRPCError) Error() string { + if e.cause != nil { + return fmt.Sprintf("%s %d: %s: %v", e.Name, e.Code, e.Message, e.cause) + } + return fmt.Sprintf("%s %d: %s", e.Name, e.Code, e.Message) +} + +func (e WebRPCError) Is(target error) bool { + if target == nil { + return false + } + if rpcErr, ok := target.(WebRPCError); ok { + return rpcErr.Code == e.Code + } + return errors.Is(e.cause, target) +} + +func (e WebRPCError) Unwrap() error { + return e.cause +} + +func (e WebRPCError) WithCause(cause error) WebRPCError { + err := e + err.cause = cause + err.Cause = cause.Error() + return err +} + +func (e WebRPCError) WithCausef(format string, args ...interface{}) WebRPCError { + cause := fmt.Errorf(format, args...) + err := e + err.cause = cause + err.Cause = cause.Error() + return err +} + +// Deprecated: Use .WithCause() method on WebRPCError. +func ErrorWithCause(rpcErr WebRPCError, cause error) WebRPCError { + return rpcErr.WithCause(cause) +} + +// Webrpc errors +var ( + ErrWebrpcEndpoint = WebRPCError{Code: 0, Name: "WebrpcEndpoint", Message: "endpoint error", HTTPStatus: 400} + ErrWebrpcRequestFailed = WebRPCError{Code: -1, Name: "WebrpcRequestFailed", Message: "request failed", HTTPStatus: 400} + ErrWebrpcBadRoute = WebRPCError{Code: -2, Name: "WebrpcBadRoute", Message: "bad route", HTTPStatus: 404} + ErrWebrpcBadMethod = WebRPCError{Code: -3, Name: "WebrpcBadMethod", Message: "bad method", HTTPStatus: 405} + ErrWebrpcBadRequest = WebRPCError{Code: -4, Name: "WebrpcBadRequest", Message: "bad request", HTTPStatus: 400} + ErrWebrpcBadResponse = WebRPCError{Code: -5, Name: "WebrpcBadResponse", Message: "bad response", HTTPStatus: 500} + ErrWebrpcServerPanic = WebRPCError{Code: -6, Name: "WebrpcServerPanic", Message: "server panic", HTTPStatus: 500} + ErrWebrpcInternalError = WebRPCError{Code: -7, Name: "WebrpcInternalError", Message: "internal error", HTTPStatus: 500} + ErrWebrpcClientAborted = WebRPCError{Code: -8, Name: "WebrpcClientAborted", Message: "request aborted by client", HTTPStatus: 400} + ErrWebrpcStreamLost = WebRPCError{Code: -9, Name: "WebrpcStreamLost", Message: "stream lost", HTTPStatus: 400} + ErrWebrpcStreamFinished = WebRPCError{Code: -10, Name: "WebrpcStreamFinished", Message: "stream finished", HTTPStatus: 200} +) + +// Schema errors +var ( + ErrUnauthorized = WebRPCError{Code: 1000, Name: "Unauthorized", Message: "Unauthorized access", HTTPStatus: 401} + ErrPermissionDenied = WebRPCError{Code: 1001, Name: "PermissionDenied", Message: "Permission denied", HTTPStatus: 403} + ErrSessionExpired = WebRPCError{Code: 1002, Name: "SessionExpired", Message: "Session expired", HTTPStatus: 403} + 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} + 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: 1008, 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 request exceeded", HTTPStatus: 429} + ErrQuotaRateLimit = WebRPCError{Code: 1201, Name: "QuotaRateLimit", Message: "Quota 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: 2000, Name: "InvalidArgument", Message: "Invalid argument", HTTPStatus: 400} + ErrUnexpected = WebRPCError{Code: 2001, Name: "Unexpected", Message: "Unexpected server error", HTTPStatus: 500} + ErrUnavailable = WebRPCError{Code: 2002, Name: "Unavailable", Message: "Unavailable resource", HTTPStatus: 400} + ErrQueryFailed = WebRPCError{Code: 2003, Name: "QueryFailed", Message: "Query failed", HTTPStatus: 400} + ErrIntentStatus = WebRPCError{Code: 2004, Name: "IntentStatus", Message: "Invalid intent status", HTTPStatus: 422} + ErrNotFound = WebRPCError{Code: 8000, Name: "NotFound", Message: "Resource not found", HTTPStatus: 400} + ErrUnsupportedNetwork = WebRPCError{Code: 8008, Name: "UnsupportedNetwork", Message: "Unsupported network", HTTPStatus: 422} + ErrClientOutdated = WebRPCError{Code: 8009, Name: "ClientOutdated", Message: "Client is outdated", HTTPStatus: 422} + ErrIntentsSkipped = WebRPCError{Code: 7000, Name: "IntentsSkipped", Message: "Intents skipped as client is attempting a transaction that does not require intents", HTTPStatus: 400} + ErrQuoteExpired = WebRPCError{Code: 7001, Name: "QuoteExpired", Message: "Intent quote has expired. Please try again.", HTTPStatus: 400} + ErrIntentsDisabled = WebRPCError{Code: 9000, Name: "IntentsDisabled", Message: "Intents service is currently unavailable", HTTPStatus: 400} +) + +const WebrpcHeader = "Webrpc" + +const WebrpcHeaderValue = "webrpc@v0.31.3;gen-golang@v0.23.3;trails-api@v1-25.12.24-babcedf7" + +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 +}