Skip to content

ryuta46/nem-kotlin

Repository files navigation

Read this in other languages: English, 日本語

nem-kotlin

nem-kotlin is a client library for easy use of NEM API.

This library wraps HTTP requests to NIS(NEM Infrastructure Server) and HTTP responses from NIS.

This library also provides crypt related utilities like key pair generation signing and verifying.

Sample

Sample projects are in samples directory.

How to setup

Download jar

Download the latest jar

for gradle users: (If you use gradle versioned 2.x, specify 'compile' instead of 'implmentaion')

implementation 'com.ryuta46:nem-kotlin:0.6.0'

for maven users:

<dependency>
  <groupId>com.ryuta46</groupId>
  <artifactId>nem-kotlin</artifactId>
  <version>0.6.0</version>
</dependency>

How to use

Account generation

'AccountGenerator' generates a NEM account. Network version is required( for main network or test network).

val account = AccountGenerator.fromRandomSeed(Version.Main)

If you have private key already, retrieve the account from the seed.

val account = AccountGenerator.fromSeed(ConvertUtils.toByteArray("YOUR_PRIVATE_KEY"), Version.Main)

Swapping the private key bytes may be needed because the endianness differs from it required in this library. (e.g. In case of using a Nano Wallet's private key.)

val account = AccountGenerator.fromSeed(ConvertUtils.swapByteArray(ConvertUtils.toByteArray("NANO_WALLET_PRIVATE_KEY")), Version.Main)

API client setup

NEM API client is constructed with a NIS URL.

val client = NemApiClient("http://62.75.251.134:7890")

'NemApiClient' is synchronous client, so it blocks the caller thread until the response is received.

You can also use reactive client 'RxNemApiClient'. 'RxNemApiClient' has the same methods of NemApiClient, but the return value is an Observable object.

val rxClient = RxNemApiClient("http://62.75.251.134:7890")

Getting super nodes information

You can get super node list as follows

var nodes: List<NodeInfo> = emptyList()
NisUtils.getSuperNodes().subscribe {
    nodes = it
}
....

// Select a node and initialize a client with it.
val node = nodes.first()
val client = NemApiClient("http://${node.ip}:${node.nisPort}")

// Create clients for all nodes
val clients = nodes.map { NemApiClient("http://${it.ip}:${it.nisPort}") }

getSuperNodes() is asynchronous function because it fetches node list from a server ( The default server URL is "https://supernodes.nem.io/nodes/").

You can also get nodes for test net with getTestNodes(). This function returns nodes list synchronously because the list is fixed.

Getting an account information

API client has methods corresponding to NEM APIs.

To get an account information,

val accountInfo = client.accountGet(account.address).account
rxClient.accountGet(account.address)
    .subscribeOn(Schedulers.newThread())
    .subscribe { response: AccountMetaDataPair ->
          val accountInfo = response.account
    }

Creating a transaction

When creating a transaction, you should get network time from the NIS and use it as timeStamp.

First, get network time from the NIS

val networkTime = client.networkTime()
val timeStamp = networkTime.receiveTimeStampBySeconds

Next, create a transaction using the acquired network time as timeStamp.

val transaction = TransactionHelper.createXemTransferTransaction(account, receiverAddress, amount, timeStamp = timeStamp)

The local time is used when the timeStamp parameter is omitted, but it may cause FAILURE_TIMESTAMP_TOO_FAR_IN_FUTURE error when the transaction is announced.

Sending XEM and Mosaics

TransactionHelper is an utility to create transactions which required account signing.

To send XEM,

val transaction = TransactionHelper.createXemTransferTransaction(account, receiverAddress, amount, timeStamp = timeStamp)
val result = client.transactionAnnounce(transaction)

Note that the amount specified above is micro nem unit. ( 1 XEM = 1,000,000 micro nem)

To send mosaic,

val transaction = TransactionHelper.createMosaicTransferTransaction(account, receiverAddress,
    listOf(MosaicAttachment(mosaicNamespaceId, mosaicName, quantity, mosaicSupply, mosaicDivisibility), timeStamp = timeStamp)
)
val result = client.transactionAnnounce(transaction)

Mosaic's supply and divisibility are used to calculate minimum transaction fee.

You can get these parameters of mosaic with 'namespaceMosaicDefinitionFromName' and 'mosaicSupply' if you don't know them.

val response = client.namespaceMosaicDefinitionFromName(namespaceId, name)
if (response != null) {
    divisibility = response.mosaic.divisibility!!
}
val response = client.mosaicSupply(mosaicId)
supply = response.supply

Sending and Receiving message.

To send XEM with a plain text message,

val message = "message".toByteArray(Charsets.UTF_8)
val transaction = TransactionHelper.createXemTransferTransaction(account, receiverAddress, amount,
        message = message,
        messageType = MessageType.Plain,
        timeStamp = timeStamp)
val result = client.transactionAnnounce(transaction)

With a encrypted message,

val message = MessageEncryption.encrypt(account, receiverPublicKey, "message".toByteArray(Charsets.UTF_8))
val transaction = TransactionHelper.createXemTransferTransaction(account, receiverAddress, amount,
        message = message,
        messageType = MessageType.Encrypted,
        timeStamp = timeStamp)
val result = client.transactionAnnounce(transaction)

You can read message from a transaction as follows

val message = transaction.asTransfer?.message ?: return

if (message.type == MessageType.Plain.rawValue) {
    // for plain text message
    val plainTextMessage = String(ConvertUtils.toByteArray(message.payload), Charsets.UTF_8)
} else {
    // for encrypted text message
    val decryptedBytes = MessageEncryption.decrypt(account, senderPublicKey, ConvertUtils.toByteArray(message.payload))
    val encryptedTextMessage = String(decryptedBytes, Charsets.UTF_8)
}

Multisig related transactions

Multisig related transactions(MultisigTransaction, MultisigSignatureTransaction, MultisigAggreageModificationTransaction) are also created by TransactionHelper.

To change an account to multisig account,

val multisigRequest = TransactionHelper.createMultisigAggregateModificationTransaction(account,
    modifications = listOf(MultisigCosignatoryModification(ModificationType.Add.rawValue, signerAccount.publicKeyString)),
    minimumCosignatoriesModification = 1,
    timeStamp = timeStamp)

val multisigResult = client.transactionAnnounce(multisigRequest)

Note that there is no way to change multisig account to normal account.

To send XEM from multisig account,

// Create inner transaction of which transfers XEM
val transferTransaction = TransactionHelper.createXemTransferTransactionObject(multisigAccountPublicKey, receiverAddress, amount, timeStamp = timeStamp)

// Create multisig transaction
val multisigRequest = TransactionHelper.createMultisigTransaction(signerAccount, transferTransaction, timeStamp = timeStamp)
val multisigResult = client.transactionAnnounce(multisigRequest)

And to sign the transaction,

val signatureRequest = TransactionHelper.createMultisigSignatureTransaction(anotherSignerAccount, innerTransactionHash, multisigAccountAddress, timeStamp = timeStamp)
val signatureResult = client.transactionAnnounce(signatureRequest)

You can get innerTransactionHash with client.accountUnconfirmedTransactions(anotherSignerAddress)

More APIs

If there is no method corresponding to the api you want to use, you can use 'get' or 'post' method of the client.

data class HarvestInfoArray(val data: List<HarvestInfo>)
...
val harvests: HarvestInfoArray = client.get("/account/harvests/", mapOf("address" to account.address")

Customizing API client behavior

You can use custom HTTP client to communicate with NIS by implementing 'HttpClient' interface.

And you can also use system depended log functionality(e.g. android.util.Log) by implementing 'Logger' interface.

To use them, specify them in constructing client object

val client = NemApiClient("http://62.75.251.134:7890", yourHttpClient, yourLogger)

The default HTTP client is 'HttpURLConnectionClient' which uses 'HttpURLConnection'.

The default logger is 'NoOutputLogger' which outputs nothing.

You can also use 'StandardOutputLogger' which output log to standard output.

How to use WebSocket client

You can use WebSocket client.

val wsClient = RxNemWebSocketClient("http://62.75.251.134:7778")

WebSocket client returns Observable object with each APIs.

The Information corresponding to each API is notified through the observable each time the information has changed.

e.g. Printing owned mosaic information each time the amount of owned mosaic has changed.

val subscription = wsClient.accountMosaicOwned(address)
                .subscribeOn(Schedulers.newThread())
                .subscribe { mosaic: Mosaic ->
                    print(Gson().toJson(mosaic))
                }

Note that you MUST dispose the subscription after you don't need to observe it.

The subscription is NOT completed automatically.

// Do not forget to dispose the subscription after you don't need to observe it.
subscription.dispose()

Author

Taizo Kusuda

Twitter @ryuta461

Donation Address: NAEZYI6YPR4YIRN4EAWSP3GEYU6ATIXKTXSVBEU5

About

Kotlin library for easy use of NEM API.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages