we-contract-sdk
Java/Kotlin contract SDK used for building Docker smart contracts.
All transaction handling is done via methods of a single class marked with @ContractHandler
annotation. The methods which implement handling logic are marked with @ContractInit
(for CreateContractTx) and @ContractAction
(for CallContractTx).
Module structure
- we-contract-sdk-api. Contains mainly interfaces and annotations for marking contract handlers and their methods. Should be used directly by *-contract-api modules.
- we-contract-sdk-core. Core processing contract transactions logic. Transport and mapping is not implemented in core module and should be provided externally.
- we-contract-sdk-grpc. Implementation with GRPC transport and Jackson object mapping. Should be used directly by the module which starts and runs the contract.
Getting started
Dependencies
Maven
<dependency>
<groupId>com.wavesenterprise</groupId>
<artifactId>we-contract-sdk-grpc</artifactId>
<version>1.0.0</version>
</dependency>
Gradle
dependencies {
implementation("com.wavesenterprise:we-contract-sdk-grpc:1.0.0")
}
Quick start
All examples are taken from the Samples.
1. Create contract handler
@ContractHandler
public class SampleContractHandler {
private final ContractState contractState;
private final ContractTransaction tx;
private final Mapping<List<MySampleContractDto>> mapping;
public SampleContractHandler(ContractState contractState, ContractTransaction tx) {
this.contractState = contractState;
mapping = contractState.getMapping(
new TypeReference<List<MySampleContractDto>>() {
}, "SOME_PREFIX");
this.tx = tx;
}
}
@ContractInit
and @ContractAction
method to handle contract transactions
2. Add public class SampleContractHandler {
// ...
@ContractInit
public void createContract(String initialParam) {
contractState.put("INITIAL_PARAM", initialParam);
}
@ContractAction
public void doSomeAction(String dtoId) {
contractState.put("INITIAL_PARAM", Instant.ofEpochMilli(tx.getTimestamp().getUtcTimestampMillis()));
if (mapping.has(dtoId)) {
throw new IllegalArgumentException("Already has " + dtoId + " on state");
}
mapping.put(dtoId,
Arrays.asList(
new MySampleContractDto("john", 18),
new MySampleContractDto("harry", 54)
));
}
}
3. Dispatch contract with specified contract handler and settings
public class MainDispatch {
public static void main(String[] args) {
ContractDispatcher contractDispatcher = GrpcJacksonContractDispatcherBuilder.builder()
.contractHandlerType(SampleContractHandler.class)
.objectMapper(getObjectMapper())
.build();
contractDispatcher.dispatch();
}
private static ObjectMapper getObjectMapper() {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new JavaTimeModule());
return objectMapper;
}
}
4. Create Docker image
FROM openjdk:8-alpine
MAINTAINER Waves Enterprise <>
ENV JAVA_MEM="-Xmx256M"
ENV JAVA_OPTS=""
ADD build/libs/*-all.jar app.jar
RUN chmod +x app.jar
RUN eval $SET_ENV_CMD
CMD ["/bin/sh", "-c", "eval ${SET_ENV_CMD} ; java $JAVA_MEM $JAVA_OPTS -jar app.jar"]
5. Push to docker repository used by WE Node mining contract transactions
Publish to registry used by WE Blockchain Node. For convenience there is a bash script build_and_push_to_docker.sh which builds and pushes to specified registry.
./build_and_push_to_docker.sh my.registry.com/contracts/my-awesome-docker-contract:1.0.0
6. Sign and broadcast transactions for creating and invoking published contract
You will need image
and imageHash
of the published contract to create it.
CreateContractTx example
{
"image": "my.registry.com/contracts/my-awesome-docker-contract:1.0.0",
"fee": 0,
"imageHash": "d17f6c1823176aa56e0e8184f9c45bc852ee9b076b06a586e40c23abde4d7dfa",
"type": 103,
"params": [
{
"type": "string",
"value": "createContract",
"key": "action"
},
{
"type": "string",
"value": "initialValue",
"key": "createContract"
}
],
"version": 2,
"sender": "3M3ybNZvLG7o7rnM4F7ViRPnDTfVggdfmRX",
"feeAssetId": null,
"contractName": "myAwesomeContract"
}
To call contract you will need contractId = CreateContractTx.id
.
CallContractTx example
{
"contractId": "7sVc6ybnqZr523xWK5Sg7xADsX597qga8iQNAS9f1D3c",
"fee": 0,
"type": 104,
"params": [
{
"type": "string",
"value": "doSomeAction",
"key": "action"
},
{
"type": "string",
"value": "someValue",
"key": "createContract"
}
],
"version": 2,
"sender": "3M3ybNZvLG7o7rnM4F7ViRPnDTfVggdfmRX",
"feeAssetId": null,
"contractVersion": 1
}
Notes on usage
Usage with Java 11+
Library has been tested against java8 and java17. When using with java17 additional java options should be specified for the io.grpc to enable optimizations.
--add-opens java.base/jdk.internal.misc=ALL-UNNAMED --add-opens=java.base/java.nio=ALL-UNNAMED -Dio.netty.tryReflectionSetAccessible=true
Full example can be found in Dockerfile for java17.