# solana-kmp
**Repository Path**: ProjectOpenSea/solana-kmp
## Basic Information
- **Project Name**: solana-kmp
- **Description**: No description available
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: main
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2025-09-23
- **Last Updated**: 2025-11-16
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# Kotlin Multiplatform SDK for Solana (KMP)
## Overview
Welcome to the Kotlin Multiplatform SDK for Solana! This SDK leverages the power of Kotlin Multiplatform to enable developers to work with Solana blockchain across various platforms including Android, iOS Ecosystem, and the JVM. Utilizing this SDK, developers can create transactions, sign them using various signer modules, and interact with Solana through RPC calls with ease.
## Features
- **Cross-Platform**: Use a single codebase to target Android, iOS Ecosystem, and JVM.
- **Create Transactions**: Construct and manage Solana transactions with ease.
- **Sign with Signer**: Safely and securely sign your transactions with a robust signer module.
- **Interact with RPC**: Facilitate communication with the Solana blockchain through RPC calls. With a powerful serialization capabilities.
- **Metaplex Read API**: Supports the Metaplex Read API extra rpc calls
- **Keypair Generator**: Generate an EDDSA keypairs
## Getting Started
### Prerequisites
Ensure you have the Kotlin Multiplatform development environment set up. Refer to the [Kotlin Multiplatform documentation](https://kotlinlang.org/docs/multiplatform.html) to set up your environment.
### Modules
We do support composability. Each module can be used alone or by referencing the main `solana` module. The modules we support:
- `:solana:` This module provides functionality and utilities for the Solana blockchain platform. This is the main module, and might be the only one you need to build on top. It exposes all the modules required to interact with solana.
- `:solanapublickeys:` This module handles public keys within the Solana ecosystem.
- `:base58:` This module encodes and decodes data in Base58 format.
- `:solanaeddsa:` This module deals with Elliptic Curve Digital Signature Algorithm (EdDSA) cryptography specific to Solana. It also provides Keypair generation. Its the want you need if you need to generate a Offline Random Keypair.
- `:amount:` This module is used for managing numeric values, potentially within the context of financial transactions or cryptocurrency amounts. For example `lamports` or `sol`
- `:readapi:` This module is designed to consume Metaplex DAS Read API. Check the [documentation](https://github.com/metaplex-foundation/api-specifications).
- `:rpc:` This module provides functionality for making remote procedure calls (RPC) to interact with a blockchain Solana network.
- `:signer:` This module handles cryptographic signing and verifying operations, often used for secure transactions and authentication. Its just an interface.
- `:mplbubblegum:` The purpose of this module is to mint and interact with compressed NFTs.
- `:mpltokenmetadata:` The purpose of this module is to interact with Metaplex Token Metadata program.
### Installation
#### KMP
Import the SDK into your Kotlin Multiplatform project by adding the following dependency to your build.gradle.kts:
```kotlin
implementation("foundation.metaplex:solana:$Version")
```
Our SDK is architecturally composable, allowing you to install only the dependencies necessary for your specific use case, which keeps your application optimized and resource-efficient. The library modules are available on **Maven**, facilitating easy integration into your Kotlin project.
For those focusing on RPC calling, you can choose to exclusively install modules pertinent to RPC interactions, promoting a clean and efficient codebase. Refer to our module documentation to understand the diverse range of modules available and how to selectively integrate them based on your requirements. This strategy ensures a lean development process, affording you the convenience of using only the tools you need, without the encumbrance of extraneous features.
```kotlin
implementation("foundation.metaplex:rpc:$Version")
```
Refer to the modules names to reference them individualy.
#### Android
Import the SDK into your Android project by adding the following dependency to your build.gradle.kts:
```kotlin
implementation("foundation.metaplex:solana:$Version")
```
#### iOS/MacOS in Xcode
By using KMP we got iOS/MacOs for free. We think that KMP is the future of developing solana sdks since it solves the performance problem and makes a good enough sdk for swift. Its far better than a RN or flutter counterparts since it uses actual Native Code.
Its important to notice that the fat binary is not swift-first. You can still use [pure swift sdk](https://github.com/metaplex-foundation/Solana.Swift). But progress is coming along KMP that I am confident that this version will become the more complete version.
This libraries prebuilds and deploys xcframeworks (fat binaries). To use them you can check the [release section and download](https://github.com/metaplex-foundation/solana-kmp/releases) the one you need.
They are provided in zip format. When unzipped a fat binary is available. Just drag and drop it in your Xcode Project. It can be possible to use it with SPM. Currently we dont provide sha256 hashes.
Using `solana.xcframework` maybe sufficient for most projects.
[
](images/xcframework-navigation.jpg)
If you don`t trust the prebuilds you are allow to build them locally. By running this gradle command from android studio. This will take some time to build.
```./gradlew buildReleaseXCFramework```
or for specific module
```./gradlew solana:buildReleaseXCFramework```
**Important:** API support callbacks and async/await. For the second **only** @MainActor scope is supported. Main Actor can be validated `Thread.current.isMainThread`.
### Usage
#### RPC
RPC a provide the implementation for the RpcInterface. Its possible to call the most important rpc calls. First we need to configure the RPC endpoints.
##### Koltin
```kotlin
val rpcUrl: String = "https://api.mainnet-beta.solana.com"
val rpc = RPC(rpcUrl)
```
##### Swift
```swift
let rpcURL = "https://api.mainnet-beta.solana.com"
let driver = NetworkDriver(httpClient: NetworkingClientKt.NetworkClient())
let rpc = RPC(
rpcUrl: rpcURL,
httpNetworkDriver: driver
)
```
This is how to make a `getSlot` rpc call:
##### Koltin
```kotlin
val slot = rpc.getSlot(null)
```
##### Swift
```swift
// await/async
let slot = await rpc.getSlot(configuration: nil)
// closure
rpc.getSlot(configuration: nil) { slot, error in
debugPrint("\(slot!)")
}
```
##### GetAccountInfo Example
```kotlin
val randomPublicKey = PublicKey("9VHphpWFmUxVHxzWyeYJYYbQADWZ7X6PLzyWER8Lc3k2")
val rpc = RPC(rpcUrl)
val metadata = rpc.getAccountInfo(
randomPublicKey,
null,
serializer = BorshAsBase64JsonArraySerializer(
Metadata.serializer()
)
)?.data
```
##### Interface
This is the RPC interface. Its possible to implement and alternative Interface.
```kotlin
interface RpcInterface {
suspend fun getAccountInfo(
publicKey: PublicKey,
configuration: RpcGetAccountInfoConfiguration?,
serializer: KSerializer,
): Account?
suspend fun getMultipleAccounts(
publicKeys: List,
configuration: RpcGetMultipleAccountsConfiguration?,
serializer: KSerializer,
): List?>?
suspend fun getProgramAccounts(
programId: PublicKey,
configuration: RpcGetProgramAccountsConfiguration?,
serializer: KSerializer
): List?>?
suspend fun getLatestBlockhash(
configuration: RpcGetLatestBlockhashConfiguration?
): BlockhashWithExpiryBlockHeight
suspend fun getSlot(
configuration: RpcGetSlotConfiguration?
): ULong
suspend fun getMinimumBalanceForRentExemption(
usize: ULong
): ULong
suspend fun requestAirdrop(
configuration: RpcRequestAirdropConfiguration
): TransactionSignature
suspend fun getBalance(
publicKey: PublicKey,
configuration: RpcGetBalanceConfiguration?
): Long
suspend fun sendTransaction(
transaction: SerializedTransaction,
configuration: RpcSendTransactionConfiguration?
): TransactionSignature
}
```
#### Transactions
Create transactions using the TransactionBuilder class which facilitates the creation of Solana transactions in a Kotlin-friendly way.
##### Kotlin
```kotlin
val memo = "Other Test memo"
val transaction: Transaction = SolanaTransactionBuilder()
.addInstruction(
writeUtf8(
signer().publicKey,
memo
)
)
.setRecentBlockHash("Eit7RCyhUixAe2hGBS8oqnw59QK3kgMMjfLME5bm9wRn")
.setSigners(listOf(signer()))
.build()
```
##### Swift
```swift
let memo = "Other Test memo"
let memoInstruction = MemoProgram.shared.writeUtf8(
account: signer().publicKey,
memo: memo
)
let transaction: = SolanaTransactionBuilder()
.addInstruction(
transactionInstruction:
memoInstruction
)
.setRecentBlockHash(recentBlockHash: "BlockHash")
.setSigners(signers: [signer()])
.build()
```
#### Signing a Transaction
Use the signer module to securely sign your transactions with the necessary credentials:
##### Kotlin
```kotlin
class SolanaKeypair(
override val publicKey: PublicKey,
override val secretKey: ByteArray
) : Keypair
class HotSigner(private val keyPair: Keypair) : Signer {
override val publicKey: PublicKey = keyPair.publicKey
override suspend fun signMessage(message: ByteArray): ByteArray = SolanaEddsa.sign(message, keyPair)
}
```
##### Swift
```swift
class SolanaKeypair: Keypair {
var publicKey: PublicKey
var secretKey: KotlinByteArray
init(publicKey: PublicKey, secretKey: KotlinByteArray) {
self.publicKey = publicKey
self.secretKey = secretKey
}
}
class HotSigner: Signer {
private let keypair: Keypair
init(keypair: Keypair) {
self.keypair = keypair
}
func signMessage(message: KotlinByteArray) async throws -> KotlinByteArray {
return try await SolanaEddsa.shared.sign(message: message, keypair: self.keypair)
}
var publicKey: PublicKey {
self.keypair.publicKey
}
}
```
## More Examples
KMP can be hard to inferred directly. Especially extensions, static methods and companion/shared objects. Here is a good example of an implementation for a signer using the HotSigner implementation previously developed.
### Keypair and signers
Generate a PublicKey from a Base58 Private key.
##### Kotlin
```kotlin
val privateKey = "4Z7cXSyeFR8wNGMVXUE1TwtKn5D5Vu7FzEv69dokLv7KrQk7h6pu4LF8ZRR9yQBhc7uSM6RTTZtU1fmaxiNrxXrs".decodeBase58().copyOfRange(0, 32)
val k = SolanaEddsa.createKeypairFromSecretKey(privateKey)
val signer = HotSigner(SolanaKeypair(k.publicKey, k.secretKey))
```
##### Swift
```swift
let decoded = try Base58Kt.decodeBase58("4Z7cXSyeFR8wNGMVXUE1TwtKn5D5Vu7FzEv69dokLv7KrQk7h6pu4LF8ZRR9yQBhc7uSM6RTTZtU1fmaxiNrxXrs")
let privateKey = decoded.toData().subdata(in: 0..<32)
let k = try await SolanaEddsa.shared.createKeypairFromSecretKey(secretKey: NSDataByteArrayKt.toByteArray(privateKey))
let signer = HotSigner(keypair: SolanaKeypair(publicKey: k.publicKey, secretKey: k.secretKey))
```
### Memo transaction
Write to a memo using the Memo program.
```kotlin
// Configure RPC
val rpcUrl = "https://api.devnet.solana.com/"
val rpc = RPC(rpcUrl)
val blockhash = rpc.getLatestBlockhash(null)
val memo = "Other Test memo"
val transaction: Transaction = SolanaTransactionBuilder()
.addInstruction(
writeUtf8(
signer.publicKey,
memo
)
)
.setRecentBlockHash(blockhash.blockhash)
.setSigners(listOf(signer))
.build()
val serializedTransaction = transaction.serialize()
val transactionSignature = rpc.sendTransaction(serializedTransaction, null)
```
```swift
// Configure RPC
let rpcURL = "https://api.devnet.solana.com/"
let rpc = RPC(
rpcUrl: self.rpcURL,
httpNetworkDriver: NetworkDriver(httpClient: NetworkingClientKt.NetworkClient())
)
let blockHash = try await rpc!.getLatestBlockhash(configuration: nil)
let memo = "Other Test memo"
let transaction = try await SolanaTransactionBuilder()
.addInstruction(
transactionInstruction: MemoProgram.shared.writeUtf8(
account: signer.publicKey,
memo: memo
)
)
.setRecentBlockHash(recentBlockHash: blockHash.blockhash)
.setSigners(signers: [signer])
.build()
let serializedTransaction = try await transaction.serialize(
config: SerializeConfig(requireAllSignatures: true, verifySignatures: true)
)
let transactionSignature = try await rpc!.sendTransaction(
transaction: serializedTransaction,
configuration: nil
)
```
### Read API
Read Api `getAssetsByOwner` example
```kotlin
val randomPublicKey = PublicKey("Geh5Ss5knQGym81toYGXDbH3MFU2JCMK7E4QyeBHor1b")
val assets = readApiDecorator.getAssetsByOwner(GetAssetsByOwnerRpcInput(randomPublicKey))
assertTrue { assets.total > 0 }
```
```swift
let decorator = ReadApiDecorator(rpcUrl: self.rpcURL, httpNetworkDriver: driver!)
let randomPublicKey = PublicKey(base58String: "Geh5Ss5knQGym81toYGXDbH3MFU2JCMK7E4QyeBHor1b")
let assets = try await decorator.getAssetsByOwner(
input: GetAssetsByOwnerRpcInput(
ownerAddress: randomPublicKey,
page: 1,
limit: nil,
before: nil,
after: nil,
sortBy: nil
)
)
assert(assets.total > 0)
```
### License
This project is licensed under the Metaplex License. See the [LICENSE](LICENSE.txt) file for details.