Java SDK for Nervos CKB.
The ckb-sdk-java is still under development and considered to be a work in progress. You should get familiar with CKB transaction structure and RPC before using it.
Note: All RPC methods in the indexer module have been deprecated since CKB version v0.36.0
and
they have been removed in the CKB version of v0.40.0
. We strongly recommend migrating to
the ckb-indexer as soon as possible. You can refer
to
the examples
of the ckb-indexer in this project.
- Java 8
- Gradle 5.0 or later
- Maven
<dependency>
<groupId>org.nervos.ckb</groupId>
<artifactId>core</artifactId>
<version>{version}</version>
</dependency>
- Gradle
implementation 'org.nervos.ckb:core:{version}'
- Maven
<dependency>
<groupId>org.nervos.ckb</groupId>
<artifactId>ckb</artifactId>
<version>{version}</version>
</dependency>
- Gradle
implementation 'org.nervos.ckb:ckb:{version}'
- Maven
<dependency>
<groupId>org.nervos.ckb</groupId>
<artifactId>ckb-api</artifactId>
<version>{version}</version>
</dependency>
- Gradle
implementation 'org.nervos.ckb-api:ckb:{version}'
You can generate the jar and import manually.
git clone https://github.com/nervosnetwork/ckb-sdk-java.git
cd ckb-sdk-java
gradle shadowJar // ./gradlew shadowJar
A console-{version}-all.jar
package will be generated in console/build/libs
, which you can put
into your project to develop with it.
A ckb-sdk-{version}-all.jar
package will be generated in ckb-sdk/build/libs
, which you can put
into your project to develop with it.
If you don't want to generate the jar by yourself, you can download a build from releases.
When you need to import ckb-java-sdk
dependency to your project, you can add
the console-{version}-all.jar
or ckb-sdk-{version}-all.jar
to your project libs
package.
If you use Java IDE (eg. IntelliJ IDEA or Eclipse or other Editors), you can import jar according to IDE option help documents.
Here we will give some most frequently used operations, to bring you enlightenment about how to use ckb-sdk-java to operate your asset in CKB chain.
ckb-java-sdk provides a convenient client to help you easily interact with CKB, CKB-indexer or Mercury node.
// Set up client. If you do not use ones of these node, just set them to null;
String ckbUrl = "http://127.0.0.1:8114";
String indexerUrl = "http://127.0.0.1:8114";
String mercuryUrl = "http://127.0.0.1:8116";
DefaultCkbApi ckbApi = new DefaultCkbApi(ckbUrl, mercuryUrl, indexerUrl, false);
You can leverage this client to call any RPC APIs provided by CKB, CKB-indexer or Mercury in Java code.
Block block = ckbApi.getBlock("0x77fdd22f6ae8a717de9ae2b128834e9b2a1424378b5fc95606ba017aab5fed75");
For more details about RPC APIs, please check:
Mercury is a development service in CKB ecosystem, providing many useful RPC APIs for development like querying transaction or getting udt asset information. You need to deploy your own mercury and sync data with the network before using it.
ckb-java-sdk also integrate with Mercury. For usage guide, please check the examples.
In order to build transaction, you have to collect the live cells that you want to spend at first. In the example below we use CKB indexer to get live cells. You also can get them by manual (e.g. check on CKB explorer) or by your own database.
// Prepare client and utils class
Api ckbApi = new Api("http://127.0.0.1:8114", false);
CkbIndexerApi ckbIndexerApi = new CkbIndexerApi("http://127.0.0.1:8114", false);
TransactionBuilder txBuilder = new TransactionBuilder(ckbApi);
IndexerCollector txUtils = new IndexerCollector(ckbApi, ckbIndexerApi);
// Find live cells and calculate capacity balance with the help of CKB indexer.
BigInteger feeRate = BigInteger.valueOf(1024);
List<String> SendAddresses = Arrays.asList("ckt1qyqrdsefa43s6m882pcj53m4gdnj4k440axqswmu83");
CollectResult collectResult = txUtils.collectInputs(SendAddresses, txBuilder.buildTx(), feeRate, Sign.SIGN_LENGTH * 2);
// Generate cell outputs - the target address.
List<CellOutput> cellOutputs = txUtils.generateOutputs(receivers, changeAddress);
txBuilder.addOutputs(cellOutputs);
// Charge back (if inputs capacity - fee > outputs capacity)
if (Numeric.toBigInt(collectResult.changeCapacity).compareTo(BigInteger.ZERO) > 0) {
cellOutputs.get(cellOutputs.size() - 1).capacity = collectResult.changeCapacity;
txBuilder.setOutputs(cellOutputs);
}
// Add witnesses placeholder for latter signature
for (CellsWithAddress cellsWithAddress : collectResult.cellsWithAddresses) {
txBuilder.addInputs(cellsWithAddress.inputs);
for (int i = 0; i < cellsWithAddress.inputs.size(); i++) {
txBuilder.addWitness(i == 0 ? new Witness(Witness.SIGNATURE_PLACEHOLDER) : "0x");
}
}
// Build the unsiged transaction
Transaction rawTx = txBuilder.buildTx();
A more recommended way is to directly call RPC API build_simple_transfer_transaction
provided by Mercury, which could help you do almost everything above in building transaction.
To send transaction you build to CKB network, you need to
- sign transaction with your private key.
- send signed transaction to CKB node, and wait it to be confirmed.
// Assume that you already have an unsigned transaction named `rawTx`
Secp256k1SighashAllBuilder signBuilder = new Secp256k1SighashAllBuilder(rawTx);
// Prepare private key to sign
List<ScriptGroupWithPrivateKeys> scriptGroupWithPrivateKeysList = new ArrayList<>();
scriptGroupWithPrivateKeysList.add(
new ScriptGroupWithPrivateKeys(
new ScriptGroup(Arrays.asList(0, 1, 2)), // indices of input cells that are to be unlocked by this private key
Arrays.asList("e79f3207ea4980b7fed79956d5934249ceac4751a4fae01a0f7c4a96884bc4e3") // your private key
)
);
// Sign transaction with private key
for (ScriptGroupWithPrivateKeys scriptGroupWithPrivateKeys : scriptGroupWithPrivateKeysList) {
signBuilder.sign(scriptGroupWithPrivateKeys.scriptGroup,
scriptGroupWithPrivateKeys.privateKeys.get(0));
}
// Send transaction
Transaction tx = signBuilder.buildTx();
ckbApi.sendTransaction(tx);;
In CKB world, a lock script can be represented as an address. secp256k1_blake160
is the most common used address and here we show how to generate it.
import org.nervos.ckb.utils.address.AddressTools;
// Generate a new address randomly
AddressTools.AddressGenerateResult address = AddressTools.generateAddress(Network.TESTNET);
System.out.println("address info - address: " + address.address
+ ", lockArgs: " + address.lockArgs
+ ", private key: " + address.privateKey);
For more details please about CKB address refer to CKB rfc 0021.
Convert elliptic curve public key to an address (secp256k1_blake160
)
// The public key sent is an elliptic curve public key of compressed format - a 65-length hex (not counting hex prefix 0x).
String address = AddressTools.convertPublicKeyToAddress(
Network.TESTNET, "0x24a501efd328e062c8675f2365970728c859c592beeefd6be8ead3d901330bc01");
Short address and bech32 address are deprecated. The standard address format is bech32m-encoded long address, which can be got from the short address or bech32 address as the following snippet code.
String address = AddressTools.convertToBech32mFullAddress("ckt1qyqxgp7za7dajm5wzjkye52asc8fxvvqy9eqlhp82g");
import org.nervos.ckb.utils.address.AddressParseResult;
import org.nervos.ckb.utils.address.AddressTools;
// validate address
AddressParseResult parseResult = AddressParser.parse("ckt1qg8mxsu48mncexvxkzgaa7mz2g25uza4zpz062relhjmyuc52ps3zn47dugwyk5e6mgxvlf5ukx7k3uyq9wlkkmegke");
System.out.println("address info - network: " + parseResult.network + ", script: " + parseResult.script + ", type: " + parseResult.type);
// `AddressFormatException` will be thrown if you are parsing invalid address
try {
AddressParseResult parseResult = AddressParser.parse("ckt1qyqz9r9w9gkf5799a497jx07kltx6qqgxv8qn492h3");
} catch (AddressFormatException e) {
System.out.println("invalid address");
}
See CONTRIBUTING.md.
The SDK is available as open source under the terms of the MIT License.
See CHANGELOG for more information.