Pear Worklet WDK Configuration
Configure Pear Worklet HRPC and JSON-RPC contexts, WDK payloads, and generic modules
This page explains how to configure a beta.14 worklet context, initialize and dispose WDK, call wallet and generic-module methods, and configure logging and suspend diagnostics. Use the table of contents to find each task.
Worklet Context
You can bind the shipped RPC handlers to your Bare worklet using registerRpcHandlers():
require('bare-node-runtime/global')
const { registerRpcHandlers } = require('@tetherto/pear-wrk-wdk/worklet')
const wdkModule = require('@tetherto/wdk', { with: { imports: 'bare-node-runtime/imports' } })
const { createModule: createPreferencesModule } = require('@your-org/wdk-module-preferences')
const WDK = wdkModule.default || wdkModule.WDK || wdkModule
const walletCache = {}
function loadWalletManager(network) {
if (walletCache[network]) return walletCache[network]
let walletModule
if (network === 'ethereum') {
walletModule = require('@tetherto/wdk-wallet-evm', { with: { imports: 'bare-node-runtime/imports' } })
}
if (network === 'spark') {
walletModule = require('@tetherto/wdk-wallet-spark', { with: { imports: 'bare-node-runtime/imports' } })
}
if (walletModule) walletCache[network] = walletModule.default || walletModule
return walletCache[network] || null
}
const walletManagers = new Proxy({}, {
get: (_, network) => loadWalletManager(network),
has: (_, network) => ['ethereum', 'spark'].includes(network)
})
const context = {
wdk: null,
WDK,
walletManagers,
protocolManagers: {},
moduleManagers: {
preferences: {
createModule: createPreferencesModule,
events: ['changed']
}
},
allowedMethods: {
ethereum: {
methods: ['getAddress', 'getBalance', 'sendTransaction']
}
},
allowedModuleMethods: {
preferences: {
methods: ['getTheme', 'setTheme']
}
},
capabilities: {},
wdkLoadError: null
}
module.exports = (rpc) => {
registerRpcHandlers(rpc, context)
Bare.on('suspend', async () => {
await context.moduleRuntime?.suspendAll()
})
Bare.on('resume', async () => {
await context.moduleRuntime?.resumeAll()
})
}Required Context Fields
wdk: The current WDK instance. Set this tonullbefore the first initialization.WDK: The WDK constructor used to create the seeded instance.walletManagers: A map from blockchain name to wallet manager implementation.protocolManagers: A map from protocol name to protocol manager implementation.wdkLoadError: Any startup error captured while loading WDK. Usenullwhen there is no load failure.
For generic modules on either transport, moduleManagers optionally maps module names to { createModule, events? }. The factory receives { seed, config, capabilities, emit } and can return an instance or a promise. capabilities is an optional host-supplied object and is empty by default. The runtime manages moduleRuntime and moduleInstances; do not initialize those fields yourself. Manual integrations must forward Bare suspend and resume events as shown if module instances should receive those lifecycle calls. See Worklet Bundler lifecycle behavior for generated entrypoints.
Restrict Dynamic Methods
callMethod() and callModule() dispatch method names supplied by the host. Add allowlists to RpcContext when that host should not reach every method on the resolved object.
Wallet and Protocol Methods
allowedMethods is keyed by network. A network's direct methods array applies to its wallet account. Protocol restrictions are nested by protocol type and protocol name:
const context = {
// Other required context fields...
allowedMethods: {
ethereum: {
methods: ['getAddress', 'getBalance', 'sendTransaction'],
protocols: {
lending: {
aave: {
methods: ['supply', 'withdraw']
}
}
}
}
}
}This map applies to the shared HRPC and JSON-RPC callMethod() handler. Restrictions are opt-in per surface:
- Omitting the map, a network,
protocols, a protocol type, a protocol name, ormethodsleaves that exact surface unrestricted. - An explicit
methods: []denies every call on that exact account or protocol surface. - Protocol calls use their nested list rather than falling back to the account list.
- A denied method fails before account or protocol dispatch with
METHOD_NOT_ALLOWED.
Generic Module Methods
allowedModuleMethods is keyed by the moduleManagers name and applies to callModule() on both HRPC and JSON-RPC:
const context = {
// Other required context fields...
allowedModuleMethods: {
preferences: {
methods: ['getTheme', 'setTheme']
}
}
}Omitting a module or its methods field leaves that module unrestricted. Set methods: [] to deny every dynamic call on it.
These maps are not default-deny. List every dynamic surface exposed to an untrusted host. The beta.14 runtime reports denied calls with METHOD_NOT_ALLOWED, but the published error-code declaration still omits that member; handle the literal runtime code until the declaration is corrected.
Worklet Config Payload
Both initializeWDK() and resetWdkWallets() expect config to be a JSON string. The decoded object must contain at least one entry under networks.
const workletConfig = {
networks: {
ethereum: {
blockchain: 'ethereum',
config: {
provider: 'https://rpc.ankr.com/eth_sepolia'
}
}
},
protocols: {
moonpay: {
blockchain: 'ethereum',
protocolName: 'moonpay',
config: {
environment: 'sandbox'
}
}
},
modules: {
preferences: {
storagePath: '/app-data/preferences'
}
}
}Payload Rules
networksis required and must contain at least one network entry.- Each network entry must include
blockchainand an objectconfig. protocolsis optional during initialization.modulesis optional and contains runtime config for named generic modules. Each key must match amoduleManagerskey in the worklet context and the corresponding build-time Worklet Bundler module name.resetWdkWallets()reads only thenetworksportion of the decoded config.
JSON-RPC generic-module support was introduced in Pear Worklet beta.13. Supply the same moduleManagers, allowedModuleMethods, and runtime modules config used by HRPC. For generated entrypoints, use Worklet Bundler beta.13 with Pear Worklet beta.14.
Initialize WDK
You can create and register the WDK instance inside the worklet using initializeWDK():
const { HRPC } = require('@tetherto/pear-wrk-wdk')
const hrpc = new HRPC(ipcStream)
await hrpc.initializeWDK({
encryptionKey: secrets.encryptionKey,
encryptedSeed: secrets.encryptedSeedBuffer,
config: JSON.stringify(workletConfig)
})Initialization Rules
- Pass both
encryptionKeyandencryptedSeed, or omit both together. - On first initialization, the worklet must receive an encrypted seed pair so it can create
context.wdk. - If
context.wdkalready exists, reinitialization closes its generic modules and callswdk.dispose()before validating the new config. With both seed fields present, beta.14 then zeroes the old retained seed buffer before attempting replacement. Validate the replacement payload before sending it; a failed replacement does not restore the old seed. - Without a seed pair, reinitialization reuses the existing WDK object and retained seed to re-register wallets and protocols. It closes generic modules without rebuilding them, even when
config.modulesis present. Supply the seed pair on every initialization that must construct or reconstruct modules. - Module
close()is called during full disposal or reinitialization. Targeted blockchain disposal leaves generic modules running. Optionalsuspend()andresume()methods run only when the host forwards Bare lifecycle events; the manual context above does so.
The runtime manages the decrypted buffer through context.wdkSeedBuffer; do not set this field yourself. If the WDK constructor throws, beta.14 zeroes the newly decrypted buffer. Later wallet or protocol registration failures retain the new instance and seed, so handle those failures and dispose the instance when abandoning initialization.
Dispose WDK
Send a full disposal request when the worklet should release its WDK instance:
hrpc.dispose({})A full disposal closes generic modules, disposes WDK, and zeroes and releases its retained seed buffer. Once module shutdown completes, the instance and buffer are cleared even if wdk.dispose() throws. HRPC dispose() is one-way and returns void; its return does not acknowledge cleanup completion.
To dispose only selected wallets, pass a non-empty blockchains array, such as hrpc.dispose({ blockchains: ['ethereum'] }). This retains the WDK instance, seed buffer, and generic modules. An omitted or empty array requests full disposal.
This cleanup covers the buffer retained by the worklet runtime. It cannot erase JavaScript strings or copies retained by application or module code.
Reset Selected Wallets
You can selectively dispose and re-register wallet modules using resetWdkWallets():
await hrpc.resetWdkWallets({
config: JSON.stringify({
networks: {
ethereum: {
blockchain: 'ethereum',
config: {
provider: 'https://rpc.ankr.com/eth_sepolia'
}
}
}
})
})Reset Rules
resetWdkWallets()requires an existing initializedcontext.wdk.- The handler calls
wdk.dispose(targetChains)with the blockchains extracted fromconfig.networks. - Only wallets listed in the request
networksobject are re-registered. - The reset flow does not re-register protocols.
- The reset flow does not close or reconstruct generic modules; existing module instances keep running.
Call Wallet and Protocol Methods
You can execute wallet account methods through callMethod():
const result = await hrpc.callMethod({
methodName: 'getAddress',
network: 'ethereum',
accountIndex: 0
})Call Method Notes
argsis optional and must be a JSON string when provided.optionsis optional and must be a JSON string when provided.- When
argsdecodes to an array, the handler spreads the values as positional method arguments. - Omitted or decoded
nullarguments call the method with no arguments. Other objects or primitives are passed as one argument. - Set
options.protocolTypetoswap,swidge,bridge,lending, orfiatto call a protocol wrapper. Every protocol call requires a non-emptyoptions.protocolName. - When
context.allowedMethodscontains the target account or protocol surface,methodNamemust appear in that exact surface'smethodsarray. - In beta.13, a missing wallet or protocol method fails with
BAD_REQUEST. The removedoptions.defaultValuefield no longer supplies a fallback; catch unsupported-method errors in the host. - Beta.14 removes
options.transformResultfrom the declarations and ignores it during dispatch. Transform the returned value in the host instead.
Invalid JSON in config, args, or options fails with BAD_REQUEST. Beta.14 reports a message such as args must be valid JSON without the native parser's error detail.
Call Generic Module Methods
On an HRPC worklet configured with matching moduleManagers and runtime modules, call a module method by name:
const response = await hrpc.callModule({
module: 'preferences',
method: 'getTheme',
args: JSON.stringify([])
})
const theme = response.result ? JSON.parse(response.result) : undefinedargs is an optional JSON string. Arrays are spread into positional arguments; omitted or decoded null arguments call the method with no arguments. Other values are passed as one argument. Promise results are awaited, .toArray() results are materialized, and Uint8Array values are normalized to hex before the response is serialized.
In beta.14, a module method that returns undefined or null produces the JSON string "null" in the HRPC response. JSON-RPC decodes it to response.result.result === null. This normalization applies to callModule(), not wallet or protocol callMethod() results.
When context.allowedModuleMethods contains the target module, method must appear in its methods array. A denied call returns METHOD_NOT_ALLOWED before module instance lookup or dispatch.
Subscribe to events declared by the module manager:
hrpc.onModuleEvent(({ module, event, payload }) => {
if (module === 'preferences' && event === 'changed') {
const value = payload ? JSON.parse(payload) : undefined
console.log('Preferences changed:', value)
}
})JSON-RPC Transport
Native hosts can register the separate framed JSON-RPC server entrypoint:
const { registerJsonRpcHandlers } = require('@tetherto/pear-wrk-wdk/jsonrpc')
module.exports = (ipc) => {
registerJsonRpcHandlers(ipc, context)
}Messages are UTF-8 JSON-RPC 2.0 objects prefixed by a four-byte unsigned big-endian payload length. Requests require an ID, and IDs must be unique while a request is in flight. The package exports no JSON-RPC host/client helper; the native host must implement framing and correlation.
JSON-RPC beta.13 supports generic-module initialization, callModule, allowedModuleMethods, and moduleEvent notifications alongside wallet and protocol operations. resetWdkWallets remains HRPC-only. See the API reference for all methods and response shapes.
Send a module call as a framed JSON-RPC request; args remains a JSON string:
{"jsonrpc":"2.0","id":1,"method":"callModule","params":{"module":"preferences","method":"getTheme","args":"[]"}}For a module that returns 'dark', the response contains the decoded value inside result.result:
{"jsonrpc":"2.0","id":1,"result":{"result":"dark"}}A declared module event arrives as a notification without an id; params.payload is already decoded:
{"jsonrpc":"2.0","method":"moduleEvent","params":{"module":"preferences","event":"changed","payload":{"theme":"dark"}}}Manual JSON-RPC entrypoints must forward Bare suspend and resume events to context.moduleRuntime as shown in the HRPC context example. Registering JSON-RPC handlers alone does not install those lifecycle listeners.
Configure Logging
Set LOG_LEVEL in the worklet environment before loading the package. Accepted values are DEBUG, INFO, WARN, ERROR, and NONE; beta.14 trims whitespace and ignores case. An unrecognized value falls back to ERROR. With LOG_LEVEL unset or empty, NODE_ENV=development selects DEBUG; other environments default to ERROR.
At every enabled level, the package logger replaces object fields named mnemonic, encryptionKey, encryptedSeed, encryptedSeedBuffer, encryptedEntropy, encryptedEntropyBuffer, or seed with [redacted]. Matching is case-sensitive. Nested objects at depth three are replaced with [object].
This is field-based redaction, not a general secret filter. Raw strings, serialized JSON, typed-array arguments, and formatted Error stacks are not scrubbed. Wallet-call arguments can appear at DEBUG, and module error messages and application logs can still expose sensitive values. Keep production logging at ERROR and avoid secrets in custom logs and errors.
Inspect Suspend Delays
Register the optional registerHandleLeakCheck() helper once in a Bare worklet entrypoint:
const { registerHandleLeakCheck } = require('@tetherto/pear-wrk-wdk/diagnostics/handle-leak-check')
registerHandleLeakCheck({ tickIntervalMs: 1000 })The helper logs a handle snapshot immediately on suspend, then repeats every tickIntervalMs milliseconds until idle or resume. The default interval is 1000 ms. Its timer is unreferenced so the diagnostic itself does not keep the event loop active. It reports handles; it does not close them or suspend modules.
Provide a positive interval; the beta.14 helper passes the value to the timer without validating it. Registration is a no-op if the optional bare-walk-handles dependency or Bare lifecycle events are unavailable. Diagnostic output uses console.warn regardless of LOG_LEVEL, so register it only when you need handle diagnostics.