Skip to content

Calling WalletApi.walletBoxesCollect throws an exception #183

@jaysee260

Description

@jaysee260

I am trying to use the new WalletApi to get some boxes from my wallet.

I created the wallet service like this

// conf is of type ErgoToolConfig
val apiClient = new ApiClient(conf.getNode.getNodeApi.getApiUrl, "ApiKeyAuth", conf.getNode.getNodeApi.getApiKey)
val walletService = apiClient.createService(classOf[WalletApi])

Then I'm using it like this to get some amount of ERG from my wallet

val getWalletBoxesRequest = new BoxesRequestHolder().targetBalance(Parameters.OneErg)
val response = walletService.walletBoxesCollect(getWalletBoxesRequest).execute()

But that call throws the following error:

Exception in thread "main" java.lang.IllegalArgumentException: Non-body HTTP method cannot contain @Body.

After a little digging, I found that WalletApi.walletBoxesCollect queries /wallet/boxes/collect in the node's API, which is a POST endpoint.
image

However, I noticed that the appkit method that queries that endpoint has a @GET annotation for it
image

It seemed like the fix was as simple as changing the annotation from @GET to @POST, so I tried it locally. For simplicity's sake, I created a Java interface in my codebase,

public interface NewWalletApi {
    @Headers({
            "Content-Type:application/json"
    })
    @POST("wallet/boxes/collect")
    Call<List<WalletBox>> walletBoxesCollect(
            @retrofit2.http.Body BoxesRequestHolder body
    );
}

updated the wallet service creation to use the interface,

val walletService = apiClient.createService(classOf[NewWalletApi])

and tried executing a request again.

val getWalletBoxesRequest = new BoxesRequestHolder().targetBalance(Parameters.OneErg)
val response = walletService.walletBoxesCollect(getWalletBoxesRequest).execute()

This time, I just got a Bad Request back from the node, no further details.

So I went to the node's Swagger page to inspect that endpoint further (POST /wallet/boxes/collect), and soon learned some things about the expected request body.

image

  1. targetAssets is required and cannot be null.
  2. In the Swagger example, targetAssets is shown as a List of Lists (the BoxesRequestHolder Java model reflects this).
  3. Sending a request, via the Swagger page, following this suggested structure results in a 400 - Bad Request.

I tried out a few combinations

// empty list
{
    "targetAssets": []
    ...
}

// empty list with empty list
{
    "targetAssets": [[]]
    ...
}

// list of lists with "dummy" values
{
    "targetAssets": [["", 0]]
    ...
}

None worked. Until finally I tried this, and it worked!

{
    // empty object/map
    "targetAssets": {}
    ...
}

image

So, it looks like

  1. targetAssets cannot be null but it can be empty.
  2. Despite the Swagger example, targetAssets needs to be an object/map (where each key/value pair represent asset_id/amount), NOT a list of lists (array of arrays).

So, I tried updating the targetAssets property in the BoxesRequestHolder model to a HashMap<String, Long>, executed another request from my code using appkit, but then I got this error:

Note: to facilitate and speed up local testing of this, I created a new model NewBoxesRequestHolder, updated targetAssets as needed, and used it to build a request body instead of BoxesRequestHolder

Click to expand
public class NewBoxesRequestHolder {
    @SerializedName("targetAssets")
    private java.util.HashMap<String, Long> targetAssets = new HashMap<>();

    @SerializedName("targetBalance")
    private Long targetBalance = null;

    public NewBoxesRequestHolder targetAssets(java.util.HashMap<String, Long> targetAssets) {
        this.targetAssets = targetAssets;
        return this;
    }

    public NewBoxesRequestHolder targetBalance(Long targetBalance) {
        this.targetBalance = targetBalance;
        return this;
    }

    public java.util.HashMap<String, Long> getTargetAssets() {
        return targetAssets;
    }

    public void setTargetAssets(java.util.HashMap<String, Long> targetAssets) {
        this.targetAssets = targetAssets;
    }

    public Long getTargetBalance() {
        return targetBalance;
    }

    public void setTargetBalance(Long targetBalance) {
        this.targetBalance = targetBalance;
    }
}

Exception in thread "main" java.lang.IllegalStateException: Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 2 path $ at com.google.gson.stream.JsonReader.beginArray(JsonReader.java:350) at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.read(CollectionTypeAdapterFactory.java:80) at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.read(CollectionTypeAdapterFactory.java:61) at retrofit2.converter.gson.GsonResponseBodyConverter.convert(GsonResponseBodyConverter.java:39) at retrofit2.converter.gson.GsonResponseBodyConverter.convert(GsonResponseBodyConverter.java:27) at retrofit2.OkHttpCall.parseResponse(OkHttpCall.java:225) at retrofit2.OkHttpCall.execute(OkHttpCall.java:188)

While I've figrued out how to send a valid request to POST /wallet/boxes/collect via the node's Swagger page, I have not yet managed to get it to work via appkit.

I'm still into it but this is as far as I've made it. If anyone has any useful insights into how to potentially resolve this issue, they'd be greatly appreciated!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions