Skip to content

Commit 2f4da2b

Browse files
authored
Merge pull request #468 from Buildwithlevo/feature/required-memo-hash-for-invoices
feat: payment memo validation and invoice tag registry (#451, #452)
2 parents 48cb59d + 19b43f5 commit 2f4da2b

3 files changed

Lines changed: 61 additions & 0 deletions

File tree

contracts/split/src/error.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ pub enum ContractError {
4545
DuplicatePayment = 29,
4646
/// Issue #434: Invoice group member expired unfunded; group rollback triggered.
4747
GroupMemberExpired = 30,
48+
/// Issue #451: payer-provided memo does not match the required memo hash.
49+
MemoMismatch = 31,
4850
/// Issue #439: Creator is in cooldown after cancelling an invoice.
4951
CreatorCooldownActive = 31,
5052
}

contracts/split/src/lib.rs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -592,6 +592,16 @@ fn payer_payment_timestamps_key(invoice_id: u64, payer: &Address) -> (Symbol, u6
592592
(symbol_short!("pay_ts"), invoice_id, payer.clone())
593593
}
594594

595+
/// Issue #451: per-invoice required memo hash.
596+
fn required_memo_hash_key(invoice_id: u64) -> (Symbol, u64) {
597+
(symbol_short!("req_memo"), invoice_id)
598+
}
599+
600+
/// Issue #452: per-invoice tags.
601+
fn invoice_tags_key(invoice_id: u64) -> (Symbol, u64) {
602+
(symbol_short!("inv_tags"), invoice_id)
603+
}
604+
595605
fn invoice_rate_limit_window_key() -> Symbol {
596606
symbol_short!("inv_rl_w")
597607
}
@@ -6548,6 +6558,50 @@ impl SplitContract {
65486558
events::payment_matched(&env, memo, memo, &payer);
65496559
}
65506560

6561+
/// Issue #451: Creator sets a required payment memo hash on an invoice.
6562+
pub fn set_invoice_memo(env: Env, creator: Address, invoice_id: u64, memo_hash: BytesN<32>) {
6563+
require_not_paused(&env);
6564+
creator.require_auth();
6565+
let invoice = load_invoice(&env, invoice_id);
6566+
assert!(invoice.creator == creator, "only creator can set memo");
6567+
assert!(invoice.status == InvoiceStatus::Pending, "invoice is not pending");
6568+
env.storage().persistent().set(&required_memo_hash_key(invoice_id), &memo_hash);
6569+
}
6570+
6571+
/// Issue #451: Pay an invoice with memo validation.
6572+
pub fn pay_with_validated_memo(
6573+
env: Env,
6574+
payer: Address,
6575+
invoice_id: u64,
6576+
payment_memo: BytesN<32>,
6577+
amount: i128,
6578+
nonce: u64,
6579+
auto_convert: bool,
6580+
via: Option<Address>,
6581+
) {
6582+
require_not_paused(&env);
6583+
payer.require_auth();
6584+
if let Some(required) = env.storage().persistent().get::<_, BytesN<32>>(&required_memo_hash_key(invoice_id)) {
6585+
assert!(payment_memo == required, "MemoMismatch");
6586+
}
6587+
Self::_pay(&env, &payer, invoice_id, amount, nonce, auto_convert, via, None, false);
6588+
events::payment_matched(&env, invoice_id, invoice_id, &payer);
6589+
}
6590+
6591+
/// Issue #452: Set tags on an invoice for searchable categorisation.
6592+
pub fn set_invoice_tags(env: Env, creator: Address, invoice_id: u64, tags: Vec<String>) {
6593+
require_not_paused(&env);
6594+
creator.require_auth();
6595+
let invoice = load_invoice(&env, invoice_id);
6596+
assert!(invoice.creator == creator, "only creator can set tags");
6597+
env.storage().persistent().set(&invoice_tags_key(invoice_id), &tags);
6598+
}
6599+
6600+
/// Issue #452: Get tags for an invoice.
6601+
pub fn get_invoice_tags(env: Env, invoice_id: u64) -> Vec<String> {
6602+
env.storage().persistent().get(&invoice_tags_key(invoice_id)).unwrap_or_else(|| Vec::new(&env))
6603+
}
6604+
65516605
/// Claim vesting cliff share after cliff timestamp has passed (issue #27).
65526606
///
65536607
/// Requires that the invoice status is Released and the cliff (if set) has passed.

contracts/split/src/storage_keys.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,3 +275,8 @@ pub fn upgrade_freeze_key() -> Symbol { symbol_short!("upg_frz") }
275275

276276
/// Contract upgrade checkpoint hash — instance storage.
277277
pub fn upgrade_checkpoint_key() -> Symbol { symbol_short!("upg_ckpt") }
278+
279+
/// Issue #451: per-invoice required memo hash — persistent storage.
280+
pub fn required_memo_hash_key(invoice_id: u64) -> (Symbol, u64) { (symbol_short!("req_memo"), invoice_id) }
281+
/// Issue #452: per-invoice tags — persistent storage.
282+
pub fn invoice_tags_key(invoice_id: u64) -> (Symbol, u64) { (symbol_short!("inv_tags"), invoice_id) }

0 commit comments

Comments
 (0)