Skip to content

feat(web-client): support non-fungible faucets and assets in WebClient - #473

Open
ricomateo wants to merge 16 commits into
nextfrom
ricomateo-nfa-wasm-bindings
Open

ricomateo wants to merge 16 commits into
nextfrom
ricomateo-nfa-wasm-bindings

Conversation

@ricomateo

@ricomateo ricomateo commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

This is the first of three stacked PRs that adds support for NFTs, as a follow up for 0xMiden/rust-sdk#2688.

Introduces the following changes

  • Pins the miden-client version to the branch that added support for NFTs
  • Adds WebClient functions to create non-fungible faucets and assets.
  • Adds request builders to mint, send and burn NFTs.
  • Adds NFT getters to AccountVaultDelta, AccountVaultPatch and AccountProof.
  • Removes the nonFungible argument from newFaucet, which now always creates a fungible faucet.
  • Adds tests for the new functions.

@ricomateo
ricomateo added this pull request to stack #476 October 14, 2026 15:53
@ricomateo
ricomateo marked this pull request as ready for review October 15, 2026 12:46
@ricomateo
ricomateo requested a review from igamigo October 15, 2026 12:52
@ricomateo ricomateo changed the title feat: non fungible assets wasm bindings feat(web-client): support non-fungible faucets and assets in WebClient Oct 15, 2026
@github-actions

Copy link
Copy Markdown

➖ WASM proving — No significant change (largest prove / consume / ecdsa-k256-keccak +1.03%, floor ±1.90%)

Head 5777a6f
Base 7d9b925 (next)
Method mean of per-rep fastest, 6 reps × 3 warm proves, release / mt, 8 threads on warp-ubuntu-latest-x64-8x — lower is better
Workload 1 benchmark (ms) — prove / consume / ecdsa-k256-keccak
Benchmark Base (ms) Head (ms) Δ Δ %
➖ prove / consume / ecdsa-k256-keccak 2,380.9 2,405.4 +24.5 +1.03%
Methodology and raw samples
  • The timed interval is the proveTransaction call on a client constructed without a web worker. Prover construction, transaction execution, the faucet draw and the production worker round-trip are outside it, so a change confined to any of those does not move this number.
  • The run configuration below — runner, profile, variant, thread count, repetition and prove counts — is reported by the benchmark job, which runs from this pull request's own code. It describes what that job says it did. Every figure and every verdict above is recomputed here from the raw samples.
  • Each benchmark keeps 6 reps × 3 warm proves on warp-ubuntu-latest-x64-8x (8 threads, release / mt). One extra repetition and the first prove of every page run first and are discarded.
  • The reported figure is the mean of each repetition's fastest prove. Within one repetition every prove is bit-identical work, so interference — which only ever adds time — is all that varies, and the repetition's fastest prove is its best observed warm prove — a lower-tail statistic, not a measured "clean" cost, and so blind to a regression that leaves the best case alone (see the mean cross-check). Across repetitions the faucet differs, which shifts the proof-of-work grind, so averaging the per-repetition minima shrinks that lottery — it averages the grind down rather than cancelling it, because each side draws its own.
  • Measured over six calibration runs of identical binaries, this estimator holds a standard deviation of 1.79%, against 2.96% for a global minimum and 5.39% for a plain median.
  • A movement is only called significant when it clears the noise floor and no repetition's paired difference contradicts the direction, with a majority of repetitions positively agreeing. Base and head run interleaved within a repetition, so the pairs saw the same machine; a movement whose repetitions disagree is reported as unresolved rather than as a result. Runs shorter than 6 repetitions are never called significant — the floor is calibrated at that count and does not transfer below it.
  • Base and head are driven one prove at a time, alternating, with the order flipped every prove. Running each side's batch back to back let the second side pay a consistent penalty — measured at +1.19% before this was fixed, which is a bias no number of repetitions removes.
  • A wide gap between the reported figure and the max has two causes and the samples below do not separate them: interference within a repetition, and the proof-of-work grind differing between repetitions. Neither invalidates the comparison — both sides ran interleaved on the same machine — but a gap much wider than usual is worth a second look.
  • Base and head are measured in the same job on the same runner, so runner-to-runner drift cancels out. The base dist may have been built by an earlier run of this workflow and restored from cache — the cache key covers the toolchain and the build commands, so the bytes match what this run would have produced.
  • Δ % = (head − base) / base on that figure. Lower is better for every benchmark in this suite.
  • Every figure above is recomputed here from the per-rep samples in the artifact; the summary statistics the bench script reported alongside them are not used.
  • The ±1.90% threshold is calibrated on warp-ubuntu-latest-x64-8x (2026-08-27): 30 runs of one build against a copy of itself, at 6 repetitions, gave a standard deviation of 0.62% and a largest movement of 1.17%. It is 3σ (1.85%) rounded up, which here is the procedure rather than a deviation from it: the largest movement across those 30 no-change runs stayed below 3σ, so nothing observed argues the tail is heavier than normal. How this is measured.
  • Full machine-readable results are attached to the run as results.json.

Per-rep samples

  prove / consume / ecdsa-k256-keccak
  base rep  1: 2,418.4, 2,587.4, 2,437.3
  base rep  2: 2,403.0, 2,429.9, 2,594.0
  base rep  3: 2,685.7, 2,418.5, 2,557.6
  base rep  4: 2,335.7, 2,350.8, 2,370.7
  base rep  5: 2,458.4, 2,370.8, 2,346.8
  base rep  6: 2,473.6, 2,413.5, 2,363.2
  base median 2,418.4 ms  min 2,335.7  max 2,685.7
  head rep  1: 2,516.7, 2,474.0, 2,726.6
  head rep  2: 2,505.2, 2,496.5, 2,435.4
  head rep  3: 2,423.4, 2,385.7, 2,377.5
  head rep  4: 2,378.0, 2,380.8, 2,357.4
  head rep  5: 2,365.2, 2,414.4, 2,479.6
  head rep  6: 2,468.8, 2,423.0, 2,434.1
  head median 2,428.8 ms  min 2,357.4  max 2,726.6

🔺 slower beyond the noise floor · 🔻 faster beyond the noise floor · ❔ no verdict — see the heading and notes above · ➖ within the noise floor


Generated by bench run 39238365530 for 5777a6f. This comment is informational only and never blocks merge.

@WiktorStarczewski

Copy link
Copy Markdown
Collaborator

The remaining verification is the pinned Rust client contract used by the new NFT API. CI is green for the current head; that source verification remains open, so the merge is held.

/// A BURN note for a public faucet is a network note targeted at that faucet, so a public
/// faucet must be a network account. Private faucets are not affected.
#[js_export(js_name = "newBurnNonFungibleAssetTransactionRequest")]
pub async fn new_burn_non_fungible_asset_transaction_request(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The rustdoc says a public faucet must be a network account, but nothing enforces it. BurnNote attaches a NetworkAccountTarget whenever the faucet ID is public and uses the default tag. Meanwhile accounts.create defaults faucets to storage: "public", and newNonFungibleFaucet builds a regular single-sig faucet. So with default settings, burning moves the NFT out of the holder's vault into a note the network won't execute and the faucet won't pick up through sync.

Comment thread Cargo.toml Outdated
Comment on lines +52 to +54
miden-client = { branch = "ricomateo-nfa-readiness", default-features = false, git = "https://github.com/0xMiden/rust-sdk" }
miden-client-proto = { branch = "ricomateo-nfa-readiness", default-features = false, git = "https://github.com/0xMiden/rust-sdk" }
miden-client-sqlite-store = { branch = "ricomateo-nfa-readiness", default-features = false, git = "https://github.com/0xMiden/rust-sdk" }

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That branch/PR has already been merged, we should point back to next.

Comment thread CHANGELOG.md
### Enhancements

* [FEATURE][web] `WebClient` supports non-fungible assets. Non-fungible faucets can be created with `WebClient.newNonFungibleFaucet`, and `newMintNonFungibleTransactionRequest`, `newSendNonFungibleAssetTransactionRequest` and `newBurnNonFungibleAssetTransactionRequest` build the mint, send and burn requests, respectively. Non-fungible assets can be built with `NonFungibleAsset.fromData` and `new NonFungibleAsset(faucetId, value)`. `NonFungibleFaucetComponent.fromAccount(account)` allows reading a faucet's symbol, name, description, logo URI and contract URI, and `Account.isNonFungibleFaucet()` identifies a non-fungible faucet ([#473](https://github.com/0xMiden/web-sdk/pull/473)).
* [FEATURE][web] `AccountVaultDelta.addedNonFungibleAssets()` / `removedNonFungibleAssets()`, `AccountVaultPatch.updatedNonFungibleAssets()` and `AccountProof.vaultNonFungibleAssets()` return the non-fungible assets next to the existing fungible getters ([rust-sdk#2688](https://github.com/0xMiden/rust-sdk/pull/2688)).

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AGENTS.md asks for both links when an entry depends on a companion rust-sdk PR. We are only linking rust-sdk#2688, we should add #473 as well.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants