Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@
- Fixed the fungible and non-fungible MINT note scripts assuming their `exec` callers provide blank stack slot ([#3668](https://github.com/0xMiden/protocol/pull/3668)).
- [BREAKING] Bounded the multisig approver set to 64 signers, enforced both by `ApproverSet::MAX_APPROVERS` at account creation and by `MAX_NUM_APPROVERS` in the `multisig` and `multisig_smart` `update_signers_and_threshold` procedures ([#3723](https://github.com/0xMiden/protocol/pull/3723)).
- The PSWAP note script now rejects a `PswapAttachment` that does not consist of exactly one word, instead of letting the attachment write past the four locals of `get_current_depth` ([#3761](https://github.com/0xMiden/protocol/pull/3761)).
- Documented that a PSWAP remainder note carries the round's `PswapAttachment` as its only attachment ([#3791](https://github.com/0xMiden/protocol/pull/3791)).

## v0.16.0 (2026-08-17)

Expand Down
9 changes: 9 additions & 0 deletions crates/miden-standards/asm/standards/notes/pswap.masm
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,15 @@ end
#! most significant element of the active note's serial number), creates the output
#! note, sets the attachment, and adds the remaining offered asset.
#!
#! The round's PswapAttachment is the remainder's only attachment: any other attachment of the
#! parent, a `NetworkAccountTarget` included, is intentionally dropped, so a routing target applies
#! to the note it is attached to and not to the order's lineage. Carrying it over would make every
#! remainder a network output note, which the consumer's auth component sponsors before paying the
#! transaction fee (see `fees::create_network_note_sponsorships`): the fill would have to provision
#! the target as a foreign account and fund its sponsorship note, and a consumer that is itself the
#! target could not fill at all, since the kernel forbids a foreign context against the native
#! account.
#!
#! Inputs: [offered_faucet_suffix, offered_faucet_prefix,
#! amt_payout, amt_offered,
#! remaining_requested, note_type, tag]
Expand Down
19 changes: 16 additions & 3 deletions crates/miden-standards/src/note/pswap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,9 @@ impl From<PswapNoteAttachment> for NoteAttachment {
/// fill amounts via note_args) and in network transactions (where note_args default to
/// `[0, 0, 0, 0]`, triggering a full fill). To route a PSWAP note to a network account,
/// set the `attachment` to a [`NetworkAccountTarget`](crate::note::NetworkAccountTarget)
/// via the builder.
/// via the builder. The routing applies to that note alone: a partial fill replaces it with a
/// remainder carrying only the PSWAP lineage attachment, so the leftover order is no longer a
/// network note and has to be filled locally or reclaimed by its creator.
///
/// Fills are priced against the note's initial offered asset.
#[derive(Debug, Clone, bon::Builder)]
Expand Down Expand Up @@ -394,8 +396,8 @@ impl PswapNote {
/// For notes targeting a network account, this may contain a
/// [`NetworkAccountTarget`](crate::note::NetworkAccountTarget) with scheme = 2. For a
/// remainder PSWAP this contains the [`Self::PSWAP_ATTACHMENT_SCHEME`] word
/// `[amt_payout, order_id, depth, 0]`. For an original PSWAP (no prior fill),
/// this is typically empty.
/// `[amt_payout, order_id, depth, 0]` and never a routing target. For an original PSWAP (no
/// prior fill), this is typically empty.
pub fn attachments(&self) -> Option<&NoteAttachment> {
self.attachment.as_ref()
}
Expand Down Expand Up @@ -632,6 +634,9 @@ impl PswapNote {
/// remainder. Both are required because the price formula uses floor division, so one isn't
/// derivable from the other across rounds in general.
///
/// Like the note the script creates, the reconstructed remainder carries `attachment` as its
/// only attachment.
///
/// # Errors
///
/// Returns an error if `attachment.depth() == 0` or if any amount is not a valid asset
Expand Down Expand Up @@ -820,6 +825,14 @@ impl PswapNote {
/// [`Self::PSWAP_ATTACHMENT_SCHEME`]. The remainder must carry this attachment so that
/// when *it* is later consumed as a parent, `get_current_depth` reads the right scheme
/// and increments depth correctly.
///
/// It is also the remainder's only attachment: a
/// [`NetworkAccountTarget`](crate::note::NetworkAccountTarget) on this note is deliberately not
/// copied over, matching the on-chain script. Carrying it over would make every remainder a
/// network output note, which a fee-paying consumer must sponsor - the fill would have to
/// provision the target as a foreign account and fund its sponsorship note, and a consumer that
/// is itself the target could not fill at all, since the kernel forbids a foreign context
/// against the native account.
fn create_remainder_pswap_note(
&self,
consumer_account_id: AccountId,
Expand Down