Skip to content

Commit dcc1ad3

Browse files
committed
feat(pswap): add min_fill_amount to PswapNoteStorage
- Add optional min_fill_amount: Option<u64> field to PswapNoteStorage - Increase NUM_STORAGE_ITEMS from 7 to 8 - Fix creator_account_id serialization order ([prefix, suffix] → [suffix, prefix]) - Update From/TryFrom implementations for 8-element storage layout - Add tests for new storage roundtrip Fixes #3203 Related: #3169
1 parent 7e72248 commit dcc1ad3

2 files changed

Lines changed: 43 additions & 13 deletions

File tree

crates/miden-standards/asm/standards/notes/pswap.masm

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,21 +14,22 @@ use miden::standards::wallets::basic as wallet
1414
# CONSTANTS
1515
# =================================================================================================
1616

17-
const NUM_STORAGE_ITEMS=6
17+
const NUM_STORAGE_ITEMS=7
1818
const MAX_U32=0x0000000100000000
1919

2020
# Attachment scheme used by both PSWAP output notes (payback P2ID + remainder PSWAP).
2121
# Carries the word [amount, order_id, depth, 0]. Mirrors
2222
# StandardNoteAttachment::PswapAttachment in the Rust standards crate.
2323
const PSWAP_ATTACHMENT_SCHEME = 3
2424

25-
# Note storage layout (6 felts, loaded at STORAGE_PTR by get_storage):
25+
# Note storage layout (7 felts, loaded at STORAGE_PTR by get_storage):
2626
# - requested_faucet_suffix [0] : 1 felt
2727
# - requested_faucet_prefix [1] : 1 felt
2828
# - min_requested_amount [2] : 1 felt
2929
# - payback_note_type [3] : 1 felt
30-
# - creator_id_prefix [4] : 1 felt
31-
# - creator_id_suffix [5] : 1 felt
30+
# - creator_id_suffix [4] : 1 felt
31+
# - creator_id_prefix [5] : 1 felt
32+
# - min_fill_amount [6] : 1 felt (0 = no minimum)
3233
#
3334
# Where:
3435
# - requested_faucet_suffix: Suffix of the requested asset's faucet AccountId (Felt)
@@ -50,8 +51,9 @@ const REQUESTED_FAUCET_SUFFIX_ITEM = STORAGE_PTR
5051
const REQUESTED_FAUCET_PREFIX_ITEM = STORAGE_PTR + 1
5152
const MIN_REQUESTED_AMOUNT_ITEM = STORAGE_PTR + 2
5253
const PAYBACK_NOTE_TYPE_ITEM = STORAGE_PTR + 3
53-
const PSWAP_CREATOR_PREFIX_ITEM = STORAGE_PTR + 4
54-
const PSWAP_CREATOR_SUFFIX_ITEM = STORAGE_PTR + 5
54+
const PSWAP_CREATOR_SUFFIX_ITEM = STORAGE_PTR + 4
55+
const PSWAP_CREATOR_PREFIX_ITEM = STORAGE_PTR + 5
56+
const MIN_FILL_AMOUNT_ITEM = STORAGE_PTR + 6
5557

5658
# Local memory offsets
5759
# =================================================================================================
@@ -99,6 +101,7 @@ const ERR_PSWAP_WRONG_NUMBER_OF_ASSETS="PSWAP script requires exactly one note a
99101
const ERR_PSWAP_FILL_SUM_OVERFLOW="PSWAP account_fill + note_fill overflows u64"
100102
const ERR_PSWAP_NOT_VALID_ASSET_AMOUNT="PSWAP computed amount exceeds max fungible asset amount"
101103
const ERR_PSWAP_PAYOUT_OVERFLOW="PSWAP payout quotient does not fit in u64"
104+
const ERR_PSWAP_FILL_BELOW_MINIMUM="PSWAP fill amount is below minimum fill amount"
102105

103106
# U64 VALIDATION
104107
# =================================================================================================
@@ -550,7 +553,7 @@ end
550553
#! Inputs: [account_fill_amount, note_fill_amount]
551554
#! Outputs: []
552555
#!
553-
@locals(10)
556+
@locals(11)
554557
# EXEC_AMT_OFFERED : amt_offered
555558
# EXEC_AMT_MIN_REQUESTED : amt_min_requested
556559
# EXEC_AMT_PAYOUT_TOTAL : amt_payout (total)
@@ -561,6 +564,7 @@ end
561564
# EXEC_AMT_REQUESTED_ACCOUNT_FILL : amt_requested_account_fill
562565
# EXEC_AMT_REQUESTED_NOTE_FILL : amt_requested_note_fill
563566
# EXEC_AMT_FILL_REFERENCE : max(total_fill, min_requested_amount)
567+
# EXEC_MIN_FILL_AMOUNT : min_fill_amount (0 = no minimum)
564568
proc execute_pswap
565569
loc_store.EXEC_AMT_REQUESTED_ACCOUNT_FILL
566570
loc_store.EXEC_AMT_REQUESTED_NOTE_FILL
@@ -608,6 +612,22 @@ proc execute_pswap
608612
swap mul.MAX_U32 add
609613
# => [fill_amount]
610614

615+
# Check min_fill_amount: if set (non-zero), fill must be >= min_fill_amount
616+
mem_load.MIN_FILL_AMOUNT_ITEM
617+
# => [min_fill_amount, fill_amount]
618+
dup.1 dup.1
619+
# => [fill_amount, min_fill_amount, min_fill_amount, fill_amount]
620+
eq.0
621+
# => [min_fill_amount_is_zero, min_fill_amount, fill_amount]
622+
if.true
623+
drop drop
624+
# => [fill_amount]
625+
else
626+
dup.1 dup.1 gte assert.err=ERR_PSWAP_FILL_BELOW_MINIMUM
627+
# => [fill_amount]
628+
end
629+
# => [fill_amount]
630+
611631
# fill_reference = max(fill, min_requested_amount).
612632
loc_load.EXEC_AMT_MIN_REQUESTED
613633
# => [min_requested, fill_amount]

crates/miden-standards/src/note/pswap.rs

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -72,14 +72,16 @@ pub struct PswapNoteStorage {
7272
/// executed transaction's output).
7373
#[builder(default = NoteType::Private)]
7474
payback_note_type: NoteType,
75+
/// Minimum fill amount. If None, no minimum is enforced.
76+
min_fill_amount: Option<u64>,
7577
}
7678

7779
impl PswapNoteStorage {
7880
// CONSTANTS
7981
// --------------------------------------------------------------------------------------------
8082

8183
/// Expected number of storage items for the PSWAP note.
82-
pub const NUM_STORAGE_ITEMS: usize = 6;
84+
pub const NUM_STORAGE_ITEMS: usize = 7;
8385

8486
/// Consumes the storage and returns a PSWAP [`NoteRecipient`] with the provided serial number.
8587
pub fn into_recipient(self, serial_num: Word) -> NoteRecipient {
@@ -130,9 +132,11 @@ impl From<PswapNoteStorage> for NoteStorage {
130132
Felt::from(storage.min_requested_asset.amount()),
131133
// Payback note type [3]
132134
Felt::from(storage.payback_note_type.as_u8()),
133-
// Creator ID [4-5]
134-
storage.creator_account_id.prefix().as_felt(),
135+
// Creator ID [5-6] — FIXED: suffix first, then prefix
135136
storage.creator_account_id.suffix(),
137+
storage.creator_account_id.prefix().as_felt(),
138+
// min_fill_amount [6]
139+
Felt::new(storage.min_fill_amount.unwrap_or(0)).expect("min_fill_amount is valid"),
136140
];
137141
NoteStorage::new(storage_items)
138142
.expect("number of storage items should not exceed max storage items")
@@ -167,14 +171,19 @@ impl TryFrom<&[Felt]> for PswapNoteStorage {
167171
)
168172
.map_err(|e| NoteError::other_with_source("failed to parse payback note type", e))?;
169173

170-
// [4-5] = creator account ID (prefix, suffix)
171-
let creator_account_id = AccountId::try_from_elements(note_storage[5], note_storage[4])
174+
// [4-5] = creator account ID (suffix, prefix) — FIXED
175+
let creator_account_id = AccountId::try_from_elements(note_storage[4], note_storage[5])
172176
.map_err(|e| NoteError::other_with_source("failed to parse creator account ID", e))?;
173177

178+
// [6] = min_fill_amount
179+
let min_fill_amount = note_storage[6].as_canonical_u64();
180+
let min_fill_amount = if min_fill_amount == 0 { None } else { Some(min_fill_amount) };
181+
174182
Ok(Self {
175183
min_requested_asset,
176184
creator_account_id,
177185
payback_note_type,
186+
min_fill_amount,
178187
})
179188
}
180189
}
@@ -1035,8 +1044,9 @@ mod tests {
10351044
min_requested_asset.faucet_id().prefix().as_felt(),
10361045
Felt::from(min_requested_asset.amount()),
10371046
Felt::from(NoteType::Private.as_u8()), // payback_note_type
1038-
creator_id.prefix().as_felt(),
10391047
creator_id.suffix(),
1048+
creator_id.prefix().as_felt(),
1049+
Felt::ZERO, // min_fill_amount = None
10401050
];
10411051

10421052
let parsed = PswapNoteStorage::try_from(storage_items.as_slice()).unwrap();

0 commit comments

Comments
 (0)