MultiversX Spring Boot Starter Reactive
This is a Spring Boot Starter project for integrating with the MultiversX Network, with the
goal of achieving an effortless autoconfigured integration with the network.
The client is implemented using project Reactor as the Reactive Streams' specification
implementation, allowing fully non-blocking operations and providing efficient demand
management when interacting with the network, ideal for building scalable reactive
microservices.
Author
Features
- Auto synchronise network configurations from the MultiversX Network at startup based on the configured gateway
- Non-blocking network requests with the reactive MultiversX client
- Easy to use Interactors for executing various blockchain operations
- A lot of abstracted complexity in creating addresses, wallets, transactions
Usage
To use the starter, add the following dependency to the dependencies section of your build descriptor:
- Maven (in your pom.xml)
<dependency>
<groupId>software.crldev</groupId>
<artifactId>multiversx-spring-boot-starter-reactive</artifactId>
<version>1.2.0</version>
</dependency>
- Gradle (in your build.gradle file)
dependencies {
implementation(group: 'software.crldev', name: 'multiversx-spring-boot-starter-reactive', version: '1.2.0')
}
- And some other required dependencies for cryptographic functions:
implementation group: 'org.bouncycastle', name: 'bcmail-jdk15on', version: '1.70'
implementation group: 'org.bouncycastle', name: 'bcpkix-jdk15on', version: '1.70'
implementation group: 'org.bouncycastle', name: 'bcprov-jdk15on', version: '1.70'
implementation group: 'org.bouncycastle', name: 'bcprov-ext-jdk15on', version: '1.70'
implementation group: 'org.bitcoinj', name: 'bitcoinj-core', version: '0.16.2'
Documentation
First part of integration is setting up application.yaml
. If nothing is set, defaults will be
used.
spring:
multiversx:
client:
(optional) gateway: devnet (default) (mainnet | testnet | devnet)
(optional) customProxyUrl: https://custom-proxy.com
readTimeoutMillis: 10000 (default)
writeTimeoutMillis: 10000 (default)
The project uses object notations from the blockchain terminology like Address, Wallet, Transaction, Nonce, Gas, Signature etc ... so it's required to be familiar with them.
An Address can be instantiated in two ways:
- fromHex (public key in HEX String)
- fromBech32 (address in Bech32 String)
A Wallet is used for signing transactions. It can be instantiated in multiple ways:
- fromPrivateKeyBuffer (private key in byte[] format)
- fromPrivateKeyHex (private key in HEX String)
- fromPemFile (using a PEM file as an input) (both File & reactive FilePart supported)
- fromMnemonic (using a mnemonic phrase)
- fromKeyStore (not yet implemented)
We can generate a mnemonic phrase by using MnemonicsUtils .
The interaction with the MultiversX Network is done with the help of a set components called ** Interactors**, which provide all the required functionalities based on segregated parts of the network:
- getAccountInfo
- getBalance
- getNonce
- getTransactions
- getStorageValue
- getStorage
- queryHyperblockByNonce
- queryHyperblockByHash
- queryShardBlockByNonceFromShard
- queryShardBlockByHashFromShard
- getNetworkConfig
- getShardStatus
- getNodeHeartbeatStatus
- sendTransaction
- sendBatchOfTransactions
- simulateTransaction
- estimateTransactionCost
- queryTransactionInfo
- queryTransactionStatus
The Transaction Interactor has methods used for a more granular approach to transaction operations.
In order to create a sendable transaction, we must first create an instance of a transaction
using Transaction
domain object (setting nonce, gasLimit, version etc), then sign it using a wallet and transform
it to a payload for the Transaction Interactor (using toSendable() method).
For a more simple way of doing transaction operations, the interactor also has overloaded methods for sending, simulating and estimating. The methods are abstracting the complexity of transaction creation: automatically assigns proper nonce value, computes fee based on data input and applies the signature before execution. The required inputs are a Wallet and the following payload with minimum necessary data:
- receiver address
- value
- data
- gas limit (optional)
Example usage:
@Autowired MxTransactionInteractor interactor;
Mono<TransactionHash> sendTransaction(File pemFile){
var wallet=WalletCreator.fromPemFile(pemFile);
var tRequest=TransactionRequest.builder()
.receiverAddress(Address.fromBech32("erd1gklqdvxxxxxxxxxxxxxxxxxxxxx"))
.value(Balance.fromEgld(3.5))
.data(PayloadData.fromString("hello MultiversX"))
.build();
return interactor.sendTransaction(wallet,tRequest);
}
- callFunction
- query
- queryHex
- queryString
- queryInt
This component has methods which interact with the smart contracts on the network (obviously).
In order to call a smart contract function, we need to pass an instance of ContractFunction:
- smart contract address
- function name
- array of arguments
- value
- gas limit (optional)
Example usage:
@Autowired MxSmartContractInteractor interactor;
Mono<TransactionHash> callFunction(File pemFile){
var wallet=WalletCreator.fromPemFile(pemFile);
var function=ContractFunction.builder()
.smartContractAddress(Address.fromBech32("erd1xxxxxxxxxxxxxxxxxxxx8llllsh6u4jp"))
.functionName(FunctionName.fromString("addName"))
.args(List.of(FunctionArg.fromString("MultiversX"))
.value(Balance.zero())
.build();
return interactor.callFunction(wallet,function);
}
The ContractFunction generates a payload based on function name and args (HEX encoded), creates, assigns nonce, gas (if not specified, default is used), signs and executes a transaction.
Also, for querying we can use the following object:
- smart contract address
- function name
- array of arguments
- value
- caller address (optional)
- processEsdtTransaction
- getTokensForAccount
- getTokenRolesForAccount
- getAllTokens
- getTokenProperties
- getTokenSpecialRoles
- getNftDataForAccount
- getNftSftForAccount
- getTokensWithRole
This component has methods which cover all the ESDT related transaction and queries on the network.
processEsdtTransaction
takes an ESDTTransaction arg, which has multiple implementations:
- ESDTIssuance ( can be of type FUNGIBLE, SEMI_FUNGIBLE, NON_FUNGIBLE or META)
- ESDTGlobalOp ( can be of type PAUSE, UNPAUSE, FREEZE, UNFREEZE or WIPE)
- ESDTLocalOp ( can be of type MINT or BURN)
- ESDTTransfer
- ESDTNFTMultiTransfer
- ESDTUpgrade
- ESDTOwnershipTransfer
- ESDTRoleAssignment ( can be SET or UNSET)
- NFTCreation
- NFTAttributesUpdate
- NFTAddURI
- NFTCreationRoleTransfer
- NFTStopCreation
- NFTSFTLocalOp ( can be of type ADD or BURN)
- NFTSFTGlobalOp ( can be of type FREEZE, UNFREEZE or WIPE)
Example on how to issue a fungible ESDT:
@Autowired MxESDTInteractor interactor;
Mono<TransactionHash> issueEsdt(File pemFile){
var wallet=WalletCreator.fromPemFile(pemFile);
var transaction=ESDTIssuance.builder()
.type(Type.FUNGIBLE)
.tokenName(TokenName.fromString("Fung Token"))
.tokenTicker(TokenTicker.fromString("FNGTNK"))
.initialSupply(TokenInitialSupply.fromNumber(BigInteger.TEN))
.decimals(TokenDecimals.fromNumber(2))
.properties(Set.of(
new TokenProperty(TokenPropertyName.CAN_FREEZE,true),
new TokenProperty(TokenPropertyName.CAN_CHANGE_OWNER,true),
new TokenProperty(TokenPropertyName.CAN_ADD_SPECIAL_ROLES,true)));
return interactor.processEsdtTransaction(wallet,transaction);
}
For all transaction, the gas limit is already configured, but you can always set a custom value.
The rest of the ESDT operations are done in a similar fashion. NFT, SFT and META creation are also made super easy. You can follow the MultiversX ESDT documentation where you have the steps for all operations regarding ESDT, which are all covered by this framework.
For more details regarding the implementation, please consult the project's official Javadoc documentation .
Demo
You can find an example of a spring-boot service using this framework HERE.
Next features
In the next releases the following features have been planned:
- Account storage API
- Wallet Connect integration
- Wallet Creator - method to instantiate wallet from password-protected JSON keystore file
- Other enhancements
Changelog
All notable features and changes to this project will be documented in CANGELOG.md file
Contributing
Contributions are always welcome!
You can get in touch with me using the links below and figure out together how to make the project better.
Also, if you appreciate my effort and want to help me develop & maintain the MultiversX Spring Boot Framework, you can buy me some coffee via xPortal.