From ecac71c6dd133af81411d5321ddf918e2ef02d6f Mon Sep 17 00:00:00 2001 From: slowbackspace Date: Sun, 3 Dec 2023 23:53:12 +0100 Subject: [PATCH] feat: add AddressUTXOsAsset and AddressUTXOsAssetAll --- api_addresses.go | 74 +++++++++++++++++++++++++++++++ api_addresses_test.go | 21 +++++++++ api_transactions.go | 28 ------------ client.go | 2 + testdata/addressutxosasset.golden | 1 + 5 files changed, 98 insertions(+), 28 deletions(-) create mode 100644 testdata/addressutxosasset.golden diff --git a/api_addresses.go b/api_addresses.go index bcf0ad5..36ad538 100644 --- a/api_addresses.go +++ b/api_addresses.go @@ -62,6 +62,7 @@ type AddressTransactions struct { } type AddressUTXO struct { + Address string `json:"address"` // Transaction hash of the UTXO TxHash string `json:"tx_hash"` @@ -74,6 +75,9 @@ type AddressUTXO struct { // The hash of the transaction output datum DataHash string `json:"data_hash"` + + InlineDatum string `json:"inline_datum"` + ReferenceScriptHash string `json:"reference_script_hash"` } type AddressTxResult struct { @@ -268,3 +272,73 @@ func (c *apiClient) AddressUTXOsAll(ctx context.Context, address string) <-chan }() return ch } + +func (c *apiClient) AddressUTXOsAsset(ctx context.Context, address, asset string, query APIQueryParams) (utxos []AddressUTXO, err error) { + requestUrl, err := url.Parse(fmt.Sprintf("%s/%s/%s/%s/%s", c.server, resourceAddresses, address, resourceUTXOs, asset)) + if err != nil { + return + } + req, err := http.NewRequestWithContext(ctx, http.MethodGet, requestUrl.String(), nil) + if err != nil { + return + } + v := req.URL.Query() + query.From = "" + query.To = "" + v = formatParams(v, query) + req.URL.RawQuery = v.Encode() + + res, err := c.handleRequest(req) + if err != nil { + return + } + defer res.Body.Close() + + if err = json.NewDecoder(res.Body).Decode(&utxos); err != nil { + return + } + return utxos, nil +} + +func (c *apiClient) AddressUTXOsAssetAll(ctx context.Context, address, asset string) <-chan AddressUTXOResult { + ch := make(chan AddressUTXOResult, c.routines) + jobs := make(chan methodOptions, c.routines) + quit := make(chan bool, 1) + + wg := sync.WaitGroup{} + + for i := 0; i < c.routines; i++ { + wg.Add(1) + go func(jobs chan methodOptions, ch chan AddressUTXOResult, wg *sync.WaitGroup) { + defer wg.Done() + for j := range jobs { + autxo, err := c.AddressUTXOsAsset(j.ctx, address, asset, j.query) + if len(autxo) != j.query.Count || err != nil { + select { + case quit <- true: + default: + } + } + res := AddressUTXOResult{Res: autxo, Err: err} + ch <- res + } + + }(jobs, ch, &wg) + } + go func() { + defer close(ch) + fetchNextPage := true + for i := 1; fetchNextPage; i++ { + select { + case <-quit: + fetchNextPage = false + default: + jobs <- methodOptions{ctx: ctx, query: APIQueryParams{Count: 100, Page: i}} + } + } + + close(jobs) + wg.Wait() + }() + return ch +} diff --git a/api_addresses_test.go b/api_addresses_test.go index 9015434..e68fb82 100644 --- a/api_addresses_test.go +++ b/api_addresses_test.go @@ -152,3 +152,24 @@ func TestAddressUTXOs(t *testing.T) { var want []blockfrost.AddressUTXO testIntUtil(t, fp, &got, &want) } + +func TestAddressUTXOsAsset(t *testing.T) { + addr := "addr1q8zsjx7vxkl4esfejafhxthyew8c54c9ch95gkv3nz37sxrc9ty742qncmffaesxqarvqjmxmy36d9aht2duhmhvekgq3jd3w2" + asset := "d436d9f6b754582f798fe33f4bed12133d47493f78b944b9cc55fd1853756d6d69744c6f64676534393539" + + api := blockfrost.NewAPIClient(blockfrost.APIClientOptions{}) + + got, err := api.AddressUTXOsAsset( + context.TODO(), + addr, + asset, + blockfrost.APIQueryParams{}, + ) + if err != nil { + t.Fatal(err) + } + + fp := filepath.Join(testdata, strings.ToLower(strings.TrimLeft(t.Name(), "Test"))+".golden") + var want []blockfrost.AddressUTXO + testIntUtil(t, fp, &got, &want) +} diff --git a/api_transactions.go b/api_transactions.go index e333202..3a32255 100644 --- a/api_transactions.go +++ b/api_transactions.go @@ -576,34 +576,6 @@ func (c *apiClient) TransactionSubmit(ctx context.Context, cbor []byte) (hash st return hash, nil } -// func readSubmitTx(data []byte) error { -// value, dataType, _, err := jsonparser.Get(data, "result", "SubmitFail") -// if err != nil { -// if errors.Is(err, jsonparser.KeyPathNotFoundError) { -// return nil -// } -// return fmt.Errorf("failed to parse SubmitTx response: %w", err) -// } - -// switch dataType { -// case jsonparser.Array: -// var messages []json.RawMessage -// if err := json.Unmarshal(value, &messages); err != nil { -// return fmt.Errorf("failed to parse SubmitTx response: array: %w", err) -// } -// if len(messages) == 0 { -// return nil -// } -// return SubmitTxError{messages: messages} - -// case jsonparser.Object: -// return SubmitTxError{messages: []json.RawMessage{value}} - -// default: -// return fmt.Errorf("SubmitTx failed: %v", string(value)) -// } -// } - func (c *apiClient) TransactionEvaluate(ctx context.Context, cbor []byte) (jsonResponse OgmiosResponse, err error) { requestUrl, err := url.Parse(fmt.Sprintf("%s/%s", c.server, resourceTxEvaluate)) diff --git a/client.go b/client.go index 1b18f9c..630597f 100644 --- a/client.go +++ b/client.go @@ -105,6 +105,8 @@ type APIClient interface { AddressTransactionsAll(ctx context.Context, address string) <-chan AddressTxResult AddressUTXOs(ctx context.Context, address string, query APIQueryParams) ([]AddressUTXO, error) AddressUTXOsAll(ctx context.Context, address string) <-chan AddressUTXOResult + AddressUTXOsAsset(ctx context.Context, address, asset string, query APIQueryParams) ([]AddressUTXO, error) + AddressUTXOsAssetAll(ctx context.Context, address, asset string) <-chan AddressUTXOResult Account(ctx context.Context, stakeAddress string) (Account, error) AccountHistory(ctx context.Context, stakeAddress string, query APIQueryParams) ([]AccountHistory, error) AccountHistoryAll(ctx context.Context, address string) <-chan AccountHistoryResult diff --git a/testdata/addressutxosasset.golden b/testdata/addressutxosasset.golden new file mode 100644 index 0000000..91d1bee --- /dev/null +++ b/testdata/addressutxosasset.golden @@ -0,0 +1 @@ +[{"address":"addr1q8zsjx7vxkl4esfejafhxthyew8c54c9ch95gkv3nz37sxrc9ty742qncmffaesxqarvqjmxmy36d9aht2duhmhvekgq3jd3w2","tx_hash":"619c90076e587b000856cc1a0830c45a51ec6ba9f8f8fb00dc9f4406b06ccf72","tx_index":1,"output_index":1,"amount":[{"unit":"lovelace","quantity":"1185250"},{"unit":"d436d9f6b754582f798fe33f4bed12133d47493f78b944b9cc55fd1853756d6d69744c6f64676534393539","quantity":"1"}],"block":"181f6b0368066e245e9510414d928f78a7ed73da33ffb2657103937275f8bb3d","data_hash":null,"inline_datum":null,"reference_script_hash":null}] \ No newline at end of file