
ICON SDK for Java
ICON supports SDK for 3rd party or user services development. You can integrate ICON SDK for your project and utilize ICON’s functionality.
This document is focused on how to use the SDK properly. For the detailed API specification, see the API reference documentation.
Prerequisite
This Java SDK works on the following platforms:
- Java 8+ (for Java 7, you can explore source code here)
- Android 3.0+ (API 11+)
Installation
Download the latest JAR or grab via Maven:
<dependency>
<groupId>foundation.icon</groupId>
<artifactId>icon-sdk</artifactId>
<version>2.5.1</version>
</dependency>
or Gradle:
dependencies {
implementation 'foundation.icon:icon-sdk:2.5.1'
}
Quick Start
We provide different types of code examples to help you to start quickly from scratch.
Please refer to the separate Quick Start project for the code examples.
IconService
APIs are called through IconService
.
IconService
can be initialized as follows.
IconService iconService = new IconService(new HttpProvider("http://localhost:9000", 3));
The code below shows initializing IconService
with a custom HTTP client.
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.readTimeout(200, TimeUnit.MILLISECONDS)
.writeTimeout(600, TimeUnit.MILLISECONDS)
.build();
IconService iconService = new IconService(new HttpProvider(okHttpClient, "http://localhost:9000", 3));
Queries
All queries are requested by a Request
object.
Query requests can be executed as Synchronized or Asynchronized.
Once the request has been executed, the same request object cannot be executed again.
Request<Block> request = iconService.getBlock(height);
try {
Block block = request.execute();
...
} catch (Exception e) {
...
}
request.execute(new Callback<Block>(){
@Override
public void onSuccess(Block block) {
...
}
@Override
public void onFailure(Exception exception) {
...
}
});
The querying APIs are as follows.
Request<Block> request = iconService.getBlock(new BigInteger("1000"));
Request<Block> request = iconService.getBlock(new Bytes("0x5e23...af83");
Request<Block> request = iconService.getLastBlock();
Request<BigInteger> request = iconService.getBalance(new Address("hxe7af...dfcb");
Request<List<ScoreApi>> request = iconService.getScoreApi(new Address("cx0000...0001"));
Request<BigInteger> request = iconService.getTotalSupply();
Request<Transaction> request = iconService.getTransaction(new Bytes("0x5e23...af83"));
Request<TransactionResult> request = iconService.getTransactionResult(new Bytes("0x5e23...af83"));
Call<BigInteger> call = new Call.Builder()
.from(wallet.getAddress())
.to(scoreAddress)
.method("balanceOf")
.params(params)
.buildWith(BigInteger.class);
Request<BigInteger> request = iconService.call(call);
Call<RpcItem> call = new Call.Builder()
.from(wallet.getAddress())
.to(scoreAddress)
.method("balanceOf")
.params(params)
.build();
Request<RpcItem> request = iconService.call(call);
try {
RpcItem rpcItem = request.execute();
BigInteger balance = rpcItem.asInteger();
...
} catch (Exception e) {
...
}
Transactions
Calling SCORE APIs to change states is requested as sending a transaction.
Before sending a transaction, the transaction should be signed. It can be done using a Wallet
object.
Loading wallets and storing the Keystore
Wallet wallet = KeyWallet.create();
Wallet wallet = KeyWallet.load(new Bytes("592eb2...27ff0c"));
File file = new File("./keystore_file");
Wallet wallet = KeyWallet.load("password", file);
File dir = new File("./");
KeyWallet.store(wallet, "password", dir);
Creating transactions
Transaction transaction = TransactionBuilder.newBuilder()
.nid(networkId)
.from(wallet.getAddress())
.to(scoreAddress)
.value(new BigInteger("150000000"))
.stepLimit(new BigInteger("1000000"))
.build();
Transaction transaction = TransactionBuilder.newBuilder()
.nid(networkId)
.from(wallet.getAddress())
.to(scoreAddress)
.stepLimit(new BigInteger("2000000000"))
.deploy("application/zip", content)
.params(params)
.build();
Transaction transaction = TransactionBuilder.newBuilder()
.nid(networkId)
.from(wallet.getAddress())
.to(scoreAddress)
.stepLimit(new BigInteger("1000000"))
.call("transfer")
.params(params)
.build();
Transaction transaction = TransactionBuilder.newBuilder()
.nid(networkId)
.from(wallet.getAddress())
.to(scoreAddress)
.stepLimit(new BigInteger("1000000"))
.message(message)
.build();
Transaction transaction = TransactionBuilder.newBuilder()
.nid(networkId)
.from(wallet.getAddress())
.to(scoreAddress)
.value(depositAmount)
.stepLimit(new BigInteger("1000000"))
.deposit()
.add()
.build();
SignedTransaction
object signs a transaction using the wallet.
And the request can be executed as Synchronized or Asynchronized like a query request.
Once the request has been executed, the same request object cannot be executed again.
SignedTransaction signedTransaction = new SignedTransaction(transaction, wallet);
Request<Bytes> request = iconService.sendTransaction(signedTransaction);
try {
Bytes txHash = request.execute();
...
} catch (Exception e) {
...
}
request.execute(new Callback<Bytes>() {
@Override
public void onSuccess(Bytes result) {
...
}
@Override
public void onFailure(Exception exception) {
...
}
});
Step Estimation
It is important to set a proper stepLimit
value in your transaction to make the submitted transaction executed successfully.
estimateStep
API provides a way to estimate the Step usage of a given transaction. Using the method, you can get an estimated Step usage before sending your transaction then make a SignedTransaction
with the stepLimit
based on the estimation.
Transaction transaction = TransactionBuilder.newBuilder()
.nid(networkId)
.from(fromAddress)
.to(toAddress)
.call("transfer")
.params(params)
.build();
BigInteger estimatedStep = iconService.estimateStep(transaction).execute();
BigInteger margin = BigInteger.valueOf(10000);
SignedTransaction signedTransaction = new SignedTransaction(transaction, wallet, estimatedStep.add(margin));
Bytes txHash = iconService.sendTransaction(signedTransaction).execute();
...
Note that the estimation can be smaller or larger than the actual amount of step to be used by the transaction,
so it is recommended to add some margin to the estimation when you set the stepLimit
of the SignedTransaction
.
Converter
All the requests and responses values are parcelled as RpcItem
(RpcObject
, RpcArray
, RcpValue
). You can convert your own class using RpcConverter
.
iconService.addConverterFactory(new RpcConverter.RpcConverterFactory() {
@Override
public RpcConverter create(Class type) {
if (type.isAssignableFrom(Person.class)) {
return new RpcConverter<Person>() {
@Override
public Person convertTo(RpcItem object) {
String name = object.asObject().getItem("name").asString();
BigInteger age = object.asObject().getItem("age").asInteger();
return new Person(name, age);
}
@Override
public RpcItem convertFrom(Person person) {
return new RpcObject.Builder()
.put("name", person.name)
.put("age", person.age)
.build();
}
};
}
return null;
}
});
...
class Person {
public Person(String name, BigInteger age) {}
}
...
Call<Person> call = new Call.Builder()
.from(fromAddress)
.to(scoreAddress)
.method("searchMember")
.params(person)
.buildWith(Person.class);
Person memberPerson = iconService.call(call).execute();
References
License
This project is available under the Apache License, Version 2.0.