A Java SDK for interacting with the Unicity network, enabling state transitions and token operations with cross-platform support for JVM and Android 12+.
- Token Operations: Mint, transfer, and manage fungible tokens
- State Transitions: Submit and verify state transitions on the Unicity network
- Cross-Platform: Works on standard JVM and Android 12+ (API level 31+)
- Type-Safe: Strongly typed API with comprehensive error handling
- CBOR Support: Built-in CBOR encoding/decoding using Jackson
- Async Operations: All network operations return
CompletableFuture
for non-blocking execution
- Java 11 or higher
- Android 12+ (API level 31+) for Android platform
- Gradle 8.8 or higher
Add JitPack repository:
repositories {
maven { url 'https://jitpack.io' }
}
dependencies {
implementation 'com.github.unicitynetwork:java-state-transition-sdk:1.1:android'
}
dependencies {
implementation 'com.github.unicitynetwork:java-state-transition-sdk:1.1:jvm'
}
dependencies {
implementation 'org.unicitylabs:java-state-transition-sdk:1.1-SNAPSHOT'
}
import org.unicitylabs.sdk.StateTransitionClient;
import org.unicitylabs.sdk.api.AggregatorClient;
// Connect to the Unicity test network
String aggregatorUrl = "https://gateway-test.unicity.network";
AggregatorClient aggregatorClient = new AggregatorClient(aggregatorUrl);
StateTransitionClient client = new StateTransitionClient(aggregatorClient);
import org.unicitylabs.sdk.token.*;
import org.unicitylabs.sdk.token.fungible.*;
import org.unicitylabs.sdk.transaction.*;
import org.unicitylabs.sdk.predicate.*;
import org.unicitylabs.sdk.signing.SigningService;
import org.unicitylabs.sdk.hash.HashAlgorithm;
// Create signing service from secret
byte[] secret = "your-secret-key".getBytes();
byte[] nonce = new byte[32]; // Generate random nonce
SigningService signingService = SigningService.createFromSecret(secret, nonce).get();
// Create token parameters
TokenId tokenId = TokenId.create(randomBytes(32));
TokenType tokenType = TokenType.create(randomBytes(32));
// Create predicate for ownership
MaskedPredicate predicate = MaskedPredicate.create(
tokenId,
tokenType,
signingService,
HashAlgorithm.SHA256,
nonce
).get();
// Create token data
TestTokenData tokenData = new TestTokenData(randomBytes(32));
// Create coins
Map<CoinId, BigInteger> coins = new HashMap<>();
coins.put(new CoinId(randomBytes(32)), BigInteger.valueOf(100));
coins.put(new CoinId(randomBytes(32)), BigInteger.valueOf(50));
TokenCoinData coinData = new TokenCoinData(coins);
// Create mint transaction
MintTransactionData<TestTokenData> mintData = new MintTransactionData<>(
tokenId,
tokenType,
predicate,
tokenData,
coinData,
null, // dataHash (optional)
randomBytes(32) // salt
);
// Submit transaction
Commitment<MintTransactionData<TestTokenData>> commitment =
client.submitMintTransaction(mintData).get();
// Wait for inclusion proof
InclusionProof inclusionProof = InclusionProofUtils.waitInclusionProof(
client,
commitment,
Duration.ofSeconds(30),
Duration.ofSeconds(1)
).get();
// Create transaction with proof
Transaction<MintTransactionData<TestTokenData>> mintTransaction =
client.createTransaction(commitment, inclusionProof).get();
// Create token
TokenState tokenState = TokenState.create(predicate, tokenData.getData());
Token<Transaction<MintTransactionData<?>>> token =
new Token<>(tokenState, (Transaction) mintTransaction);
System.out.println("Token minted with ID: " + token.getId().toJSON());
Long blockHeight = client.getAggregatorClient().getBlockHeight().get();
System.out.println("Current block height: " + blockHeight);
// Create transfer transaction data
TransactionData transferData = TransactionData.create(
token.getState(),
recipientAddress.toString(),
randomBytes(32), // salt
dataHash, // optional data hash
messageBytes // optional message
).get();
// Submit transfer
Commitment<TransactionData> transferCommitment =
client.submitTransaction(transferData, signingService).get();
// Wait for inclusion proof and create transaction
InclusionProof transferProof = InclusionProofUtils.waitInclusionProof(
client, transferCommitment
).get();
Transaction<TransactionData> transferTransaction =
client.createTransaction(transferCommitment, transferProof).get();
The SDK supports offline token transfers, where the transaction is prepared by the sender and can be transferred offline (e.g., via NFC, QR code, or file) to the recipient.
// Step 1: Sender creates offline commitment
TransactionData transactionData = TransactionData.create(
token.getState(),
recipientAddress.toString(),
randomBytes(32), // salt
dataHash, // optional data hash
messageBytes // optional message
).get();
// Create authenticator (signed by sender)
Authenticator authenticator = Authenticator.create(
senderSigningService,
transactionData.getHash(),
transactionData.getSourceState().getHash()
).get();
// Create request ID
RequestId requestId = RequestId.create(
senderSigningService.getPublicKey(),
transactionData.getSourceState().getHash()
).get();
// Create commitment
Commitment<TransactionData> commitment = new Commitment<>(
requestId,
transactionData,
authenticator
);
// Step 2: Transfer commitment data offline (NFC, QR code, file, etc.)
// In a real scenario, serialize commitment and token data for transfer
// Step 3: Recipient submits the commitment online
SubmitCommitmentResponse response = client.submitCommitment(commitment).get();
// Wait for inclusion proof
InclusionProof inclusionProof = InclusionProofUtils.waitInclusionProof(
client,
commitment
).get();
// Create transaction with proof
Transaction<TransactionData> confirmedTransaction =
client.createTransaction(commitment, inclusionProof).get();
// Finish transaction with recipient's state
TokenState recipientTokenState = TokenState.create(recipientPredicate, recipientData);
Token<?> updatedToken = client.finishTransaction(
token,
recipientTokenState,
confirmedTransaction
).get();
git clone https://github.com/unicitynetwork/java-state-transition-sdk.git
cd java-state-transition-sdk
./gradlew build
# Run unit tests
./gradlew test
# Run integration tests (requires Docker)
./gradlew integrationTest
# Run E2E tests against deployed aggregator
AGGREGATOR_URL=https://gateway-test.unicity.network ./gradlew integrationTest
The SDK is compatible with Android 12+ (API level 31+). It uses:
- OkHttp for HTTP operations instead of Java 11's HttpClient
- Android-compatible Guava version
- Animal Sniffer plugin to ensure API compatibility
The standard JVM version uses:
- Java 11 APIs
- Full Guava JRE version
- All Java 11 features
The SDK follows a modular architecture under org.unicitylabs.sdk
:
api
: Core API interfaces and aggregator clientaddress
: Address schemes and implementations (DirectAddress, ProxyAddress)hash
: Cryptographic hashing (SHA256, SHA224, SHA384, SHA512, RIPEMD160)jsonrpc
: JSON-RPC transport layer with OkHttpmtree
: Merkle tree implementationsplain
: Sparse Merkle Tree (SMT)sum
: Sparse Merkle Sum Tree (SMST)
predicate
: Ownership predicates (Masked, Unmasked, Burn, Default)serializer
: CBOR and JSON serializers hierarchycbor/
: CBOR serializers for all domain objectsjson/
: JSON serializers for all domain objects
signing
: Digital signature support (ECDSA secp256k1)token
: Token types including fungible tokens and nametagsfungible
: Fungible token support with CoinId and TokenCoinData
transaction
: Transaction types and builderssplit
: Token splitting functionality with TokenSplitBuilder
util
: Utilities including BitString and HexConverter
All async operations return CompletableFuture
which can be handled with standard Java patterns:
client.submitMintTransaction(mintData)
.thenApply(commitment -> {
System.out.println("Transaction submitted: " + commitment.getRequestId());
return commitment;
})
.exceptionally(error -> {
System.err.println("Transaction failed: " + error.getMessage());
return null;
});
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature
) - Commit your changes (
git commit -m 'Add amazing feature'
) - Push to the branch (
git push origin feature/amazing-feature
) - Open a Pull Request
- Follow Java naming conventions
- Add unit tests for new features
- Update documentation as needed
- Ensure compatibility with both JVM and Android platforms
- Run
./gradlew build
before submitting PR
The SDK includes comprehensive test suites:
Located in src/test/java
, these test individual components in isolation.
Located in src/test/java/org/unicitylabs/sdk/
:
integration/TokenIntegrationTest
: Tests against Docker-based local aggregatore2e/TokenE2ETest
: E2E tests using CommonTestFlow (requiresAGGREGATOR_URL
env var)e2e/BasicE2ETest
: Basic connectivity and performance tests
# All tests
./gradlew test
# Integration tests only
./gradlew integrationTest
# Specific test class
./gradlew test --tests "org.unicitylabs.sdk.api.RequestIdTest"
This project is licensed under the MIT License - see the LICENSE file for details.
For issues and feature requests, please use the GitHub issue tracker.
For questions about the Unicity Labs, visit unicity-labs.com.
- Built on the Unicity network protocol
- Uses Jackson for CBOR encoding
- Uses Bouncy Castle for cryptographic operations
- Uses OkHttp for Android-compatible HTTP operations