@bsv/sdk
Version:
BSV Blockchain Software Development Kit
561 lines (527 loc) • 17.1 kB
text/typescript
import {
Base64String,
BasketStringUnder300Bytes,
BEEF,
BooleanDefaultFalse,
BooleanDefaultTrue,
Byte,
CertificateFieldNameUnder50Bytes,
DescriptionString5to50Bytes,
EntityIconURLStringMax500Bytes,
EntityNameStringMax100Bytes,
HexString,
ISOTimestampString,
KeyIDStringUnder800Bytes,
LabelStringUnder300Bytes,
OutpointString,
OutputTagStringUnder300Bytes,
PositiveInteger,
PositiveIntegerDefault10Max10000,
PositiveIntegerMax10,
PositiveIntegerOrZero,
ProtocolString5To400Bytes,
PubKeyHex,
SatoshiValue,
SecurityLevel,
TXIDHexString,
VersionString7To30Bytes,
WalletInterface
} from '../Wallet.interfaces.js'
import Random from '../../primitives/Random.js'
import * as Utils from '../../primitives/utils.js'
import { WalletError } from '../WalletError.js'
import { CallType } from './WalletWireCalls.js'
type ReactNativeWindow = Window & {
ReactNativeWebView: {
postMessage: (message: any) => void
}
}
/**
* Facilitates wallet operations over cross-document messaging.
*/
export default class ReactNativeWebView implements WalletInterface {
private readonly domain: string
constructor(domain: string = '*') {
if (typeof window !== 'object') {
throw new Error('The XDM substrate requires a global window object.')
}
if (!(window as unknown as ReactNativeWindow).hasOwnProperty("ReactNativeWebView")) {
throw new Error(
'The window object does not have a ReactNativeWebView property.'
)
}
if (typeof (window as unknown as ReactNativeWindow).ReactNativeWebView.postMessage !== 'function') {
throw new Error(
'The window.ReactNativeWebView property does not seem to support postMessage calls.'
)
}
this.domain = domain
}
async invoke(call: CallType, args: any): Promise<any> {
return await new Promise((resolve, reject) => {
const id = Utils.toBase64(Random(12))
const listener = (e: MessageEvent): void => {
const data = JSON.parse(e.data)
if (
data.type !== 'CWI' ||
data.id !== id ||
data.isInvocation === true
) {
return
}
if (typeof window.removeEventListener === 'function') {
window.removeEventListener('message', listener)
}
if (data.status === 'error') {
const err = new WalletError(data.description, data.code)
reject(err)
} else {
resolve(data.result)
}
}
window.addEventListener('message', listener)
;(window as unknown as ReactNativeWindow).ReactNativeWebView.postMessage(
JSON.stringify({
type: 'CWI',
isInvocation: true,
id,
call,
args
})
)
})
}
async createAction(args: {
description: DescriptionString5to50Bytes
inputs?: Array<{
tx?: BEEF
outpoint: OutpointString
unlockingScript?: HexString
unlockingScriptLength?: PositiveInteger
inputDescription: DescriptionString5to50Bytes
sequenceNumber?: PositiveIntegerOrZero
}>
outputs?: Array<{
lockingScript: HexString
satoshis: SatoshiValue
outputDescription: DescriptionString5to50Bytes
basket?: BasketStringUnder300Bytes
customInstructions?: string
tags?: OutputTagStringUnder300Bytes[]
}>
lockTime?: PositiveIntegerOrZero
version?: PositiveIntegerOrZero
labels?: LabelStringUnder300Bytes[]
options?: {
signAndProcess?: BooleanDefaultTrue
acceptDelayedBroadcast?: BooleanDefaultTrue
trustSelf?: 'known'
knownTxids?: TXIDHexString[]
returnTXIDOnly?: BooleanDefaultFalse
noSend?: BooleanDefaultFalse
noSendChange?: OutpointString[]
sendWith?: TXIDHexString[]
}
}): Promise<{
txid?: TXIDHexString
tx?: BEEF
noSendChange?: OutpointString[]
sendWithResults?: Array<{
txid: TXIDHexString
status: 'unproven' | 'sending' | 'failed'
}>
signableTransaction?: { tx: BEEF, reference: Base64String }
}> {
return await this.invoke('createAction', args)
}
async signAction(args: {
spends: Record<
PositiveIntegerOrZero,
{ unlockingScript: HexString, sequenceNumber?: PositiveIntegerOrZero }
>
reference: Base64String
options?: {
acceptDelayedBroadcast?: BooleanDefaultTrue
returnTXIDOnly?: BooleanDefaultFalse
noSend?: BooleanDefaultFalse
noSendChange?: OutpointString[]
sendWith: TXIDHexString[]
}
}): Promise<{
txid?: TXIDHexString
tx?: BEEF
noSendChange?: OutpointString[]
sendWithResults?: Array<{
txid: TXIDHexString
status: 'unproven' | 'sending' | 'failed'
}>
}> {
return await this.invoke('signAction', args)
}
async abortAction(args: {
reference: Base64String
}): Promise<{ aborted: true }> {
return await this.invoke('abortAction', args)
}
async listActions(args: {
labels: LabelStringUnder300Bytes[]
labelQueryMode?: 'any' | 'all'
includeLabels?: BooleanDefaultFalse
includeInputs?: BooleanDefaultFalse
includeInputSourceLockingScripts?: BooleanDefaultFalse
includeInputUnlockingScripts?: BooleanDefaultFalse
includeOutputs?: BooleanDefaultFalse
includeOutputLockingScripts?: BooleanDefaultFalse
limit?: PositiveIntegerDefault10Max10000
offset?: PositiveIntegerOrZero
}): Promise<{
totalActions: PositiveIntegerOrZero
actions: Array<{
txid: TXIDHexString
satoshis: SatoshiValue
status:
| 'completed'
| 'unprocessed'
| 'sending'
| 'unproven'
| 'unsigned'
| 'nosend'
| 'nonfinal'
isOutgoing: boolean
description: DescriptionString5to50Bytes
labels?: LabelStringUnder300Bytes[]
version: PositiveIntegerOrZero
lockTime: PositiveIntegerOrZero
inputs?: Array<{
sourceOutpoint: OutpointString
sourceSatoshis: SatoshiValue
sourceLockingScript?: HexString
unlockingScript?: HexString
inputDescription: DescriptionString5to50Bytes
sequenceNumber: PositiveIntegerOrZero
}>
outputs?: Array<{
outputIndex: PositiveIntegerOrZero
satoshis: SatoshiValue
lockingScript?: HexString
spendable: boolean
outputDescription: DescriptionString5to50Bytes
basket: BasketStringUnder300Bytes
tags: OutputTagStringUnder300Bytes[]
customInstructions?: string
}>
}>
}> {
return await this.invoke('listActions', args)
}
async internalizeAction(args: {
tx: BEEF
outputs: Array<{
outputIndex: PositiveIntegerOrZero
protocol: 'wallet payment' | 'basket insertion'
paymentRemittance?: {
derivationPrefix: Base64String
derivationSuffix: Base64String
senderIdentityKey: PubKeyHex
}
insertionRemittance?: {
basket: BasketStringUnder300Bytes
customInstructions?: string
tags?: OutputTagStringUnder300Bytes[]
}
}>
description: DescriptionString5to50Bytes
labels?: LabelStringUnder300Bytes[]
}): Promise<{ accepted: true }> {
return await this.invoke('internalizeAction', args)
}
async listOutputs(args: {
basket: BasketStringUnder300Bytes
tags?: OutputTagStringUnder300Bytes[]
tagQueryMode?: 'all' | 'any'
include?: 'locking scripts' | 'entire transactions'
includeCustomInstructions?: BooleanDefaultFalse
includeTags?: BooleanDefaultFalse
includeLabels?: BooleanDefaultFalse
limit?: PositiveIntegerDefault10Max10000
offset?: PositiveIntegerOrZero
}): Promise<{
totalOutputs: PositiveIntegerOrZero
outputs: Array<{
outpoint: OutpointString
satoshis: SatoshiValue
lockingScript?: HexString
tx?: BEEF
spendable: boolean
customInstructions?: string
tags?: OutputTagStringUnder300Bytes[]
labels?: LabelStringUnder300Bytes[]
}>
}> {
return await this.invoke('listOutputs', args)
}
async relinquishOutput(args: {
basket: BasketStringUnder300Bytes
output: OutpointString
}): Promise<{ relinquished: true }> {
return await this.invoke('relinquishOutput', args)
}
async getPublicKey(args: {
identityKey?: true
protocolID?: [SecurityLevel, ProtocolString5To400Bytes]
keyID?: KeyIDStringUnder800Bytes
privileged?: BooleanDefaultFalse
privilegedReason?: DescriptionString5to50Bytes
counterparty?: PubKeyHex | 'self' | 'anyone'
forSelf?: BooleanDefaultFalse
}): Promise<{ publicKey: PubKeyHex }> {
return await this.invoke('getPublicKey', args)
}
async revealCounterpartyKeyLinkage(args: {
counterparty: PubKeyHex
verifier: PubKeyHex
privilegedReason?: DescriptionString5to50Bytes
privileged?: BooleanDefaultFalse
}): Promise<{
prover: PubKeyHex
verifier: PubKeyHex
counterparty: PubKeyHex
revelationTime: ISOTimestampString
encryptedLinkage: Byte[]
encryptedLinkageProof: Byte[]
}> {
return await this.invoke('revealCounterpartyKeyLinkage', args)
}
async revealSpecificKeyLinkage(args: {
counterparty: PubKeyHex
verifier: PubKeyHex
protocolID: [SecurityLevel, ProtocolString5To400Bytes]
keyID: KeyIDStringUnder800Bytes
privilegedReason?: DescriptionString5to50Bytes
privileged?: BooleanDefaultFalse
}): Promise<{
prover: PubKeyHex
verifier: PubKeyHex
counterparty: PubKeyHex
protocolID: [SecurityLevel, ProtocolString5To400Bytes]
keyID: KeyIDStringUnder800Bytes
encryptedLinkage: Byte[]
encryptedLinkageProof: Byte[]
proofType: Byte
}> {
return await this.invoke('revealSpecificKeyLinkage', args)
}
async encrypt(args: {
plaintext: Byte[]
protocolID: [SecurityLevel, ProtocolString5To400Bytes]
keyID: KeyIDStringUnder800Bytes
privilegedReason?: DescriptionString5to50Bytes
counterparty?: PubKeyHex | 'self' | 'anyone'
privileged?: BooleanDefaultFalse
}): Promise<{ ciphertext: Byte[] }> {
return await this.invoke('encrypt', args)
}
async decrypt(args: {
ciphertext: Byte[]
protocolID: [SecurityLevel, ProtocolString5To400Bytes]
keyID: KeyIDStringUnder800Bytes
privilegedReason?: DescriptionString5to50Bytes
counterparty?: PubKeyHex | 'self' | 'anyone'
privileged?: BooleanDefaultFalse
}): Promise<{ plaintext: Byte[] }> {
return await this.invoke('decrypt', args)
}
async createHmac(args: {
data: Byte[]
protocolID: [SecurityLevel, ProtocolString5To400Bytes]
keyID: KeyIDStringUnder800Bytes
privilegedReason?: DescriptionString5to50Bytes
counterparty?: PubKeyHex | 'self' | 'anyone'
privileged?: BooleanDefaultFalse
}): Promise<{ hmac: Byte[] }> {
return await this.invoke('createHmac', args)
}
async verifyHmac(args: {
data: Byte[]
hmac: Byte[]
protocolID: [SecurityLevel, ProtocolString5To400Bytes]
keyID: KeyIDStringUnder800Bytes
privilegedReason?: DescriptionString5to50Bytes
counterparty?: PubKeyHex | 'self' | 'anyone'
privileged?: BooleanDefaultFalse
}): Promise<{ valid: true }> {
return await this.invoke('verifyHmac', args)
}
async createSignature(args: {
data?: Byte[]
hashToDirectlySign?: Byte[]
protocolID: [SecurityLevel, ProtocolString5To400Bytes]
keyID: KeyIDStringUnder800Bytes
privilegedReason?: DescriptionString5to50Bytes
counterparty?: PubKeyHex | 'self' | 'anyone'
privileged?: BooleanDefaultFalse
}): Promise<{ signature: Byte[] }> {
return await this.invoke('createSignature', args)
}
async verifySignature(args: {
data?: Byte[]
hashToDirectlyVerify?: Byte[]
signature: Byte[]
protocolID: [SecurityLevel, ProtocolString5To400Bytes]
keyID: KeyIDStringUnder800Bytes
privilegedReason?: DescriptionString5to50Bytes
counterparty?: PubKeyHex | 'self' | 'anyone'
forSelf?: BooleanDefaultFalse
privileged?: BooleanDefaultFalse
}): Promise<{ valid: true }> {
return await this.invoke('verifySignature', args)
}
async acquireCertificate(args: {
type: Base64String
subject: PubKeyHex
serialNumber: Base64String
revocationOutpoint: OutpointString
signature: HexString
fields: Record<CertificateFieldNameUnder50Bytes, string>
certifier: PubKeyHex
keyringRevealer: PubKeyHex | 'certifier'
keyringForSubject: Record<CertificateFieldNameUnder50Bytes, Base64String>
acquisitionProtocol: 'direct' | 'issuance'
certifierUrl?: string
}): Promise<{
type: Base64String
subject: PubKeyHex
serialNumber: Base64String
certifier: PubKeyHex
revocationOutpoint: OutpointString
signature: HexString
fields: Record<CertificateFieldNameUnder50Bytes, string>
}> {
return await this.invoke('acquireCertificate', args)
}
async listCertificates(args: {
certifiers: PubKeyHex[]
types: Base64String[]
limit?: PositiveIntegerDefault10Max10000
offset?: PositiveIntegerOrZero
privileged?: BooleanDefaultFalse
privilegedReason?: DescriptionString5to50Bytes
}): Promise<{
totalCertificates: PositiveIntegerOrZero
certificates: Array<{
type: Base64String
subject: PubKeyHex
serialNumber: Base64String
certifier: PubKeyHex
revocationOutpoint: OutpointString
signature: HexString
fields: Record<CertificateFieldNameUnder50Bytes, string>
}>
}> {
return await this.invoke('listCertificates', args)
}
async proveCertificate(args: {
certificate: {
type: Base64String
subject: PubKeyHex
serialNumber: Base64String
certifier: PubKeyHex
revocationOutpoint: OutpointString
signature: HexString
fields: Record<CertificateFieldNameUnder50Bytes, string>
}
fieldsToReveal: CertificateFieldNameUnder50Bytes[]
verifier: PubKeyHex
privileged?: BooleanDefaultFalse
privilegedReason?: DescriptionString5to50Bytes
}): Promise<{
keyringForVerifier: Record<CertificateFieldNameUnder50Bytes, Base64String>
}> {
return await this.invoke('proveCertificate', args)
}
async relinquishCertificate(args: {
type: Base64String
serialNumber: Base64String
certifier: PubKeyHex
}): Promise<{ relinquished: true }> {
return await this.invoke('relinquishCertificate', args)
}
async discoverByIdentityKey(args: {
identityKey: PubKeyHex
limit?: PositiveIntegerDefault10Max10000
offset?: PositiveIntegerOrZero
}): Promise<{
totalCertificates: PositiveIntegerOrZero
certificates: Array<{
type: Base64String
subject: PubKeyHex
serialNumber: Base64String
certifier: PubKeyHex
revocationOutpoint: OutpointString
signature: HexString
fields: Record<CertificateFieldNameUnder50Bytes, Base64String>
certifierInfo: {
name: EntityNameStringMax100Bytes
iconUrl: EntityIconURLStringMax500Bytes
description: DescriptionString5to50Bytes
trust: PositiveIntegerMax10
}
publiclyRevealedKeyring: Record<
CertificateFieldNameUnder50Bytes,
Base64String
>
decryptedFields: Record<CertificateFieldNameUnder50Bytes, string>
}>
}> {
return await this.invoke('discoverByIdentityKey', args)
}
async discoverByAttributes(args: {
attributes: Record<CertificateFieldNameUnder50Bytes, string>
limit?: PositiveIntegerDefault10Max10000
offset?: PositiveIntegerOrZero
}): Promise<{
totalCertificates: PositiveIntegerOrZero
certificates: Array<{
type: Base64String
subject: PubKeyHex
serialNumber: Base64String
certifier: PubKeyHex
revocationOutpoint: OutpointString
signature: HexString
fields: Record<CertificateFieldNameUnder50Bytes, Base64String>
certifierInfo: {
name: EntityNameStringMax100Bytes
iconUrl: EntityIconURLStringMax500Bytes
description: DescriptionString5to50Bytes
trust: PositiveIntegerMax10
}
publiclyRevealedKeyring: Record<
CertificateFieldNameUnder50Bytes,
Base64String
>
decryptedFields: Record<CertificateFieldNameUnder50Bytes, string>
}>
}> {
return await this.invoke('discoverByAttributes', args)
}
async isAuthenticated(args: {}): Promise<{ authenticated: true }> {
return await this.invoke('isAuthenticated', args)
}
async waitForAuthentication(args: {}): Promise<{ authenticated: true }> {
return await this.invoke('waitForAuthentication', args)
}
async getHeight(args: {}): Promise<{ height: PositiveInteger }> {
return await this.invoke('getHeight', args)
}
async getHeaderForHeight(args: {
height: PositiveInteger
}): Promise<{ header: HexString }> {
return await this.invoke('getHeaderForHeight', args)
}
async getNetwork(args: {}): Promise<{ network: 'mainnet' | 'testnet' }> {
return await this.invoke('getNetwork', args)
}
async getVersion(args: {}): Promise<{ version: VersionString7To30Bytes }> {
return await this.invoke('getVersion', args)
}
}