Skip to content

Commit 00d404d

Browse files
author
Alex Oakley
authored
Merge pull request #2 from sublabdev/dev
Initial release
2 parents c1a1504 + 5fcc592 commit 00d404d

File tree

129 files changed

+6156
-1594
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

129 files changed

+6156
-1594
lines changed

.gitignore

+4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
1+
.DS_Store
2+
13
# Xcode
24
#
35
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
46

7+
.DS_Store
8+
59
## User settings
610
xcuserdata/
711

Doc/RpcClient.md

+84
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
### RpcClient Example
2+
3+
Bellow is provided an example of RPC client that handles sending requests using several different ways.
4+
5+
## Initialization
6+
7+
First of all, we need to create the client. `RpcClient`'s initializer takes two parameters. The
8+
first one is the required `URL`, and the second one is a `URLSession` which is predefined and
9+
can be omitted during the initialization (the `shared` session is used, by default).
10+
Here is how the client can be created:
11+
```Swift
12+
guard let url = URL(string: "https://www.example.com") else { return }
13+
let client = RpcClient(url: url)
14+
```
15+
16+
## Usage
17+
18+
After creating the client, we can send requests. There are 2 ways to do that.
19+
20+
The first option is to use `RpcRequest` object. It's a wrapper object that encapsulates all the
21+
necessary data for RPC requests. Here is how it looks like:
22+
23+
```Swift
24+
struct RpcRequest<T: Codable>: Codable {
25+
var jsonrpc = "2.0"
26+
let id: Int64
27+
let method: String
28+
var params: T
29+
}
30+
```
31+
32+
As you can see, it has four properties. The first one is the `JSON RPC` version, which is
33+
by default set to `2.0`. The second one is the request's `id`. The third one is the `method`
34+
for which we want to get a response. The last one is a generic parameter called `params` which can
35+
be any type conforming to `Codable`.
36+
37+
Also, there is an "empty" object `None` that conforms to `Codable` which can be used for cases
38+
when there is no parameter is required for the request.
39+
40+
Here is how one could create the request:
41+
```Swift
42+
let method = "state_getMetadata"
43+
let id: Int64 = 0
44+
let request = RpcRequest(
45+
id: id,
46+
method: method,
47+
params: Nothing()
48+
)
49+
```
50+
51+
After we have the `request`, we can send it using the `client`'s `send` method. It has two parameters
52+
the first of which the `request` itself, and the second on is the completion with either the request's optional result or `RpcError`.
53+
The result contains a response of type `RpcResponse`. It like the `request` has four parameters.
54+
55+
```Swift
56+
struct RpcResponse<T: Codable>: Codable {
57+
let jsonrpc: String
58+
let id: Int64
59+
var result: T? = nil
60+
var error: RpcResponseError? = nil
61+
}
62+
```
63+
The first one is the `JSON RPC` version. The second one is the response's `id`. The third on
64+
is the `result` which is of a generic type, which conforms to `Codable`. And the last one
65+
is an optional `RpcResponseError`.
66+
67+
Here is how the request is made by using `RpcRequest` object defined earlier.
68+
69+
```Swift
70+
client.send(request) { (response: RpcResponse<String>?, error: RpcError?) in
71+
// Do something with the response or the error
72+
}
73+
```
74+
75+
Another way of making a request is to use the `client`'s `sendRequest` method. It receives two
76+
parameters. The first one is the method for which we want to get a response, and the second one
77+
is the completion with a generic optional `Result`, conforming to `Codable` or optional `RpcError`.
78+
79+
```Swift
80+
client.sendRequest(method: method) { (response: String?, error: RpcError?) in
81+
// Do something with the result or error
82+
}
83+
```
84+

Doc/RuntimeMetadata.md

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
### RuntimeMetadata example
2+
3+
Bellow is provided an example of how RPC client can be used to fetch runtime metadata.
4+
5+
*NOTE:* `CommonSwift` library can be found [here](https://github.com/sublabdev/common-swift).
6+
The `hex` extension for `String` is defined in `CommonSwift` library.
7+
`ScaleCodecSwift` library can be found [here](https://github.com/sublabdev/scale-codec-swift). `ScaleCoder` is defined in `ScaleCodecSwift` library.
8+
9+
## Initialization
10+
11+
First of all, we need to create the client. `RpcClient`'s initializer takes two parameters. The
12+
first one is the required `URL`, and the second one is a `URLSession` which is predefined and
13+
can be omitted during the initialization (the `shared` session is used, by default).
14+
Here is how the client can be created:
15+
```Swift
16+
guard let url = URL(string: "https://www.example.com") else { return }
17+
let client = RpcClient(url: url)
18+
```
19+
20+
## Usage
21+
22+
After getting the client, we can send requests. For that we will use the `client`'s `sendRequest` method.
23+
It receives two parameters. The first one is the method for which we want to get a response, and the second one
24+
is the completion with a generic optional `Result`, conforming to `Codable` or optional `RpcError`.
25+
If we have a response, we try to get a metadata from it.
26+
27+
```Swift
28+
client.sendRequest(method: method) { [weak self] (response: String?, error: RpcError?) in
29+
guard let string = response else { return }
30+
31+
do {
32+
guard let hexData = string.hex.decode() else { return }
33+
34+
let codec = ScaleCoder.defaultCoder()
35+
let runtimeMetadata = codec.decoder.decode(RuntimeMetadata.self, from: hexData)
36+
37+
// Do something with the metadata
38+
} catch let error {
39+
// Do something with the error
40+
}
41+
}
42+
```
43+
44+
To get a metadata from the response first we try to decode the hex-encoded string (result from the request above).
45+
After doing that we decode the decoded data to `RuntimeMetadata` object.
46+
`RuntimeMetadata` holds all the necessary information for the metadata. It has a magic number,
47+
version, runtime lookup, an array of runtime modules and a runtime extrinsic.

Doc/SubstrateConstantsService.md

+92
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
### SubstrateConstantsService example
2+
3+
Bellow is provided an example of how `SubstrateConstantsService` can be used to get runtime module
4+
constant and/or it's value bytes decoded to a specified generic type `T` conforming to `Codable`.
5+
6+
## Initialization
7+
8+
First of all, we need to create the client. `SubstrateClient`'s initializer takes two parameters.
9+
The first one is the required `URL`, and the second one is settings. It helps to define how the
10+
data should be fetched and how it should be handled after that and on which dispatch queue
11+
the results should be returned. It has a static func `default()`
12+
which provides the settings with a default configuration. In this case the main
13+
thread is used. There is another method called `default(clientQueue:)` where a custom
14+
queue can be created. The settings parameter in`SubstrateClient`'s
15+
initializer is predefined and set to default.
16+
17+
Here is how one could create a client:
18+
19+
```Swift
20+
guard let url = URL(string: network.endpointInfo.url) else { return nil }
21+
let client = SubstrateClient(url: url)
22+
```
23+
or
24+
25+
```Swift
26+
let client = SubstrateClient(url: url, settings: SubstrateClientSettings.default(clientQueue: DispatchQueue(label: "Some queue"))
27+
```
28+
29+
The client has a property called `module` from where we can get access to an interface for getting
30+
`RuntimeMetadata` and fetching `StorageItems`.
31+
32+
Next we need to get `SubstrateConstantsService`. To do that we can call
33+
`constantsService(completion:)` method on the client. It will return a ready service to be used:
34+
35+
```Swift
36+
client.constantsService { constantService in
37+
// Do something with the service
38+
}
39+
```
40+
Before taking a look how to use the service, it's worth mentioning that runtime
41+
module constant is defined by the following object:
42+
```Swift
43+
public class RuntimeModuleConstant: Codable {
44+
let name: String
45+
let type: BigUInt
46+
let valueBytes: [UInt8]
47+
let docs: [String]
48+
49+
init(name: String, type: BigUInt, valueBytes: [UInt8], docs: [String]) {
50+
self.name = name
51+
self.type = type
52+
self.valueBytes = valueBytes
53+
self.docs = docs
54+
}
55+
}
56+
```
57+
It's `valueBytes` property is decoded into a specified generic `T` type.
58+
59+
## Usage
60+
61+
Now, when we have all the required components, we can start getting the constants. To do that
62+
we should call `fetch(moduleName:constantName:completion:)` method on `SubstrateConstantsService`.
63+
This method finds a runtime module constant by the constant's name in a specified module
64+
and in its completion it returns the module's value bytes decoded into a specified type.
65+
66+
```Swift
67+
try constantService.fetch(
68+
moduleName: constant.module,
69+
constantName: constant.constant
70+
) { (storageItem: T?) in
71+
// Do something with the storage item
72+
}
73+
```
74+
75+
Also, it's possible to get `RuntimeModuleConstant` object itself, to access
76+
it's other properties.
77+
78+
```Swift
79+
try constantService.find(
80+
moduleName: constant.module,
81+
constantName: constant.constant
82+
) { runtimeModuleConstant in
83+
// Do something with the runtime module constant
84+
}
85+
```
86+
87+
After that, the module constant can be used to get a decoded object of a
88+
generic type `T` from it's `valueBytes` property.
89+
90+
```Swift
91+
let fetchedValue = try service.fetch(T.self, constant: runtimeModuleConstant)
92+
```

Doc/SubstrateExtrinsicsService.md

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
### SubstrateExtrinsicsService example
2+
3+
Bellow is provided an example of how `SubstrateExtrinsicsService` can be used to get extrinsics.
4+
5+
## Initialization
6+
7+
First of all, we need to create the client. `SubstrateClient`'s initializer takes two parameters.
8+
The first one is the required `URL`, and the second one is settings. It helps to define how the
9+
data should be fetched and how it should be handled after that and on which dispatch queue
10+
the results should be returned. It has a static func `default()`
11+
which provides the settings with a default configuration. In this case the main
12+
thread is used. There is another method called `default(clientQueue:)` where a custom
13+
queue can be created. The settings parameter in`SubstrateClient`'s
14+
initializer is predefined and set to default.
15+
16+
Here is how one could create a client:
17+
18+
```Swift
19+
guard let url = URL(string: network.endpointInfo.url) else { return nil }
20+
let client = SubstrateClient(url: url)
21+
```
22+
23+
or
24+
25+
```
26+
let client = SubstrateClient(url: url, settings: SubstrateClientSettings.default(clientQueue: DispatchQueue(label: "Some queue"))
27+
```
28+
29+
## Usage
30+
31+
Now when we have the client, we can get the extrinsics service.
32+
33+
```Swift
34+
client.extrinsicsService { [weak self] extrinsicsService in
35+
// Do something with the extrinsics service
36+
}
37+
```
38+
39+
After getting the extrinsics service, we can get an unsigned extrinsic itself.
40+
41+
```Swift
42+
extrinsicsService.makeUnsigned(
43+
moduleName: "moduleName",
44+
callName: "callName",
45+
callValue: T
46+
) { unsigned in
47+
// Do something with the unsigned extrinsic
48+
}
49+
```
50+
51+
The method takes a module name and a call name as `String`s and a call value
52+
of a generic type `T` conforming to `Codable`. The last parameter of the method
53+
is a completion closure with an unsigned extrinsic on our specified queue on
54+
the client above.

Doc/SubstrateStorageService.md

+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
### SubstrateStorageService example
2+
3+
Bellow is provided an example of how `SubstrateStorageService` can be used to get a storage item.
4+
5+
## Initialization
6+
7+
First of all, we need to create the client. `SubstrateClient`'s initializer takes two parameters.
8+
The first one is the required `URL`, and the second one is settings. It has a static func `default()`
9+
which provides the settings with a default configuration. In this case the main
10+
thread is used. There is another method called `default(clientQueue:)` where a custom
11+
queue can be created. The settings parameter in`SubstrateClient`'s
12+
initializer is predefined and set to default.
13+
14+
Here is how one could create a client:
15+
16+
```Swift
17+
guard let url = URL(string: network.endpointInfo.url) else { return nil }
18+
let client = SubstrateClient(url: url)
19+
```
20+
21+
or
22+
23+
```Swift
24+
let client = SubstrateClient(url: url, settings: SubstrateClientSettings.default(clientQueue: DispatchQueue(label: "Some queue"))
25+
```
26+
27+
The client has a property called `module` from where we can get access to an interface for getting
28+
`RuntimeMetadata` and fetching `StorageItems`.
29+
30+
After the client is created, we need to create a substrate storage service. It can be done
31+
by calling `storageService(completion:)` method on the client. It's completion closure has
32+
one parameter which ia `SubstrateStorageService` object.
33+
34+
```Swift
35+
client.storageService { storageService in
36+
}
37+
```
38+
39+
## Usage
40+
41+
Now, when we have all the required components, we can start getting storage items. To do that
42+
we should call `fetch(moduleName:itemName:keys:completion)` method on `SubstrateStorageService`.
43+
This method's completion has either a response of optional generic type `T` which conforms to
44+
`Codable` or an optional `RpcError`.
45+
46+
```Swift
47+
let keysExample: [Data] = []
48+
storageService.fetch(
49+
moduleName: "timestamp",
50+
itemName: "now",
51+
keys: keysExample
52+
) { (response: T?, error: RpcError?) in
53+
// Do something with the response or with the error
54+
}
55+
```
56+
57+
Also, it is possible to fetch the wrapper object (`FindStorageItemResult`) over
58+
runtime module storage item and runtime module storage itself separately to have
59+
a more control over the process.
60+
61+
```Swift
62+
let result = try storageService.find(moduleName: "timestamp", itemName: "now")
63+
```
64+
65+
And after that the fetching method can be called on the service using the data from
66+
the `result` above
67+
68+
```Swift
69+
guard let storage = result?.storage, let resultItem = result?.item else { return }
70+
do {
71+
try service.fetch(
72+
item: resultItem,
73+
keys: item.keys,
74+
storage: storage
75+
) { (response: T?, error: RpcError?) in
76+
// Do something with the response or with the error
77+
}
78+
}
79+
```

0 commit comments

Comments
 (0)