diff --git a/xchange-coinbase/pom.xml b/xchange-coinbase/pom.xml index 7e56cdc8687..4086b5c266d 100644 --- a/xchange-coinbase/pom.xml +++ b/xchange-coinbase/pom.xml @@ -1,36 +1,52 @@ - + - 4.0.0 + 4.0.0 - - org.knowm.xchange - xchange-parent + + org.knowm.xchange + xchange-parent 5.2.3-SNAPSHOT - + - xchange-coinbase + xchange-coinbase - XChange Coinbase - XChange implementation for Coinbase + XChange Coinbase + XChange implementation for Coinbase - http://knowm.org/open-source/xchange/ - 2012 - - - Knowm Inc. - http://knowm.org/open-source/xchange/ - - - - + http://knowm.org/open-source/xchange/ + 2012 - - org.knowm.xchange - xchange-core - ${project.version} - - - + + Knowm Inc. + http://knowm.org/open-source/xchange/ + + + + + + + org.knowm.xchange + xchange-core + ${project.version} + + + com.auth0 + java-jwt + + + org.bouncycastle + bcprov-jdk15on + 1.70 + + + org.bouncycastle + bcpkix-jdk15on + 1.70 + + + diff --git a/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/Coinbase.java b/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/Coinbase.java index bc8f6e36564..72ac49a5c09 100644 --- a/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/Coinbase.java +++ b/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/Coinbase.java @@ -9,8 +9,9 @@ import jakarta.ws.rs.core.MediaType; import java.io.IOException; import org.knowm.xchange.coinbase.v2.dto.CoinbaseException; -import org.knowm.xchange.coinbase.v2.dto.marketdata.CoinbaseCurrencyData; +import org.knowm.xchange.coinbase.v2.dto.marketdata.CoinbaseCryptocurrencyData; import org.knowm.xchange.coinbase.v2.dto.marketdata.CoinbaseExchangeRateData; +import org.knowm.xchange.coinbase.v2.dto.marketdata.CoinbaseFiatCurrencyData; import org.knowm.xchange.coinbase.v2.dto.marketdata.CoinbasePriceData; import org.knowm.xchange.coinbase.v2.dto.marketdata.CoinbaseTimeData; import org.slf4j.Logger; @@ -33,7 +34,12 @@ public interface Coinbase { @GET @Path("currencies") - CoinbaseCurrencyData getCurrencies(@HeaderParam(CB_VERSION) String apiVersion) + CoinbaseFiatCurrencyData getFiatCurrencies(@HeaderParam(CB_VERSION) String apiVersion) + throws IOException, CoinbaseException; + + @GET + @Path("currencies/crypto") + CoinbaseCryptocurrencyData getCryptocurrencies(@HeaderParam(CB_VERSION) String apiVersion) throws IOException, CoinbaseException; @GET diff --git a/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/CoinbaseAuthenticated.java b/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/CoinbaseAuthenticated.java index a26857e17b4..3fdcbdd33fe 100644 --- a/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/CoinbaseAuthenticated.java +++ b/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/CoinbaseAuthenticated.java @@ -31,8 +31,8 @@ public interface CoinbaseAuthenticated extends Coinbase { * *

All request bodies should have content type application/json and be valid JSON. * - *

The CB-ACCESS-SIGN header is generated by creating a sha256 HMAC using the secret key on the - * prehash string timestamp + method + requestPath + body (where + represents string + *

The CB-ACCESS-SIGN header is generated by creating a sha256 HMAC using the secret key on + * the prehash string timestamp + method + requestPath + body (where + represents string * concatenation). The timestamp value is the same as the CB-ACCESS-TIMESTAMP header. * *

The body is the request body string or omitted if there is no request body (typically for @@ -47,128 +47,79 @@ public interface CoinbaseAuthenticated extends Coinbase { String CB_ACCESS_SIGN = "CB-ACCESS-SIGN"; String CB_ACCESS_TIMESTAMP = "CB-ACCESS-TIMESTAMP"; + String CB_AUTHORIZATION_KEY = "Authorization"; String CONTENT_TYPE = "Content-Type"; @GET @Path("accounts/{accountId}/transactions") - CoinbaseTransactionsResponse getTransactions( - @HeaderParam(CB_VERSION) String apiVersion, + CoinbaseTransactionsResponse getTransactions(@HeaderParam(CB_VERSION) String apiVersion, @HeaderParam(CB_ACCESS_KEY) String apiKey, @HeaderParam(CB_ACCESS_SIGN) ParamsDigest signature, @HeaderParam(CB_ACCESS_TIMESTAMP) BigDecimal timestamp, - @PathParam("accountId") String accountId) - throws IOException, CoinbaseException; + @PathParam("accountId") String accountId) throws IOException, CoinbaseException; @GET @Path("accounts/{accountId}/buys") - CoinbaseBuySellResponse getBuys( - @HeaderParam(CB_VERSION) String apiVersion, + CoinbaseBuySellResponse getBuys(@HeaderParam(CB_VERSION) String apiVersion, @HeaderParam(CB_ACCESS_KEY) String apiKey, @HeaderParam(CB_ACCESS_SIGN) CoinbaseV2Digest signature, @HeaderParam(CB_ACCESS_TIMESTAMP) BigDecimal timestamp, - @PathParam("accountId") String accountId, - @QueryParam("limit") Integer limit, - @QueryParam("starting_after") String startingAfter) - throws IOException, CoinbaseException; + @PathParam("accountId") String accountId, @QueryParam("limit") Integer limit, + @QueryParam("starting_after") String startingAfter) throws IOException, CoinbaseException; @GET @Path("accounts/{accountId}/sells") - CoinbaseBuySellResponse getSells( - @HeaderParam(CB_VERSION) String apiVersion, + CoinbaseBuySellResponse getSells(@HeaderParam(CB_VERSION) String apiVersion, @HeaderParam(CB_ACCESS_KEY) String apiKey, @HeaderParam(CB_ACCESS_SIGN) CoinbaseV2Digest signature, @HeaderParam(CB_ACCESS_TIMESTAMP) BigDecimal timestamp, - @PathParam("accountId") String accountId, - @QueryParam("limit") Integer limit, - @QueryParam("starting_after") String startingAfter) - throws IOException, CoinbaseException; + @PathParam("accountId") String accountId, @QueryParam("limit") Integer limit, + @QueryParam("starting_after") String startingAfter) throws IOException, CoinbaseException; @GET @Path("accounts/{accountId}/deposits") - Map getDeposits( - @HeaderParam(CB_VERSION) String apiVersion, + Map getDeposits(@HeaderParam(CB_VERSION) String apiVersion, @HeaderParam(CB_ACCESS_KEY) String apiKey, @HeaderParam(CB_ACCESS_SIGN) ParamsDigest signature, @HeaderParam(CB_ACCESS_TIMESTAMP) BigDecimal timestamp, - @PathParam("accountId") String accountId) - throws IOException, CoinbaseException; + @PathParam("accountId") String accountId) throws IOException, CoinbaseException; @GET @Path("accounts/{accountId}/withdrawals") - Map getWithdrawals( - @HeaderParam(CB_VERSION) String apiVersion, + Map getWithdrawals(@HeaderParam(CB_VERSION) String apiVersion, @HeaderParam(CB_ACCESS_KEY) String apiKey, @HeaderParam(CB_ACCESS_SIGN) ParamsDigest signature, @HeaderParam(CB_ACCESS_TIMESTAMP) BigDecimal timestamp, - @PathParam("accountId") String accountId) - throws IOException, CoinbaseException; + @PathParam("accountId") String accountId) throws IOException, CoinbaseException; @GET @Path("accounts") CoinbaseAccountsData getAccounts( - @HeaderParam(CB_VERSION) String apiVersion, - @HeaderParam(CB_ACCESS_KEY) String apiKey, - @HeaderParam(CB_ACCESS_SIGN) ParamsDigest signature, - @HeaderParam(CB_ACCESS_TIMESTAMP) BigDecimal timestamp, - @QueryParam("limit") Integer limit, - @QueryParam("starting_after") String starting_after) + @HeaderParam(CB_AUTHORIZATION_KEY) ParamsDigest authTokenGenerator, + @QueryParam("limit") Integer limit, @QueryParam("starting_after") String starting_after) throws IOException, CoinbaseException; @GET @Path("accounts/{currency}") - CoinbaseAccountData getAccount( - @HeaderParam(CB_VERSION) String apiVersion, - @HeaderParam(CB_ACCESS_KEY) String apiKey, - @HeaderParam(CB_ACCESS_SIGN) CoinbaseV2Digest signature, - @HeaderParam(CB_ACCESS_TIMESTAMP) BigDecimal timestamp, - @PathParam("currency") String currency) - throws IOException, CoinbaseException; - - @POST - @Path("accounts") - @Consumes(MediaType.APPLICATION_JSON) - CoinbaseAccountData createAccount( - @HeaderParam(CONTENT_TYPE) String contentType, - @HeaderParam(CB_VERSION) String apiVersion, - @HeaderParam(CB_ACCESS_KEY) String apiKey, - @HeaderParam(CB_ACCESS_SIGN) String signature, - @HeaderParam(CB_ACCESS_TIMESTAMP) BigDecimal timestamp, - Object payload) - throws IOException, CoinbaseException; - - @GET - @Path("payment-methods") - CoinbasePaymentMethodsData getPaymentMethods( - @HeaderParam(CB_VERSION) String apiVersion, - @HeaderParam(CB_ACCESS_KEY) String apiKey, - @HeaderParam(CB_ACCESS_SIGN) CoinbaseV2Digest signature, - @HeaderParam(CB_ACCESS_TIMESTAMP) BigDecimal timestamp) - throws IOException, CoinbaseException; + CoinbaseAccountData getAccount(@HeaderParam(CB_AUTHORIZATION_KEY) ParamsDigest authTokenGenerator, + @PathParam("currency") String currency) throws IOException, CoinbaseException; @POST @Path("accounts/{account}/buys") @Consumes(MediaType.APPLICATION_JSON) - CoinbaseBuyData buy( - @HeaderParam(CONTENT_TYPE) String contentType, - @HeaderParam(CB_VERSION) String apiVersion, - @HeaderParam(CB_ACCESS_KEY) String apiKey, + CoinbaseBuyData buy(@HeaderParam(CONTENT_TYPE) String contentType, + @HeaderParam(CB_VERSION) String apiVersion, @HeaderParam(CB_ACCESS_KEY) String apiKey, @HeaderParam(CB_ACCESS_SIGN) String signature, @HeaderParam(CB_ACCESS_TIMESTAMP) BigDecimal timestamp, - @PathParam("account") String accountId, - Object payload) - throws IOException, CoinbaseException; + @PathParam("account") String accountId, Object payload) throws IOException, CoinbaseException; @POST @Path("accounts/{account}/sells") @Consumes(MediaType.APPLICATION_JSON) - CoinbaseSellData sell( - @HeaderParam(CONTENT_TYPE) String contentType, - @HeaderParam(CB_VERSION) String apiVersion, - @HeaderParam(CB_ACCESS_KEY) String apiKey, + CoinbaseSellData sell(@HeaderParam(CONTENT_TYPE) String contentType, + @HeaderParam(CB_VERSION) String apiVersion, @HeaderParam(CB_ACCESS_KEY) String apiKey, @HeaderParam(CB_ACCESS_SIGN) String signature, @HeaderParam(CB_ACCESS_TIMESTAMP) BigDecimal timestamp, - @PathParam("account") String accountId, - Object payload) - throws IOException, CoinbaseException; + @PathParam("account") String accountId, Object payload) throws IOException, CoinbaseException; } diff --git a/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/CoinbaseExchange.java b/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/CoinbaseExchange.java index 7c95f4e19bd..b56cad6050f 100644 --- a/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/CoinbaseExchange.java +++ b/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/CoinbaseExchange.java @@ -6,6 +6,10 @@ import org.knowm.xchange.coinbase.v2.service.CoinbaseAccountService; import org.knowm.xchange.coinbase.v2.service.CoinbaseMarketDataService; import org.knowm.xchange.coinbase.v2.service.CoinbaseTradeService; +import org.knowm.xchange.service.account.AccountService; +import org.knowm.xchange.service.marketdata.MarketDataService; +import org.knowm.xchange.service.trade.TradeService; +import org.knowm.xchange.utils.AuthUtils; public class CoinbaseExchange extends BaseExchange implements Exchange { diff --git a/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/CoinbaseV2Digest.java b/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/CoinbaseV2Digest.java index fe7cbede7fb..41c125b1a9f 100644 --- a/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/CoinbaseV2Digest.java +++ b/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/CoinbaseV2Digest.java @@ -1,30 +1,243 @@ package org.knowm.xchange.coinbase.v2; -import static org.knowm.xchange.coinbase.v2.CoinbaseAuthenticated.CB_ACCESS_TIMESTAMP; - -import jakarta.ws.rs.HeaderParam; +import com.auth0.jwt.JWT; +import com.auth0.jwt.algorithms.Algorithm; +import java.io.IOException; +import java.io.StringReader; +import java.security.GeneralSecurityException; +import java.security.KeyFactory; +import java.security.KeyPair; +import java.security.SecureRandom; +import java.security.Security; +import java.security.interfaces.ECPrivateKey; +import java.security.interfaces.ECPublicKey; +import java.security.spec.ECFieldFp; +import java.time.Instant; +import java.util.Collections; +import java.util.Date; +import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; +import org.bouncycastle.jce.ECNamedCurveTable; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec; +import org.bouncycastle.openssl.PEMKeyPair; +import org.bouncycastle.openssl.PEMParser; +import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter; import org.knowm.xchange.service.BaseParamsDigest; -import org.knowm.xchange.utils.DigestUtils; import si.mazi.rescu.RestInvocation; +/** + *

Generates ECDSA-signed JWT tokens for authenticating requests to the Coinbase Advanced + * Trading API. This class handles key pair initialization, JWT generation, and Bearer token + * construction for REST API requests.

+ * + *

Uses the BouncyCastle provider for elliptic curve cryptography operations (P-256 curve) and + * the Java Cryptographic Extension (JCE) for key pair derivation.

+ * + * @since 1.0 + */ public class CoinbaseV2Digest extends BaseParamsDigest { - private CoinbaseV2Digest(String secretKey) { + private static final SecureRandom RNG = new SecureRandom(); + + static { + if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) { + Security.addProvider(new BouncyCastleProvider()); + } + } + + private final ECPrivateKey privateKey; + private final ECPublicKey publicKey; + private final String keyName; + + /** + *

Initializes a new digest instance with the provided API key name and secret key.

+ * + *

Performs the following actions:

+ *
    + *
  1. Registers the BouncyCastle security provider if not already present
  2. + *
  3. Parses and validates the EC key pair from the secret key PEM
  4. + *
  5. Stores the public/private keys and key name for JWT generation
  6. + *
+ * + * @param keyName API key name (identifies the key in Coinbase API) + * @param secretKey PEM-encoded private key string (ECDSA P-256 format) + * @throws Exception If key parsing fails or BouncyCastle provider initialization fails + * @see #createInstance(String, String) + */ + + private CoinbaseV2Digest(String keyName, String secretKey) throws Exception { super(secretKey, HMAC_SHA_256); + + KeyPair kp = loadECKeyPair(normalizePem(secretKey)); + this.publicKey = (ECPublicKey) kp.getPublic(); + this.privateKey = (ECPrivateKey) kp.getPrivate(); + this.keyName = keyName; } - public static CoinbaseV2Digest createInstance(String secretKey) { - return secretKey == null ? null : new CoinbaseV2Digest(secretKey); + /** + * Creates a new instance of CoinbaseV3Digest. + * + * @param keyName API key name + * @param secretKey PEM-encoded private key string + * @return Initialized digest instance + * @throws IllegalStateException If key parsing fails or provider initialization fails + */ + + public static CoinbaseV2Digest createInstance(String keyName, String secretKey) { + if (keyName == null || secretKey == null) { + return null; + } + try { + return new CoinbaseV2Digest(keyName, secretKey); + } catch (Exception e) { + throw new IllegalStateException("Failed to initialize CoinbaseV2Digest", e); + } } + /** + *

Loads an EC key pair from PEM-encoded input, supporting both PEMKeyPair and PrivateKeyInfo + * formats.

+ * + *

For PrivateKeyInfo inputs, derives the public key using the P-256 curve parameters.

+ * + * @param secretKey PEM-encoded private key string + * @return KeyPair containing the EC private and public keys + * @throws IOException If parsing fails + * @throws GeneralSecurityException If key derivation or conversion fails + * @throws IllegalArgumentException If input format is unrecognized + */ + + private static KeyPair loadECKeyPair(String secretKey) + throws IOException, GeneralSecurityException { + try (PEMParser parser = new PEMParser(new StringReader(secretKey))) { + Object obj = parser.readObject(); + JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC"); + + ECPrivateKey priv; + ECPublicKey pub; + + if (obj instanceof PEMKeyPair) { + KeyPair kp = converter.getKeyPair((PEMKeyPair) obj); + return kp; + } + + if (obj instanceof PrivateKeyInfo) { + // 1) get the private key + priv = (ECPrivateKey) converter.getPrivateKey((PrivateKeyInfo) obj); + + // 2) derive BC curve + base point + ECNamedCurveParameterSpec bcSpec = ECNamedCurveTable.getParameterSpec("P-256"); + org.bouncycastle.math.ec.ECPoint Q = bcSpec.getG().multiply(priv.getS()).normalize(); + + // 3) convert BC point → JCA point + java.security.spec.ECPoint w = new java.security.spec.ECPoint( + Q.getAffineXCoord().toBigInteger(), Q.getAffineYCoord().toBigInteger()); + + // 4) build JCA curve spec from BC parameters + java.security.spec.EllipticCurve curve = new java.security.spec.EllipticCurve( + new ECFieldFp(bcSpec.getCurve().getField().getCharacteristic()), + bcSpec.getCurve().getA().toBigInteger(), bcSpec.getCurve().getB().toBigInteger(), + bcSpec.getSeed()); + java.security.spec.ECParameterSpec jcaSpec = new java.security.spec.ECParameterSpec(curve, + new java.security.spec.ECPoint(bcSpec.getG().getAffineXCoord().toBigInteger(), + bcSpec.getG().getAffineYCoord().toBigInteger()), bcSpec.getN(), + bcSpec.getH().intValue()); + + // 5) build the JCA public‐key spec and generate it + java.security.spec.ECPublicKeySpec pubSpec = new java.security.spec.ECPublicKeySpec(w, + jcaSpec); + KeyFactory kf = KeyFactory.getInstance("EC", "BC"); + pub = (ECPublicKey) kf.generatePublic(pubSpec); + + return new KeyPair(pub, priv); + } + + throw new IllegalArgumentException("Unknown PEM object: " + obj.getClass()); + } + } + + /** + * Generates a random hexadecimal string for use as a nonce in JWT headers. + * + * @param bytes Number of bytes to generate (1 byte = 2 hex characters) + * @return Random hexadecimal string of length 2 * bytes + */ + + private static String randomHex(int bytes) { + byte[] buf = new byte[bytes]; + RNG.nextBytes(buf); + StringBuilder sb = new StringBuilder(bytes * 2); + for (byte b : buf) { + sb.append(String.format("%02x", b)); + } + return sb.toString(); + } + + /** + * Normalizes PEM-formatted key strings by: + * + * + * @param pem Raw PEM string (may contain escaped newlines) + * @return Normalized PEM string ready for parsing + */ + + private static String normalizePem(String pem) { + return pem.replace("\\n", "\n") + .replaceAll("-----BEGIN (.*) KEY-----\\s+", "-----BEGIN $1 KEY-----\n") + .replaceAll("\\s+\n-----END (.*) KEY-----", "-----END $1 KEY-----"); + } + + /** + *

Generates a JWT token for Coinbase Advanced Trading API authentication using the ES256 + * algorithm.

+ * + *

Includes the following claims:

+ * + * + * @param method HTTP method (e.g., "GET", "POST") + * @param uri Host/path combination (e.g., "api.coinbase.com/v3/brokerage/orders") + * @return Signed JWT string + */ + + private String generateJWT(String method, String uri) { + long now = Instant.now().getEpochSecond(); + Date nbf = Date.from(Instant.ofEpochSecond(now)); + Date exp = Date.from(Instant.ofEpochSecond(now + 120)); + + Algorithm alg = Algorithm.ECDSA256(publicKey, privateKey); + + return JWT.create().withKeyId(keyName).withIssuer("cdp").withSubject(keyName).withNotBefore(nbf) + .withExpiresAt(exp).withClaim("uri", method + " " + uri) + .withHeader(Collections.singletonMap("nonce", randomHex(16))).sign(alg); + } + + /** + *

Generates the Bearer authentication header for REST API requests.

+ * + *

Constructs a JWT token containing the request method and path, then formats it as a Bearer + * token:

+ *
"Bearer " + JWT_TOKEN
+ * + * @param restInvocation Rescu {@link RestInvocation} object containing request metadata + * @return Bearer token string for authentication header + */ + @Override public String digestParams(RestInvocation restInvocation) { - final String pathWithQueryString = - restInvocation.getInvocationUrl().replace(restInvocation.getBaseUrl(), ""); - final String timestamp = - restInvocation.getParamValue(HeaderParam.class, CB_ACCESS_TIMESTAMP).toString(); - final String message = timestamp + restInvocation.getHttpMethod() + pathWithQueryString; - - return DigestUtils.bytesToHex(getMac().doFinal(message.getBytes())); + String hostAndPath = + restInvocation.getBaseUrl().replaceFirst("^https?://", "") + restInvocation.getPath(); + return "Bearer " + generateJWT(restInvocation.getHttpMethod(), hostAndPath); } } diff --git a/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/dto/marketdata/CoinbaseCryptocurrencyData.java b/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/dto/marketdata/CoinbaseCryptocurrencyData.java new file mode 100644 index 00000000000..c8a5ca662d0 --- /dev/null +++ b/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/dto/marketdata/CoinbaseCryptocurrencyData.java @@ -0,0 +1,64 @@ +package org.knowm.xchange.coinbase.v2.dto.marketdata; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.Collections; +import java.util.List; + +public class CoinbaseCryptocurrencyData { + + private List data; + + public List getData() { + return Collections.unmodifiableList(data); + } + + public void setData(List data) { + this.data = data; + } + + public static class CoinbaseCryptocurrency { + + private final String name; + private final String code; + + @JsonCreator + public CoinbaseCryptocurrency(@JsonProperty("name") String name, @JsonProperty("code") String code) { + this.name = name; + this.code = code; + } + + public String getName() { + return name; + } + + public String getCode() { + return code; + } + + @Override + public int hashCode() { + return code.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + CoinbaseCryptocurrency other = (CoinbaseCryptocurrency) obj; + return code.equals(other.code); + } + + @Override + public String toString() { + return code + " (" + name + ")"; + } + } +} diff --git a/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/dto/marketdata/CoinbaseCurrencyData.java b/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/dto/marketdata/CoinbaseCurrencyData.java deleted file mode 100644 index 25f454dffa7..00000000000 --- a/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/dto/marketdata/CoinbaseCurrencyData.java +++ /dev/null @@ -1,77 +0,0 @@ -package org.knowm.xchange.coinbase.v2.dto.marketdata; - -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.core.ObjectCodec; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import java.io.IOException; -import java.util.Collections; -import java.util.List; - -public class CoinbaseCurrencyData { - - private List data; - - public List getData() { - return Collections.unmodifiableList(data); - } - - public void setData(List data) { - this.data = data; - } - - @JsonDeserialize(using = CoinbaseCurrencyDeserializer.class) - public static class CoinbaseCurrency { - private final String name; - private final String id; - - public CoinbaseCurrency(String name, final String id) { - this.name = name; - this.id = id; - } - - public String getName() { - return name; - } - - public String getId() { - return id; - } - - @Override - public int hashCode() { - return id.hashCode(); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) return true; - if (obj == null) return false; - if (getClass() != obj.getClass()) return false; - CoinbaseCurrency other = (CoinbaseCurrency) obj; - return id.equals(other.id); - } - - @Override - public String toString() { - return id + " (" + name + ")"; - } - } - - // [TODO] can we not do this with @JsonCreator - static class CoinbaseCurrencyDeserializer extends JsonDeserializer { - - @Override - public CoinbaseCurrency deserialize(JsonParser jp, DeserializationContext ctxt) - throws IOException, JsonProcessingException { - ObjectCodec oc = jp.getCodec(); - JsonNode node = oc.readTree(jp); - String name = node.get("name").asText(); - String id = node.get("id").asText(); - return new CoinbaseCurrency(name, id); - } - } -} diff --git a/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/dto/marketdata/CoinbaseFiatCurrencyData.java b/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/dto/marketdata/CoinbaseFiatCurrencyData.java new file mode 100644 index 00000000000..3b3a0c0dba9 --- /dev/null +++ b/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/dto/marketdata/CoinbaseFiatCurrencyData.java @@ -0,0 +1,72 @@ +package org.knowm.xchange.coinbase.v2.dto.marketdata; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.math.BigDecimal; +import java.util.Collections; +import java.util.List; + +public class CoinbaseFiatCurrencyData { + + private List data; + + public List getData() { + return Collections.unmodifiableList(data); + } + + public void setData(List data) { + this.data = data; + } + + public static class CoinbaseFiatCurrency { + + private final String name; + private final String id; + private final BigDecimal minSize; + + @JsonCreator + public CoinbaseFiatCurrency(@JsonProperty("name") String name, @JsonProperty("id") String id, + @JsonProperty("min_size") BigDecimal minSize) { + this.name = name; + this.id = id; + this.minSize = minSize; + } + + public String getName() { + return name; + } + + public String getId() { + return id; + } + + public BigDecimal getMinSize() { + return minSize; + } + + @Override + public int hashCode() { + return id.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + CoinbaseFiatCurrency other = (CoinbaseFiatCurrency) obj; + return id.equals(other.id); + } + + @Override + public String toString() { + return id + " (" + name + ")"; + } + } +} diff --git a/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/service/CoinbaseAccountServiceRaw.java b/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/service/CoinbaseAccountServiceRaw.java index c16660ba2ea..3a63e26ae65 100644 --- a/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/service/CoinbaseAccountServiceRaw.java +++ b/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/service/CoinbaseAccountServiceRaw.java @@ -1,8 +1,5 @@ package org.knowm.xchange.coinbase.v2.service; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.ObjectMapper; -import jakarta.ws.rs.core.MediaType; import java.io.IOException; import java.math.BigDecimal; import java.util.ArrayList; @@ -12,7 +9,6 @@ import org.knowm.xchange.Exchange; import org.knowm.xchange.coinbase.v2.Coinbase; import org.knowm.xchange.coinbase.v2.dto.account.CoinbaseAccountData.CoinbaseAccount; -import org.knowm.xchange.coinbase.v2.dto.account.CoinbasePaymentMethodsData.CoinbasePaymentMethod; import org.knowm.xchange.coinbase.v2.dto.account.CoinbaseTransactionsResponse; import org.knowm.xchange.currency.Currency; @@ -26,47 +22,39 @@ public CoinbaseTransactionsResponse getTransactions(String accountId) throws IOE String apiKey = exchange.getExchangeSpecification().getApiKey(); BigDecimal timestamp = coinbase.getTime(Coinbase.CB_VERSION_VALUE).getData().getEpoch(); - return coinbase.getTransactions( - Coinbase.CB_VERSION_VALUE, apiKey, signatureCreator2, timestamp, accountId); + return coinbase.getTransactions(Coinbase.CB_VERSION_VALUE, apiKey, authTokenGenerator, + timestamp, accountId); } public Map getDeposits(String accountId) throws IOException { String apiKey = exchange.getExchangeSpecification().getApiKey(); BigDecimal timestamp = coinbase.getTime(Coinbase.CB_VERSION_VALUE).getData().getEpoch(); - return coinbase.getDeposits( - Coinbase.CB_VERSION_VALUE, apiKey, signatureCreator2, timestamp, accountId); + return coinbase.getDeposits(Coinbase.CB_VERSION_VALUE, apiKey, authTokenGenerator, timestamp, + accountId); } public Map getWithdrawals(String accountId) throws IOException { String apiKey = exchange.getExchangeSpecification().getApiKey(); BigDecimal timestamp = coinbase.getTime(Coinbase.CB_VERSION_VALUE).getData().getEpoch(); - return coinbase.getWithdrawals( - Coinbase.CB_VERSION_VALUE, apiKey, signatureCreator2, timestamp, accountId); + return coinbase.getWithdrawals(Coinbase.CB_VERSION_VALUE, apiKey, authTokenGenerator, timestamp, + accountId); } /** * Authenticated resource that shows the current user accounts. * * @see developers.coinbase.com/api/v2#list-accounts + * href="https://developers.coinbase.com/api/v2#list-accounts">developers.coinbase.com/api/v2#list-accounts */ public List getCoinbaseAccounts() throws IOException { - String apiKey = exchange.getExchangeSpecification().getApiKey(); - List returnList = new ArrayList<>(); List tmpList = null; String lastAccount = null; do { - BigDecimal timestamp = coinbase.getTime(Coinbase.CB_VERSION_VALUE).getData().getEpoch(); - - tmpList = - coinbase - .getAccounts( - Coinbase.CB_VERSION_VALUE, apiKey, signatureCreator2, timestamp, 100, lastAccount) - .getData(); + tmpList = coinbase.getAccounts(authTokenGenerator, 100, lastAccount).getData(); lastAccount = null; if (tmpList != null && tmpList.size() > 0) { @@ -92,70 +80,10 @@ private boolean isValidUUID(String uuid) { * Authenticated resource that shows the current user account for the give currency. * * @see developers.coinbase.com/api/v2#show-an-account + * href="https://developers.coinbase.com/api/v2#show-an-account">developers.coinbase.com/api/v2#show-an-account */ public CoinbaseAccount getCoinbaseAccount(Currency currency) throws IOException { - String apiKey = exchange.getExchangeSpecification().getApiKey(); - BigDecimal timestamp = coinbase.getTime(Coinbase.CB_VERSION_VALUE).getData().getEpoch(); - - return coinbase - .getAccount( - Coinbase.CB_VERSION_VALUE, - apiKey, - signatureCreator2, - timestamp, - currency.getCurrencyCode()) - .getData(); - } - - /** - * Authenticated resource that creates a new BTC account for the current user. - * - * @see developers.coinbase.com/api/v2#create-account - */ - public CoinbaseAccount createCoinbaseAccount(String name) throws IOException { - - CreateCoinbaseAccountPayload payload = new CreateCoinbaseAccountPayload(name); - - String path = "/v2/accounts"; - String apiKey = exchange.getExchangeSpecification().getApiKey(); - BigDecimal timestamp = coinbase.getTime(Coinbase.CB_VERSION_VALUE).getData().getEpoch(); - String body = new ObjectMapper().writeValueAsString(payload); - String signature = getSignature(timestamp, HttpMethod.POST, path, body); - showCurl(HttpMethod.POST, apiKey, timestamp, signature, path, body); - - return coinbase - .createAccount( - MediaType.APPLICATION_JSON, - Coinbase.CB_VERSION_VALUE, - apiKey, - signature, - timestamp, - payload) - .getData(); - } - - /** - * Authenticated resource that shows the current user payment methods. - * - * @see developers.coinbase.com/api/v2?shell#list-payment-methods - */ - public List getCoinbasePaymentMethods() throws IOException { - String apiKey = exchange.getExchangeSpecification().getApiKey(); - BigDecimal timestamp = coinbase.getTime(Coinbase.CB_VERSION_VALUE).getData().getEpoch(); - - return coinbase - .getPaymentMethods(Coinbase.CB_VERSION_VALUE, apiKey, signatureCreator2, timestamp) - .getData(); + return coinbase.getAccount(authTokenGenerator, currency.getCurrencyCode()).getData(); } - public static class CreateCoinbaseAccountPayload { - @JsonProperty String name; - - CreateCoinbaseAccountPayload(String name) { - this.name = name; - } - } } diff --git a/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/service/CoinbaseBaseService.java b/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/service/CoinbaseBaseService.java index 28b540fd5fd..20ffbb8c26e 100644 --- a/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/service/CoinbaseBaseService.java +++ b/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/service/CoinbaseBaseService.java @@ -12,7 +12,8 @@ import org.knowm.xchange.coinbase.v2.Coinbase; import org.knowm.xchange.coinbase.v2.CoinbaseAuthenticated; import org.knowm.xchange.coinbase.v2.CoinbaseV2Digest; -import org.knowm.xchange.coinbase.v2.dto.marketdata.CoinbaseCurrencyData.CoinbaseCurrency; +import org.knowm.xchange.coinbase.v2.dto.marketdata.CoinbaseCryptocurrencyData.CoinbaseCryptocurrency; +import org.knowm.xchange.coinbase.v2.dto.marketdata.CoinbaseFiatCurrencyData.CoinbaseFiatCurrency; import org.knowm.xchange.coinbase.v2.dto.marketdata.CoinbaseTimeData.CoinbaseTime; import org.knowm.xchange.service.BaseExchangeService; import org.knowm.xchange.service.BaseService; @@ -21,30 +22,39 @@ public class CoinbaseBaseService extends BaseExchangeService implements BaseService { protected final CoinbaseAuthenticated coinbase; - protected final CoinbaseV2Digest signatureCreator2; + protected final CoinbaseV2Digest authTokenGenerator; protected CoinbaseBaseService(Exchange exchange) { super(exchange); - coinbase = - ExchangeRestProxyBuilder.forInterface( - CoinbaseAuthenticated.class, exchange.getExchangeSpecification()) - .build(); + coinbase = ExchangeRestProxyBuilder.forInterface(CoinbaseAuthenticated.class, + exchange.getExchangeSpecification()).build(); - signatureCreator2 = - CoinbaseV2Digest.createInstance(exchange.getExchangeSpecification().getSecretKey()); + authTokenGenerator = CoinbaseV2Digest.createInstance( + exchange.getExchangeSpecification().getApiKey(), + exchange.getExchangeSpecification().getSecretKey()); } /** - * Unauthenticated resource that returns currencies supported on Coinbase. + * Unauthenticated resource that returns cryptocurrencies supported on Coinbase. * - * @return A list of currency names and their corresponding ISO code. + * @return A list of cryptocurrency names and their corresponding ISO code. * @see developers.coinbase.com/api/v2#get-currencies + * href="https://docs.cdp.coinbase.com/coinbase-app/docs/track/api-currencies">https://docs.cdp.coinbase.com/coinbase-app/docs/track/api-currencies */ - public List getCoinbaseCurrencies() throws IOException { + public List getCoinbaseCryptocurrencies() throws IOException { + return coinbase.getCryptocurrencies(Coinbase.CB_VERSION_VALUE).getData(); + } - return coinbase.getCurrencies(Coinbase.CB_VERSION_VALUE).getData(); + /** + * Unauthenticated resource that returns fiat currencies supported on Coinbase. + * + * @return A list of fiat currency names and their corresponding ISO code. + * @see https://docs.cdp.coinbase.com/coinbase-app/docs/track/api-currencies + */ + public List getCoinbaseFiatCurrencies() throws IOException { + return coinbase.getFiatCurrencies(Coinbase.CB_VERSION_VALUE).getData(); } /** @@ -52,7 +62,7 @@ public List getCoinbaseCurrencies() throws IOException { * * @return The current server time. * @see developers.coinbase.com/api/v2#get-current-time + * href="https://developers.coinbase.com/api/v2#get-current-time">developers.coinbase.com/api/v2#get-current-time */ public CoinbaseTime getCoinbaseTime() throws IOException { @@ -67,30 +77,22 @@ protected String getSignature(BigDecimal timestamp, HttpMethod method, String pa return DigestUtils.bytesToHex(bytes); } - protected void showCurl( - HttpMethod method, - String apiKey, - BigDecimal timestamp, - String signature, - String path, - String json) { - String headers = - String.format( - "-H 'CB-VERSION: 2017-11-26' -H 'CB-ACCESS-KEY: %s' -H 'CB-ACCESS-SIGN: %s' -H 'CB-ACCESS-TIMESTAMP: %s'", - apiKey, signature, timestamp); + protected void showCurl(HttpMethod method, String apiKey, BigDecimal timestamp, String signature, + String path, String json) { + String headers = String.format( + "-H 'CB-VERSION: 2017-11-26' -H 'CB-ACCESS-KEY: %s' -H 'CB-ACCESS-SIGN: %s' -H 'CB-ACCESS-TIMESTAMP: %s'", + apiKey, signature, timestamp); if (method == HttpMethod.GET) { Coinbase.LOG.debug(String.format("curl %s https://api.coinbase.com%s", headers, path)); } else if (method == HttpMethod.POST) { String payload = "-d '" + json + "'"; Coinbase.LOG.debug( - String.format( - "curl -X %s -H 'Content-Type: %s' %s %s https://api.coinbase.com%s", - method, MediaType.APPLICATION_JSON, headers, payload, path)); + String.format("curl -X %s -H 'Content-Type: %s' %s %s https://api.coinbase.com%s", method, + MediaType.APPLICATION_JSON, headers, payload, path)); } } public enum HttpMethod { - GET, - POST + GET, POST } } diff --git a/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/service/CoinbaseTradeService.java b/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/service/CoinbaseTradeService.java index ab7345c1fd3..7531a7507e2 100644 --- a/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/service/CoinbaseTradeService.java +++ b/xchange-coinbase/src/main/java/org/knowm/xchange/coinbase/v2/service/CoinbaseTradeService.java @@ -91,7 +91,7 @@ public UserTrades getBuyTradeHistory(CoinbaseTradeHistoryParams params, String a coinbase.getBuys( Coinbase.CB_VERSION_VALUE, apiKey, - signatureCreator2, + authTokenGenerator, timestamp, accountId, params.getLimit(), @@ -111,7 +111,7 @@ public UserTrades getSellTradeHistory(CoinbaseTradeHistoryParams params, String coinbase.getSells( Coinbase.CB_VERSION_VALUE, apiKey, - signatureCreator2, + authTokenGenerator, timestamp, accountId, params.getLimit(), diff --git a/xchange-coinbase/src/test/java/org/knowm/xchange/coinbase/v2/dto/marketdata/CoinbaseMarketDataJsonTest.java b/xchange-coinbase/src/test/java/org/knowm/xchange/coinbase/v2/dto/marketdata/CoinbaseMarketDataJsonTest.java index ce9dae5ec52..8565ed06e43 100644 --- a/xchange-coinbase/src/test/java/org/knowm/xchange/coinbase/v2/dto/marketdata/CoinbaseMarketDataJsonTest.java +++ b/xchange-coinbase/src/test/java/org/knowm/xchange/coinbase/v2/dto/marketdata/CoinbaseMarketDataJsonTest.java @@ -11,7 +11,7 @@ import java.util.Map; import org.junit.Test; import org.knowm.xchange.coinbase.v2.dto.CoinbasePrice; -import org.knowm.xchange.coinbase.v2.dto.marketdata.CoinbaseCurrencyData.CoinbaseCurrency; +import org.knowm.xchange.coinbase.v2.dto.marketdata.CoinbaseCryptocurrencyData.CoinbaseCryptocurrency; public class CoinbaseMarketDataJsonTest { @@ -45,14 +45,14 @@ public void testDeserializeCurrencies() throws IOException { // Use Jackson to parse it ObjectMapper mapper = new ObjectMapper(); - JavaType javaType = mapper.getTypeFactory().constructType(CoinbaseCurrencyData.class); - CoinbaseCurrencyData rawdata = mapper.readValue(is, javaType); + JavaType javaType = mapper.getTypeFactory().constructType(CoinbaseCryptocurrencyData.class); + CoinbaseCryptocurrencyData rawdata = mapper.readValue(is, javaType); - List currencies = rawdata.getData(); + List currencies = rawdata.getData(); assertThat(currencies.size()).isEqualTo(168); - CoinbaseCurrency currency = currencies.get(167); - assertThat(currency.getId()).isEqualTo("ZWL"); + CoinbaseCryptocurrency currency = currencies.get(167); + assertThat(currency.getCode()).isEqualTo("ZWL"); assertThat(currency.getName()).isEqualTo("Zimbabwean Dollar"); } diff --git a/xchange-coinbase/src/test/java/org/knowm/xchange/coinbase/v2/services/AccountServiceIntegration.java b/xchange-coinbase/src/test/java/org/knowm/xchange/coinbase/v2/service/AccountServiceIntegration.java similarity index 61% rename from xchange-coinbase/src/test/java/org/knowm/xchange/coinbase/v2/services/AccountServiceIntegration.java rename to xchange-coinbase/src/test/java/org/knowm/xchange/coinbase/v2/service/AccountServiceIntegration.java index 852e4f5965c..fabd394f2c6 100644 --- a/xchange-coinbase/src/test/java/org/knowm/xchange/coinbase/v2/services/AccountServiceIntegration.java +++ b/xchange-coinbase/src/test/java/org/knowm/xchange/coinbase/v2/service/AccountServiceIntegration.java @@ -1,4 +1,4 @@ -package org.knowm.xchange.coinbase.v2.services; +package org.knowm.xchange.coinbase.v2.service; import java.util.List; import java.util.stream.Collectors; @@ -8,11 +8,11 @@ import org.junit.Test; import org.knowm.xchange.Exchange; import org.knowm.xchange.ExchangeFactory; +import org.knowm.xchange.ExchangeSpecification; import org.knowm.xchange.coinbase.v2.CoinbaseExchange; import org.knowm.xchange.coinbase.v2.dto.CoinbaseException; import org.knowm.xchange.coinbase.v2.dto.account.CoinbaseAccountData.CoinbaseAccount; import org.knowm.xchange.coinbase.v2.dto.account.CoinbasePaymentMethodsData.CoinbasePaymentMethod; -import org.knowm.xchange.coinbase.v2.service.CoinbaseAccountService; import org.knowm.xchange.currency.Currency; import org.knowm.xchange.service.account.AccountService; import org.knowm.xchange.utils.AuthUtils; @@ -24,8 +24,9 @@ public class AccountServiceIntegration { @BeforeClass public static void beforeClass() { - exchange = ExchangeFactory.INSTANCE.createExchange(CoinbaseExchange.class); - AuthUtils.setApiAndSecretKey(exchange.getExchangeSpecification()); + ExchangeSpecification exchangeSpecification = ExchangeFactory.INSTANCE.createExchange(CoinbaseExchange.class).getDefaultExchangeSpecification(); + AuthUtils.setApiAndSecretKey(exchangeSpecification); + exchange = ExchangeFactory.INSTANCE.createExchange(exchangeSpecification); accountService = exchange.getAccountService(); } @@ -36,7 +37,7 @@ public void listAccounts() throws Exception { CoinbaseAccountService coinbaseService = (CoinbaseAccountService) accountService; List accounts = coinbaseService.getCoinbaseAccounts(); - Assert.assertTrue(accounts.size() > 0); + Assert.assertFalse(accounts.isEmpty()); CoinbaseAccount btcAccount = accounts.stream() @@ -57,30 +58,4 @@ public void getAccountByCurrency() throws Exception { Assert.assertEquals("BTC", btcAccount.getBalance().getCurrency()); Assert.assertEquals("BTC Wallet", btcAccount.getName()); } - - @Test - public void createAccount() throws Exception { - - Assume.assumeNotNull(exchange.getExchangeSpecification().getApiKey()); - - CoinbaseAccountService coinbaseService = (CoinbaseAccountService) accountService; - try { - coinbaseService.createCoinbaseAccount("BTC Test"); - } catch (CoinbaseException ex) { - Assert.assertEquals(400, ex.getHttpStatusCode()); - Assert.assertEquals( - "Creation of multiple BTC accounts is not supported (HTTP status code: 400)", - ex.getMessage()); - } - } - - @Test - public void listPaymentMethods() throws Exception { - - Assume.assumeNotNull(exchange.getExchangeSpecification().getApiKey()); - - CoinbaseAccountService coinbaseService = (CoinbaseAccountService) accountService; - List methods = coinbaseService.getCoinbasePaymentMethods(); - Assert.assertTrue(methods.size() > 0); - } } diff --git a/xchange-coinbase/src/test/java/org/knowm/xchange/coinbase/v2/services/BaseServiceIntegration.java b/xchange-coinbase/src/test/java/org/knowm/xchange/coinbase/v2/service/BaseServiceIntegration.java similarity index 89% rename from xchange-coinbase/src/test/java/org/knowm/xchange/coinbase/v2/services/BaseServiceIntegration.java rename to xchange-coinbase/src/test/java/org/knowm/xchange/coinbase/v2/service/BaseServiceIntegration.java index e19b7ecd036..cd2ca1f8ecc 100644 --- a/xchange-coinbase/src/test/java/org/knowm/xchange/coinbase/v2/services/BaseServiceIntegration.java +++ b/xchange-coinbase/src/test/java/org/knowm/xchange/coinbase/v2/service/BaseServiceIntegration.java @@ -1,4 +1,4 @@ -package org.knowm.xchange.coinbase.v2.services; +package org.knowm.xchange.coinbase.v2.service; import static org.assertj.core.api.Assertions.assertThat; @@ -9,7 +9,6 @@ import org.knowm.xchange.ExchangeFactory; import org.knowm.xchange.coinbase.v2.CoinbaseExchange; import org.knowm.xchange.coinbase.v2.dto.marketdata.CoinbaseTimeData.CoinbaseTime; -import org.knowm.xchange.coinbase.v2.service.CoinbaseBaseService; public class BaseServiceIntegration { diff --git a/xchange-coinbase/src/test/java/org/knowm/xchange/coinbase/v2/services/MarketDataServiceIntegration.java b/xchange-coinbase/src/test/java/org/knowm/xchange/coinbase/v2/service/MarketDataServiceIntegration.java similarity index 57% rename from xchange-coinbase/src/test/java/org/knowm/xchange/coinbase/v2/services/MarketDataServiceIntegration.java rename to xchange-coinbase/src/test/java/org/knowm/xchange/coinbase/v2/service/MarketDataServiceIntegration.java index f643dcd4e13..6b59fc59440 100644 --- a/xchange-coinbase/src/test/java/org/knowm/xchange/coinbase/v2/services/MarketDataServiceIntegration.java +++ b/xchange-coinbase/src/test/java/org/knowm/xchange/coinbase/v2/service/MarketDataServiceIntegration.java @@ -1,20 +1,20 @@ -package org.knowm.xchange.coinbase.v2.services; +package org.knowm.xchange.coinbase.v2.service; import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.assertTrue; import java.math.BigDecimal; import java.util.Date; import java.util.List; import java.util.Map; -import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; import org.knowm.xchange.Exchange; import org.knowm.xchange.ExchangeFactory; import org.knowm.xchange.coinbase.v2.CoinbaseExchange; import org.knowm.xchange.coinbase.v2.dto.CoinbasePrice; -import org.knowm.xchange.coinbase.v2.dto.marketdata.CoinbaseCurrencyData.CoinbaseCurrency; -import org.knowm.xchange.coinbase.v2.service.CoinbaseMarketDataService; +import org.knowm.xchange.coinbase.v2.dto.marketdata.CoinbaseCryptocurrencyData.CoinbaseCryptocurrency; +import org.knowm.xchange.coinbase.v2.dto.marketdata.CoinbaseFiatCurrencyData.CoinbaseFiatCurrency; import org.knowm.xchange.currency.Currency; import org.knowm.xchange.service.marketdata.MarketDataService; @@ -33,19 +33,29 @@ public static void beforeClass() { } @Test - public void listCurrencies() throws Exception { + public void listCryptocurrencies() throws Exception { + CoinbaseMarketDataService coinbaseService = (CoinbaseMarketDataService) marketDataService; + List currencies = coinbaseService.getCoinbaseCryptocurrencies(); + + assertTrue(currencies.stream() + .anyMatch(crypto -> crypto.getName().equals("Bitcoin") && crypto.getCode().equals("BTC"))); + } + @Test + public void listFiatCurrencies() throws Exception { CoinbaseMarketDataService coinbaseService = (CoinbaseMarketDataService) marketDataService; - List currencies = coinbaseService.getCoinbaseCurrencies(); - assertThat(currencies).contains(new CoinbaseCurrency("Bitcoin", "BTC")); + List currencies = coinbaseService.getCoinbaseFiatCurrencies(); + + assertTrue(currencies.stream() + .anyMatch(fiat -> fiat.getName().equals("Euro") && fiat.getId().equals("EUR"))); } @Test - public void listExchageRates() throws Exception { + public void listExchangeRates() throws Exception { CoinbaseMarketDataService coinbaseService = (CoinbaseMarketDataService) marketDataService; Map exchangeRates = coinbaseService.getCoinbaseExchangeRates(); - Assert.assertTrue(exchangeRates.get("EUR") instanceof BigDecimal); + assertTrue(exchangeRates.get("EUR") instanceof BigDecimal); } @Test @@ -53,28 +63,23 @@ public void listPrices() throws Exception { CoinbaseMarketDataService coinbaseService = (CoinbaseMarketDataService) marketDataService; CoinbasePrice money = coinbaseService.getCoinbaseBuyPrice(Currency.BTC, Currency.USD); - assertThat(money) - .hasFieldOrPropertyWithValue("currency", Currency.USD) + assertThat(money).hasFieldOrPropertyWithValue("currency", Currency.USD) .hasNoNullFieldsOrProperties(); money = coinbaseService.getCoinbaseSellPrice(Currency.BTC, Currency.USD); - assertThat(money) - .hasFieldOrPropertyWithValue("currency", Currency.USD) + assertThat(money).hasFieldOrPropertyWithValue("currency", Currency.USD) .hasNoNullFieldsOrProperties(); money = coinbaseService.getCoinbaseSpotRate(Currency.BTC, Currency.USD); - assertThat(money) - .hasFieldOrPropertyWithValue("currency", Currency.USD) + assertThat(money).hasFieldOrPropertyWithValue("currency", Currency.USD) .hasNoNullFieldsOrProperties(); money = coinbaseService.getCoinbaseSpotRate(Currency.BTC, Currency.USD); - assertThat(money) - .hasFieldOrPropertyWithValue("currency", Currency.USD) + assertThat(money).hasFieldOrPropertyWithValue("currency", Currency.USD) .hasNoNullFieldsOrProperties(); money = coinbaseService.getCoinbaseHistoricalSpotRate(Currency.BTC, Currency.USD, new Date()); - assertThat(money) - .hasFieldOrPropertyWithValue("currency", Currency.USD) + assertThat(money).hasFieldOrPropertyWithValue("currency", Currency.USD) .hasNoNullFieldsOrProperties(); } } diff --git a/xchange-coinbase/src/test/java/org/knowm/xchange/coinbase/v2/services/TradeServiceIntegration.java b/xchange-coinbase/src/test/java/org/knowm/xchange/coinbase/v2/service/TradeServiceIntegration.java similarity index 68% rename from xchange-coinbase/src/test/java/org/knowm/xchange/coinbase/v2/services/TradeServiceIntegration.java rename to xchange-coinbase/src/test/java/org/knowm/xchange/coinbase/v2/service/TradeServiceIntegration.java index 91db5ab9c44..71225c192ac 100644 --- a/xchange-coinbase/src/test/java/org/knowm/xchange/coinbase/v2/services/TradeServiceIntegration.java +++ b/xchange-coinbase/src/test/java/org/knowm/xchange/coinbase/v2/service/TradeServiceIntegration.java @@ -1,34 +1,40 @@ -package org.knowm.xchange.coinbase.v2.services; +package org.knowm.xchange.coinbase.v2.service; import java.io.IOException; import java.math.BigDecimal; +import java.util.List; +import java.util.stream.Collectors; import org.junit.Assert; import org.junit.Assume; import org.junit.BeforeClass; import org.junit.Test; import org.knowm.xchange.Exchange; import org.knowm.xchange.ExchangeFactory; +import org.knowm.xchange.ExchangeSpecification; import org.knowm.xchange.coinbase.v2.CoinbaseExchange; import org.knowm.xchange.coinbase.v2.dto.CoinbaseAmount; import org.knowm.xchange.coinbase.v2.dto.CoinbasePrice; +import org.knowm.xchange.coinbase.v2.dto.account.CoinbaseAccountData.CoinbaseAccount; import org.knowm.xchange.coinbase.v2.dto.account.CoinbaseBuyData.CoinbaseBuy; import org.knowm.xchange.coinbase.v2.dto.account.CoinbaseSellData.CoinbaseSell; -import org.knowm.xchange.coinbase.v2.service.CoinbaseAccountService; -import org.knowm.xchange.coinbase.v2.service.CoinbaseTradeService; import org.knowm.xchange.currency.Currency; +import org.knowm.xchange.service.account.AccountService; import org.knowm.xchange.service.trade.TradeService; import org.knowm.xchange.utils.AuthUtils; public class TradeServiceIntegration { static Exchange exchange; - static TradeService tradeService; + static CoinbaseTradeService tradeService; + static CoinbaseAccountService accountService; @BeforeClass public static void beforeClass() { - exchange = ExchangeFactory.INSTANCE.createExchange(CoinbaseExchange.class); - AuthUtils.setApiAndSecretKey(exchange.getExchangeSpecification()); - tradeService = exchange.getTradeService(); + ExchangeSpecification exchangeSpecification = ExchangeFactory.INSTANCE.createExchange(CoinbaseExchange.class).getDefaultExchangeSpecification(); + AuthUtils.setApiAndSecretKey(exchangeSpecification); + exchange = ExchangeFactory.INSTANCE.createExchange(exchangeSpecification); + tradeService = (CoinbaseTradeService) exchange.getTradeService(); + accountService = (CoinbaseAccountService) exchange.getAccountService(); } @Test @@ -40,15 +46,14 @@ public void buy() throws Exception { BigDecimal amount = new BigDecimal("10.00"); BigDecimal total = new BigDecimal("10.00"); - CoinbaseTradeService coinbaseService = (CoinbaseTradeService) tradeService; - CoinbaseBuy res = coinbaseService.buy(accountId(currency), total, currency, false); + CoinbaseBuy res = tradeService.buy(getAccountId(currency), total, currency, false); Assert.assertNotNull(res.getId()); Assert.assertEquals("created", res.getStatus()); Assert.assertEquals(new CoinbasePrice(new BigDecimal("1.00"), Currency.EUR), res.getFee()); Assert.assertEquals(new CoinbaseAmount("BTC", new BigDecimal("0.0001")), res.getAmount()); Assert.assertEquals(Currency.EUR, res.getSubtotal().getCurrency()); Assert.assertEquals(Currency.EUR, res.getTotal().getCurrency()); - Assert.assertEquals(false, res.isCommitted()); + Assert.assertFalse(res.isCommitted()); } @Test @@ -60,15 +65,14 @@ public void sell() throws Exception { BigDecimal amount = new BigDecimal("0.0001"); BigDecimal total = null; - CoinbaseTradeService coinbaseService = (CoinbaseTradeService) tradeService; - CoinbaseSell res = coinbaseService.sell(accountId(currency), total, currency, false); + CoinbaseSell res = tradeService.sell(getAccountId(currency), total, currency, false); Assert.assertNotNull(res.getId()); Assert.assertEquals("created", res.getStatus()); Assert.assertEquals(new CoinbasePrice(new BigDecimal("1.00"), Currency.EUR), res.getFee()); Assert.assertEquals(new CoinbaseAmount("BTC", new BigDecimal("0.0001")), res.getAmount()); Assert.assertEquals(Currency.EUR, res.getSubtotal().getCurrency()); Assert.assertEquals(Currency.EUR, res.getTotal().getCurrency()); - Assert.assertEquals(false, res.isCommitted()); + Assert.assertFalse(res.isCommitted()); } @Test @@ -80,19 +84,18 @@ public void quote() throws Exception { BigDecimal amount = new BigDecimal("0.0001"); BigDecimal total = null; - CoinbaseTradeService coinbaseService = (CoinbaseTradeService) tradeService; - CoinbaseSell res = coinbaseService.quote(accountId(currency), total, currency); + CoinbaseSell res = tradeService.quote(getAccountId(currency), amount, currency); Assert.assertNull(res.getId()); Assert.assertEquals("quote", res.getStatus()); Assert.assertEquals(new CoinbasePrice(new BigDecimal("1.00"), Currency.EUR), res.getFee()); Assert.assertEquals(new CoinbaseAmount("BTC", new BigDecimal("0.0001")), res.getAmount()); Assert.assertEquals(Currency.EUR, res.getSubtotal().getCurrency()); Assert.assertEquals(Currency.EUR, res.getTotal().getCurrency()); - Assert.assertEquals(false, res.isCommitted()); + Assert.assertFalse(res.isCommitted()); } - private String accountId(Currency currency) throws IOException { - CoinbaseAccountService accountService = (CoinbaseAccountService) exchange.getAccountService(); - return accountService.getCoinbaseAccount(currency).getId(); + private String getAccountId(Currency currency) throws IOException { + CoinbaseAccount account = accountService.getCoinbaseAccount(currency); + return account.getId(); } } diff --git a/xchange-examples/src/main/java/org/knowm/xchange/examples/coinbase/v2/marketdata/CoinbaseMarketDataDemo.java b/xchange-examples/src/main/java/org/knowm/xchange/examples/coinbase/v2/marketdata/CoinbaseMarketDataDemo.java index 9fad740cebe..b2ca94821fb 100644 --- a/xchange-examples/src/main/java/org/knowm/xchange/examples/coinbase/v2/marketdata/CoinbaseMarketDataDemo.java +++ b/xchange-examples/src/main/java/org/knowm/xchange/examples/coinbase/v2/marketdata/CoinbaseMarketDataDemo.java @@ -10,7 +10,7 @@ import org.knowm.xchange.ExchangeFactory; import org.knowm.xchange.coinbase.v2.CoinbaseExchange; import org.knowm.xchange.coinbase.v2.dto.CoinbasePrice; -import org.knowm.xchange.coinbase.v2.dto.marketdata.CoinbaseCurrencyData.CoinbaseCurrency; +import org.knowm.xchange.coinbase.v2.dto.marketdata.CoinbaseCryptocurrencyData.CoinbaseCryptocurrency; import org.knowm.xchange.coinbase.v2.service.CoinbaseMarketDataService; import org.knowm.xchange.currency.Currency; @@ -22,7 +22,7 @@ public static void main(String[] args) throws IOException, ParseException { CoinbaseMarketDataService marketDataService = (CoinbaseMarketDataService) coinbaseExchange.getMarketDataService(); - List currencies = marketDataService.getCoinbaseCurrencies(); + List currencies = marketDataService.getCoinbaseCryptocurrencies(); System.out.println("Currencies: " + currencies); Map exchangeRates = marketDataService.getCoinbaseExchangeRates();