Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ mvn -pl library package # build only the library module
mvn -pl examples package # build only the examples module
```

There are no tests in this project. CI runs `mvn clean package` on PRs targeting `master`.
The library's JUnit 5 tests use JSON fixtures and Mockito mocks of `KrakenRestRequester`, without network access or API keys. Run them with `mvn -pl library test`. CI runs `mvn clean package` on PRs targeting `master`.

Java 25 with Temurin is required (configured via `maven-compiler-plugin` with `<release>25</release>`).

Expand Down
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,24 @@ The endpoint is run through the same `KrakenRestRequester` as the built-in ones,

Pull requests adding such an endpoint to the library are welcome, see the [architecture documentation](docs/ARCHITECTURE.md).

### Funding

The ten Funding operations listed in issue #82 have typed methods: `depositMethods`, `depositAddresses`, `depositStatus`, `withdrawalMethods`, `withdrawalAddresses`, `withdrawalInfo`, `withdraw`, `withdrawalStatus`, `cancelWithdrawal`, and `walletTransfer`. These wrap Kraken's `/0/private` Funding endpoints, now grouped as [Funding (Legacy)](https://docs.kraken.com/api-reference/funding/get-deposit-methods); Funding (Beta) is a separate API group.

```java
List<DepositMethod> methods = api.depositMethods(DepositMethodsParams.builder().asset("XBT").build());
DepositStatus page = api.depositStatus(DepositStatusParams.builder().asset("XBT").cursor(true).limit(25).build());
if (page.nextCursor() != null && !page.nextCursor().isEmpty()) {
DepositStatus nextPage = api.depositStatus(DepositStatusParams.builder().cursor(page.nextCursor()).limit(25).build());
}
WithdrawalInfo estimate = api.withdrawalInfo(WithdrawalInfoParams.builder()
.asset("XBT").key("my-saved-withdrawal-key").amount(new BigDecimal("0.01")).build());
```

Status methods return the same response record for paginated objects and unpaginated arrays. Pass `cursor(true)` to start pagination and then pass each non-empty `nextCursor()` token to retrieve the next page. An unlimited deposit limit is represented by `DepositLimit.unlimited() == true`, with a null amount; a missing limit remains null. Amounts and fees use `BigDecimal`.

`withdraw` submits a withdrawal to a saved key, `cancelWithdrawal` requests cancellation, and `walletTransfer` moves assets from the Spot Wallet to the Futures Wallet. Withdrawal parameters support address confirmation and `maxFee`. A false cancellation result means Kraken did not accept the cancellation. Deposit address parameters support generating a new address and specifying the amount for Lightning invoices; responses preserve destination tags and memos.

### Custom REST requester

The current implementation of the library uses the JDK's HttpsURLConnection to make HTTP request. If that doesn't suit your needs and wish to use something else (e.g. Spring RestTemplate, Apache HttpComponents, OkHttp), you can implement the KrakenRestRequester interface and pass it to the KrakenAPI constructor:
Expand Down
21 changes: 21 additions & 0 deletions library/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,25 @@
<artifactId>kraken-api</artifactId>
<name>Java Kraken API Client</name>

<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>${assertj.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>${mockito.version}</version>
<scope>test</scope>
</dependency>
</dependencies>

</project>
193 changes: 193 additions & 0 deletions library/src/main/java/dev/andstuff/kraken/api/KrakenAPI.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,34 @@
import dev.andstuff.kraken.api.endpoint.earn.response.AllocationStatus;
import dev.andstuff.kraken.api.endpoint.earn.response.EarnAllocations;
import dev.andstuff.kraken.api.endpoint.earn.response.EarnStrategies;
import dev.andstuff.kraken.api.endpoint.funding.CancelWithdrawalEndpoint;
import dev.andstuff.kraken.api.endpoint.funding.DepositAddressesEndpoint;
import dev.andstuff.kraken.api.endpoint.funding.DepositMethodsEndpoint;
import dev.andstuff.kraken.api.endpoint.funding.DepositStatusEndpoint;
import dev.andstuff.kraken.api.endpoint.funding.WalletTransferEndpoint;
import dev.andstuff.kraken.api.endpoint.funding.WithdrawEndpoint;
import dev.andstuff.kraken.api.endpoint.funding.WithdrawalAddressesEndpoint;
import dev.andstuff.kraken.api.endpoint.funding.WithdrawalInfoEndpoint;
import dev.andstuff.kraken.api.endpoint.funding.WithdrawalMethodsEndpoint;
import dev.andstuff.kraken.api.endpoint.funding.WithdrawalStatusEndpoint;
import dev.andstuff.kraken.api.endpoint.funding.params.CancelWithdrawalParams;
import dev.andstuff.kraken.api.endpoint.funding.params.DepositAddressesParams;
import dev.andstuff.kraken.api.endpoint.funding.params.DepositMethodsParams;
import dev.andstuff.kraken.api.endpoint.funding.params.DepositStatusParams;
import dev.andstuff.kraken.api.endpoint.funding.params.WalletTransferParams;
import dev.andstuff.kraken.api.endpoint.funding.params.WithdrawParams;
import dev.andstuff.kraken.api.endpoint.funding.params.WithdrawalAddressesParams;
import dev.andstuff.kraken.api.endpoint.funding.params.WithdrawalInfoParams;
import dev.andstuff.kraken.api.endpoint.funding.params.WithdrawalMethodsParams;
import dev.andstuff.kraken.api.endpoint.funding.params.WithdrawalStatusParams;
import dev.andstuff.kraken.api.endpoint.funding.response.DepositAddress;
import dev.andstuff.kraken.api.endpoint.funding.response.DepositMethod;
import dev.andstuff.kraken.api.endpoint.funding.response.DepositStatus;
import dev.andstuff.kraken.api.endpoint.funding.response.FundingReference;
import dev.andstuff.kraken.api.endpoint.funding.response.WithdrawalAddress;
import dev.andstuff.kraken.api.endpoint.funding.response.WithdrawalInfo;
import dev.andstuff.kraken.api.endpoint.funding.response.WithdrawalMethod;
import dev.andstuff.kraken.api.endpoint.funding.response.WithdrawalStatus;
import dev.andstuff.kraken.api.endpoint.market.AssetInfoEndpoint;
import dev.andstuff.kraken.api.endpoint.market.AssetPairEndpoint;
import dev.andstuff.kraken.api.endpoint.market.ServerTimeEndpoint;
Expand Down Expand Up @@ -68,6 +96,7 @@
import dev.andstuff.kraken.api.rest.KrakenCredentials;
import dev.andstuff.kraken.api.rest.KrakenNonceGenerator;
import dev.andstuff.kraken.api.rest.KrakenRestRequester;

import lombok.Builder;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
Expand Down Expand Up @@ -354,6 +383,170 @@ public boolean cancelReport(String id) {
return query(new RemoveReportEndpoint(RemoveReportParams.of(id, RemovalType.CANCEL))).wasCanceled();
}

/**
* Queries the {@code DepositMethods} endpoint.
*
* @param params the request options
* @return the deposit methods returned by Kraken
* @throws KrakenException if Kraken rejects the request
* @throws IllegalStateException if credentials are missing
*/
public List<DepositMethod> depositMethods(DepositMethodsParams params) {
return query(new DepositMethodsEndpoint(params));
}

/**
* Queries the {@code DepositAddresses} endpoint.
*
* @param params the request options
* @return the deposit addresses returned by Kraken
* @throws KrakenException if Kraken rejects the request
* @throws IllegalStateException if credentials are missing
*/
public List<DepositAddress> depositAddresses(DepositAddressesParams params) {
return query(new DepositAddressesEndpoint(params));
}

/**
* Queries the {@code DepositStatus} endpoint using default options.
*
* @return the deposit status returned by Kraken
* @throws KrakenException if Kraken rejects the request
* @throws IllegalStateException if credentials are missing
*/
public DepositStatus depositStatus() {
return query(new DepositStatusEndpoint());
}

/**
* Queries the {@code DepositStatus} endpoint.
*
* @param params the request options
* @return the deposit status returned by Kraken
* @throws KrakenException if Kraken rejects the request
* @throws IllegalStateException if credentials are missing
*/
public DepositStatus depositStatus(DepositStatusParams params) {
return query(new DepositStatusEndpoint(params));
}

/**
* Queries the {@code WithdrawMethods} endpoint using default options.
*
* @return the withdrawal methods returned by Kraken
* @throws KrakenException if Kraken rejects the request
* @throws IllegalStateException if credentials are missing
*/
public List<WithdrawalMethod> withdrawalMethods() {
return query(new WithdrawalMethodsEndpoint());
}

/**
* Queries the {@code WithdrawMethods} endpoint.
*
* @param params the request options
* @return the withdrawal methods returned by Kraken
* @throws KrakenException if Kraken rejects the request
* @throws IllegalStateException if credentials are missing
*/
public List<WithdrawalMethod> withdrawalMethods(WithdrawalMethodsParams params) {
return query(new WithdrawalMethodsEndpoint(params));
}

/**
* Queries the {@code WithdrawAddresses} endpoint using default options.
*
* @return the withdrawal addresses returned by Kraken
* @throws KrakenException if Kraken rejects the request
* @throws IllegalStateException if credentials are missing
*/
public List<WithdrawalAddress> withdrawalAddresses() {
return query(new WithdrawalAddressesEndpoint());
}

/**
* Queries the {@code WithdrawAddresses} endpoint.
*
* @param params the request options
* @return the withdrawal addresses returned by Kraken
* @throws KrakenException if Kraken rejects the request
* @throws IllegalStateException if credentials are missing
*/
public List<WithdrawalAddress> withdrawalAddresses(WithdrawalAddressesParams params) {
return query(new WithdrawalAddressesEndpoint(params));
}

/**
* Queries the {@code WithdrawInfo} endpoint.
*
* @param params the request options
* @return the withdrawal info returned by Kraken
* @throws KrakenException if Kraken rejects the request
* @throws IllegalStateException if credentials are missing
*/
public WithdrawalInfo withdrawalInfo(WithdrawalInfoParams params) {
return query(new WithdrawalInfoEndpoint(params));
}

/**
* Requests a withdrawal using {@code Withdraw}.
*
* @param params the request options
* @return the withdraw returned by Kraken
* @throws KrakenException if Kraken rejects the request
* @throws IllegalStateException if credentials are missing
*/
public FundingReference withdraw(WithdrawParams params) {
return query(new WithdrawEndpoint(params));
}

/**
* Queries the {@code WithdrawStatus} endpoint using default options.
*
* @return the withdrawal status returned by Kraken
* @throws KrakenException if Kraken rejects the request
* @throws IllegalStateException if credentials are missing
*/
public WithdrawalStatus withdrawalStatus() {
return query(new WithdrawalStatusEndpoint());
}

/**
* Queries the {@code WithdrawStatus} endpoint.
*
* @param params the request options
* @return the withdrawal status returned by Kraken
* @throws KrakenException if Kraken rejects the request
* @throws IllegalStateException if credentials are missing
*/
public WithdrawalStatus withdrawalStatus(WithdrawalStatusParams params) {
return query(new WithdrawalStatusEndpoint(params));
}

/**
* Requests cancellation of a withdrawal using {@code WithdrawCancel}.
*
* @param params the request options
* @return whether Kraken accepted the cancellation; false is a valid unsuccessful result
* @throws KrakenException if Kraken rejects the request
* @throws IllegalStateException if credentials are missing
*/
public boolean cancelWithdrawal(CancelWithdrawalParams params) {
return query(new CancelWithdrawalEndpoint(params));
}

/**
* Transfers funds between wallets using {@code WalletTransfer}.
*
* @param params the request options
* @return the wallet transfer returned by Kraken
* @throws KrakenException if Kraken rejects the request
* @throws IllegalStateException if credentials are missing
*/
public FundingReference walletTransfer(WalletTransferParams params) {
return query(new WalletTransferEndpoint(params));
}

/**
* Queries the private {@code CreateSubaccount} endpoint, creating a trading subaccount. It must be called with an API key of the master account, having the withdraw funds permission.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package dev.andstuff.kraken.api.endpoint.funding;

import com.fasterxml.jackson.core.type.TypeReference;

import dev.andstuff.kraken.api.endpoint.funding.params.CancelWithdrawalParams;
import dev.andstuff.kraken.api.endpoint.priv.PrivateEndpoint;

/**
* The private {@code WithdrawCancel} endpoint for cancel withdrawal.
*/
public class CancelWithdrawalEndpoint extends PrivateEndpoint<Boolean> {

/**
* Creates the {@code WithdrawCancel} endpoint using the required parameters.
*
* @param asset the asset sent to Kraken
* @param referenceId the referenceId sent to Kraken
*/
public CancelWithdrawalEndpoint(String asset, String referenceId) {
this(CancelWithdrawalParams.builder().asset(asset).referenceId(referenceId).build());
}

/**
* Creates the {@code WithdrawCancel} endpoint.
*
* @param params the request parameters
*/
public CancelWithdrawalEndpoint(CancelWithdrawalParams params) {
super("WithdrawCancel", params, new TypeReference<>() {});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package dev.andstuff.kraken.api.endpoint.funding;

import java.util.List;

import com.fasterxml.jackson.core.type.TypeReference;

import dev.andstuff.kraken.api.endpoint.funding.params.DepositAddressesParams;
import dev.andstuff.kraken.api.endpoint.funding.response.DepositAddress;
import dev.andstuff.kraken.api.endpoint.priv.PrivateEndpoint;

/**
* The private {@code DepositAddresses} endpoint for deposit addresses.
*/
public class DepositAddressesEndpoint extends PrivateEndpoint<List<DepositAddress>> {

/**
* Creates the {@code DepositAddresses} endpoint using the required parameters.
*
* @param asset the asset sent to Kraken
* @param method the method sent to Kraken
*/
public DepositAddressesEndpoint(String asset, String method) {
this(DepositAddressesParams.builder().asset(asset).method(method).build());
}

/**
* Creates the {@code DepositAddresses} endpoint.
*
* @param params the request parameters
*/
public DepositAddressesEndpoint(DepositAddressesParams params) {
super("DepositAddresses", params, new TypeReference<>() {});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package dev.andstuff.kraken.api.endpoint.funding;

import java.util.List;

import com.fasterxml.jackson.core.type.TypeReference;

import dev.andstuff.kraken.api.endpoint.funding.params.DepositMethodsParams;
import dev.andstuff.kraken.api.endpoint.funding.response.DepositMethod;
import dev.andstuff.kraken.api.endpoint.priv.PrivateEndpoint;

/**
* The private {@code DepositMethods} endpoint for deposit methods.
*/
public class DepositMethodsEndpoint extends PrivateEndpoint<List<DepositMethod>> {

/**
* Creates the {@code DepositMethods} endpoint using the required parameters.
*
* @param asset the asset sent to Kraken
*/
public DepositMethodsEndpoint(String asset) {
this(DepositMethodsParams.builder().asset(asset).build());
}

/**
* Creates the {@code DepositMethods} endpoint.
*
* @param params the request parameters
*/
public DepositMethodsEndpoint(DepositMethodsParams params) {
super("DepositMethods", params, new TypeReference<>() {});
}
}
Loading