WalletPlugin
The WalletPlugin
is a type of plugin for the Session Kit that enables the use of external authenticators and wallets for both authentication and transaction signing purposes.
Usage
For application developers that wish to include a WalletPlugin
in their application, the plugin code needs to be included in the project and then passed to either the SessionKit factory or included as an argument on a new Session.
SessionKit
One or more WalletPlugin
instances need to be passed as part of the SessionKit arguments during instantiation to provide users who perform the Login method with a choice in how to authenticate.
const sessionKit = new SessionKit({
// ...arguments
walletPlugins: [new WalletPluginAnchor()],
})
If only one WalletPlugin
is provided, the SessionKit will default to using it and the user will not be prompted to choose a specific wallet.
Session
A single WalletPlugin
instance must also be passed as an argument to a Session during manual creation.
const session = new Session({
// ...arguments
walletPlugin: new WalletPluginPrivateKey(
"5Jtoxgny5tT7NiNFp1MLogviuPJ9NniWjnU4wKzaX4t7pL4kJ8s"
),
})
Development
The WalletPlugin
interface and AbstractWalletPlugin
abstract class are tools for developers to integrate external authenticators and wallets for use in the SessionKit.
The wallet-plugin-template is available on Github to help developers get started.
Class
When building a WalletPlugin
, it is recommended that a class is created which extends the AbstractWalletPlugin
. This will cause the new plugin to inherit some base helper functions, as well as ensure that the requirements of the WalletPlugin
interface are met.
class WalletPluginExample extends AbstractWalletPlugin {}
Config
The new plugin should include a property called config
that outlines the capabilities and requirements of the external application.
class WalletPluginExample extends AbstractWalletPlugin {
readonly config = {
requiresChainSelect: true,
requiresPermissionSelect: false,
supportedChains: [
"73e4385a2708e6d7048834fbc1079f2fabb17b3c125b146af438971e90716c4d",
],
}
}
This configuration is read by the SessionKit to determine how to prompt the end user during the Login call.
requiresChainSelect
: Determines if the Session Kit needs to ask the end user which blockchain to authenticate against. Set this value tofalse
if the wallet itself will offer the opportunity to select a blockchain.requiresPermissionSelect
: Determines if the Session Kit needs to ask the end user which account and permission it will authenticate against. Set this value tofalse
if the wallet will allow the user to select an account during authentication.supportedChains
: This value is optional and can be set to a list of blockchain IDs that the wallet supports. Only define this if the wallet is specific to one or more blockchains.
Metadata
The new plugin should also specify a property called metadata
that describes the plugin and the application it integrates with.
class WalletPluginExample extends AbstractWalletPlugin {
readonly metadata = {
name: "Example Wallet Plugin",
description: "This is a test wallet plugin",
logo: "[[base_64_encoded_image]]",
/* Alternative syntax for logo for light/dark mode
logo: {
light: "[[base_64_encoded_image]]",
dark: "[[base_64_encoded_image]]",
},
*/
homepage: "https://someplace.com",
download: "https://someplace.com/download",
}
}
This information will be used to present users with information about the application this plugin integrates with:
name
: A human readable string with the application name (e.g. “Anchor Wallet”)description
: A human readable description of the applicationlogo
: A base64 encoded image (or set of images) of the logo for this application (example)homepage
: A complete URL pointing to the homepage for the applicationdownload
: A complete URL pointing to a webpage with download instructions
Unique ID
Each plugin will also need a unique identifier that is used internally by the SessionKit for serialization and retrieval.
class WalletPluginExample extends AbstractWalletPlugin {
get id() {
return "wallet-plugin-example"
}
}
This is accomplished by specifying a get id()
method on the class which returns a verbose string in an effort to make it unique.
Method: Login
In order for the WalletPlugin
to authenticate users, it will need to implement the login
method. This method accepts a LoginContext as its only parameter, which provides the plugin and wallet with potential information about the request.
Note: If during the Login process the WalletPlugin
needs to interact with the end user, the UserInterface instance provided on the LoginContext can be used to prompt the user.
The WalletPlugin
will now need to communicate with the external application in order to formulate a response. The goal is for the login
method to return an object that matches the WalletPluginLoginResponse interface that contains:
chain
: A typed Checksum256 indicating the blockchain ID the user is logging in with.permissionLevel
: A typed PermissionLevel indicating which account and permission the user has selected.
Once that information is retrieved, it can then be returned to the Session Kit to finish the process.
A complete example of what this method may look like is outlined below.
class WalletPluginExample extends AbstractWalletPlugin {
async login(context) {
// Communicate with external application
const response = await externalWallet.login()
// Get the blockchain ID from the response
const chainId = response.chain // e.g. "73e4385a2708e6d7048834fbc1079f2fabb17b3c125b146af438971e90716c4d"
// Get the permission level from the response
const permission = response.perm // e.g. "wharfkit1111@test"
// Return this information to the Session Kit
return {
chain: Checksum256.from(chainId),
permissionLevel: PermissionLevel.from(permission),
}
}
}
If the WalletPlugin
does not support any form of Login for end users and only supports signing transactions, simply throw an error in this method call:
class WalletPluginExample extends AbstractWalletPlugin {
async login() {
throw new Error("This plugin does not support the Login method.")
}
}
Method: Sign
One of the primary purposes of a WalletPlugin
is to facilitate the signing of transactions. To do this it must implement the sign
method, which accepts two parameters: a ResolvedSigningRequest
and a TransactContext.
Note: If during the Transact process the WalletPlugin
needs to interact with the end user, the UserInterface instance provided on the TransactContext can be used to prompt the user.
The WalletPlugin
will then need to communicate with the external application, relaying the transaction, in order to retrieve a signature. This process should return an object that matches the WalletPluginSignResponse interface that contains:
signatures
: An array containing one or more Signature typed objects with signatures authorizing the transaction.resolved
: An optionalResolvedSigningRequest
, in the event that the transaction was modified by the wallet.
Note: If the WalletPlugin
or external application modifies the transaction and returns it, it may invalidate any signatures previously created by the TransactPlugin calls that were originally made. We would recommend that the wallet should not modify the transaction when signatures are present already during the signing process and instead write a plugin for Wharf.
Once completed, this information can be returned to the Session Kit to complete the transaction.
A complete example of what this method may look like is outlined below:
class WalletPluginExample extends AbstractWalletPlugin {
async sign(resolved, context) {
// Retrieve the transaction from the resolved signing request
const transaction = resolved.transaction
// Communicate with external application
const response = await externalWallet.sign(transaction)
// Get the signature from the wallet response
const sig = Signature.from(response.signature)
// e.g. "SIG_K1_KfqBXGdSRnVgZbAXyL9hEYbAvrZjcaxUCenD7Z3aX6yzf6MEyc4Cy3ywToD4j3SKkzSg7L1uvRUirEPHwAwrbg5c9z27Z3"
// Return the signatures from the external application
return {
signatures: [sig],
}
}
}