Solana-blockchain client, written in pure swift.
- Key pairs generation
- Networking with POST methods for comunicating with solana-based networking system
- Create, sign transactions
- Socket communication
- Orca swap
- Serum DEX Swap
- RenVM (Support: Bitcoin)
To run the example project, clone the repo, and run pod install
from the Example directory first.
Demo wallet: p2p-wallet
- iOS 11 or later
- RxSwift
- RxAlamofire
- TweetNacl
- CryptoSwift
- Starscream
SolanaSwift is available through CocoaPods. To install it, simply add the following line to your Podfile:
pod 'SolanaSwift', :git => 'https://github.com/p2p-org/solana-swift.git'
-
Every class or struct is defined within namespace
SolanaSDK
, for example:SolanaSDK.Account
,SolanaSDK.Error
. -
Import
import SolanaSwift
- Create an
AccountStorage
for saving account'skeyPairs
(public and private key), for example:KeychainAccountStorage
for saving intoKeychain
in production, orInMemoryAccountStorage
for temporarily saving into memory for testing. TheAccountStorage
must conform to protocolSolanaSDKAccountStorage
, which has 2 requirements: function for savingsave(_ account:) throws
and computed propertyaccount: SolanaSDK.Account?
for retrieving user's account.
Example:
import KeychainSwift
struct KeychainAccountStorage: SolanaSDKAccountStorage {
let tokenKey = <YOUR_KEY_TO_STORE_IN_KEYCHAIN>
func save(_ account: SolanaSDK.Account) throws {
let data = try JSONEncoder().encode(account)
keychain.set(data, forKey: tokenKey)
}
var account: SolanaSDK.Account? {
guard let data = keychain.getData(tokenKey) else {return nil}
return try? JSONDecoder().decode(SolanaSDK.Account.self, from: data)
}
}
struct InMemoryAccountStorage: SolanaSDKAccountStorage {
private var _account: SolanaSDK.Account?
func save(_ account: SolanaSDK.Account) throws {
_account = account
}
var account: SolanaSDK.Account? {
_account
}
}
- Creating an instance of
SolanaSDK
:
let solanaSDK = SolanaSDK(endpoint: <YOUR_API_ENDPOINT>, accountStorage: KeychainAccountStorage.shared) // endpoint example: https://api.mainnet-beta.solana.com
- Creating an account:
let mnemonic = Mnemonic()
let account = try SolanaSDK.Account(phrase: mnemonic.phrase, network: .mainnetBeta, derivablePath: .default)
try solanaSDK.accountStorage.save(account)
- Send pre-defined POST methods, which return a
RxSwift.Single
. List of predefined methods:
Example:
solanaSDK.getBalance(account: account, commitment: "recent")
.subscribe(onNext: {balance in
print(balance)
})
.disposed(by: disposeBag)
- Send token:
solanaSDK.sendNativeSOL(
to destination: String,
amount: UInt64,
isSimulation: Bool = false
)
.subscribe(onNext: {result in
print(result)
})
.disposed(by: disposeBag)
solanaSDK.sendSPLTokens(
mintAddress: String,
decimals: Decimals,
from fromPublicKey: String,
to destinationAddress: String,
amount: UInt64,
isSimulation: Bool = false
)
.subscribe(onNext: {result in
print(result)
})
.disposed(by: disposeBag)
- Send custom method, which was not defined by using method
request<T: Decodable>(method:, path:, bcMethod:, parameters:) -> Single<T>
Example:
(solanaSDK.request(method: .post, bcMethod: "aNewMethodThatReturnsAString", parameters: []) as Single<String>)
- Subscribe and observe socket events:
// accountNotifications
solanaSDK.subscribeAccountNotification(account: <ACCOUNT_PUBLIC_KEY>, isNative: <BOOL>) // isNative = true if you want to observe native solana account
solanaSDK.observeAccountNotifications() // return an Observable<(pubkey: String, lamports: Lamports)>
// signatureNotifications
solanaSDK.observeSignatureNotification(signature: <SIGNATURE>) // return an Completable
- To test transitive swap with orca, the account must have some
SOL
,SLIM
andKURO
token
extension OrcaSwapTransitiveTests {
var kuroPubkey: String {
<KURO-pubkey-here>
}
var secretPhrase: String {
<account-seed-phrases>
}
var slimPubkey: String {
<SLIM-pubkey-here>
}
}
- Create instance of orca
let orcaSwap = OrcaSwap(
apiClient: OrcaSwap.MockAPIClient(network: "mainnet"),
solanaClient: solanaSDK,
accountProvider: solanaSDK,
notificationHandler: socket
)
- Swap
// load any dependencies for swap to work
orcaSwap.load()
// find any destination that can be swapped to from a defined token mint
orcaSwap.findPosibleDestinationMints(fromMint: btcMint)
// get multiple tradable pools pairs (or routes) for a token pair (each pool pairs contains 1 or 2 pools for swapping)
orcaSwap.getTradablePoolsPairs(fromMint: btcMint, toMint: ethMint)
// get bestPool pair for swapping from tradable pools pairs that got from getTradablePoolsPair method, this method return a pool pair that can be used for swapping
orcaSwap.findBestPoolsPairForInputAmount(inputAmount, from: poolsPairs)
orcaSwap.findBestPoolsPairForEstimatedAmount(estimatedAmount, from: poolsPairs)
// swap
orcaSwap.swap(
fromWalletPubkey: <BTC wallet>,
toWalletPubkey: <ETH wallet>?,
bestPoolsPair: <best pool pair>,
amount: amount,
slippage: 0.05,
isSimulation: false
)
.subscribe(onNext: {result in
print(result)
})
.disposed(by: disposeBag)
- Create an instance of SerumSwap
let serumSwap = SerumSwap(client: solanaSDK, accountProvider: solanaSDK)
- swap
serumSwap.swap(
fromMint: <PublicKey>,
toMint: <PublicKey>,
amount: <Lamports>,
minExpectedSwapAmount: <Lamports?>,
referral: <PublicKey?>,
quoteWallet: <PublicKey?>,
fromWallet: <PublicKey>,
toWallet: <PublicKey?>,
feePayer: <PublicKey?>,
configs: <SolanaSDK.RequestConfiguration? = nil>
)
- For supporting new methods, data types, edit
SolanaSDK+Methods
orSolanaSDK+Models
- For testing, run
Example
project and creating test usingRxBlocking
- Welcome to contribute, feel free to change and open a PR.
Chung Tran, chung.t@p2p.org
SolanaSwift is available under the MIT license. See the LICENSE file for more info.