Signing Request
Signing Requests are a data type used in the Antelope ecosystem to facilitate the transferring of transaction related data between applications for the purposes of generating a signature. This is done using a compressed string format designed to encapsulate and make it easier to transport data, which can be communicated through various methods such as QR codes and hyperlinks.
The Wharfkit Signing Request library provides a convenient way to interact with this type of data through the @wharfkit/signing-request
package. The SigningRequest
class exported by this package offers methods to easily encode or decode transactions, and resolve incomplete transaction data.
Creation
Signing requests are primarily created to serve two purposes:
- Requesting a signature to authorize a transaction and perform smart contract actions.
- Requesting a signature to verify ownership of an account with an identity proof.
The SigningRequest
class offers different methods in order to encode signing requests based on these different purposes. The create
method is used when passing in smart contract actions or a full transaction to authorize a transaction, and the identity
method is called to verify ownership of an account.
The create
method
The SigningRequest
class can statically be called in order to create a signing request payload with the intention of authorizing a transaction. This can be done by passing one or more Action or action-like objects, or a full Transaction. This is done by calling the create
method and passing one of these data types alongside any metadata for the request.
The result of this call will be a SigningRequest
instance which contains the request data, which can then be transported and interpreted by another application.
An Action
A single action may be passed to the create
method by passing it as the action
property of the object passed in as the first parameter.
import { SigningRequest } from "@wharfkit/signing-request"
const action = {
account: "eosio.token",
name: "transfer",
authorization: [
{
actor: "alice",
permission: "active",
},
],
data: {
from: "alice",
to: "bob",
quantity: "1.0000 EOS",
memo: "Thanks for the coffee!",
},
}
const request = SigningRequest.create({
action, // A single action
})
Multiple Actions
Multiple actions can also be passed by passing by providing them as an array on the actions
property.
import { SigningRequest } from "@wharfkit/signing-request"
const request = SigningRequest.create({
actions: [action1, action2],
})
A Transaction
A complete transaction can also be utilized in the create
method by passing it as the transaction
property of the object.
import { SigningRequest } from '@wharfkit/signing-request';
import { Transaction } from '@wharfkit/antelope';
const transaction = Transaction.from({ ... })
const request = SigningRequest.create({
transaction,
});
Additional Parameters
A number of additional parameters can be specified when creating a signing request, both to append metadata to the request and to change how the request is encoded.
Callback
A callback can be specified which requests that any application handling this request perform a callback with information related to the transaction.
// export type CallbackType = string | {url: string; background: boolean}
const request = SigningRequest.create({
// ...
callback: {
url: "https://some.domain/endpoint",
background: true,
},
})
The url
indicates an HTTP endpoint which should be called, with a background
flag that indicates whether the request should be handled in the background (as a POST request) or in the foreground (as a GET request).
Broadcast
The broadcast flag can be set which informs the application handling the request whether or not they should broadcast the transaction to the blockchain after creating a signature for the request. This value defaults to true
, telling the application to broadcast after signing, but can be set to false
to request that they do not broadcast the transaction themselves, but instead forward the signature back through the callback service to allow the application itself to perform the broadcast action.
const request = SigningRequest.create({
// ...
broadcast: false,
})
Optional Parameters
A second parameter can optionally also be included to provide additional ways for the SigningRequest
instance to retrieve and process request data.
import { APIClient } from "@wharfkit/antelope"
import { ABICache } from "@wharfkit/abicache"
import zlib from "pako"
const client = new APIClient({ url: "https://jungle4.greymass.com" })
const signingRequest = SigningRequest.create(
{
action,
},
{
abiProvider: new ABICache(client),
zlib,
}
)
This object may contain options for both the abiProvider
and zlib
.
The abiProvider
value is an object which understands how to efficiently retrieve ABI data from a given endpoint. In the example above, an instance of an ABICache
from the @wharfkit/abicache
package is provided, which can both load and cache ABIs from the API endpoint provided.
The zlib
value allows passing in a library that supports the zlib compression algorithm, which we’d recommend using the pako
package for.
The identity
method
For application that wish to verify the ownership of an account through the verification of a signature, the static identity
method on the SigningRequest
class can be used directly in order to create a mock transaction which can be signed. This is used typically to authenticate application users without performing an on-chain transaction, but still verifying their on-chain credentials.
A parameter defining the callback
is the only required data for this call, and is used to relay back the signature created for verification purposes.
import { SigningRequest } from "@wharfkit/signing-request"
const request = SigningRequest.identity({
callback,
})
The callback provided matches the same data type as used in the create
request and can be found here.
Scope
During the creation of an identity
request, a scope
value may be passed to define an arbitrary Name type value that defines the scope of the login.
import { SigningRequest } from "@wharfkit/signing-request"
const request = SigningRequest.identity({
callback,
scope: "foo",
})
Permission
By default, when performing an identity
request, no specific PermissionLevel is defined and the application responding to the request can allow the user to select any valid permission. To specify a specific account and permission that needs to be verified, it can be passed in as the permission
property when creating the request.
import { SigningRequest } from "@wharfkit/signing-request"
const request = SigningRequest.identity({
callback,
permission: "account@active",
})
Usage
Encoding a Request
Once a SigningRequest
instance is created, it can be encoded as a string in URI format that allows it to easily be passed to other applications:
import { SigningRequest } from "@wharfkit/signing-request"
const request = SigningRequest.identity({
callback,
permission: "account@active",
})
const uri = request.encode()
// alternatively:
// const uri = String(request)
The encode
method accepts 3 optional parameters:
- 1st: A boolean to specify whether the URI should be compressed
- 2nd: A boolean to specify whether slashes should be included in the URI
- 3rd: A string indicating what URI scheme should be used
request.encode(compress, slashes, scheme)
Decoding a Request
A URI string can be decoded into a SigningRequest
instance using the from
static method.
const request = SigningRequest.from(
"esr://gmNgZGRkAIFXBqEFopc6760yugsVYWBggtKCMIEFRnclpF9eTWUACgAA"
)
Once decoded, the SigningRequest
instance will then be usable with any of the additional methods outlined below.
Resolving a Request
A SigningRequest
instance does not necessarily always contain a transaction that is ready for signing or use on the blockchain. A signing request may be a single action, or an array of actions, or perhaps an action with placeholder values for the authorization or action data. For this reason, signing requests need to be resolved from this incomplete state into a resolved request.
The resolve
method available on SigningRequest
instances is used to resolve a request, and accepts 3 arguments:
- The
abis
related to the actions in the request, to serialize the required data. For identity requests, this can be an empty array. - The
authorization
to use within the completed transaction. - The
context
of the transaction, including transaction header details and optionally the chain ID.
It is also useful to provide both an abiProvider
to fetch ABI data as well as a zlib
library in order to handle compressed requests.
After a SigningRequest
is resolved with the resolve
method, a ResolvedSigningRequest
object is created and returned.
import { APIClient } from "@wharfkit/antelope"
import { ABICache } from "@wharfkit/abicache"
import zlib from "pako"
// Setup a new API Client for the designated chain
const client = new APIClient({ url: "https://jungle4.greymass.com" })
// Define the options used when decoding/resolving the request
const options = {
abiProvider: new ABICache(client),
zlib,
}
// Decode a signing request payload
const signingRequest = SigningRequest.from(
"esr://gmNgZGRkAIFXBqEFopc6760yugsVYWBggtKCMIEFRnclpF9eTWUACgAA",
options
)
// Utilize a built-in helper to retrieve the related ABIs from an API endpoint
const abis = await signingRequest.fetchAbis()
// Define which authorization should be used for any missing authorization data
const authorization = {
actor: "teamgreymass",
permission: "active",
}
// Generate a transaction header to resolve any missing transaction data
const info = await client.v1.chain.get_info()
const header = info.getTransactionHeader()
// Resolve the transaction using the supplied data
const resolvedSigningRequest = await signingRequest.resolve(
abis,
authorization,
header
)
The ResolvedSigningRequest
returned now represents a complete transaction, which can be used for creating a signature. This can be done using one of the methods listed below.
Signing Digest
Retrieve the Checksum256 value of the transaction from the signingDigest
property of the ResolvedSigningRequest
.
resolvedSigningRequest.signingDigest // Checksum256
Serialized Transaction
Access the serializedTransaction
property on the ResolvedSigningRequest
to retrieve a Uint8array
that represents the transaction.
resolvedSigningRequest.serializedTransaction // Uint8array