ICONKit, ICON SDK for Swift
ICON supports SDK for 3rd party or user services development. You can integrate ICON SDK for your project and utilize ICON’s functionality.
- ICONKit now provide Swift 5
Installation
CocoaPods
CocoaPods is a dependency manager for Swift Cocoa projects.
$ sudo gem install cocoapods
To integrate ICONKit into your project, specify it in your Podfile
target '<Your Target Name>' do
use_frameworks!
...
pod 'ICONKit', '~> 0.4.3'
...
end
Now install the ICONKit.
$ pod install
Swift 4.x version
0.3.2 is a last version of ICONKit for Swift 4.x.
try
pod 'ICONKit', '0.3.2'
Quick start
A simple query of the block by height is as follows.
// ICON Mainnet
let iconService = ICONService(provider: "https://ctz.solidwallet.io/api/v3", nid: "0x1")
// Gets a block matching the block height.
let request: Request<Response.Block> = iconService.getBlock(height: height)
let result = request.execute()
switch result {
case .success(let responseBlock):
...
case .failure(let error):
...
}
ICONService
APIs called through ICONService
.
It can be initialized as follows.
let iconService = ICONService(provider: "https://ctz.solidwallet.io/api/v3", nid: "0x1")
Queries
All queries are requested by a Request<T>
.
Synchronous
execute()
requests a query synchronously.
let response = iconService.getLastBlock().execute()
switch response {
case .success(let responseBlock):
print(responseBlock.blockHash)
...
case .failure(let error):
print(error.errorDescription)
...
}
Asynchronous
You can request a query asynchronously using a async
closure as below.
iconService.getLastBlock().async { (result) in
switch result {
case .success(let responseBlock):
print(responseBlock.blockHash)
...
case .failure(let error):
print(err.errorDescription)
...
}
}
Querying API
The querying APIs are as follows.
- getLastBlock
- getBlock(height)
- getBlock(hash)
- call
- getBalance
- getScoreAPI
- getTotalSupply
- getTransaction
- getTransactionResult
- sendTransaction
getLastBlock
Gets the last block information.
let request: Request<Response.Block> = iconService.getLastBlock()
getBlock(height)
Gets block information by block height.
let request: Request<Response.Block> = iconService.getBlock(height: height)
getBlock(hash)
Gets block information by block hash.
let request: Request<Response.Block> = iconService.getBlock(hash: "0x000...000")
call
Calls a SCORE API.
Use the return type of method's outputs you want to call in generic type.
For example, the type of output that is String
use String
.
let call = Call<String>(from: wallet.address, to: scoreAddress, method: "name", params: params)
let request: Request<String> = iconService.call(call)
let response: Result<String, ICError> = request.execute()
If the output type of method is hex String (ex. "0x56bc75e2d63100000"
), you can use String
and BigUInt
too!
Just input BigUInt
at generic type, then ICONKit convert the output value to BigUInt.
If you want to use hex String
, use String
.
// Using `BigUInt`
let call = Call<BigUInt>(from: wallet.address, to: scoreAddress, method: "balanceOf", params: params)
let request: Request<BigUInt> = iconService.call(call)
let response: Result<BigUInt, ICError> = request.execute() // return 100000000000000000000 or ICError
// Using `String`
let call = Call<String>(from: wallet.address, to: scoreAddress, method: "balanceOf", params: params)
let request: Request<String> = iconService.call(call)
let response: Result<String, ICError> = request.execute() // return "0x56bc75e2d63100000" or ICError
getBalance
Gets the balance of an given account.
let request: Request<BigUInt> = iconService.getBalance(address: "hx000...1")
getScoreAPI
Gets a list of ScoreAPI.
let request: Request<Response.ScoreAPI> = iconService.getScoreAPI(scoreAddress: "cx000...1")
getTotalSupply
Gets the total supply of ICX.
let request: Request<BigUInt> = iconService.getTotalSupply()
getTransaction
Gets a transaction matching the given transaction hash.
let request: Request<Response.TransactionByHashResult> = iconService.getTransaction(hash: "0x000...000")
getTransactionResult
Gets the transaction result requested by transaction hash.
let request: Request<Response.TransactionResult> = iconService.getTransactionResult(hash: "0x000...000")
Sending transactions
Calling SCORE APIs to change states is requested as sending a transaction.
Before sending a transaction, the transaction should be signed.
Loading wallets and storing the Keystore
// Generates a wallet.
let wallet = Wallet(privateKey: nil)
// Load a wallet from the private key.
let privateKey = PrivateKey(hex: data)
let wallet = Wallet(privateKey: privateKey)
// Save wallet keystore.
let wallet = Wallet(privateKey: nil)
do {
try wallet.generateKeystore(password: "YOUR_WALLET_PASSWORD")
try wallet.save(filepath: "YOUR_STORAGE_PATH")
} catch {
// handle errors
}
// Load a wallet from the keystore.
do {
let jsonData: Data = try Data(contentsOf: "YOUR_KEYSTORE_PATH")
let decoder = JSONDecoder()
let keystore = try decoder.decoder(Keystore.self, from: jsonData)
let wallet = Wallet(keystore: keystore, password: "YOUR_WALLET_PASSWORD")
} catch {
// handle errors
}
Creating transactions
// Sending ICX
let coinTransfer = Transaction()
.from(wallet.address)
.to(to)
.value(BigUInt(15000000))
.stepLimit(BigUInt(1000000))
.nid(self.iconService.nid)
.nonce("0x1")
// SCORE function call
let call = CallTransaction()
.from(wallet.address)
.to(scoreAddress)
.stepLimit(BigUInt(1000000))
.nid(self.iconService.nid)
.nonce("0x1")
.method("transfer")
.params(["_to": to, "_value": "0x1234"])
// Message transfer
let transaction = MessageTransaction()
.from(wallet.address)
.to(to)
.value(BigUInt(15000000))
.stepLimit(BigUInt(1000000))
.nonce("0x1")
.nid(self.iconService.nid)
.message("Hello, ICON!")
Sending Requests
SignedTransaction
object signs a transaction using the wallet.
And a request is executed as Synchronized or Asynchronized like a querying request.
Synchronous request
do {
let signed = try SignedTransaction(transaction: coinTransfer, privateKey: privateKey)
let request = iconService.sendTransaction(signedTransaction: signed)
let response = request.execute()
switch response {
case .success(let result):
print("SUCCESS: TXHash - \(result)")
...
case .failure(let error):
print("FAIL: \(error.errorDescription)")
...
}
} catch {
print(error)
...
}
Asynchronous request
do {
let signed = try SignedTransaction(transaction: coinTransfer, privateKey: privateKey)
let request = iconService.sendTransaction(signedTransaction: signed)
request.async { (result) in
switch result {
case .success(let result):
print(result)
...
case .failure(let error):
print(error.errorDescription)
...
}
}
} catch {
print(error)
}
Estimate step
It is important to set a proper stepLimit
value in your transaction to make the submitted transaction executed successfully.
estimateStep
API provides a way to estimate the Step usage of a given transaction. Using the method, you can get an estimated Step usage before sending your transaction then make a SignedTransaction
with the stepLimit
based on the estimation.
// Make a raw transaction without the stepLimit
let message = MessageTransaction()
.from(fromAddress)
.to(toAddress)
.nid(self.iconService.nid)
.message("Hello, ICON!")
// Estimate step
let estimateStepResponse = iconService.estimateStep(transaction: message).execute()
if let estimatedStepCost = try? estimateStepResponse.get() {
// Add some margin to estimated step
let stepLimit = estimatedStepCost + 10000
message.stepLimit(estimatedStepCost)
}
// Send transaction
do {
let signed = try SignedTransaction(transaction: message, privateKey: yourPrivateKey)
let response = self.iconService.sendTransaction(signedTransaction: signed).execute()
switch response {
case .success(let result):
print("Message tx result - \(result)")
case .failure(let error):
print(error)
}
} catch {
print(error)
}
Note that the estimation can be smaller or larger than the actual amount of step to be used by the transaction, so it is recommended to add some margin to the estimation when you set the stepLimit
of the SignedTransaction
.
Converter
ICONKit supports converter functions.
// Convert ICX or gLoop to loop.
let balance: BigUInt = 100
let ICXToLoop: BigUInt = balance.convert() // 100000000000000000000
let gLoopToLoop: BigUInt = balance.convert(unit: .gLoop) // 100000000000
// Convert `BigUInt` value to HEX `String`.
let hexString: String = ICXToLoop.toHexString() // 0x56bc75e2d63100000
// Convert HEX `String` to `BigUInt`.
let hexBigUInt: BigUInt = hexString.hexToBigUInt()! // 100000000000000000000
// Convert HEX `String` to `Date`
let timestamp: String = "0x5850adcbaa178"
let confirmedDate: Date = timestamp.hexToDate()! // 2019-03-27 03:16:22 +0000
Reference
Version
0.4.0
Requirement
- Xcode 10 or higher
- iOS 10 or higher
- Swift 5
- Cocoapods
License
This project follows the Apache 2.0 License. Please refer to LICENSE for details.