Welcome, builders. This repo holds the Dfns Java SDK. Useful links:
Requires Java 17+.
Maven Central publishing is being finalized. Until it lands, build the SDK from source using one of the two approaches below.
Works for both Gradle and Maven. Build the SDK from source, install it into your
local Maven repository (~/.m2), then depend on it with the normal coordinates
(group co.dfns, artifact dfns-sdk-java, version 1.0.0).
Clone this repository and publish it to your local Maven repository:
git clone https://github.com/dfns/dfns-sdk-java.git
cd dfns-sdk-java
./gradlew publishToMavenLocalThis installs co.dfns:dfns-sdk-java:1.0.0 into ~/.m2/repository. The bundled Gradle
wrapper downloads its own Gradle, so no local Gradle install is needed. Re-run this command
after pulling a newer version of the SDK.
Point your build at the local Maven repository (mavenLocal()) and add the dependency.
Gradle — in build.gradle:
repositories {
mavenLocal()
mavenCentral()
}
dependencies {
implementation("co.dfns:dfns-sdk-java:1.0.0")
}Maven — the local repository is on Maven's search path by default, so just add the
dependency to pom.xml:
<dependency>
<groupId>co.dfns</groupId>
<artifactId>dfns-sdk-java</artifactId>
<version>1.0.0</version>
</dependency>Transitive dependencies (Jackson, Bouncy Castle) are declared in the published POM and resolved automatically.
If you use Gradle, you can skip the publish step and build the SDK from a cloned copy as part of your own build, using a composite build. This needs no local Maven repository. It also works well in CI: add the SDK as a git submodule pinned to a commit, and your build resolves it reproducibly with no registry.
Clone this repository into your project (for example as a git submodule), then reference it
from your settings.gradle:
includeBuild("third-party/dfns-sdk-java")Depend on it without a version. Gradle builds the SDK from source and substitutes it for the dependency, resolving its transitive dependencies automatically:
dependencies {
implementation("co.dfns:dfns-sdk-java")
}import co.dfns.sdk.DfnsClient;
import co.dfns.sdk.DfnsClientConfig;
// Create the client (read-only operations)
DfnsClientConfig config = DfnsClientConfig.builder()
.authToken("your-auth-token")
// .baseUrl("https://api.dfns.io") // Optional, this is the default
.build();
DfnsClient client = new DfnsClient(config);
// List wallets
var wallets = client.wallets.listWallets(null);
System.out.println(wallets);An async client with the same API but CompletableFuture return types is also available:
import co.dfns.sdk.DfnsAsyncClient;
DfnsAsyncClient asyncClient = new DfnsAsyncClient(config);Some operations (like creating wallets or signing transactions) require user action signing. Configure a signer to enable these operations:
import co.dfns.sdk.DfnsClient;
import co.dfns.sdk.DfnsClientConfig;
import co.dfns.sdk.auth.KeySigner;
import co.dfns.sdk.auth.Signer;
import co.dfns.sdk.wallets.model.CreateWalletRequest;
import co.dfns.sdk.wallets.model.Network;
// Create a signer from your credential ID and private key bytes
Signer signer = KeySigner.fromEd25519PrivateKey("cr-xxx-xxx", privateKeyBytes);
DfnsClientConfig config = DfnsClientConfig.builder()
.authToken("your-auth-token")
.signer(signer)
.build();
DfnsClient client = new DfnsClient(config);
// Operations requiring signatures are signed automatically
var wallet = client.wallets.createWallet(new CreateWalletRequest(Network.EthereumSepolia));
System.out.println(wallet);In some setups you want your server to talk to Dfns on behalf of a user, while the user
keeps signing every request themselves (e.g. with a WebAuthn credential in a web app). The
DfnsDelegatedClient supports this: it needs no Signer, and every operation that needs
a user action signature is split into an ...Init / ...Complete pair.
...Inittakes the request payload and returns aUserActionChallengeto be signed out-of-band (typically by the end user in the browser)....Completetakes the same payload, the challenge identifier, and the signedCredentialAssertion, and performs the request.
A typical flow: the server calls ...Init and sends the challenge to the user; the user
signs it with their credential and returns the assertion; the server calls ...Complete.
import co.dfns.sdk.DfnsClientConfig;
import co.dfns.sdk.DfnsDelegatedClient;
import co.dfns.sdk.auth.CredentialAssertion;
import co.dfns.sdk.auth.UserActionChallenge;
import co.dfns.sdk.wallets.model.CreateWalletRequest;
import co.dfns.sdk.wallets.model.Network;
// No signer needed — challenges are signed out-of-band (e.g. by the end user).
DfnsClientConfig config = DfnsClientConfig.builder()
.authToken("user-auth-token")
.build();
DfnsDelegatedClient client = new DfnsDelegatedClient(config);
CreateWalletRequest body = new CreateWalletRequest(Network.EthereumSepolia);
// Step 1 (server): start the action, get a challenge.
UserActionChallenge challenge = client.wallets.createWalletInit(body);
// Step 2 (client): the user signs `challenge` with their credential and returns the
// signed assertion (a CredentialAssertion) to the server.
CredentialAssertion assertion = signChallengeOutOfBand(challenge);
// Step 3 (server): complete the action with the signed challenge.
var wallet = client.wallets.createWalletComplete(body, challenge.challengeIdentifier(), assertion);A DfnsDelegatedAsyncClient with CompletableFuture return types is also available.
client.addressWatches— AddressWatchesClientclient.agreements— AgreementsClientclient.allocations— AllocationsClientclient.auth— AuthClientclient.exchanges— ExchangesClientclient.feeSponsors— FeeSponsorsClientclient.keys— KeysClientclient.networks— NetworksClientclient.payins— PayinsClientclient.payouts— PayoutsClientclient.permissions— PermissionsClientclient.policies— PoliciesClientclient.signers— SignersClientclient.staking— StakingClientclient.swaps— SwapsClientclient.vaults— VaultsClientclient.wallets— WalletsClientclient.webhooks— WebhooksClient
Each domain provides typed methods for all available API endpoints. The same domains are
exposed (as Delegated*Client) on DfnsDelegatedClient.
import co.dfns.sdk.DfnsException;
try {
var result = client.wallets.listWallets(null);
} catch (DfnsException e) {
System.err.println("HTTP status: " + e.getHttpStatus());
System.err.println("Dfns error code: " + e.getDfnsErrorCode());
System.err.println("Message: " + e.getErrorMessage());
}The KeySigner supports the following private key types, each via a factory method that
takes the credential ID and the private key bytes:
| Factory method | Key type |
|---|---|
KeySigner.fromEd25519PrivateKey |
Ed25519 (EdDSA) |
KeySigner.fromEcdsaP256PrivateKey |
ECDSA (P-256) |
KeySigner.fromSecp256k1PrivateKey |
ECDSA (secp256k1) |
KeySigner.fromRsaPrivateKey |
RSA (PKCS#1 v1.5, SHA-256) |
MIT License - See LICENSE file for details.