Extrinsics Service

Extrinsics. This is how you do changes in the blockchain using your account (or its private key).

It all works around making Calls:

open class Call<T: Codable> {
    public let moduleName: String
    public let name: String
    public let value: T
    
    public init(moduleName: String, name: String, value: T) {
        self.moduleName = moduleName
        self.name = name
        self.value = value
    }
}

We made this as an open class, as we know, you would create many instances for the calls you want. Simply provide in what module this call is in, its function name, set the SCALE codec conforming value and tell us its type to help us unwrap this!

Let us take a AddMemo function from crowdloan pallet as an example:

public struct AddMemo: Codable {
    public let index: UInt32
    public let memo: Data
}

public final class AddMemoCall: Call<AddMemo> {
    public init(value: AddMemo) {
        super.init(moduleName: "crowdloan", name: "add_memo", value: value)
    }
}

To get the call Payload use this:

let client: SubstrateClient
let addMemoIndex: UInt32
let memo: Data

let call: Call<AddMemo> = AddMemoCall(value: AddMemo(index: addMemoIndex, memo: memo))
let unsigned: Payload? = try await client.extrinsics.makeUnsigned(call: call)

Returned Payload is a trivial interface:

public protocol Payload {
    var moduleName: String? { get }
    var callName: String? { get }

    func toData() throws -> Data
}

Module and call name is provided for our internal library needs, while it also conforms to byte array convertible interface, which we need to tell that this type can convert to byte array.

So you can use received payload to convert it to Data:

This is useful, if you want to create, let's say, batch request. We still haven't implemented this. So if you need it urgently, this is your bro.

To make a signed request there are two different methods.

Using key pair from encrypting library:

guard let extrinsics = client.extrinsics as? SubstrateExtrinsicsService else { return }

let keyPair: KeyPair
let client: SubstrateClient
let call: AddMemoCall

let yourDesiredTip: Int

let signed = try await extrinsics.makeSigned(
    call: call,
    tip: Balance(value: BigUInt(yourDesiredTip)),
    keyPair:keyPair
)

This way you can get an extrinsic, which you can push using whatever rpc method you want. Or calculate a fee.

If you don't have a key pair, you still need our encrypting library to make your own implementation of SignatureEngine as we need its Signer super-interface. It looks redundant, so we will try to down this requirement to simply Signer in the nearest release.

Or you can specify what kind of our signature engines from the very same library you want to define, like sr25519:

guard let extrinsics = client.extrinsics as? SubstrateExtrinsicsService else { return }

let keyPair: KeyPair
let client: SubstrateClient
let call: AddMemoCall

let yourDesiredTip: Int

let signed: Payload = try await client.extrinsics.makeSigned(
    call: call,
    tip: Balance(value: BigUInt(yourDesiredTip)),
    accountId: privateKey.sr25519.publicKey().ss58.accountId(),
    signatureEngine: privateKey.sr25519
 )

Mock signed extrinsics

If you want to calculate fee, but don't want to stress test your application by creating actual signature. Feel free to pass a key pair of signature engine with a private key consisting of all bytes equal to 0. For a fee calculation, this won't be a difference.

Last updated