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
111 changes: 81 additions & 30 deletions crates/miden-standards/asm/standards/notes/swap.masm
Original file line number Diff line number Diff line change
@@ -1,28 +1,58 @@
use miden::protocol::active_note
use miden::protocol::asset
use miden::protocol::output_note
use miden::protocol::note::NOTE_TYPE_PRIVATE
use miden::standards::notes::p2id
use miden::standards::wallets::basic->wallet

# CONSTANTS
# =================================================================================================

const SWAP_NOTE_NUM_STORAGE_ITEMS=14

const SWAP_NOTE_NUM_STORAGE_ITEMS=16

# Note storage layout (16 felts, loaded at STORAGE_PTR by get_storage):
# - requested_asset_key [0..3]
# - requested_asset_value [4..7]
# - payback_recipient [8..11] (private mode only; zero in public mode)
# - payback_note_type [12]
# - payback_tag [13]
# - payback_target_prefix [14] (public mode only; zero in private mode)
# - payback_target_suffix [15] (public mode only; zero in private mode)
#
# In private mode, the recipient digest and tag are precomputed off-chain by the creator and
# embedded as opaque values; the consumer of the SWAP cannot learn who the payback targets from
# storage alone. In public mode, the recipient must be reconstructible by any consumer, so the
# payback target account id is embedded in plaintext and the MASM derives the recipient at
# consume time. The payback tag is stored explicitly in both modes; the creator is responsible
# for picking a tag that targets the payback receiver in public mode.
#
# The payback target id could be derived from `active_note::get_sender` since today the target
# equals the SWAP sender, but it's stored explicitly because this slot is meant to represent an
# arbitrary payback target (not necessarily the creator) in a future iteration.
const STORAGE_PTR=0
const REQUESTED_ASSET_PTR=0
const PAYBACK_RECIPIENT_PTR=8
const PAYBACK_NOTE_TYPE_PTR=12
const PAYBACK_NOTE_TAG_PTR=13
const PAYBACK_TAG_PTR=13
const PAYBACK_TARGET_PREFIX_PTR=14
const PAYBACK_TARGET_SUFFIX_PTR=15

# Storage is exactly 16 felts = 4 words, so the asset region starts right after.
const ASSET_PTR=16

# ERRORS
# ERRORS
# =================================================================================================

const ERR_SWAP_UNEXPECTED_NUMBER_OF_STORAGE_ITEMS="SWAP script expects exactly 14 note storage items"
const ERR_SWAP_UNEXPECTED_NUMBER_OF_STORAGE_ITEMS="SWAP script expects exactly 16 note storage items"

const ERR_SWAP_WRONG_NUMBER_OF_ASSETS="SWAP script requires exactly 1 note asset"

#! Swap script: adds an asset from the note into consumers account and
#! creates a note consumable by note issuer containing requested asset.
#! Swap script: adds the offered asset from the note into the consumer's account and creates a
#! P2ID payback note addressed to the creator carrying the requested asset.
#!
#! The payback note type (selected by the creator) determines how the payback recipient is
#! derived: private uses an opaque precomputed digest, public derives it from the payback target
#! account id stored in plaintext.
#!
#! Requires that the account exposes:
#! - miden::standards::wallets::basic::receive_asset procedure.
Expand All @@ -31,47 +61,68 @@ const ERR_SWAP_WRONG_NUMBER_OF_ASSETS="SWAP script requires exactly 1 note asset
#! Inputs: [ARGS]
#! Outputs: []
#!
#! Note storage is assumed to be as follows:
#! - REQUESTED_ASSET_KEY
#! - REQUESTED_ASSET_VALUE
#! - PAYBACK_RECIPIENT
#! - payback_note_type
#! - payback_note_tag
#!
#! Panics if:
#! - account does not expose miden::standards::wallets::basic::receive_asset procedure.
#! - account does not expose miden::standards::wallets::basic::move_asset_to_note procedure.
#! - account does not expose the required wallet procedures.
#! - account vault does not contain the requested asset.
#! - adding a fungible asset would result in amount overflow, i.e., the total amount would be
#! greater than 2^63.
@note_script
pub proc main
# dropping note args
# drop note args
dropw
# => []

# --- create a payback note with the requested asset ----------------

# store note storage into memory starting at address 0
push.0 exec.active_note::get_storage
# store note storage into memory starting at STORAGE_PTR
push.STORAGE_PTR exec.active_note::get_storage
# => [num_storage_items]

# check number of storage items
eq.SWAP_NOTE_NUM_STORAGE_ITEMS assert.err=ERR_SWAP_UNEXPECTED_NUMBER_OF_STORAGE_ITEMS
# => []

padw mem_loadw_le.PAYBACK_RECIPIENT_PTR
# => [PAYBACK_NOTE_RECIPIENT]
# --- create payback P2ID note

# load payback P2ID details
# Branch on payback note type.
mem_load.PAYBACK_NOTE_TYPE_PTR
mem_load.PAYBACK_NOTE_TAG_PTR
# => [tag, note_type, PAYBACK_NOTE_RECIPIENT]

# create payback P2ID note
exec.output_note::create
eq.NOTE_TYPE_PRIVATE
# => [is_private]

if.true
# --- PRIVATE PAYBACK ---
# Load the precomputed payback recipient (4 felts, word-aligned).
padw mem_loadw_le.PAYBACK_RECIPIENT_PTR
# => [PAYBACK_RECIPIENT]

mem_load.PAYBACK_NOTE_TYPE_PTR
mem_load.PAYBACK_TAG_PTR
# => [tag, note_type, PAYBACK_RECIPIENT]

exec.output_note::create
# => [note_idx]
else
# --- PUBLIC PAYBACK ---
# Derive P2ID serial = SWAP_SERIAL with the least significant element +1.
exec.active_note::get_serial_number
add.1
# => [s0', s1, s2, s3]

mem_load.PAYBACK_NOTE_TYPE_PTR
mem_load.PAYBACK_TAG_PTR
# => [tag, note_type, s0', s1, s2, s3]

# Load payback target id (prefix then suffix) to match `p2id::new`'s signature
# [target_id_suffix, target_id_prefix, tag, note_type, SERIAL_NUM].
mem_load.PAYBACK_TARGET_PREFIX_PTR
mem_load.PAYBACK_TARGET_SUFFIX_PTR
# => [target_suffix, target_prefix, tag, note_type, s0', s1, s2, s3]

exec.p2id::new
# => [note_idx]
end
# => [note_idx]

# --- move requested asset into the payback P2ID note

padw push.0.0.0 movup.7
# => [note_idx, pad(7)]

Expand All @@ -85,7 +136,7 @@ pub proc main
dropw dropw
# => [pad(8)]

# --- move assets from the SWAP note into the account -------------------------
# --- move offered asset from the SWAP note into the consumer's account

# store the number of note assets to memory starting at address ASSET_PTR
push.ASSET_PTR exec.active_note::get_assets
Expand Down
2 changes: 1 addition & 1 deletion crates/miden-standards/src/note/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ mod pswap;
pub use pswap::{PswapNote, PswapNoteStorage};

mod swap;
pub use swap::{SwapNote, SwapNoteStorage};
pub use swap::{SwapNote, SwapNoteStorage, SwapPayback, payback_serial_from_swap};

mod network_account_target;
pub use network_account_target::{NetworkAccountTarget, NetworkAccountTargetError};
Expand Down
Loading
Loading