Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
124 commits
Select commit Hold shift + click to select a range
3226e96
init workflow
lukachi Aug 3, 2026
81a1415
build: vendor the smplx SDK fork as a submodule
lukachi Aug 3, 2026
f3483e3
chore: exclude the smplx submodule and wfctl-managed files from forma…
lukachi Aug 3, 2026
ae34218
build: advance the smplx submodule to the wasm-ready fork branch
lukachi Aug 3, 2026
fe67ac8
test: stand up a test runner
lukachi Aug 3, 2026
f651023
feat(liquid): load the smplx wasm module in the extension
lukachi Aug 3, 2026
cbd7224
feat(liquid): accept a manifest action request and refuse an incomple…
lukachi Aug 3, 2026
8cfeef5
test(liquid): add the canonical p2pk contract as a fixture
lukachi Aug 3, 2026
765b99c
test(liquid): assert compile parameters change the covenant address
lukachi Aug 4, 2026
310a74a
build: advance the smplx submodule past the parameterised-contract bi…
lukachi Aug 4, 2026
6c29e80
feat(liquid): resolve a contract's compile-time parameters from the r…
lukachi Aug 4, 2026
665df24
feat(liquid): derive a covenant address and check it against the chain
lukachi Aug 4, 2026
7cd5150
feat(liquid): read what the chain says is at an outpoint
lukachi Aug 4, 2026
1909e20
feat(liquid): establish what the wallet knows about an action before …
lukachi Aug 4, 2026
a7a6a4f
feat(liquid): verify a manifest action's covenants before anyone appr…
lukachi Aug 4, 2026
8b61692
test(liquid): assert the wallet signer derives stable, network-correc…
lukachi Aug 4, 2026
439696c
test(liquid): assert transaction assembly takes what it should and re…
lukachi Aug 4, 2026
317650e
test(liquid): drive a transaction through blinding, signing and final…
lukachi Aug 4, 2026
55ffcf5
feat(liquid): establish the fee rate, and reach the account mnemonic …
lukachi Aug 4, 2026
081a72b
feat(liquid): work out what an action's outputs actually pay
lukachi Aug 4, 2026
9edbbd4
feat(liquid): broadcast a signed transaction, not only a PSET
lukachi Aug 4, 2026
b15c289
feat(liquid): choose which of the wallet's outputs pay for an action
lukachi Aug 4, 2026
422283f
feat(liquid): build, sign and broadcast a manifest action
lukachi Aug 4, 2026
c0f8dbd
refactor(liquid): settle the transaction in review, leave only signin…
lukachi Aug 4, 2026
bed3536
feat(liquid): pay out what a covenant actually holds
lukachi Aug 4, 2026
b98c567
test(liquid): measure what a pre-approval dry-run of a signature cove…
lukachi Aug 4, 2026
a152ed0
build: advance the smplx submodule past covenant inputs and the dry-run
lukachi Aug 4, 2026
29b5d8e
fix(liquid): actually spend the covenant the wallet verified
lukachi Aug 4, 2026
d8c3f97
test(liquid): drive the whole method, and stop it needing a browser t…
lukachi Aug 4, 2026
968c5e4
refactor(liquid): keep one copy of the three shape tests a manifest n…
lukachi Aug 4, 2026
a04960d
feat(liquid): read every generation of the manifest format as one doc…
lukachi Aug 4, 2026
f0c96b3
refactor(liquid): answer "where are the covenants" in one place
lukachi Aug 4, 2026
9439fd2
feat(liquid): resolve a reference by where it sits, not by what it lo…
lukachi Aug 4, 2026
62e3e0b
feat(liquid): say which parts of a manifest the wallet did not read
lukachi Aug 4, 2026
2cffed8
refactor(liquid): read the manifest through the runtime core, not ele…
lukachi Aug 4, 2026
87108ec
test(liquid): prove the two manifest shapes reach the same transaction
lukachi Aug 4, 2026
1b53f82
feat(liquid): make an output amount an expression rather than one ref…
lukachi Aug 4, 2026
8509fa1
feat(liquid): settle the covenant hashes a manifest works out for itself
lukachi Aug 4, 2026
c4702e2
fix(liquid): ask the signer for the signature a covenant demands
lukachi Aug 4, 2026
a529ac5
feat(liquid): price the transaction before asking anyone to approve it
lukachi Aug 4, 2026
f3bfc61
feat(liquid): declare a value's bytes rather than inferring them
lukachi Aug 4, 2026
23372c1
feat(liquid): derive a covenant address with the leaves it actually has
lukachi Aug 4, 2026
c07f61f
feat(liquid): refuse what this wallet cannot honour, by name
lukachi Aug 4, 2026
474ec67
feat(liquid): refuse a transaction that spends what nobody asked for
lukachi Aug 4, 2026
fb72f6d
feat(liquid): make a value carry where it came from, by type
lukachi Aug 4, 2026
820899d
test(liquid): keep the runtime a function of what it is given
lukachi Aug 4, 2026
64e9aeb
test(liquid): measure this wallet against every published protocol
lukachi Aug 4, 2026
4f84ff6
feat(liquid): build the three constructs the specification put in scope
lukachi Aug 4, 2026
5962f90
test(liquid): pin the addresses, because a wrong one looks exactly li…
lukachi Aug 4, 2026
f24c6d0
test(liquid): pin what the deployed protocol's own contracts compile to
lukachi Aug 4, 2026
a5adf77
feat(liquid): encode the boolean parameters the deployed contracts take
lukachi Aug 4, 2026
d889c13
build: teach CI about the second wasm module
lukachi Aug 4, 2026
fb9ac5e
chore: take the workflow environment upgrade that was sitting in the …
lukachi Aug 4, 2026
8a689e2
feat(liquid): show the contract action, with where each value came from
lukachi Aug 4, 2026
404dd73
test(liquid): measure that the transaction balances, and that the dec…
lukachi Aug 4, 2026
92968e6
refactor(manifest): make the runtime a package, and give it a surface…
lukachi Aug 4, 2026
581128e
feat(liquid): show the address and key a contract action is actually …
lukachi Aug 4, 2026
3401c5d
chore(workflow): take the shipped stop-guard fix and drop the local one
lukachi Aug 4, 2026
1ad57c9
fix(check): typecheck the two thirds of the repository that were neve…
lukachi Aug 4, 2026
2a80318
fix(hooks): let the commit hook see the same three projects the gate …
lukachi Aug 4, 2026
8f9d53e
fix(manifest): refuse a value that cannot be its declared type, befor…
lukachi Aug 4, 2026
9be44e9
fix(liquid): read the contract identity of the account whose page you…
lukachi Aug 4, 2026
6baf6ee
fix(ci): build the wasm the gate actually needs
lukachi Aug 4, 2026
35657b7
fix(liquid): show the confirmation screen, which nothing was ever ask…
lukachi Aug 4, 2026
26f6253
fix(liquid): pay a covenant output its script, not the address it is …
lukachi Aug 4, 2026
a962c6a
fix(liquid): never fund a contract action from a confidential output
lukachi Aug 4, 2026
63bfd31
test(liquid): make every builder substitute as strict as the module i…
lukachi Aug 4, 2026
a46041d
chore(skills): install the client delivery skills
lukachi Aug 5, 2026
24ce978
fix(ci): pin the toolchain the workspace actually declares
lukachi Aug 5, 2026
1e89b81
chore(workflow): wfctl upgrade
lukachi Aug 5, 2026
3edd3a2
fix(liquid): ask the chain for what it serves, not for what it summar…
lukachi Aug 5, 2026
d896c72
fix(rpc): let the wallet's own screens say what went wrong
lukachi Aug 5, 2026
520dc8b
fix(rpc): carry the reason underneath, not just the sentence on top
lukachi Aug 5, 2026
088f347
fix(liquid): let the wallet pay an address that hides nothing
lukachi Aug 5, 2026
6a28ded
fix(liquid): let the wallet see its own money that hides nothing
lukachi Aug 5, 2026
ad36f30
fix(liquid): offer only the explicit outputs this path can actually sign
lukachi Aug 5, 2026
5a96c8d
chore(workflow): apply wfctl upgrade
lukachi Aug 7, 2026
4cb6681
refactor(tx-manifest): put each module behind its own boundary
lukachi Aug 7, 2026
40657e9
fix(settings): tell a person what they can do, not what broke
lukachi Aug 7, 2026
3f7756a
chore(lint): run the fixer the repository already provides
lukachi Aug 7, 2026
e519be5
chore(liquid): give the scan worker the logger it already had
lukachi Aug 7, 2026
eee88be
chore(lint): settle every warning this change owns, by fixing or by s…
lukachi Aug 7, 2026
409b4a9
feat(tx-manifest): give every refusal a name a program can branch on
lukachi Aug 7, 2026
8671e9f
chore(smplx): repoint the submodule at the audited fork
lukachi Aug 7, 2026
efe18c1
chore(smplx): repoint at the fork with the remaining audit tidying
lukachi Aug 7, 2026
9901e2a
chore(smplx): repoint at the fork carrying its own change target
lukachi Aug 7, 2026
09eddaa
chore(smplx): repoint at the merge with upstream dev
lukachi Aug 7, 2026
d1c8302
docs(liquid): the funding limit is this wallet's, not the signing mod…
lukachi Aug 7, 2026
31f3d3e
chore(smplx): repoint at the fork that keeps upstream's compile-once
lukachi Aug 7, 2026
f06c5db
chore(smplx): repoint at the fork whose generated artifacts compile a…
lukachi Aug 7, 2026
ce57878
chore(smplx): repoint at the fork with its CI edits reverted
lukachi Aug 7, 2026
1a8d1b3
feat(liquid): hold the compiler version here, and follow the SDK's re…
lukachi Aug 7, 2026
de760b9
chore(smplx): follow upstream's own branch, and stop following the ab…
lukachi Aug 7, 2026
3408e7f
feat(web): read a manifest by running the reader, not by describing one
lukachi Aug 7, 2026
b3da57b
test(web): assert what the inspector draws, not only what it is handed
lukachi Aug 7, 2026
48082df
fix(web): say how many fields would refuse, not only which one is named
lukachi Aug 7, 2026
28a0b41
feat(tx-manifest): read the protocols as their authors write them now
lukachi Aug 7, 2026
fc1b65f
test(tx-manifest): count the actions nothing refuses, which an estima…
lukachi Aug 7, 2026
4f168a0
chore(workflow): wfctl upgrade
lukachi Aug 7, 2026
ec21d46
chore(workflow): wfctl upgrade — the completion render, the stop guar…
lukachi Aug 9, 2026
30aceef
chore(workflow): upgrade the installed workflow assets
lukachi Aug 12, 2026
d038ff1
chore(workflow): wfctl upgrade — promotion gate, handoff checkpoints,…
lukachi Aug 13, 2026
6417901
test(tx-manifest): the corpus proves both generations and both refere…
lukachi Aug 13, 2026
6b2030a
feat(tx-manifest): deploy a contract instance, in whichever generatio…
lukachi Aug 13, 2026
5289c19
feat(tx-manifest): run the hooks that compute values after inputs res…
lukachi Aug 13, 2026
4e44907
fix(tx-manifest): a top-level contract source decides nothing, so it …
lukachi Aug 13, 2026
3a35dbd
feat(tx-manifest): create the asset an input issues, from the output …
lukachi Aug 13, 2026
3bcf55a
feat(tx-manifest): produce the stated witness value that selects a sp…
lukachi Aug 13, 2026
f134184
feat(tx-manifest): resolve every output's blinding by the order the f…
lukachi Aug 13, 2026
261ce8b
feat(tx-manifest): fill the parameters a protocol already knows the a…
lukachi Aug 13, 2026
262c8aa
feat(tx-manifest): hide the outputs the document wants hidden
lukachi Aug 14, 2026
1fccf99
feat(tx-manifest): land an input or output where the document says it…
lukachi Aug 14, 2026
ef84555
test(tx-manifest): account for every construct the corpus uses, in bo…
lukachi Aug 14, 2026
d1387f9
chore(smplx): branch from upstream dev, where our work was merged
lukachi Aug 14, 2026
d393175
Record the signing module at the commit that exposes issuance
lukachi Aug 14, 2026
096d2ea
Teach the checks what the wallet started doing
lukachi Aug 14, 2026
153e723
Put the asset an action creates into the transaction that gets signed
lukachi Aug 14, 2026
1d6b3fc
Refuse when the module disagrees about the asset it issued
lukachi Aug 14, 2026
0e0e5f9
Move the compiler version to where both readers can reach it
lukachi Aug 14, 2026
eb0ba08
Ask which network, and run the two checks that need its asset
lukachi Aug 14, 2026
272adca
Read the contracts, or say they went unread
lukachi Aug 14, 2026
8b18ee9
feat(tx-manifest): perform a real deployed protocol, end to end
lukachi Aug 20, 2026
65d71c2
chore(smplx): follow the covenant-spend work through its review fixes
lukachi Aug 20, 2026
f913b06
Follow smplx's transaction-level sequence, and refuse what one input …
lukachi Aug 21, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 71 additions & 0 deletions .github/actions/build-smplx-wasm/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
name: Build smplx_wasm
description: >-
Build the smplx_wasm WASM package from the vendored `smplx` git submodule so the
`file:smplx/crates/wasm/pkg` dependency resolves. Like `lwk_wasm/pkg`, it is a wasm-pack
build artifact that is not committed, so it must be produced in CI. The output is cached
by the pinned submodule commit, so the Rust build only runs when the submodule bumps.
Requires the repo to be checked out with `submodules: recursive`.

The fork's build script needs a C compiler with a WebAssembly backend — Apple's system
clang has none, and neither does a bare ubuntu runner without LLVM's clang on PATH, which
is why one is installed rather than assumed. Without it the build fails inside
`secp256k1-sys` and `simplicity-sys` with "unable to create target", which points at the
crates and misleads.

runs:
using: composite
steps:
- name: Resolve pinned smplx commit
id: smplx
shell: bash
run: echo "sha=$(git rev-parse HEAD:smplx)" >> "$GITHUB_OUTPUT"

- name: Restore built smplx pkg
id: pkg-cache
uses: actions/cache@v4
with:
path: smplx/crates/wasm/pkg
key: smplx-wasm-${{ runner.os }}-${{ steps.smplx.outputs.sha }}

# 1.91.0 because `crates/simplex` declares it as the workspace's minimum, and the
# build tree demands it independently: `ar_archive_writer` reached through wasm-pack
# requires 1.88.0. An earlier pin of 1.85.0 built locally on a newer toolchain and
# failed here on the runner's, which is the whole reason to pin rather than inherit.
- name: Install Rust toolchain (1.91.0 + wasm32)
if: steps.pkg-cache.outputs.cache-hit != 'true'
uses: dtolnay/rust-toolchain@master
with:
toolchain: "1.91.0"
targets: wasm32-unknown-unknown

- name: Cache cargo registry + build
if: steps.pkg-cache.outputs.cache-hit != 'true'
uses: actions/cache@v4
with:
path: |
~/.cargo/registry/index
~/.cargo/registry/cache
~/.cargo/git/db
smplx/target
key: cargo-smplx-${{ runner.os }}-${{ steps.smplx.outputs.sha }}
restore-keys: |
cargo-smplx-${{ runner.os }}-

- name: Install wasm-pack
if: steps.pkg-cache.outputs.cache-hit != 'true'
uses: jetli/wasm-pack-action@v0.4.0

# Installed rather than exported: the fork's build script already searches for a clang
# with a WebAssembly backend and fails with a clear message when there is none, so
# putting one on PATH keeps that guard rather than bypassing it.
- name: Install a C compiler with a WebAssembly backend
if: steps.pkg-cache.outputs.cache-hit != 'true'
shell: bash
run: |
sudo apt-get update
sudo apt-get install --no-install-recommends -y clang llvm

- name: Build smplx_wasm
if: steps.pkg-cache.outputs.cache-hit != 'true'
shell: bash
run: smplx/crates/wasm/build.sh
7 changes: 6 additions & 1 deletion .github/workflows/build-extension.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout (with the lwk submodule)
- name: Checkout (with the lwk and smplx submodules)
uses: actions/checkout@v4
with:
submodules: recursive
Expand All @@ -38,6 +38,11 @@ jobs:
with:
profile: ${{ inputs.profile }}

# The manifest runtime compiles Simplicity contracts and signs with them, so the
# extension does not build without this package either.
- name: Build smplx_wasm
uses: ./.github/actions/build-smplx-wasm

- name: Install dependencies
uses: ./.github/actions/install

Expand Down
49 changes: 49 additions & 0 deletions .github/workflows/check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
name: Check

# Runs the same gate a commit runs locally, on every push and pull request. Until this
# existed nothing in CI ran the tests at all: the only workflows were a manual extension
# build and two deploys, so 456 tests protected nothing that could block a merge.
on:
push:
branches: ["**"]
pull_request:

permissions:
contents: read

jobs:
check:
runs-on: ubuntu-latest
steps:
# Recursive, and both wasm packages are built before anything else runs. A leaner
# job was tried and does not work: on a checkout without them `bun install` reports
# "Failed to install 2 packages", typechecking fails with eleven errors about
# `lwk_wasm` and `smplx-wasm` having no declarations, and three test files fail
# outright on `Cannot find module 'smplx-wasm/smplx_wasm_bg.js'` — they drive the
# real module rather than a substitute, which is the point of them.
- name: Checkout (with the lwk and smplx submodules)
uses: actions/checkout@v4
with:
submodules: recursive

- name: Setup Bun
uses: ./.github/actions/setup-bun

# dev rather than release: this job checks code, and an unoptimised wasm builds
# faster. The release profile belongs to the build workflow, which ships the result.
- name: Build lwk_wasm
uses: ./.github/actions/build-lwk-wasm
with:
profile: dev

- name: Build smplx_wasm
uses: ./.github/actions/build-smplx-wasm

- name: Install dependencies
uses: ./.github/actions/install

# typecheck across apps/extension, packages/ and apps/web, then lint, format and
# the test suite. The three projects are separate deliberately — see the comment in
# lefthook.yml for why one `tsc --noEmit` never covered them.
- name: Check
run: bun run check
4 changes: 4 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,7 @@
path = lwk
url = https://github.com/lukachi/lwk.git
branch = humid/esplora-backend-config
[submodule "smplx"]
path = smplx
url = https://github.com/lukachi/smplx.git
branch = humid/wasm-issuance
2 changes: 2 additions & 0 deletions .oxfmtrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
"sortTailwindcss": true,
"ignorePatterns": [
"lwk/**",
"smplx/**",
"**/__fixtures__/**",
"AGENTS.md",
"CLAUDE.md",
"README.md",
Expand Down
1 change: 1 addition & 0 deletions .oxlintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
},
"ignorePatterns": [
"lwk/**",
"smplx/**",
"dist/**",
"build/**",
"node_modules/**",
Expand Down
35 changes: 35 additions & 0 deletions apps/extension/src/background.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import { createAccountRegistry } from "@/core/accounts/application/account-registry";
import type { AccountModelState } from "@/core/accounts/application/account-registry/model/account-model";
import type { AccountGroupId } from "@/core/accounts/application/account-registry/model/identifiers";
import type {
ActivityPage,
EstimateMaxSendInput,
Expand All @@ -16,6 +17,10 @@
import type { Caip25Scopes } from "@/core/caip25";
import { addUnlockedChainRecord } from "@/core/chains/application/chain-store/addChainRecord";
import { getUnlockedChainStoreState } from "@/core/chains/application/chain-store/secureChainStore";
import {
type LiquidContractIdentity,
readLiquidContractIdentity,
} from "@/core/chains/liquid/application/contractIdentity";
import {
buildLiquidDappAccountScope,
resolveAccountGroupIdsForIdentifiers,
Expand Down Expand Up @@ -113,7 +118,7 @@
/** Skip a background refresh if the cached snapshot was synced within this window. */
const BACKGROUND_REFRESH_MIN_INTERVAL_MS = 60_000;

const init = async () => {

Check warning on line 121 in apps/extension/src/background.ts

View workflow job for this annotation

GitHub Actions / check

unicorn(consistent-function-scoping)

Function `disconnectWalletConnect` does not capture any variables from its parent scope

Check warning on line 121 in apps/extension/src/background.ts

View workflow job for this annotation

GitHub Actions / check

unicorn(consistent-function-scoping)

Function `listWalletConnectSessions` does not capture any variables from its parent scope

Check warning on line 121 in apps/extension/src/background.ts

View workflow job for this annotation

GitHub Actions / check

unicorn(consistent-function-scoping)

Function `disconnectWalletConnect` does not capture any variables from its parent scope

Check warning on line 121 in apps/extension/src/background.ts

View workflow job for this annotation

GitHub Actions / check

unicorn(consistent-function-scoping)

Function `listWalletConnectSessions` does not capture any variables from its parent scope
const { eventBus, messageBus } = setupBackgroundTransport();

// Capture the event bus so the wallet-event broadcaster can push provider events (accountsChanged,
Expand Down Expand Up @@ -275,6 +280,35 @@
const getReceiveAddress = async (): Promise<ReceiveAddress> =>
liquidChainGroup.accountRuntime.getReceiveAddress((await resolveSelectedLiquidAccount()).input);

// The address and key contract actions are signed with, for the selected account. Not
// the same as the receive address above: the contract SDK signs with one key at a fixed
// path and returns change to that key's own unblinded address, so a covenant action can
// only spend what sits there. Reading it is what makes that limit visible.
const readContractIdentity = async (
accountGroupId?: AccountGroupId,
): Promise<LiquidContractIdentity> => {
const { input } = await resolveSelectedLiquidAccount();

// The settings page is per-account, and the account it shows is not necessarily the
// selected one. Reading the selected account's identity there would put one
// account's address and key on another account's screen, with nothing to say so —
// and the values are what someone then funds and locks a covenant to.
const group =
accountGroupId === undefined
? undefined
: input.keyManagerState.accountModel.accountGroups[accountGroupId];

if (accountGroupId !== undefined && !group) {
throw new Error(`No account group ${accountGroupId}.`);
}

return readLiquidContractIdentity({
accountGroupIndex: group ? (group.groupIndex ?? 0) : input.accountGroupIndex,
chain: input.chain,
keyManagerState: input.keyManagerState,
});
};

// In-extension send: preview then execute against the SELECTED account (resolved exactly like
// getReceiveAddress). Both call the chain group's runtime, which calls the same backend fns the
// dapp path uses — but WITHOUT the dapp confirmation popup, because the popup's own review screen
Expand Down Expand Up @@ -392,7 +426,7 @@
if (!group) continue;

accountIds.push(
await liquidChainGroup.accountRuntime.resolveAccountIdentifier({

Check warning on line 429 in apps/extension/src/background.ts

View workflow job for this annotation

GitHub Actions / check

eslint(no-await-in-loop)

Unexpected `await` inside a loop.

Check warning on line 429 in apps/extension/src/background.ts

View workflow job for this annotation

GitHub Actions / check

eslint(no-await-in-loop)

Unexpected `await` inside a loop.
accountGroupId: group.id,
accountGroupIndex: group.groupIndex ?? 0,
chain,
Expand Down Expand Up @@ -580,6 +614,7 @@
getActivity,
getPortfolio,
getReceiveAddress,
readContractIdentity,
inspectTransfer,
purgeAccountPortfolio,
purgeAccountWalletConnectSessions,
Expand Down
14 changes: 14 additions & 0 deletions apps/extension/src/bun-test-env.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/// <reference types="bun-types" />

// Makes `bun:test` resolvable to `tsc`, which the test files import from.
//
// `@types/bun` re-exports `bun-types` and is supposed to be picked up automatically,
// but it is not under this project's configuration, so the reference is stated once
// here rather than repeated at the top of every test file — the same arrangement
// `vite-env.d.ts` already uses for Vite's ambient types.
//
// Side effect worth knowing: this also makes Bun's globals visible to application
// code, which does not run under Bun. Reach for a browser or extension API there,
// not `Bun.*`.

export {};
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import type { LiquidWalletBackend } from "../../application/backends/LiquidWalletBackend";
import { getWalletActivityForAsset } from "./wallet/getActivity";
import { getWalletBalanceForAsset } from "./wallet/getBalance";
import { getWalletReceiveAddress } from "./wallet/getReceiveAddress";
import { getWalletUtxosForAsset } from "./wallet/getUTXOs";
import { getWalletReceiveAddress, getWalletSigningAddress } from "./wallet/getReceiveAddress";
import { getExplicitWalletUtxosForAsset, getWalletUtxosForAsset } from "./wallet/getUTXOs";
import { getWalletDescriptorEntries } from "./wallet/getWalletDescriptor";
import { readChainTipHeight } from "./wallet/readChainTipHeight";
import { createLwkLiquidAccount } from "./wallet/resolveAccount";
import { estimateMaxSend, inspectTransfer, sendTransfer } from "./wallet/sendTransfer";
import { inspectMessageSigning, signMessage } from "./wallet/signMessage";
Expand All @@ -16,7 +17,10 @@ export function createLwkWalletBackend(): LiquidWalletBackend {
getActivity: getWalletActivityForAsset,
getBalance: getWalletBalanceForAsset,
getReceiveAddress: getWalletReceiveAddress,
getSigningAddress: getWalletSigningAddress,
getDescriptorEntries: getWalletDescriptorEntries,
getExplicitUtxos: getExplicitWalletUtxosForAsset,
getTipHeight: readChainTipHeight,
getUtxos: getWalletUtxosForAsset,
inspectMessageSigning,
inspectTransfer,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { SyncWorkerClient } from "./createWorkerScanClient";
import {
broadcastPset as runBroadcastPset,
broadcastTransaction as runBroadcastTransaction,
readActivity as runReadActivity,
scanAndRead as runScanAndRead,
scanFresh as runScanFresh,
Expand All @@ -20,6 +21,9 @@ export function createInlineScanClient(): SyncWorkerClient {
async broadcast(input) {
return { txid: await runBroadcastPset({ ...input, id: (seq += 1) }) };
},
async broadcastTransaction(input) {
return { txid: await runBroadcastTransaction({ ...input, id: (seq += 1) }) };
},
async readActivity(input) {
return runReadActivity({ ...input, id: (seq += 1) });
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import browser from "webextension-polyfill";

import type {
BroadcastInput,
BroadcastTxInput,
ReadActivityInput,
ScanInput,
SyncWorkerClient,
Expand Down Expand Up @@ -52,6 +53,7 @@ async function ensureOffscreenDocument(offscreen: ChromeOffscreenApi): Promise<v
/** A scan/read/broadcast request payload for the offscreen document (the target is added on send). */
type OffscreenRequestPayload =
| { input: BroadcastInput; op: "broadcast" }
| { input: BroadcastTxInput; op: "broadcastTransaction" }
| { input: ScanInput; op: "scan" | "scanAndRead" }
| { input: ReadActivityInput; op: "readActivity" };

Expand All @@ -75,6 +77,17 @@ async function requestScan(payload: OffscreenRequestPayload): Promise<OffscreenS
*/
export function createOffscreenScanClient(): SyncWorkerClient {
return {
async broadcastTransaction(input) {
const response = await requestScan({ input, op: "broadcastTransaction" });

if (!response.ok) throw new Error(response.error);

if (response.op !== "broadcastTransaction") {
throw new Error("Unexpected offscreen scan response.");
}

return { txid: response.txid };
},
async broadcast(input) {
const response = await requestScan({ input, op: "broadcast" });

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,14 @@
export type BroadcastInput = { chain: ScanInput["chain"]; psetBase64: string };
export type BroadcastResult = { txid: string };

/**
* Inputs to broadcast an already-signed, consensus-encoded transaction.
*
* The manifest path produces one of these rather than a PSET: smplx blinds, signs and
* finalises internally and hands back a finished transaction.
*/
export type BroadcastTxInput = { chain: ScanInput["chain"]; txHex: string };

/** Inputs to read one asset's activity page from the worker's cached wollet. */
export type ReadActivityInput = ScanInput & {
cursor: string | null;
Expand All @@ -33,6 +41,7 @@
/** A promise-per-request handle to a scan backend (a dedicated worker, offscreen, or inline). */
export type SyncWorkerClient = {
broadcast: (input: BroadcastInput) => Promise<BroadcastResult>;
broadcastTransaction: (input: BroadcastTxInput) => Promise<BroadcastResult>;
readActivity: (input: ReadActivityInput) => Promise<ActivityPageResult>;
scan: (input: ScanInput) => Promise<ScanResult>;
scanAndRead: (input: ScanInput) => Promise<ScanAndReadResult>;
Expand Down Expand Up @@ -90,11 +99,18 @@

return new Promise<SuccessResponse>((resolve, reject) => {
pending.set(id, { reject, resolve });
worker.postMessage({ ...input, id });

Check warning on line 102 in apps/extension/src/core/chains/liquid/adapters/lwk/sync-worker/createWorkerScanClient.ts

View workflow job for this annotation

GitHub Actions / check

unicorn(require-post-message-target-origin)

Missing the `targetOrigin` argument.

Check warning on line 102 in apps/extension/src/core/chains/liquid/adapters/lwk/sync-worker/createWorkerScanClient.ts

View workflow job for this annotation

GitHub Actions / check

unicorn(require-post-message-target-origin)

Missing the `targetOrigin` argument.
});
}

return {
broadcastTransaction() {
// Same reason as `broadcast` below: LWK's Esplora client needs a `window` this
// context does not have.
return Promise.reject(
new Error("The dedicated worker cannot broadcast; use the offscreen or inline client."),
);
},
broadcast() {
// LWK can't run in a dedicated Worker (Esplora's async retry/sleep needs a `window` a
// Worker lacks), so this path never broadcasts — the offscreen/inline clients do. Present
Expand Down
Loading
Loading