-
Notifications
You must be signed in to change notification settings - Fork 10
HowTo Examples
Here you can find helpful tutorials and examples how to use the substrate-client-java library.
This tutorial provides information about the initial steps you have to take in order to configure you application to use our substrate-client-java library.
In order to use the substrate-client-java library you need to add it to you application.
First, setup the necessary repositories:
repositories {
mavenCentral()
maven { url 'https://jitpack.io' }
maven {
name = "GitHubPackages"
url = "https://maven.pkg.github.com/strategyobject/substrate-client-java"
credentials {
username = project.findProperty("gpr.user") ?: System.getenv("GITHUB_USERNAME")
password = project.findProperty("gpr.key") ?: System.getenv("GITHUB_TOKEN")
}
}
}
Second, add substrate-client-java library's dependencies:
def substrateClientVersion = '0.2.0'
dependencies {
implementation "com.strategyobject.substrateclient:common:${substrateClientVersion}"
implementation "com.strategyobject.substrateclient:crypto:${substrateClientVersion}"
implementation "com.strategyobject.substrateclient:scale:${substrateClientVersion}"
implementation "com.strategyobject.substrateclient:transport:${substrateClientVersion}"
implementation "com.strategyobject.substrateclient:rpc:${substrateClientVersion}"
implementation "com.strategyobject.substrateclient:rpc-api:${substrateClientVersion}"
implementation "com.strategyobject.substrateclient:pallet:${substrateClientVersion}"
implementation "com.strategyobject.substrateclient:api:${substrateClientVersion}"
annotationProcessor "com.strategyobject.substrateclient:scale-codegen:${substrateClientVersion}"
annotationProcessor "com.strategyobject.substrateclient:rpc-codegen:${substrateClientVersion}"
annotationProcessor "com.strategyobject.substrateclient:pallet-codegen:${substrateClientVersion}"
}
In order to communicate with a blockchain node, we need to instantiate a component responsible for the transport layer.
Here we use the same concept of providers used in polkadot{.js}.
Currently the WebSocket provider is the only type that is implemented by our Java API. You can use it like shown in this example:
Supplier<ProviderInterface> wsProvider = WsProvider.builder().setEndpoint("ws://127.0.0.1:9944");
Use the Api.with
method to create the API, as shown below:
Api api = Api.with(wsProvider).build().join();
The client library have the following registries:
- ScaleReaderRegistry
- ScaleWriterRegistry
- RpcDecoderRegistry
- RpcEncoderRegistry
- EventRegistry
All readers, writers, encoders, decoders and events found in the library are registered automatically at startup. Custom objects have to be registered during the api configuration with ApiBuilder.configure()
method.
For example, to register all events in a package "com.example":
try (Api api = Api.with(wsProvider)
.configure(defaultModule ->
defaultModule.configureEventRegistry(registry ->
registry.registerAnnotatedFrom("com.example")))
.build()
.join()) {
// ...
}
Now we have the API ready for action.
This tutorial explains how to make an RPC call. First, you need to define a corresponding RPC interface. Here is an example code of a "system" interface with a single "accountNextIndex" call:
@RpcInterface("system")
public interface System {
@RpcCall("accountNextIndex")
CompletableFuture<Index> accountNextIndex(AccountId accountId);
}
Second, you need to instantiate an RPC interface with api.rpc()
method:
final System system = api.rpc(System.class);
And then execute a call:
final Index index = system.accountNextIndex(aliceAccountId).join();
This tutorial provides an example of how to read storage structures from a pallet.
Our API uses a declarative approach and code generation. In order to access a substrate pallet, you have to describe it with a Java interface.
You can find more information about describing and reading a pallet's storage here.
The code below shows an example in Rust defining a BlockHash
storage item in the System
pallet.
/// Map of block numbers to block hashes.
#[pallet::storage]
#[pallet::getter(fn block_hash)]
pub type BlockHash<T: Config> =
StorageMap<_, Twox64Concat, T::BlockNumber, T::Hash, ValueQuery>;
The above definition written in Java looks like this:
@Pallet("System")
public interface SystemPallet {
@Storage(
value = "BlockHash",
keys = {
@StorageKey(
type = @Scale(BlockNumber.class),
hasher = StorageHasher.TWOX_64_CONCAT
)
})
StorageNMap<BlockHash> blockHash();
}
As you can see it is quite similar to the original definition in Rust. The implementation of the interface will be generated automatically at compilation time.
Next, we need to instantiate the pallet with our Java API so we can start executing queries. The code for creating the pallet is:
System systemPallet = api.pallet(System.class);
After we have initialized the pallet, we can now access its storage items.
Example query for accessing the BlockHash
storage:
BlockHash blockHash = systemPallet
.blockHash()
.get(BlockNumber.GENESIS)
.join();
The Events
storage is defined in System pallet, so in order to listen to the events you need to subscribe to that storage. More information on Storage API can be found on Storage page.
Supplier<CompletableFuture<Boolean>> unsubscribe = api.pallet(System.class)
.events()
.subscribe((exception, block, eventRecords, keys) -> {
eventRecords.forEach(x -> out.printf("%s::(phase={\"ApplyExtrinsic\":%s})%n",
x.getEvent().getEvent().getClass().getSimpleName(),
x.getPhase().getApplyExtrinsicIndex()));
}, Arg.EMPTY)
.join();
The getEvent()
method of EventRecord
contains a reference to EventDescriptor
, an intermediate class containing the name of the pallet that the event corresponds to, its index within the pallet, and a reference to the deserialized event itself.
Don't forget to unsubscribe:
unsubscribe.get().join();