Java4Ever is a feature-rich framework for smart-contracts development, testing & accessing TVM-compatible blockchains like Everscale, Venom, GOSH and so on. Closest alternative is a TypeScript-based locklift framework by Broxus.
Framework internally uses JSON-RPC connection to wrapped native EVER-SDK library (java4ever-binding).
- Auto-generation of Java smart contract wrappers to create, deploy, transact with and call smart contracts from native Java code
- Auto-conversion of ABI types to Java native types for any input/output
- Easy work with TIP 3.2 Fungible Tokens
- Easy work with Multisig Wallets
- Easy work with EverNode-SE Givers (with easy polymorph to giving from wallet on production)
- Access to transaction tree of complex calls
- Complete typed implementation of latest EVER-SDK JSON-RPC client API
- Pluggable EVER-SDK library support (no rebuild needed, just plug-in SDK lib you like with special Loaders)
Java4Ever only runtime dependencies are its own binding and utils libs and Jackson Core for fast JSON serialisation/deserialization It also uses JavaPoet for generating smart contract wrappers.
- Java4Ever
- Install JDK 20 (link)
- Gradle
dependencies {
implementation 'tech.deplant.java4ever:java4ever-framework:2.5.0'
}
- Maven
<dependency>
<groupId>tech.deplant.java4ever</groupId>
<artifactId>java4ever-framework</artifactId>
<version>2.5.0</version>
</dependency>
Sdk class is a provider of connection to EVER-SDK lib and TVM blockchain. It is a primary object that you need to run most interactions in Java4Ever:
var sdk1 = Sdk.DEFAULT();
var sdk2 = Sdk.DEFAULT("http://localhost/graphql");
var sdk3 = Sdk.builder().networkEndpoints("http://localhost/graphql").build();
You can find a list of endpoints here: https://docs.evercloud.dev/products/evercloud/networks-endpoints
If you're working with Everscale mainnet, here you can register your app and receive "ProjectID" part of the URL: https://dashboard.evercloud.dev/
Sdk.Builder is a Builder-style config for EVER-SDK, so you can easily config only needed parts of library.
var sdk = Sdk.builder()
.networkEndpoints("http://localhost/graphql")
.abiWorkchain(-1)
.networkRetriesCount(10)
.abiMessageExpirationTimeout(30000)
.build();
If you want to use custom ton-client
lib or have some problem with the included ones, specify custom location as:
var sdk = Sdk.builder()
.networkEndpoints("http://localhost/graphql")
.build(new AbsolutePathLoader(Path.of("\home\ton\lib\libton_client.so")));
You can find precompiled ton_client files here. Specifying path to downloaded custom "ton_client" libs can be done in multiple ways by using different loaders.
AbsolutePathLoader.ofSystemEnv("TON_CLIENT_LIB")
- path from Environment variableAbsolutePathLoader.ofUserDir("libton_client.so")
- file from ~ (user home)new AbsolutePathLoader(Path.of("\home\ton\lib\libton_client.so"))
- any absolute pathnew JavaLibraryPathLoader("ton_client");
- gets library from java.library.path JVM argument
Java4Ever includes easy API to work with files and java resources (both json-based and byte[]-based). Here are simple examples of getting contract ABIs and TVCs artifacts:
-
ContractAbi.ofFile("/path/to/your.abi.json")
- reads abi from file (can be relative) -
ContractAbi.ofResource("yourresource.abi.json")
- reads abi from resources of your project -
ContractAbi.ofString("")
-reads abi from JSON string -
ContractAbi.ofJsonNode(node)
- reads abi from JSON node -
Tvc.ofFile("/path/to/your.tvc")
- reads tvc from file (can be relative) -
Tvc.ofResource("yourresource.tvc")
- reads tvc from resources of your project -
Tvc.ofBase64String("")
-reads tvc from base64 encoded string -
new Tvc(bytes)
- reads tvc from byte array
Also, you can check JsonFile
, JsonResource
, ByteFile
, ByteResource
helpers for custom artifacts.
ContractWrapper
class is a generator that will create java wrapper
classes for all your contracts. You need only abi.json
and .tvc
artifacts of your contracts as a source for code generation.
Run the following, specifying your artifacts and where to place generated classes in params:
ContractWrapper.generate(ContractAbi.ofResource("mycontract.abi.json").abiContract(),
Tvc.ofResource("mycontract.tvc"),
Path.of("src/gen/java"),
"MyContract",
"org.example.contract",
"org.example.template",
new String[]{});
Contract and template wrappers will appear in packages that you specified.
Java4Ever includes basic helpers to create your seeds, key pairs and signatures. If you want some specific EVER-SDK functions, just use them firectly as all EVER-SDK API is available from Java4Ever.
var keys = Credentials.RANDOM(sdk);
String sk = keys.secretKey();
String pk = keys.publicKey();
var seed = Seed.RANDOM(sdk);
String wordsPhrase = seed.phrase();
int wordsCount = seed.words();
var keys = Credentials.ofSeed(sdk,seed);
String sk = keys.secretKey();
String pk = keys.publicKey();
var seed = new Seed("your seed phrase with 12 words or 24 with second constructor param");
var keys = new Credentials("publickey_string","secretkey_string");
If you generated your contract wrappers via generator
(MyContract
in this example),
all its methods are now available from MyContract.class
.
If you're working with standard contracts, all wrappers are
already generated (for Multisig Wallets, Givers, TIP3 and TIP4
contracts and so on -
check javadoc)
To access contract account, create instance of your contract class by passing SDK Provider and address of deployed contract.
MyContract contr = new MyContract(sdk, "0:your_contract_address");
contr.accountBalance(); // currency balance on account
contr.account().isActive(); // contract status
or with additional Credentials
param for signing external calls:
MyContract contr = new MyContract(sdk, "0:your_contract_address", keys);
Now, when your contract object is created, just get handle of one of functions by calling one of the contract methods.
FunctionHandle getCustodiansFunctionHandle = contr.getCustodians();
Function in this example doesn't have params, but yours can have.
With FunctionHandle
you can make external calls, run get methods using remote or local boc and so on like this:
MyContract.ResultOfGetCustodians custodians = getCustodiansFunctionHandle.get();
Map<String,Object> custodiansMap = getCustodiansFunctionHandle.getAsMap();
MyContract.ResultOfGetCustodians custodians = getCustodiansFunctionHandle.call();
Map<String,Object> custodiansMap = getCustodiansFunctionHandle.callAsMap();
MyContract.ResultOfGetCustodians custodians = getCustodiansFunctionHandle.getLocal(locallySavedBoc);
All the described functions, handles and return types are auto-generated when you generate contract wrapper
Variants of calls to function:
- get() - runs getter method and returns auto-generated type
- call() - makes external call (make sure you added credentials to contract object if contract checks signatures)
- getLocal() - runs getter against provided boc
- ...AsMap() - each method have AsMap variant that returns Map<String, Object> insted of type
var walletContract = new SafeMultisigWallet(sdk,"", walletKeys);
getCustodiansFunctionHandle.sendFrom(walletContract, CurrencyUnit.VALUE(EVER,"1.25"), true, MessageFlag.FEE_EXTRA);
sendFrom() method also has sendFromAsMap() variant.
Calls and sends also has ...Tree() variants that can be used to monitor transaction tree execution and collect errors.
You can encode FunctionHandle
as a payload for internal call like this:
var payload = getCustodiansFunctionHandle.toPayload();
Second class created by contract generator is MyContractTemplate.class
.
It's a companion class that stores ABI and TVC info for deployment.
You can create template object with no additional params.
If you didn't use generator, use AbstractTemplate
class and
pass ABI and TVC to it manually.
MyContractTemplate myTemplate = new MyContractTemplate();
var abi = myTemplate.abi(); // getting ABI from template
var tvc = myTemplate.tvc(); // getting TVC from template
myTemplate.tvc().code() // getting code cell
myTemplate.tvc().codeHash() // getting code hash
There are much more methods for TVC and ABI, including decoding and encoding initial data, various helpers for all sort of interactions.
DeployHandle
is a handle of prepared deployment with all needed params.
As with function handles, Template::prepareDeploy
params may vary depending on your contract -
your static variables and constructor params.
DeployHandle deployHandle = myTemplate.prepareDeploy(sdk, Credentials.NONE,"hello_world");
MyContract myContract = deployHandle.deploy();
MyContract myContract = deployHandle.deployWithGiver(walletContract, CurrencyUnit.VALUE(EVER,"1.25"));
MyContract myContract = deployHandle.deployWithGiver(EverOSGiver.V2(sdk), CurrencyUnit.VALUE(EVER,"1.25"));
Each deployment creates a ready contract object after deploy is done.
Also, you can use deployHandle.toAddress()
if you need only address calculation.
Here's the example of universal deployment that switches between evernode-se giver & msig wallet without any additional code:
Giver giver = null;
if (isEverOsNet()) {
giver = EverOSGiver.V2(sdk);
} else {
giver = new SafeMultisigWallet(sdk,"0:your_address");
}
deployHandle.deployWithGiver(giver, CurrencyUnit.VALUE(EVER,"1.25"));
This is possible as all Java4Ever wallet classes are implementing Giver interface.
Java4Ever uses the JDK Platform Loggging (JEP 264: Platform Logging API and Service),
so can be easily bridged to any logging framework. For example, to use log4j2, just add org.apache.logging.log4j:log4j-jpl
to your Maven/Gradle build.
If you can't answer in this readme or have a bug/improvement to report: