Swift sdk library for interacting with a CSPR node.
SDK to streamline the 3rd party Swift client integration processes. Such 3rd parties include exchanges & app developers.
The SDK use Swift 5.0 and support device running IOS from 10.0, MacOS from 10.4.4, WatchOS from 5.3, tvOS from 12.3
Go to the SDK root folder.
- Build command in terminal, type this in terminal
swift build
- Test command in terminal, type this in terminal
swift test
- For a full build,test on the SDK, run the following command
swift package clean
swift build
swift test
Note: The project is built and tested successfully without error and warning in Xcode. There are 2 warnings in Github automated script for building or when you run with build script "swift build" in Terminal. The warnings comes from the external package https://github.com/leif-ibsen/SwiftECC.git, which refers to the unused result of 2 function calls, in detail the two warnings are:
-
SwiftECC/Sources/SwiftECC/Domain2/Domain2.swift, at line 196: result of call to 'add' is unused seq1.add(ASN1Integer(BInt(self.rp.k1)))
-
SwiftECC/Sources/SwiftECC/Domain2/Domain2.swift, at line 198: result of call to 'add' is unused seq1.add(ASN1Sequence()
The result maybe used for the next work of the external package. (More implementation or extenstion of the encrypting functions).
It does not effect the overall performance or cause any problem to the SDK.
In order to build and test the SDK from Xcode, you need to have a Mac run MacOS and Xcode 13 (or above) installed.
To install Xcode 13 go to this link: https://developer.apple.com/xcode/
Once you have Xcode 13 or above installed in your Mac machine, download or clone the CasperSDKInSwift from Github. An easy way to clone the SDK is just to open XCode and choose "Clone an existing project" in the left panel like what it is in the image below
Enter the Github url (for example "https://github.com/hienbui9999/CasperSDKInSwift.git") for the Clone address, then hit "Clone"
When you have the SDK and all source code in your local machine, you can then Build or Test the SDK.
To Build the project hit "Product->Build" like this
To Test the project hit "Product->Test" like this
You can see log information by "View->Debug Area->Active Console" or just press the combination of Key "Cmd + Shift + C"
The Key wrapper do the following work:(for both Secp256k1 and Ed25519):
-
(PrivateKey,PublicKey) generation
-
Sign message
-
Verify message
-
Read PrivateKey/PublicKey from PEM file
-
Write PrivateKey/PublicKey to PEM file
The key wrapper is used in account_put_deploy RPC method to generate approvals signature based on deploy hash.
The put deploy RPC method implements the call "account_put_deploy". User needs to declare a deploy and assign the information for the deploy (header,payment,session,approvals). The following information is generated based on the deploy:
-
Deploy body hash - base on the serialization of deploy body, which is a string of payment serialization + deploy session serialization. Then use the blake2b256 hash over the generated serialized string to make the deploy body hash. The deploy body hash is an attribute of the deploy header.
-
Deploy hash: Use the blake2b256 hash over the header of the deploy.
-
Signature in deploy approvals is generated using the deploy hash using the key wrapper to sign over the deploy hash. You can use either Secp256k1 or Ed25519 to sign over the deploy hash, based on the account type of the deploy. If the deploy use account of type Secp256k1 then you have to sign with Secp256k1 key. If the deploy use account of type Ed25519 then you have to sign with Ed25519 key.
-
The whole deploy with full information is then serialized to a Json string and sent with a POST request to Casper main or test net, or localhost to call account_put_deploy RPC method.
-
Know what your account type is, Ed25519 or Secp256k1.
-
Save your private key in 1 place that you can point to from code.
-
Choose correct path to private key to sign for the deploy hash in put deploy function.
Instantiate one instance of the CasperSKD, which is defined in file "CasperSDK.swift". In the test section of this project we instantiate it from "CasperSDKInSwiftTests.swift" file and set parameter and call the methods from this file.
The call for each method will then send POST request and get data back from server, this process is done in "HttpHandler" file.
For processing the data back from server (as Json format), base on which method call, the corresponding class and functions will be call to catch and put data in proper data structure, which started from this code line "if self.methodCall == .chainGetStateRootHash {" in "HttpHandler" file.
To query a node, use the CasperSDK as the entry point. Instantiate CasperSDK with the URL address for calling Casper method, like this:
let casperSDK:CasperSKD = CasperSDK(url:"https://node-clarity-testnet.make.services/rpc");
You can change the method url by running this code line
casperSDK.setMethodUrl(url: "https://node-clarity-mainnet.make.services/rpc")
Retrieves the state root hash String. There are 3 cases for calling this method
1 - call method without any param 2 - call method with BlockHash param 3 - call method with BlockHeight param
And this is the detail for each cases:
1- call method without any param:
let getStateRootHashParam:GetStateRootHashParam = GetStateRootHashParam();
do {
try casperSDK.getStateRootHash(getStateRootHashParam: getStateRootHashParam)
} catch {
throw error
}
2 - call method with BlockHash param - You can test with other block_hash by replacing the below hash in .Hash("") parameter
do {
getStateRootHashParam.block_identifier = .Hash("20e6cf8001a9456e9e202f0923393b1f551470934683800f62d11c1685d4710d")
try casperSDK.getStateRootHash(getStateRootHashParam: getStateRootHashParam)
} catch {
throw error
}
3 - call method with BlockHeight param - You can test with other block Height by replacing the below Height in the .Height() parameter
do {
getStateRootHashParam.block_identifier = .Height(473861)
try casperSDK.getStateRootHash(getStateRootHashParam: getStateRootHashParam)
} catch {
throw error
}
Retrieves a list of Peers.
do {
try casperSDK.getPeers()
} catch {
throw error
}
Retrieves a Deploy object.
call parameters :
- deploy hash
do {
let getDeployParam:GetDeployParams = GetDeployParams();
getDeployParam.deploy_hash = "1b6a2f1a67bc087babe46455f4c6e7775528999fd3a37dbcba5b438f439abda2";
try casperSDK.getDeploy(getDeployParam: getDeployParam)
} catch {
throw error
}
Retrieves a NodeStatus object.
do {
try casperSDK.getStatus()
} catch {
throw error
}
Retrieves Transfert List within a block.
call parameters :
- block_identifier, and enum type which can be either BlockHash or BlockHeight
Call by BlockHash, you can change the BlockHash in the .Hash("") parameter
do {
let block_identifier : BlockIdentifier = .Hash("ae173969cb6ce3c99439c81e5b803c15797a8559796d980daa99f52beb7192e3")
try casperSDK.getBlockTransfers(input: block_identifier)
} catch {
throw error
}
Call by BlockHeight, you can change the BlockHeight in the .Height() parameter
do {
let block_identifier : BlockIdentifier = .Height(448471)
try casperSDK.getBlockTransfers(input: block_identifier)
} catch {
throw error
}
Retrieves a Block object.
call parameters :
- block_identifier, and enum type which can be either BlockHash or BlockHeight
Call by BlockHash, you can change the BlockHash in the .Hash("") parameter
do {
let block_identifier2 : BlockIdentifier = .Hash("830fd58dd08189981d7535fc9de0606bc789b2c8ef2af895ebce5ffc23c4530e")
try casperSDK.getBlock(input: block_identifier2)
} catch {
throw error
}
Call by BlockHeight, you can change the BlockHeight in the .Height() parameter
do {
let block_identifier:BlockIdentifier = .Height(449797)
try casperSDK.getBlock(input: block_identifier)
} catch {
throw error
}
Retrieves an EraSummury object.
call parameters :
- switch block (last block within an era) hash, which is a block_identifier, and enum type which can be either BlockHash or BlockHeight
Call by BlockHeight, you can change the BlockHeight in the .Height() parameter
do {
let block_identifier:BlockIdentifier = .Height(441636)
try casperSDK.getEraBySwitchBlock(input: block_identifier)
} catch {
throw error
}
Call by BlockHash, you can change the BlockHash in the .Hash("") parameter
do {
let block_identifier:BlockIdentifier = .Hash("83a86ba2d753d85cdd974cf2bb0f6cb5d446f00c2f7f89b5a5e4fef208b19fcc")
try casperSDK.getEraBySwitchBlock(input: block_identifier)
} catch {
throw error
}
Retrieves a StoredValue object.
Here is some example of getting different kinds of StoredValue
call parameters :
- state root hash
- contract hash
do {
let getStateItemParam:GetItemParams = GetItemParams();
getStateItemParam.state_root_hash = "83f6dca28102ecf1cf79d2e32172044b2eacf527e47a8781cead3850d01e6328"
getStateItemParam.key = "hash-b36478fa545160796de902e61ac504b33bc14624eea245a9df525b4d92d150bc"
try casperSDK.getItem(input: getStateItemParam)
} catch {
throw error
}
call parameters :
- state root hash
- account hash
do {
let getStateItemParam:GetItemParams = GetItemParams();
getStateItemParam.state_root_hash = "b31f42523b6799d6d403a3119596c958abf2cdba31066322f01e5fa38ef999aa"
getStateItemParam.key = "account-hash-ff2ae80f71c1ffcac4921100a21b67ddecf59a30fb86eb6979f47c8838b3b7d3"
try casperSDK.getItem(input: getStateItemParam)
} catch {
throw error
}
call parameters :
- state root hash
- transfer hash
do {
let getStateItemParam:GetItemParams = GetItemParams();
// getStateItemParam.state_root_hash = "1416302b2c637647e2fe8c0b9f7ee815582cc7a323af5823313ff8a8684c8cf8"
// getStateItemParam.key = "transfer-8218fa8c55c19264e977bf2bae9f5889082aee4d2c4eaf9642478173c37d1ed4"
try casperSDK.getItem(input: getStateItemParam)
} catch {
throw error
}
call parameters :
- state root hash
- deploy info hash
do {
let getStateItemParam:GetItemParams = GetItemParams();
getStateItemParam.state_root_hash = "1416302b2c637647e2fe8c0b9f7ee815582cc7a323af5823313ff8a8684c8cf8"
getStateItemParam.key = "deploy-a49c06f9b2adf02812a7b2fdcad08804a2ce4896ffec7c06c920d417e7e39cfe"
try casperSDK.getItem(input: getStateItemParam)
} catch {
throw error
}
call parameters :
- state root hash
- bid hash This example call the main net
do {
casperSDK.setMethodUrl(url: "https://node-clarity-mainnet.make.services/rpc")
let getStateItemParam:GetItemParams = GetItemParams();
getStateItemParam.state_root_hash = "647C28545316E913969B032Cf506d5D242e0F857061E70Fb3DF55980611ace86"
getStateItemParam.key = "bid-24b6D5Aabb8F0AC17D272763A405E9CECa9166B75B745Cf200695E172857c2dD"
try casperSDK.getItem(input: getStateItemParam)
} catch {
throw error
}
call parameters :
- state root hash
- withdraw hash This example call the test net
do {
casperSDK.setMethodUrl(url: "https://node-clarity-mainnet.make.services/rpc")
let getStateItemParam:GetItemParams = GetItemParams();
getStateItemParam.state_root_hash = "d360e2755f7cee816cce3f0eeb2000dfa03113769743ae5481816f3983d5f228"
getStateItemParam.key = "withdraw-df067278a61946b1b1f784d16e28336ae79f48cf692b13f6e40af9c7eadb2fb1"
try casperSDK.getItem(input: getStateItemParam)
} catch {
throw error
}
Retrieves a CLValue object.
call parameters :
- state root hash
- dictionary_identifier (which an enum type defined in this page https://docs.rs/casper-node/latest/casper_node/rpcs/state/enum.DictionaryIdentifier.html) - there can be 4 possible kinds of value for parameters:
1 - AccountNamedKey
2 - ContractNamedKey
3 - URef
4 - Dictionary
Call specification in detail for each type:
do {
let getDic : GetDictionaryItemParams = GetDictionaryItemParams();
getDic.state_root_hash = "146b860f82359ced6e801cbad31015b5a9f9eb147ab2a449fd5cdb950e961ca8";
getDic.dictionary_identifier = DictionaryIdentifier.AccountNamedKey(key: "account-hash-ad7e091267d82c3b9ed1987cb780a005a550e6b3d1ca333b743e2dba70680877", dictionary_name: "dict_name", dictionary_item_key: "abc_name")
try casperSDK.getDictionaryItem(from: getDic)
} catch {
throw error
}
do {
let getDic : GetDictionaryItemParams = GetDictionaryItemParams();
getDic.state_root_hash = "146b860f82359ced6e801cbad31015b5a9f9eb147ab2a449fd5cdb950e961ca8";
getDic.dictionary_identifier = DictionaryIdentifier.ContractNamedKey(key: "hash-d5308670dc1583f49a516306a3eb719abe0ba51651cb08e606fcfc1f9b9134cf", dictionary_name: "dictname", dictionary_item_key: "abcname")
try casperSDK.getDictionaryItem(from: getDic)
} catch {
throw error
}
do {
let getDic : GetDictionaryItemParams = GetDictionaryItemParams();
getDic.state_root_hash = "146b860f82359ced6e801cbad31015b5a9f9eb147ab2a449fd5cdb950e961ca8";
getDic.dictionary_identifier = DictionaryIdentifier.URef(seed_uref: "uref-30074a46a79b2d80cff437594d2422383f6c754de453b732448cc711b9f7e129-007", dictionary_item_key: "abc_name")
try casperSDK.getDictionaryItem(from: getDic)
} catch {
throw error
}
do {
let getDic : GetDictionaryItemParams = GetDictionaryItemParams();
getDic.state_root_hash = "146b860f82359ced6e801cbad31015b5a9f9eb147ab2a449fd5cdb950e961ca8";
getDic.dictionary_identifier = DictionaryIdentifier.Dictionary( "dictionary-5d3e90f064798d54e5e53643c4fce0cbb1024aadcad1586cc4b7c1358a530373")
} catch {
throw error
}
Retrieves the balances(in motes) of an account
call parameters :
- state root hash
- account uref hash
do {
let getBP : GetBalanceParams = GetBalanceParams();
getBP.state_root_hash = "8b463b56f2d124f43e7c157e602e31d5d2d5009659de7f1e79afbd238cbaa189";
getBP.purse_uref = "uref-be1dc0fd639a3255c1e3e5e2aa699df66171e40fa9450688c5d718b470e057c6-007";
try casperSDK.getStateBalance(input: getBP)
} catch {
throw error
}
Retrieves an AutionState object.
call parameters :
- block_identifier, and enum type which can be either BlockHash or BlockHeight
Call by BlockHash, you can change the BlockHash in the .Hash("") parameter
do {
let block_identifier:BlockIdentifier = .Hash("cb8dab9f455538bc6cedb217a6234faeece8ce32c94d053b5b770450290b3a30")
try casperSDK.getAuctionInfo(input: block_identifier)
} catch {
}
Call by BlockHeight, you can change the BlockHeight in the .Height() parameter
do {
let block_identifier:BlockIdentifier = .Height(473576)
try casperSDK.getAuctionInfo(input: block_identifier)
} catch {
}
Are built with Swift enumeration type, which consisted of all the value described in this page:
https://docs.rs/casper-types/1.4.6/casper_types/enum.CLType.html
Is the value corresponding to the CLType. In Swift it is built with enumeration type also, with two attributes: CLType and the value of CLType
Are built with corresponding Swift classes, and in Entity folder
The serialization for CLType, CLValue and Deploy (which consists of Deploy Header, Deploy Session, Deploy Payment, Approvals) is implemented based on the document at this address: https://casper.network/docs/design/serialization-standard#serialization-standard-state-keys
For CLType and CLValue, the serialization is done within class CLTypeSerializeHelper, which consists of two main methods:
-
CLTypeSerialize is for CLType serialization
-
CLValueSerialize is for CLValue serialization
For Deploy and Deploy related objects (Deploy Header, Deploy Session, Deploy Payment, Approvals) is done within file DeploySerialization.swift, in which there are classes for the serialization work
-
DeploySerialization class is for Deploy Serialization
-
DeployHeaderSerialization class is for Deploy Header Serialization
-
ExecutableDeployItemSerializaton class is for Session and Payment Serialization
-
DeployApprovalSerialization class is for Approval Serialization
For detail information please refer to this:
This package use the following external packages from github
-
https://github.com/leif-ibsen/SwiftECC.git for implementing Secp256k1 key Wrapper
-
https://github.com/tesseract-one/Blake2.swift.git for implementing blake2b256 hash function
All the packages are under MIT licence.
The code in the SDK follows the coding standard defined at this tool address:
https://github.com/sleekbyte/tailor
Here are all the file and folder that contains Swift coding/configuration file, counting from the Swift SDK root folder:
File: "Package.swift" Folder: "Sources" Folder: "Tests"
To test the coding standard for the SDK source file, please install the "tailor" tool at this address: https://github.com/sleekbyte/tailor (follow the install instruction at the "tailor" project github readme file).
To check for file/folder of the Swift SDK source code, please enter the root folder of the Swift SDK source code and just run the following command in Terminal:
- To check coding standard for "Package.swift" file, run this command in Terminal:
tailor Package.swift
- To check coding standard for main source code of the SDK, run this command in Terminal:
tailor Sources
- To check coding standard for test folder of the SDK, run this command in Terminal:
tailor Tests