Major refactoring and compatibility with dynamic bundle#22
Merged
jonathunne merged 41 commits intotetherto:mainfrom Feb 8, 2026
Merged
Major refactoring and compatibility with dynamic bundle#22jonathunne merged 41 commits intotetherto:mainfrom
jonathunne merged 41 commits intotetherto:mainfrom
Conversation
Remove the tight coupling to pear-wrk-wdk by accepting bundleConfig (containing bundle and HRPC) as explicit function parameters instead of importing from the package directly. Key changes: - WorkletLifecycleService.startWorklet now accepts bundleConfig as 2nd param - WdkAppProvider passes bundleConfig directly to startWorklet - ensureWorkletStarted simplified to just validate worklet is started - Remove pear-wrk-wdk from dependencies - Update all tests to use local mocks instead of pear-wrk-wdk imports - Define HRPC types locally in src/types/hrpc.ts This allows consumers to provide their own bundle and HRPC implementation, making the library more flexible and decoupled.
- Add Bundle Configuration section explaining two options: - Option A: Generate custom bundle with wdk-worklet-bundler CLI - Option B: Use pre-built pear-wrk-wdk package - Update all examples to include the required bundle prop - Update WdkAppProvider API reference with full props interface - Add TypeScript configuration guidance for generated bundles
Set SecureStorage in WalletSetupService synchronously during render instead of in a useEffect. This prevents race conditions where child components' useEffect hooks run before the parent's effect, causing "SecureStorage not initialized" errors.
Update the `pear-wrk-wdk` dependency to the `nampc/dynamic-worklet` branch. This change also involves removing unused dependency entries from `package-lock.json`.
This commit introduces several improvements: - **Generics for `callAccountMethod`**: The `AccountService.callAccountMethod` function now supports generic type parameters (`TMethods`, `K`) to enable strict typing of method arguments and return values. Developers can define custom `MethodMap` types for their applications, improving type safety and developer experience. - **New Wallet Generation Utilities**: `useWalletManager` now exposes `generateEntropyAndEncrypt`, `getMnemonicFromEntropy`, and `getSeedAndEntropyFromMnemonic` functions. These utilities facilitate custom wallet creation flows, such as displaying mnemonics before saving or importing existing wallets. - **Type Definitions**: New type definitions for `MethodMap` and `LooseMethods` are added to `src/types/accountMethods.ts` to support the generic typing of account methods. - **Provider Props**: The `WdkAppProviderProps` interface now accepts a generic type parameter `TConfig` to allow for more specific network configurations. - **Type Casting**: Minor type casting adjustments in `useWallet` and `walletUtils` to align with the new generic types. - **Dependency Updates**: Removed unused dependencies from `package-lock.json`.
|
I don't know what has been changed in this MR, but would be nice to have:
my adapters:export interface WdkAppContextValue extends UpstreamWdkAppContextValue {
/** Start worklet manually. Idempotent (safe to call multiple times), fire-and-forget. */
startWorklet: () => void
}
export interface WdkAppProviderProps
extends Omit<UpstreamWdkAppProviderProps, 'networkConfigs'> {
networkConfigs: NetworkConfigs
autoStartWorklet?: boolean
}
const WdkAppContext = createContext<WdkAppContextValue | null>(null)
function WdkAppContextBridge({
networkConfigs,
autoStartWorklet,
children,
}: {
networkConfigs: NetworkConfigs
autoStartWorklet: boolean
children: React.ReactNode
}) {
const upstreamContext = useUpstreamWdkApp()
const { isLoading: isWorkletLoading, isWorkletStarted } = useWorklet()
const startWorklet = useCallback(() => {
if (isWorkletLoading || isWorkletStarted) return
WorkletLifecycleService.startWorklet(
networkConfigs as unknown as UpstreamNetworkConfigs
).catch(console.error)
}, [networkConfigs, isWorkletLoading, isWorkletStarted])
useEffect(() => {
if (!autoStartWorklet) return
if (isWorkletLoading || isWorkletStarted) return
WorkletLifecycleService.startWorklet(
networkConfigs as unknown as UpstreamNetworkConfigs
).catch(console.error)
}, [autoStartWorklet, isWorkletLoading, isWorkletStarted, networkConfigs])
const contextValue: WdkAppContextValue = useMemo(
() => ({
...upstreamContext,
startWorklet,
}),
[upstreamContext, startWorklet]
)
return (
<WdkAppContext.Provider value={contextValue}>
{children}
</WdkAppContext.Provider>
)
}
export function WdkAppProvider({
autoStartWorklet = false,
children,
...upstreamProps
}: WdkAppProviderProps) {
// CRITICAL: Set secure storage synchronously before any children mount
// This prevents "SecureStorage not initialized" race condition
useMemo(() => {
const storage = createSecureStorage()
WalletSetupService.setSecureStorage(storage)
return storage
}, [])
return (
<UpstreamWdkAppProvider
{...upstreamProps}
networkConfigs={
upstreamProps.networkConfigs as unknown as UpstreamNetworkConfigs
}
>
<WdkAppContextBridge
networkConfigs={upstreamProps.networkConfigs}
autoStartWorklet={autoStartWorklet}
>
{children}
</WdkAppContextBridge>
</UpstreamWdkAppProvider>
)
}
export function useWdkApp(): WdkAppContextValue {
const context = useContext(WdkAppContext)
if (!context) {
throw new Error('useWdkApp must be used within WdkAppProvider')
}
return context
}
export { WdkAppContext }export function useWalletManager(
walletId?: string,
networkConfigs?: Parameters<typeof useUpstreamWalletManager>[1]
): UseWalletManagerResult {
const upstream = useUpstreamWalletManager(walletId, networkConfigs)
const walletStore = getWalletStore()
const initializeFromMnemonic = useCallback(
async (mnemonic: string, walletIdParam?: string) => {
const targetWalletId = walletIdParam ?? walletId ?? 'default'
await upstream.initializeFromMnemonic(mnemonic, walletIdParam)
walletStore.setState(prev =>
produce(prev, (state: WalletStoreState) => {
const existingIndex = state.walletList.findIndex(
w => w.identifier === targetWalletId
)
if (existingIndex >= 0) {
state.walletList[existingIndex] = {
identifier: targetWalletId,
exists: true,
isActive: true,
}
} else {
state.walletList.push({
identifier: targetWalletId,
exists: true,
isActive: true,
})
}
for (const wallet of state.walletList) {
if (wallet.identifier !== targetWalletId) {
wallet.isActive = false
}
}
state.activeWalletId = targetWalletId
})
)
},
[upstream.initializeFromMnemonic, walletId, walletStore]
)
return useMemo(
() => ({
...upstream,
initializeFromMnemonic,
}),
[upstream, initializeFromMnemonic]
)
}
export type { UseWalletManagerResult } |
|
btw please also take note on balance fetching, because right now You can use ext-provider-multicall on evm and my implementation/**
* @param {string[]} tokenAddresses
* @returns {Promise<Record<string, bigint>>}
*/
async getTokenBalances(tokenAddresses) {
if (!this._provider) {
throw new Error(
'The wallet must be connected to a provider to retrieve token balances.'
)
}
const address = await this.getAddress()
const abi = ['function balanceOf(address owner) view returns (uint256)']
const multicallProvider = new MulticallProvider(this._provider)
const balances = await Promise.all(
tokenAddresses.map(async tokenAddress => {
const contract = new Contract(tokenAddress, abi, multicallProvider)
return contract.balanceOf(address)
})
)
return tokenAddresses.reduce((acc, tokenAddress, index) => {
acc[tokenAddress] = balances[index]
return acc
}, {})
} /**
* Returns the account balances for a list of SPL tokens.
*
* @param {string[]} tokenAddresses - The smart contract addresses of the tokens.
* @returns {Promise<Record<string, bigint>>} The token balances (in base unit).
*/
async getTokenBalances(tokenAddresses) {
if (!this._rpc) {
throw new Error(
'The wallet must be connected to a provider to retrieve token balances.'
)
}
const addr = await this.getAddress()
const ownerAddress = address(addr)
// Deduplicate token addresses
const uniqueTokenAddresses = [...new Set(tokenAddresses)]
const mints = uniqueTokenAddresses.map(t => address(t))
// Calculate ATAs
const atas = await Promise.all(
mints.map(mint =>
findAssociatedTokenPda({
mint,
owner: ownerAddress,
tokenProgram: TOKEN_PROGRAM_ADDRESS,
}).then(([ata]) => ata)
)
)
// Fetch accounts
const { value: accounts } = await this._rpc
.getMultipleAccounts(atas, {
commitment: this._commitment,
encoding: 'base64',
})
.send()
const balances = {}
const base64Encoder = getBase64Encoder()
for (let i = 0; i < uniqueTokenAddresses.length; i++) {
const tokenAddress = uniqueTokenAddresses[i]
const account = accounts[i]
if (!account) {
balances[tokenAddress] = 0n
continue
}
// Parse amount from account data
// Account data is [base64_string, 'base64']
const dataBase64 = account.data[0]
const bytes = base64Encoder.encode(dataBase64)
// Amount is at offset 64, 8 bytes, little endian
const view = new DataView(
bytes.buffer,
bytes.byteOffset,
bytes.byteLength
)
const amount = view.getBigUint64(64, true)
balances[tokenAddress] = amount
}
return balances
} |
multi-argument methods
|
please update |
| */ | ||
| export const networkConfigsSchema = z.record( | ||
| export const wdkNetworkConfigsSchema = z.record( | ||
| z.string().regex(/^[a-zA-Z0-9_-]+$/, { |
There was a problem hiding this comment.
please allow caip-2 specification
e.g. eip155:1 or eip155:ethereum
- Added missing features needed for Rumble - Implemented functions in the new specs
itsdeka
approved these changes
Feb 7, 2026
jonathunne
approved these changes
Feb 8, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.