The Flow JVM SDK is a library for JVM languages (e.g. Java, Kotlin, Scala, Groovy) that provides utilities to interact with the Flow blockchain.
For a summary of the breaking changes introduced in the latest release, please refer to BREAKING_CHANGES.md.
At the moment, this SDK includes the following features:
This repository is organized as a multi-module project, consisting of the following modules:
The core module that includes all the necessary tools and libraries to interact with the Flow blockchain. This module provides the main functionalities such as transaction preparation, signing, and interaction with the Flow Access API. It also implements and tests use of the SDK via Java Annotations or Kotlin Extensions which can optionally be used when integrating.
This module contains example implementations demonstrating how to use the Flow JVM SDK in a Java application. It includes sample code for various use cases, making it easier for developers to understand and integrate the SDK into their Java projects.
Similar to the Java Example module, this module provides sample implementations in Kotlin. It showcases how to leverage the SDK's capabilities in a Kotlin environment.
The common utils module contains resources shared across all 3 above sub-modules, such as Cadence scripts and testing infrastructure.
We welcome all community contributions and will gladly review improvements and other proposals as PRs.
Read the contributing guide to get started.
This SDK requires Java Developer Kit (JDK) 8 or newer.
To add the SDK to your project, check out this README for sample Maven and Gradle setup configurations.
Flow uses ECDSA to control access to user accounts. Each key pair can be used in combination with the SHA2-256 or SHA3-256 hashing algorithms.
Here's how to generate an ECDSA key pair for the P-256 (secp256r1) curve:
val keyPair = Crypto.generateKeyPair(SignatureAlgorithm.ECDSA_P256)
val privateKey = keyPair.private
val publicKey = keyPair.public
One can then retrieve the hexadecimal representation of the key or encode it as bytes:
val privateKeyHex = privateKey.hex
val privateKeyBytes = privateKey.hex.toByteArray()
The example above uses an ECDSA key pair on the P-256 (secp256r1) elliptic curve. Flow also supports the secp256k1 curve used by Bitcoin and Ethereum.
Here's how to generate an ECDSA private key for the secp256k1 curve:
val keyPair = Crypto.generateKeyPair(SignatureAlgorithm.ECDSA_SECP256k1)
Here's a full list of the supported signature and hash algorithms on Flow: Flow Signature & Hash Algorithms.
You can communicate with any Flow Access Node using the Flow JVM SDK. This includes official Access Nodes, nodes you run yourself, and hosted nodes. Flow JVM SDK currently only supports gRPC communication with Access Nodes.
Here's how to create a new gRPC client for any network:
private const val MAINNET_HOSTNAME = "access.mainnet.nodes.onflow.org"
private const val TESTNET_HOSTNAME = "access.devnet.nodes.onflow.org"
fun newAccessApiConnnection(): FlowAccessApi = Flow.newAccessApi(MAINNET_HOSTNAME)
val accessAPIConnection = newAccessApiConnnection()
Once you have generated a key pair, you can create a new account using its public key. Check out the Create Account example for a runnable code snippet in Java or Kotlin.
Transaction signing is accomplished through the Crypto.Signer
interface. Below is a simple example of how to sign a transaction using a PrivateKey
generated with Crypto.generateKeyPair()
.
val latestBlockId
val payerAddress
val payerAccountKey
var tx = FlowTransaction(
script = FlowScript(ExamplesUtils.loadScript(scriptName)),
arguments = listOf(),
referenceBlockId = latestBlockID,
gasLimit = gasLimit,
proposalKey = FlowTransactionProposalKey(
address = payerAddress,
keyIndex = payerAccountKey.id,
sequenceNumber = payerAccountKey.sequenceNumber.toLong()
),
payerAddress = payerAddress,
authorizers = listOf(payerAddress)
)
val signer = Crypto.getSigner(privateKey, payerAccountKey.hashAlgo)
tx = tx.addEnvelopeSignature(payerAddress, payerAccountKey.id, signer)
Check out the Transaction Signing example for runnable code snippets in Java or Kotlin.
The Transaction Signing example introduces multiple transaction signing paradigms, including:
Before trying these examples, we recommend that you read through the transaction signature documentation.
You can submit a transaction to the Flow network using the Access API client.
See the Send Transaction example for runnable code snippets in Java or Kotlin; we include both a simple transaction and a more complex transaction with passed arguments.
After you have submitted a transaction, you can query its status by ID:
fun getTransactionResult(txID: FlowId): FlowTransactionResult = when (val response = accessAPI.getTransactionResultById(txID)) {
is FlowAccessApi.AccessApiCallResponse.Success -> {
if (response.data.errorMessage.isNotEmpty()) {
throw Exception(response.data.errorMessage)
}
response.data
}
is FlowAccessApi.AccessApiCallResponse.Error -> throw Exception(response.message, response.throwable)
}
The result includes a status
field that will be one of the following values:
UNKNOWN
- The transaction has not yet been seen by the network.PENDING
- The transaction has not yet been included in a block.FINALIZED
- The transaction has been included in a block.EXECUTED
- The transaction has been executed but the result has not yet been sealed.SEALED
- The transaction has been executed and the result is sealed in a block.val txResult = getTransactionResult(txID)
if (txResult.status == FlowTransactionStatus.SEALED) {
println("Transaction Sealed")
}
In the event of a failure, the FlowAccessApi.AccessApiCallResponse
class returns an Error
object which contains the corresponding error message
and throwable Exception
.
See the Get Transaction example for runnable code snippets in Java or Kotlin.
The Access API exposes getLatestBlock
, getBlockById
, and getBlockByHeight
methods for querying blocks on-chain. See the Get Blocks example for runnable code snippets in Java or Kotlin.
A block contains the following fields:
id
- The ID (hash) of the block.
parentId
- The ID of the previous block in the chain.
height
- The height of the block in the chain.
collectionGuarantees
- The list of collections included in the block.
You can use the simpleFlowScript
method to execute a Cadence script against the latest sealed execution state.
This is an example of a valid script:
access(all) fun main(a: Int): Int {
return a + 10
}
fun executeSimpleScript(): FlowScriptResponse {
val loadedScript = ExamplesUtils.loadScriptContent("cadence/execute_simple_script_example.cdc")
return accessAPI.simpleFlowScript {
script { loadedScript }
arg { JsonCadenceBuilder().int(5) }
}.let { response ->
when (response) {
is FlowAccessApi.AccessApiCallResponse.Success -> response.data
is FlowAccessApi.AccessApiCallResponse.Error -> throw Exception(response.message, response.throwable)
}
}
}
See the Execute Script example for runnable code snippets in Java or Kotlin.
The Access API exposes getEventsForHeightRange
, getEventsForBlockIds
, and getTransactionResultById
methods for querying events on-chain. See the Get Events example for runnable code snippets in Java or Kotlin.
The Access API exposes getAccountAtLatestBlock
and getAccountByBlockHeight
methods for querying accounts on-chain. See the Get Accounts example for runnable code snippets in Java or Kotlin.
A FlowAccount
contains the following fields:
address
: FlowAddress
- The account address.
balance
: BigDecimal
- The account balance.
contracts
: Map<String, FlowCode>
- The contracts deployed at this account.
keys
: List<FlowAccountKey>
- A list of the public keys associated with this account.
For a complete list of supported examples use-cases, see the Examples Summary.
The Flow JVM SDK maintainers have included