diff --git a/apps/walletd/config/providers.tsx b/apps/walletd/config/providers.tsx index 46c37e9a4..7cec3aced 100644 --- a/apps/walletd/config/providers.tsx +++ b/apps/walletd/config/providers.tsx @@ -4,6 +4,7 @@ import { WalletsProvider } from '../contexts/wallets' import { AddressesProvider } from '../contexts/addresses' import { EventsProvider } from '../contexts/events' import { LedgerProvider } from '../contexts/ledger' +import { AppProvider } from '../contexts/app' type Props = { children: React.ReactNode @@ -11,19 +12,21 @@ type Props = { export function Providers({ children }: Props) { return ( - - - - - - {/* this is here so that dialogs can use all the other providers, + + + + + + + {/* this is here so that dialogs can use all the other providers, and the other providers can trigger dialogs */} - - {children} - - - - - + + {children} + + + + + + ) } diff --git a/apps/walletd/contexts/app/index.tsx b/apps/walletd/contexts/app/index.tsx new file mode 100644 index 000000000..a11cd151c --- /dev/null +++ b/apps/walletd/contexts/app/index.tsx @@ -0,0 +1,27 @@ +import { createContext, useContext, useEffect } from 'react' +import { initSDK } from '@siafoundation/sdk' + +function useAppMain() { + // Initialize the SDK on app load + useEffect(() => { + const func = async () => { + await initSDK() + } + func() + }, []) + return {} +} + +type State = ReturnType + +const AppContext = createContext({} as State) +export const useApp = () => useContext(AppContext) + +type Props = { + children: React.ReactNode +} + +export function AppProvider({ children }: Props) { + const state = useAppMain() + return {children} +} diff --git a/libs/sdk/src/index.ts b/libs/sdk/src/index.ts index e59a87889..6ab683a62 100644 --- a/libs/sdk/src/index.ts +++ b/libs/sdk/src/index.ts @@ -1,2 +1,3 @@ -export * from './init' export * from './types' +export { initSDK } from './init' +export { getSDK, SDK } from './sdk' diff --git a/libs/sdk/src/sdk.ts b/libs/sdk/src/sdk.ts index 948d8a4c6..330af96d7 100644 --- a/libs/sdk/src/sdk.ts +++ b/libs/sdk/src/sdk.ts @@ -1,12 +1,22 @@ import { WebTransportClient } from './transport' -import { WASM } from './types' +import { WasmApi } from './types' -export function getSDK() { +export type SDK = WasmApi & { + WebTransportClient: typeof WebTransportClient +} + +export function getWasmApi(): WasmApi { // eslint-disable-next-line @typescript-eslint/no-explicit-any - const wasm = (global as any).sia as WASM + return (globalThis as any).sia as WasmApi +} + +export function getSDK(): SDK { + const wasmApi = getWasmApi() + if (wasmApi === undefined) { + throw new Error('The Sia SDK has not been initialized') + } return { - rhp: wasm.rhp, - wallet: wasm.wallet, + ...wasmApi, WebTransportClient, } } diff --git a/libs/sdk/src/transport.ts b/libs/sdk/src/transport.ts index 372c4e848..c9c1735e5 100644 --- a/libs/sdk/src/transport.ts +++ b/libs/sdk/src/transport.ts @@ -1,3 +1,4 @@ +import { getWasmApi } from './sdk' import { RPCReadSectorResponse, RPCSettingsResponse, @@ -9,26 +10,27 @@ import { RPCWriteSector, RPCSettings, } from './types' -import { WASM } from './types' export class WebTransportClient { #url: string #cert: string - #wasm: WASM #transport!: WebTransport - constructor(url: string, cert: string, wasm: WASM) { + constructor(url: string, cert: string) { this.#url = url this.#cert = cert - this.#wasm = wasm - } - async connect() { + if (!getWasmApi()) { + throw new Error('The Sia SDK has not been initialized.') + } + if (!('WebTransport' in window)) { throw new Error('WebTransport is not supported in your browser.') } + } + async connect() { try { this.#transport = new WebTransport(this.#url, { serverCertificateHashes: this.#cert @@ -101,8 +103,8 @@ export class WebTransportClient { ): Promise { return this.sendRequest( readSector, - this.#wasm.rhp.encodeReadSectorRequest, - this.#wasm.rhp.decodeReadSectorResponse + getWasmApi().rhp.encodeReadSectorRequest, + getWasmApi().rhp.decodeReadSectorResponse ) } @@ -111,16 +113,16 @@ export class WebTransportClient { ): Promise { return this.sendRequest( writeSector, - this.#wasm.rhp.encodeWriteSectorRequest, - this.#wasm.rhp.decodeWriteSectorResponse + getWasmApi().rhp.encodeWriteSectorRequest, + getWasmApi().rhp.decodeWriteSectorResponse ) } async sendRPCSettingsRequest(): Promise { return this.sendRequest( undefined, - this.#wasm.rhp.encodeSettingsRequest, - this.#wasm.rhp.decodeSettingsResponse + getWasmApi().rhp.encodeSettingsRequest, + getWasmApi().rhp.decodeSettingsResponse ) } } diff --git a/libs/sdk/src/types.ts b/libs/sdk/src/types.ts index 8d3458dc4..1008727aa 100644 --- a/libs/sdk/src/types.ts +++ b/libs/sdk/src/types.ts @@ -3,15 +3,14 @@ import { UnlockConditions, ConsensusNetwork, ConsensusState, + Currency, + Signature, + Address, + Hash256, + PublicKey, + PrivateKey, } from '@siafoundation/types' -type Currency = string -type Signature = string -type Address = string -type Hash256 = string // 32 bytes -type PrivateKey = string -type PublicKey = string // 32 bytes - type AccountToken = { account: PublicKey validUntil: string @@ -92,7 +91,7 @@ export type RPCWriteSector = { export type RPC = RPCSettings | RPCReadSector | RPCWriteSector -export type WASM = { +export type WasmApi = { rhp: { generateAccount: () => { privateKey?: PrivateKey @@ -189,7 +188,7 @@ export type WASM = { encodedTransaction?: string error?: string } - signTransaction: ( + signTransactionV1: ( cs: ConsensusState, cn: ConsensusNetwork, txn: Transaction, diff --git a/libs/sdk/src/wasm.ts b/libs/sdk/src/wasm.ts index d257acab8..f004fe447 100644 --- a/libs/sdk/src/wasm.ts +++ b/libs/sdk/src/wasm.ts @@ -5,7 +5,7 @@ export async function initWASM(): Promise { try { const go = new window.Go() const source = await wasm(go.importObject) - await go.run(source.instance) + go.run(source.instance) } catch (e) { throw new Error(`failed to initialize WASM: ${(e as Error).message}`) } diff --git a/libs/types/src/core.ts b/libs/types/src/core.ts index aff0d7e63..3a297a5bb 100644 --- a/libs/types/src/core.ts +++ b/libs/types/src/core.ts @@ -8,6 +8,7 @@ export type OutputID = string export type EncryptionKey = string export type FileContractID = string export type PublicKey = string +export type PrivateKey = string export type TransactionID = Hash256 export type SiacoinOutputID = Hash256 export type SiafundOutputID = Hash256 diff --git a/sdk/main.go b/sdk/main.go index 8759cce1e..5ebf343d3 100644 --- a/sdk/main.go +++ b/sdk/main.go @@ -34,7 +34,7 @@ func main() { "addressFromSpendPolicy": jsFunc(addressFromSpendPolicy), "encodeTransaction": jsFunc(encodeTransaction), "transactionId": jsFunc(transactionID), - "signTransaction": jsFunc(signTransaction), + "signTransactionV1": jsFunc(signTransactionV1), }, }) c := make(chan bool, 1) diff --git a/sdk/wallet.go b/sdk/wallet.go index 1d9cb04a4..0f9ad4611 100644 --- a/sdk/wallet.go +++ b/sdk/wallet.go @@ -3,6 +3,7 @@ package main import ( "bytes" "crypto/ed25519" + "encoding/base64" "encoding/hex" "fmt" "syscall/js" @@ -146,7 +147,7 @@ func encodeTransaction(this js.Value, args []js.Value) result { } // SignTransaction returns the signature of a transaction. -func signTransaction(this js.Value, args []js.Value) result { +func signTransactionV1(this js.Value, args []js.Value) result { if err := checkArgs(args, js.TypeObject, js.TypeObject, js.TypeObject, js.TypeNumber, js.TypeString); err != nil { return resultErr(err) } @@ -187,8 +188,9 @@ func signTransaction(this js.Value, args []js.Value) result { } else { sigHash = cs.PartialSigHash(txn, tsig.CoveredFields) } + sig := privateKey.SignHash(sigHash) return result(map[string]any{ - "signature": privateKey.SignHash(sigHash).String(), + "signature": base64.StdEncoding.EncodeToString(sig[:]), }) }