An Introduction to the KotlinTezos SDK

Malena LBC
camlCase upDates
Published in
7 min readOct 14, 2020
Photo by Barn Images on Unsplash

Introduction

KotlinTezos is the business logic layer for any Android application that wants to communicate with the Tezos blockchain. It follows TezosKit’s lead on providing an easy interface and convenience methods for developers. Its mission is to be an agnostic, decentralized and secure library.

It’s written in Kotlin, a statically-typed language developed by JetBrains. Kotlin compiles to bytecode, so it’s 100% interoperable with the Java language. The language has proven over time to be more concise and flexible and thus gave the name to the KotlinTezos library itself.

The library was written with the Android version of Magma in mind. We architected it using the Lazysodium library which provides the native libsodium libraries compatible with the Android ABIs.

If you are planning to build an Android app to operate on the Tezos blockchain, we think KotlinTezos has all the functionality you’ll need. This article will guide you through the main features it provides with some coding samples.

Features

The Wallet

The Wallet object encapsulates all the information about a public key hash, commonly known as address, in the Tezos ecosystem. It will generate the public and secret keys of a Wallet when provided with a BIP39 (Bitcoin Improvement Proposal #39) English mnemonic. The mnemonic phrase can be used any time to recover the keys, and it’s returned within the object to either store securely or show to the user (so they can write it down).

val wallet = Wallet(
listOf(
"abandon",
"ability",
"able",
"about",
"above",
"absent",
"absorb",
"abstract",
"absurd",
"abuse",
"access",
"accident"
)
)

At the time of implementation, BitcoinJ library provided a reliable PBKDF2-SHA512 proposal to be used in Java/Kotlin. You can read more about their design policy on deterministic wallets here.

The only caveat was the use of Guava, a notoriously cumbersome dependency. The binary seed values code was extracted to be used inside KotlinTezos to keep the library as lightweight as possible.

Since the Wallet also holds the secret key needed to sign operations, it implements that functionality through the SignatureProvider interface. This is important as the clients will require an object which signs operations but will delegate it to implementations.

The Clients

Tezos Node Client

The main client of the library provides several entry points to work with the Tezos network. It makes sure to be self contained, so every method will call whatever it needs to work or will ask it as an argument.

As the Magma Android implementation has evolved, new methods have been added that work around some of the limitations of the blockchain.

For example, when interacting with smart contracts you might want to calculateFees/bundleFees first to show your user and then runOperations if the user approves. For more granularity these methods call different services that you could use separately (see the Services section below).

val wallet = Wallet(...)
var operationList = OperationFactory.createOperation(
OperationType.TRANSACTION,
OperationParams.Transaction(Tez(1.0), source = wallet.address, to = "tz1..."),
OperationFees.simulationFees
)
client.bundleFees(
operations = operationList,
source = wallet.address,
signatureProvider = wallet,
operationPolicy = OperationFeesPolicy.Estimate(),
callback = object : TezosCallback<List<Operation>> {
...
// bundleFees clones the operation list but the fees are the estimated ones instead of OperationFees.simulationFees
operationList = result
...
}
)
client.runOperations(
operations = operationList,
source = wallet.address,
signatureProvider = wallet,
callback = object : TezosCallback<String> { ... }
)

FA1.2 Token Client

KotlinTezos supports working with FA1.2 tokens through TokenContractClient class. It uses a flexible implementation of Michelson parameters to populate the needed information to query the balance or send tokens from one address to another.

The SmartContractClient interface attribute is responsible for communicating with the blockchain. You can provide your own implementation or can use TezosNodeClient.

For example, to operate with a certain FA1.2 token you’d create a TokenContractClient with the FA1.2 token contract address. When sending some token value we use an immutable arbitrary-precision integer in the form of Java’s BigInteger (FA1.2 tokens vary on decimal places. It’s the responsibility of the receiver to parse the integer value to its appropriate decimal representation).

// 5 decimal point FA1.2 integer value of 1.23
val amount = 123000
val tokenClient = TokenContractClient(
network = TezosNetwork.MAINNET,
tokenContractAddress = "KT1...",
client = TezosNodeClient(...)
)
tokenClient.send(
amount = amount,
to = "tz1...",
from = "tz1...",
signatureProvider = wallet,
feesPolicy = OperationFeesPolicy.Estimate(),
callback = object : TezosCallback<String> { ... }
)

Dexter Token Client

camlCase has developed Dexter, a decentralized exchange that allows trading of XTZ and FA1.2 tokens. The DexterTokenClient in the library provides the Java/Kotlin bridge to operate with it.

To work, it needs the two smart contracts related to the FA1.2 token you want to swap: Its contract address and the Dexter smart contract address for that token. The SmartContractClient attribute has the same role as in the TokenContractClient.

Showing the user the input and output values before approving the operation is a key part of the trading flow. The tradeTezForToken and tradeTokenForTez methods will expect those amounts that should be calculated beforehand. To help in this process, we provide the DexterExchange class:

val dexterClient = DexterTokenClient(
client = TezosNodeClient(...),
exchangeContractAddress = "KT1...",
tokenContractAddress = "KT1..."
)
val tezAmount = Tez(...)
val tokenAmount = DexterExchange.getTokenTradeAmount(
amount = tezAmount,
xtzPool = dexterClient.getExchangeBalance(...),
tokenPool = dexterClient.getTokenBalance(...)
)
dexterClient.tradeTezForToken(
source = "tz1...",
amount = tezAmount,
tokenAmount = tokenAmount,
signatureProvider = wallet,
callback = TezosCallback<String> { ... }
)

Conseil Client

To overcome the limitations of blockchain operations, many services have appeared to query the information via database. Magma uses Conseil to list the user’s operations through KotlinTezos’ ConseilClient.

val client = ConseilClient(
url = "http://conseil.server",
network = TezosNetwork.MAINNET,
apiKey = "You can get some at https://nautilus.cloud/home/keys"
)
client.operations(
from = "tz1...",
sentTypes = ,
sentTypes = listOf(OperationType.TRANSACTION, OperationType.DELEGATION),
callback = TezosCallback<List<ConseilOperation>> { ... }
)

The operation method handles three main queries: Operations with the address as source; with the address as destination; and operations with the address inside the parameters field of a smart contract operation. The last one is vital to get Dexter operations even if the query might take longer than usual.

The Services

OperationFeesFactory

Fee, gas and storage need to be appended to every operation and can vary greatly depending on the type and destination of it. The parameter feesPolicy used in some functions lets you decide how you want these values to be calculated:

  • Default: Use the standard fees listed in the protocol documentation.
  • Custom: Set specific fees.
  • Estimate: Simulate the operation and calculate the fees.

The FeeEstimatorService is the service called when you want to estimate the best value for the operation you plan to inject. It dry-runs the operation with fee values of zero, and follows the protocol documentation to calculate the fees, gas and storage values.

ForgingService

Before injecting your operation, it needs to be forged and signed to ensure its integrity. KotlinTezos provides all this functionality in its forging service and signature provider implementations.

For now, it only supports forging remotely by calling the Tezos RPC /forge/operations and, afterwards, parsing the resulting hash to ensure the node has not been compromised in any way.

Future

Photo by Dominik Scythe on Unsplash

Multiplatform Support

Java is the primary language for finance and banking. From the beginning, we understood the potential of a stable library that could be used in both Java and Kotlin codebases and beyond the Android ecosystem.

The main stakeholder for KotlinTezos has been Magma, our mobile wallet, so our efforts were concentrated in finishing an Android library that could provide all the business logic needed for the wallet app.

Moving forward, our plan is to configure our project to handle more than Android ABIs. A Kotlin Multiplatform project with the native libraries for Libsodium bundled for every architecture is our long-term goal.

BIP44

The current Wallet object is created following the BIP39 standard but we plan on being BIP44 compatible too. This would give the user access to multiple accounts under the same private key.

Our objective is to support the most used schemas for most Tezos wallets out there.

Baking Bad indexer

KotlinTezos supports Conseil, but there are many other services to help navigate the blockchain. The Baking Bad team has put together the TzKT explorer that also lets you query blockchain data.

The API provides endpoints for operation data and offers multiple filters as query parameters. Our plan is to create a TzKtClient that would help any team using this solution.

Conclusion

The KotlinTezos library is meant to help Android developers integrate the Tezos blockchain into their projects. The library is stable, production ready and keeps growing as we develop further features for the Magma wallet. Expect new releases roughly every month or so.

If you are looking for a similar solution for your iOS project, check out camlKit, our Swift SDK!

--

--