Skip to content

Commit

Permalink
Move wasm instantiation to a web worker (#15)
Browse files Browse the repository at this point in the history
* feat: moved wasm instantiation to a web worker

* feat: web worker is working
  • Loading branch information
alexlwn123 authored Sep 17, 2024
1 parent 739fe15 commit 2185071
Show file tree
Hide file tree
Showing 14 changed files with 625 additions and 173 deletions.
5 changes: 5 additions & 0 deletions .changeset/clever-llamas-build.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@fedimint/core-web': patch
---

Moved wasm instantiation off the main thread and onto a web worker
5 changes: 5 additions & 0 deletions .changeset/modern-impalas-train.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@fedimint/core-web': patch
---

Fixed types of new RPCs
123 changes: 105 additions & 18 deletions examples/vite-core/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,30 @@
import { useEffect, useState } from 'react'
import { useCallback, useEffect, useState } from 'react'
import { wallet } from './wallet'

const TESTNET_FEDERATION_CODE =
'fed11qgqrgvnhwden5te0v9k8q6rp9ekh2arfdeukuet595cr2ttpd3jhq6rzve6zuer9wchxvetyd938gcewvdhk6tcqqysptkuvknc7erjgf4em3zfh90kffqf9srujn6q53d6r056e4apze5cw27h75'

const useIsOpen = () => {
const [isOpen, setIsOpen] = useState(false)

const checkIsOpen = useCallback(() => {
if (isOpen !== wallet.isOpen()) setIsOpen(wallet.isOpen())
return isOpen
}, [wallet])

return { isOpen, checkIsOpen }
}

const useBalance = () => {
const [balance, setBalance] = useState(0)

useEffect(() => {
// console.log('subscribing')
const unsubscribe = wallet.subscribeBalance((balance: number) => {
// console.log('balance', balance)
console.log('balance', balance)
setBalance(balance)
})

return () => {
// console.log('unsubscribing')
unsubscribe()
}
}, [])
Expand All @@ -24,31 +33,40 @@ const useBalance = () => {
}

const App = () => {
const { isOpen, checkIsOpen } = useIsOpen()
return (
<>
<header>
<h1>Fedimint Typescript Library Demo</h1>
<h2>This is a WIP</h2>
</header>
<main>
<WalletStatus />
<JoinFederation />
<WalletStatus isOpen={isOpen} checkIsOpen={checkIsOpen} />
<JoinFederation isOpen={isOpen} checkIsOpen={checkIsOpen} />
<GenerateLightningInvoice />
<RedeemEcash />
<SendLightning />
</main>
</>
)
}

const WalletStatus = () => {
const WalletStatus = ({
isOpen,
checkIsOpen,
}: {
isOpen: boolean
checkIsOpen: () => boolean
}) => {
const balance = useBalance()

return (
<div className="section">
<h3>Wallet Status</h3>
<div className="row">
<strong>Is Wallet Open?</strong>
<div>{wallet.isOpen() ? 'Yes' : 'No'}</div>
<div>{isOpen ? 'Yes' : 'No'}</div>
<button onClick={() => checkIsOpen()}>Check</button>
</div>
<div className="row">
<strong>Balance:</strong>
Expand All @@ -59,22 +77,32 @@ const WalletStatus = () => {
)
}

const JoinFederation = () => {
const JoinFederation = ({
isOpen,
checkIsOpen,
}: {
isOpen: boolean
checkIsOpen: () => boolean
}) => {
const [inviteCode, setInviteCode] = useState(TESTNET_FEDERATION_CODE)
const [joinResult, setJoinResult] = useState<string | null>(null)
const [joinError, setJoinError] = useState('')

const joinFederation = async (e: React.FormEvent) => {
e.preventDefault()
const open = checkIsOpen()
console.log('OPEN', open)
if (open) return

console.log('Joining federation:', inviteCode)
try {
const res = await wallet?.joinFederation(inviteCode)
console.warn('join federation res', res)
console.log('join federation res', res)
setJoinResult('Joined!')
setJoinError('')
} catch (e) {
} catch (e: any) {
console.log('Error joining federation', e)
setJoinError(e as string)
setJoinError(typeof e === 'object' ? e.toString() : (e as string))
setJoinResult('')
}
}
Expand All @@ -89,15 +117,13 @@ const JoinFederation = () => {
required
value={inviteCode}
onChange={(e) => setInviteCode(e.target.value)}
disabled={wallet.isOpen()}
disabled={isOpen}
/>
<button type="submit" disabled={wallet.isOpen()}>
<button type="submit" disabled={isOpen}>
Join
</button>
</form>
{!joinResult && wallet.isOpen() && (
<i>(You've already joined a federation)</i>
)}
{!joinResult && isOpen && <i>(You've already joined a federation)</i>}
{joinResult && <div className="success">{joinResult}</div>}
{joinError && <div className="error">{joinError}</div>}
</div>
Expand All @@ -113,7 +139,7 @@ const RedeemEcash = () => {
e.preventDefault()
try {
const res = await wallet.redeemEcash(ecashInput)
console.warn('redeem ecash res', res)
console.log('redeem ecash res', res)
setRedeemResult('Redeemed!')
setRedeemError('')
} catch (e) {
Expand Down Expand Up @@ -177,4 +203,65 @@ const SendLightning = () => {
)
}

const GenerateLightningInvoice = () => {
const [amount, setAmount] = useState('')
const [description, setDescription] = useState('')
const [invoice, setInvoice] = useState('')
const [error, setError] = useState('')

const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault()
setInvoice('')
setError('')

try {
const response = await wallet.createBolt11Invoice(
Number(amount),
description,
)
setInvoice(response.invoice)
} catch (e) {
console.error('Error generating Lightning invoice', e)
setError(e instanceof Error ? e.message : String(e))
}
}

return (
<div className="section">
<h3>Generate Lightning Invoice</h3>
<form onSubmit={handleSubmit}>
<div className="input-group">
<label htmlFor="amount">Amount (sats):</label>
<input
id="amount"
type="number"
placeholder="Enter amount"
required
value={amount}
onChange={(e) => setAmount(e.target.value)}
/>
</div>
<div className="input-group">
<label htmlFor="description">Description:</label>
<input
id="description"
placeholder="Enter description"
required
value={description}
onChange={(e) => setDescription(e.target.value)}
/>
</div>
<button type="submit">Generate Invoice</button>
</form>
{invoice && (
<div className="success">
<strong>Generated Invoice:</strong>
<pre>{invoice}</pre>
</div>
)}
{error && <div className="error">{error}</div>}
</div>
)
}

export default App
30 changes: 30 additions & 0 deletions examples/vite-core/src/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,36 @@ button:disabled {
font-size: 1.1rem;
}

.input-group {
display: flex;
flex-direction: row;
gap: 0.5rem;
padding-bottom: 0.5rem;
}

.input-group label {
font-weight: bold;
align-self: center;
}

.input-group input {
padding: 0.5rem;
font-size: 1rem;
}

.column {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 0.5rem;
}

button[type='submit'] {
padding: 0.5rem 1rem;
font-size: 1rem;
cursor: pointer;
}

@media (min-width: 768px) {
body {
padding: 2rem;
Expand Down
10 changes: 5 additions & 5 deletions examples/vite-core/src/wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import { FedimintWallet } from '@fedimint/core-web'

const wallet = new FedimintWallet()

try {
await wallet.open()
} catch (e) {
console.warn('Failed to open wallet', e)
}
// try {
wallet.open()
// } catch (e) {
// console.warn('Failed to open wallet', e)
// }

export { wallet }
12 changes: 12 additions & 0 deletions examples/vite-core/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,16 @@ export default defineConfig({
optimizeDeps: {
exclude: ['@fedimint/core-web'], // Required for wasm support
},
worker: {
format: 'es',
plugins: () => [wasm(), topLevelAwait()],
},
build: {
target: 'esnext',
},
// resolve: {
// alias: {
// '@fedimint/core-web': '../../packages/core-web/src',
// },
// },
})
3 changes: 3 additions & 0 deletions packages/core-web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,15 @@
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"devDependencies": {
"@rollup/plugin-node-resolve": "^15.2.3",
"@rollup/plugin-terser": "^0.4.4",
"@rollup/plugin-typescript": "^11.1.6",
"@rollup/plugin-wasm": "^6.2.2",
"@types/node": "^20.14.8",
"@web/rollup-plugin-import-meta-assets": "^2.2.1",
"rollup": "^4.21.2",
"rollup-plugin-typescript2": "^0.36.0",
"rollup-plugin-web-worker-loader": "^1.6.1",
"tslib": "^2.7.0",
"typescript": "^5.2.2"
}
Expand Down
57 changes: 34 additions & 23 deletions packages/core-web/rollup.config.js
Original file line number Diff line number Diff line change
@@ -1,29 +1,40 @@
/** @type {import('rollup').RollupOptions} */
import typescript from '@rollup/plugin-typescript'
import { wasm } from '@rollup/plugin-wasm'
import { importMetaAssets } from '@web/rollup-plugin-import-meta-assets'
import terser from '@rollup/plugin-terser'

export default {
input: {
index: 'src/index.ts',
export default [
{
input: { worker: 'src/wasm.worker.js' },
output: [
{
dir: 'dist',
format: 'es',
sourcemap: true,
},
],
plugins: [
wasm({
maxFileSize: 0,
}),
terser(),
],
},
output: [
{
dir: 'dist',
format: 'esm',
sourcemap: true,
},
],
plugins: [
typescript({
sourceMap: true,
inlineSources: true,
}),
wasm({
maxFileSize: 0,
}),
importMetaAssets(),
terser(),
],
}
{
input: { index: 'src/index.ts' },
output: [
{
dir: 'dist',
format: 'es',
sourcemap: true,
},
],
plugins: [
typescript({
sourceMap: true,
inlineSources: true,
}),
terser(),
],
},
]
Loading

0 comments on commit 2185071

Please sign in to comment.