anchor-link
Version:
Library for authenticating and signing transactions using the Anchor Link protocol
618 lines (609 loc) • 23.9 kB
TypeScript
/**
* Anchor Link v3.6.0
* https://github.com/greymass/anchor-link
*
* @license
* Copyright (c) 2020 Greymass Inc. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistribution of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistribution in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*
* YOU ACKNOWLEDGE THAT THIS SOFTWARE IS NOT DESIGNED, LICENSED OR INTENDED FOR USE
* IN THE DESIGN, CONSTRUCTION, OPERATION OR MAINTENANCE OF ANY MILITARY FACILITY.
*/
import * as _wharfkit_antelope from '@wharfkit/antelope';
import { Name, PublicKey, PermissionLevel, NameType, PermissionLevelType, PublicKeyType, PrivateKeyType, Bytes, APIClient, AnyTransaction, AnyAction, Signature, Transaction, API, ABIDef, ABISerializable } from '@wharfkit/antelope';
export * from '@wharfkit/antelope';
import { CallbackPayload, ChainId, ChainIdType, SigningRequest, ResolvedSigningRequest, ResolvedTransaction, IdentityProof, AbiProvider, SigningRequestCreateArguments } from '@wharfkit/signing-request';
export * from '@wharfkit/signing-request';
export { CallbackPayload, ChainId, ChainIdType, ChainName, IdentityProof, IdentityProofType } from '@wharfkit/signing-request';
/** Service that handles waiting for a ESR callback to be sent to an url. */
interface LinkCallbackService {
create(): LinkCallback;
}
/** Can be returned by callback services if the user explicitly rejects the request. */
interface LinkCallbackRejection {
/** Rejection message. */
rejected: string;
}
/** Callback response, can either be a ESR callback payload or a rejection message. */
declare type LinkCallbackResponse = CallbackPayload | LinkCallbackRejection;
/** Callback that can be waited for. */
interface LinkCallback {
/** Url that should be hit to trigger the callback. */
url: string;
/** Wait for the callback to resolve. */
wait(): Promise<LinkCallbackResponse>;
/** Cancel a pending callback. */
cancel(): void;
}
/**
* Interface storage adapters should implement.
*
* Storage adapters are responsible for persisting [[LinkSession]]s and can optionally be
* passed to the [[Link]] constructor to auto-persist sessions.
*/
interface LinkStorage {
/** Write string to storage at key. Should overwrite existing values without error. */
write(key: string, data: string): Promise<void>;
/** Read key from storage. Should return `null` if key can not be found. */
read(key: string): Promise<string | null>;
/** Delete key from storage. Should not error if deleting non-existing key. */
remove(key: string): Promise<void>;
}
/**
* Type describing a link session that can create a eosjs compatible
* signature provider and transact for a specific auth.
*/
declare abstract class LinkSession {
/** @internal */
constructor();
/** The underlying link instance used by the session. */
abstract link: Link;
/** App identifier that owns the session. */
abstract identifier: Name;
/** Id of the chain where the session is valid. */
abstract chainId: ChainId;
/** The public key the session can sign for. */
abstract publicKey: PublicKey;
/** The EOSIO auth (a.k.a. permission level) the session can sign for. */
abstract auth: PermissionLevel;
/** Session type, e.g. 'channel'. */
abstract type: string;
/** Arbitrary metadata that will be serialized with the session. */
abstract metadata: {
[key: string]: any;
};
/** Creates a eosjs compatible signature provider that can sign for the session public key. */
abstract makeSignatureProvider(): any;
/**
* Transact using this session. See [[Link.transact]].
*/
abstract transact(args: TransactArgs, options?: TransactOptions): Promise<TransactResult>;
/** Returns a JSON-encodable object that can be used recreate the session. */
abstract serialize(): SerializedLinkSession;
/**
* Convenience, remove this session from associated [[Link]] storage if set.
* Equivalent to:
* ```ts
* session.link.removeSession(session.identifier, session.auth, session.chainId)
* ```
*/
remove(): Promise<void>;
/** API client for the chain this session is valid on. */
get client(): _wharfkit_antelope.APIClient;
/** Restore a previously serialized session. */
static restore(link: Link, data: SerializedLinkSession): LinkSession;
}
/** @internal */
interface SerializedLinkSession {
type: string;
metadata: {
[key: string]: any;
};
data: any;
}
/** @internal */
interface ChannelInfo {
/** Public key requests are encrypted to. */
key: PublicKeyType;
/** The wallet given channel name, usually the device name. */
name: string;
/** The channel push url. */
url: string;
}
/** @internal */
interface LinkChannelSessionData {
/** App identifier that owns the session. */
identifier: NameType;
/** Authenticated user permission. */
auth: PermissionLevelType;
/** Public key of authenticated user */
publicKey: PublicKeyType;
/** The wallet channel url. */
channel: ChannelInfo;
/** The private request key. */
requestKey: PrivateKeyType;
/** The session chain id. */
chainId: ChainIdType;
}
/**
* Link session that pushes requests over a channel.
* @internal
*/
declare class LinkChannelSession extends LinkSession implements LinkTransport {
readonly link: Link;
readonly chainId: ChainId;
readonly auth: PermissionLevel;
readonly identifier: Name;
readonly type = "channel";
metadata: any;
readonly publicKey: PublicKey;
serialize: () => SerializedLinkSession;
private timeout;
private encrypt;
private channelKey;
private channelUrl;
private channelName;
constructor(link: Link, data: LinkChannelSessionData, metadata: any);
onSuccess(request: any, result: any): void;
onFailure(request: any, error: any): void;
onRequest(request: SigningRequest, cancel: any): void;
addLinkInfo(request: SigningRequest): void;
prepare(request: any): Promise<any>;
showLoading(): void;
recoverError(error: Error, request: SigningRequest): boolean;
makeSignatureProvider(): any;
transact(args: TransactArgs, options?: TransactOptions): Promise<TransactResult>;
}
/** @internal */
interface LinkFallbackSessionData {
auth: PermissionLevelType;
publicKey: PublicKeyType;
identifier: NameType;
chainId: ChainIdType;
}
/**
* Link session that sends every request over the transport.
* @internal
*/
declare class LinkFallbackSession extends LinkSession implements LinkTransport {
readonly link: Link;
readonly chainId: ChainId;
readonly auth: PermissionLevel;
readonly type = "fallback";
readonly identifier: Name;
readonly metadata: {
[key: string]: any;
};
readonly publicKey: PublicKey;
serialize: () => SerializedLinkSession;
constructor(link: Link, data: LinkFallbackSessionData, metadata: any);
onSuccess(request: any, result: any): void;
onFailure(request: any, error: any): void;
onRequest(request: any, cancel: any): void;
prepare(request: any): Promise<any>;
showLoading(): void;
makeSignatureProvider(): any;
transact(args: TransactArgs, options?: TransactOptions): Promise<TransactResult>;
}
/**
* Protocol link transports need to implement.
*
* A transport is responsible for getting the request to the
* user, e.g. by opening request URIs or displaying QR codes.
*/
interface LinkTransport {
/**
* Present a signing request to the user.
* @param request The signing request.
* @param cancel Can be called to abort the request.
*/
onRequest(request: SigningRequest, cancel: (reason: string | Error) => void): void;
/** Called if the request was successful. */
onSuccess?(request: SigningRequest, result: TransactResult): void;
/** Called if the request failed. */
onFailure?(request: SigningRequest, error: Error): void;
/**
* Called when a session request is initiated.
* @param session Session where the request originated.
* @param request Signing request that will be sent over the session.
*/
onSessionRequest?(session: LinkSession, request: SigningRequest, cancel: (reason: string | Error) => void): void;
/** Can be implemented if transport provides a storage as well. */
storage?: LinkStorage;
/** Can be implemented to modify request just after it has been created. */
prepare?(request: SigningRequest, session?: LinkSession): Promise<SigningRequest>;
/** Called immediately when the transaction starts */
showLoading?(): void;
/** User agent reported to the signer. */
userAgent?(): string;
/** Send session request payload, optional. Can return false to indicate it has to be sent over the socket. */
sendSessionPayload?(payload: Bytes, session: LinkSession): boolean;
/**
* Can be implemented to recover from certain errors, if the function returns true the error will
* not bubble up to the caller of .transact or .login and the link will continue waiting for the callback.
*/
recoverError?(error: Error, request: SigningRequest): boolean;
}
/**
* Type describing a EOSIO chain.
*/
interface LinkChainConfig {
/**
* The chains unique 32-byte id.
*/
chainId: ChainIdType;
/**
* URL to EOSIO node to communicate with (or a @wharfkit/antelope APIClient instance).
*/
nodeUrl: string | APIClient;
}
/**
* Available options when creating a new [[Link]] instance.
*/
interface LinkOptions {
/**
* Link transport responsible for presenting signing requests to user.
*/
transport: LinkTransport;
/**
* Chain configurations to support.
* For example for a link that can login and transact on EOS and WAX:
* ```ts
* [
* {
* chainId: 'aca376f206b8fc25a6ed44dbdc66547c36c6c33e3a119ffbeaef943642f0e906',
* nodeUrl: 'https://eos.greymass.com',
* },
* {
* chainId: '1064487b3cd1a897ce03ae5b6a865651747e2e152090f99c1d19d44e01aea5a4',
* nodeUrl: 'https://wax.greymass.com',
* },
* ]
* ```
*/
chains: (LinkChainConfig | LinkChain)[];
/**
* ChainID or esr chain name alias for which the link is valid.
* @deprecated Use [[chains]] instead.
*/
chainId?: ChainIdType;
/**
* URL to EOSIO node to communicate with or a `@wharfkit/antelope` APIClient instance.
* @deprecated Use [[chains]] instead.
*/
client?: string | APIClient;
/**
* URL to callback forwarder service or an object implementing [[LinkCallbackService]].
* See [buoy-nodejs](https://github.com/greymass/buoy-nodejs) and (buoy-golang)[https://github.com/greymass/buoy-golang]
* for reference implementations.
* @default `https://cb.anchor.link`
*/
service?: string | LinkCallbackService;
/**
* Optional storage adapter that will be used to persist sessions. If not set will use the transport storage
* if available, explicitly set this to `null` to force no storage.
* @default Use transport storage.
*/
storage?: LinkStorage | null;
/**
* Whether to verify identity proofs submitted by the signer, if this is disabled the
* [[Link.login | login]] and [[Link.identify | identify]] methods will not return an account object.
* @default `false`
*/
verifyProofs?: boolean;
/**
* Whether to encode the chain ids with the identity request that establishes a session.
* Only applicable when using multiple chain configurations, can be set to false to
* decrease QR code sizes when supporting many chains.
* @default `true`
*/
encodeChainIds?: boolean;
}
/** @internal */
declare namespace LinkOptions {
/** @internal */
const defaults: {
service: string;
verifyProofs: boolean;
encodeChainIds: boolean;
};
}
/**
* Payload accepted by the [[Link.transact]] method.
* Note that one of `action`, `actions` or `transaction` must be set.
*/
interface TransactArgs {
/** Full transaction to sign. */
transaction?: AnyTransaction;
/** Action to sign. */
action?: AnyAction;
/** Actions to sign. */
actions?: AnyAction[];
}
/**
* Options for the [[Link.transact]] method.
*/
interface TransactOptions {
/**
* Whether to broadcast the transaction or just return the signature.
* Defaults to true.
*/
broadcast?: boolean;
/**
* Chain to use when configured with multiple chains.
*/
chain?: LinkChainType;
/**
* Whether the signer can make modifications to the request
* (e.g. applying a cosigner action to pay for resources).
*
* Defaults to false if [[broadcast]] is true or unspecified; otherwise true.
*/
noModify?: boolean;
}
/**
* The result of a [[Link.transact]] call.
*/
interface TransactResult {
/** The resolved signing request. */
resolved: ResolvedSigningRequest;
/** The chain that was used. */
chain: LinkChain;
/** The transaction signatures. */
signatures: Signature[];
/** The callback payload. */
payload: CallbackPayload;
/** The signer authority. */
signer: PermissionLevel;
/** The resulting transaction. */
transaction: Transaction;
/** Resolved version of transaction, with the action data decoded. */
resolvedTransaction: ResolvedTransaction;
/** Push transaction response from api node, only present if transaction was broadcast. */
processed?: {
[key: string]: any;
};
}
/**
* The result of a [[Link.identify]] call.
*/
interface IdentifyResult extends TransactResult {
/** The identified account, not present unless [[LinkOptions.verifyProofs]] is set to true. */
account?: API.v1.AccountObject;
/** The identity proof. */
proof: IdentityProof;
}
/**
* The result of a [[Link.login]] call.
*/
interface LoginResult extends IdentifyResult {
/** The session created by the login. */
session: LinkSession;
}
/**
* Link chain, can be a [[LinkChain]] instance, a chain id or a index in [[Link.chains]].
* @internal
*/
declare type LinkChainType = LinkChain | ChainIdType | number;
/**
* Class representing a EOSIO chain.
*/
declare class LinkChain implements AbiProvider {
/** EOSIO ChainID for which requests are valid. */
chainId: ChainId;
/** API client instance used to communicate with the chain. */
client: APIClient;
private abiCache;
private pendingAbis;
/** @internal */
constructor(chainId: ChainIdType, clientOrUrl: APIClient | string);
/**
* Fetch the ABI for given account, cached.
* @internal
*/
getAbi(account: Name): Promise<ABIDef>;
}
/**
* Anchor Link main class.
*
* @example
*
* ```ts
* import AnchorLink from 'anchor-link'
* import ConsoleTransport from 'anchor-link-console-transport'
*
* const link = new AnchorLink({
* transport: new ConsoleTransport(),
* chains: [
* {
* chainId: 'aca376f206b8fc25a6ed44dbdc66547c36c6c33e3a119ffbeaef943642f0e906',
* nodeUrl: 'https://eos.greymass.com',
* },
* ],
* })
*
* const result = await link.transact({actions: myActions})
* ```
*/
declare class Link {
/** Package version. */
static version: string;
/** Chains this instance is configured with. */
readonly chains: LinkChain[];
/** Transport used to deliver requests to the users wallet. */
readonly transport: LinkTransport;
/** Storage adapter used to persist sessions. */
readonly storage?: LinkStorage;
private callbackService;
private verifyProofs;
private encodeChainIds;
/** Create a new link instance. */
constructor(options: LinkOptions);
/**
* The APIClient instance for communicating with the node.
* @note This returns the first APIClient when link is configured with multiple chains.
*/
get client(): APIClient;
/**
* Return a [[LinkChain]] object for given chainId or chain reference.
* @throws If this link instance has no configured chain for given reference.
* @internal
*/
getChain(chain: LinkChainType): LinkChain;
/**
* Create a SigningRequest instance configured for this link.
* @internal
*/
createRequest(args: SigningRequestCreateArguments, chain?: LinkChain, transport?: LinkTransport): Promise<{
request: SigningRequest;
callback: LinkCallback;
}>;
/**
* Send a SigningRequest instance using this link.
* @internal
*/
sendRequest(request: SigningRequest, callback: LinkCallback, chain?: LinkChain, transport?: LinkTransport, broadcast?: boolean): Promise<TransactResult>;
/**
* Sign and optionally broadcast a EOSIO transaction, action or actions.
*
* Example:
*
* ```ts
* let result = await myLink.transact({transaction: myTx})
* ```
*
* @param args The action, actions or transaction to use.
* @param options Options for this transact call.
* @param transport Transport override, for internal use.
*/
transact(args: TransactArgs, options?: TransactOptions, transport?: LinkTransport): Promise<TransactResult>;
/**
* Send an identity request and verify the identity proof if [[LinkOptions.verifyProofs]] is true.
* @param args.scope The scope of the identity request.
* @param args.requestPermission Optional request permission if the request is for a specific account or permission.
* @param args.info Metadata to add to the request.
* @note This is for advanced use-cases, you probably want to use [[Link.login]] instead.
*/
identify(args: {
scope: NameType;
requestPermission?: PermissionLevelType;
info?: {
[key: string]: ABISerializable | Bytes;
};
}): Promise<IdentifyResult>;
/**
* Login and create a persistent session.
* @param identifier The session identifier, an EOSIO name (`[a-z1-5]{1,12}`).
* Should be set to the contract account if applicable.
*/
login(identifier: NameType): Promise<LoginResult>;
/**
* Restore previous session, use [[login]] to create a new session.
* @param identifier The session identifier, must be same as what was used when creating the session with [[login]].
* @param auth A specific session auth to restore, if omitted the most recently used session will be restored.
* @param chainId If given function will only consider that specific chain when restoring session.
* @returns A [[LinkSession]] instance or null if no session can be found.
* @throws If no [[LinkStorage]] adapter is configured or there was an error retrieving the session data.
**/
restoreSession(identifier: NameType, auth?: PermissionLevelType, chainId?: ChainIdType): Promise<LinkSession | null>;
/**
* List stored session auths for given identifier.
* The most recently used session is at the top (index 0).
* @throws If no [[LinkStorage]] adapter is configured or there was an error retrieving the session list.
**/
listSessions(identifier: NameType): Promise<{
auth: PermissionLevel;
chainId: ChainId;
}[]>;
/**
* Remove stored session for given identifier and auth.
* @throws If no [[LinkStorage]] adapter is configured or there was an error removing the session data.
*/
removeSession(identifier: NameType, auth: PermissionLevel, chainId: ChainId): Promise<void>;
/**
* Remove all stored sessions for given identifier.
* @throws If no [[LinkStorage]] adapter is configured or there was an error removing the session data.
*/
clearSessions(identifier: string): Promise<void>;
/**
* Create an eosjs compatible signature provider using this link.
* @param availableKeys Keys the created provider will claim to be able to sign for.
* @param chain Chain to use when configured with multiple chains.
* @param transport (internal) Transport override for this call.
* @note We don't know what keys are available so those have to be provided,
* to avoid this use [[LinkSession.makeSignatureProvider]] instead. Sessions can be created with [[Link.login]].
*/
makeSignatureProvider(availableKeys: string[], chain?: LinkChainType, transport?: LinkTransport): any;
/** Makes sure session is in storage list of sessions and moves it to top (most recently used). */
private touchSession;
/**
* Makes sure session is in storage list of sessions and moves it to top (most recently used).
* @internal
*/
storeSession(session: LinkSession): Promise<void>;
/** Session storage key for identifier and suffix. */
private sessionKey;
/**
* Return user agent of this link.
* @internal
*/
getUserAgent(): string;
}
/**
* Error codes. Accessible using the `code` property on errors thrown by [[Link]] and [[LinkSession]].
* - `E_DELIVERY`: Unable to route message to wallet.
* - `E_TIMEOUT`: Request was delivered but user/wallet didn't respond in time.
* - `E_CANCEL`: The [[LinkTransport]] canceled the request.
* - `E_IDENTITY`: Identity proof failed to verify.
*/
declare type LinkErrorCode = 'E_DELIVERY' | 'E_TIMEOUT' | 'E_CANCEL' | 'E_IDENTITY';
/**
* Error that is thrown if a [[LinkTransport]] cancels a request.
* @internal
*/
declare class CancelError extends Error {
code: string;
constructor(reason?: string);
}
/**
* Error that is thrown if an identity request fails to verify.
* @internal
*/
declare class IdentityError extends Error {
code: string;
constructor(reason?: string);
}
/**
* Error originating from a [[LinkSession]].
* @internal
*/
declare class SessionError extends Error {
code: 'E_DELIVERY' | 'E_TIMEOUT';
session: LinkSession;
constructor(reason: string, code: 'E_DELIVERY' | 'E_TIMEOUT', session: LinkSession);
}
export { CancelError, IdentifyResult, IdentityError, Link, LinkCallback, LinkCallbackRejection, LinkCallbackResponse, LinkCallbackService, LinkChain, LinkChainConfig, LinkChainType, LinkChannelSession, LinkChannelSessionData, LinkErrorCode, LinkFallbackSession, LinkFallbackSessionData, LinkOptions, LinkSession, LinkStorage, LinkTransport, LoginResult, SerializedLinkSession, SessionError, TransactArgs, TransactOptions, TransactResult, Link as default };