-
Notifications
You must be signed in to change notification settings - Fork 131
Expand file tree
/
Copy pathtypes.rs
More file actions
1956 lines (1844 loc) · 72.7 KB
/
Copy pathtypes.rs
File metadata and controls
1956 lines (1844 loc) · 72.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
use soroban_sdk::{contracttype, Address, Bytes, BytesN, Env, String, Symbol, Vec};
/// Total basis points representing 100% — ratio vecs must sum to exactly this value.
pub const BASIS_POINTS_TOTAL: u32 = 10_000;
/// (base, quote) asset pair for oracle-priced invoices.
#[contracttype]
#[derive(Clone, Debug)]
pub struct AssetPair {
pub base: Symbol,
pub quote: Symbol,
}
#[contracttype]
#[derive(Clone, Debug, PartialEq)]
pub enum OverflowBehavior {
/// Reject the payment outright. The payer receives an error and the
/// transaction does not credit the invoice.
Reject,
/// Accept the full payment and mark the surplus for refund to the payer
/// at release time.
Refund,
/// Accept the full payment and treat the surplus as a protocol donation;
/// no refund is issued.
Donate,
}
/// Issue #420: creator-configurable behaviour when a payment would push an
/// invoice's `funded` total past its target.
///
/// This is the authority for overfunding decisions in `_pay`. `Cap` — the
/// default, and the value legacy invoices are migrated to — preserves the
/// historical behaviour by delegating to the per-invoice [`OverflowBehavior`]
/// setting, so invoices created before this field existed are unaffected.
///
/// # Relationship
///
/// `OverfundingPolicy` is the *outer* policy selector stored on the invoice.
/// When it is `Cap`, the contract falls back to the per-invoice
/// [`OverflowBehavior`] value to decide the exact outcome. The other two
/// variants (`AcceptAll`, `ReturnSurplus`) bypass `OverflowBehavior` entirely
/// and implement their own semantics directly in `_pay`.
#[contracttype]
#[derive(Clone, Debug, PartialEq)]
pub enum OverfundingPolicy {
/// Preserve legacy behaviour by delegating to the invoice's
/// [`OverflowBehavior`] field.
Cap,
/// Accept the payment in full; `funded` is allowed to exceed the total and
/// the surplus is distributed pro-rata to recipients at release time.
AcceptAll,
/// Accept only the portion that fits under the total and immediately
/// transfer the remainder back to the payer.
ReturnSurplus,
}
#[contracttype]
#[derive(Clone, Debug)]
pub struct CloneOverrides {
pub new_deadline: Option<u64>,
pub new_amounts: Option<Vec<i128>>,
pub new_recipients: Option<Vec<Address>>,
pub new_overflow_behavior: Option<Symbol>,
/// New off-chain metadata hash (IPFS CID / SHA-256) for the cloned invoice.
pub new_metadata_hash: Option<BytesN<32>>,
}
/// Issue: Split rule for a single recipient — evaluated at release time.
#[contracttype]
#[derive(Clone, Debug)]
pub enum SplitRule {
/// Pay this exact amount regardless of the invoice's funded total.
///
/// # Example
///
/// `Fixed(2_500)` always pays out `2_500`, whether `funded` is `2_500`,
/// `10_000`, or anything else.
Fixed(i128),
/// Pay `funded * bps / 10_000` to the recipient, where `bps` is basis
/// points (10_000 = 100%).
///
/// # Example
///
/// `Percentage(3_000)` (30%) on `funded = 10_000` yields
/// `10_000 * 3_000 / 10_000 = 3_000`.
Percentage(u32),
/// Pay `funded * bps / 10_000` only once `funded` strictly exceeds
/// `threshold`; otherwise pay `0`. Encoded as `(threshold, bps)`.
///
/// # Example
///
/// `Tiered(5_000, 2_000)` (20% once past 5_000) on `funded = 8_000`
/// yields `8_000 * 2_000 / 10_000 = 1_600`, because `8_000 > 5_000`.
/// On `funded = 4_000` it yields `0`, because `4_000 <= 5_000`.
Tiered(i128, u32),
}
/// Issue: Action taken by an auto-resolve rule.
#[contracttype]
#[derive(Clone, Debug, PartialEq)]
pub enum ResolveAction {
Release,
Refund,
}
/// Issue: Auto-resolve rule — if funded/total >= min_funded_bps/10_000, execute action.
#[contracttype]
#[derive(Clone, Debug)]
pub struct ResolveRule {
/// Minimum funding threshold in basis points (e.g. 5000 = 50%).
pub min_funded_bps: u32,
pub action: ResolveAction,
}
/// Issue #285: Volume-based fee tier for creators.
#[contracttype]
#[derive(Clone, Debug)]
pub struct FeeTier {
/// Minimum creator lifetime volume threshold to qualify for this tier.
pub volume_threshold: u64,
/// Fee in basis points (e.g. 100 = 1%).
pub fee_bps: u32,
}
/// Issue #409: Rebate tier for high-volume creators.
#[contracttype]
#[derive(Clone, Debug)]
pub struct RebateTier {
pub min_volume: i128,
pub rebate_bps: u32,
}
/// Issue #299: Per-creator analytics aggregator.
#[contracttype]
#[derive(Clone, Debug)]
pub struct CreatorStats {
/// Total number of invoices created.
pub total_invoices: u32,
/// Total amount raised across all invoices.
pub total_raised: u64,
/// Total amount released to recipients.
pub total_released: u64,
/// Total number of unique payers.
pub total_payers: u32,
/// Average funding time in ledgers (running average).
pub avg_funding_time_ledgers: u32,
/// Total number of refunded invoices.
pub total_refunded: u32,
}
/// Issue #: A single (invoice_id, amount) pair for pool_pay.
#[contracttype]
#[derive(Clone, Debug)]
pub struct InvoicePayment {
pub invoice_id: u64,
pub amount: i128,
}
#[contracttype]
#[derive(Clone, Debug)]
pub struct Bid {
pub bidder: Address,
pub amount: i128,
}
// ---------------------------------------------------------------------------
// Invoice status
// ---------------------------------------------------------------------------
/// Status of an invoice lifecycle.
#[contracttype]
#[derive(Clone, Debug, PartialEq)]
pub enum InvoiceStatus {
Pending,
Released,
Refunded,
Expired,
Cancelled,
Disputed,
PartiallyReleased,
/// Alias for Released used as the parent-finalisation gate (#522).
/// An invoice is considered Finalised once it has been Released.
Finalised,
/// Soft-deleted invoice — tombstone record preserved for audit trail.
Deleted,
/// Issue #564: Payout in progress — intermediate state during release_funds.
PayoutInProgress,
}
// ---------------------------------------------------------------------------
// Payment
// ---------------------------------------------------------------------------
/// A single payment made toward an invoice.
#[contracttype]
#[derive(Clone, Debug)]
pub struct Payment {
pub payer: Address,
pub amount: i128,
pub tip: i128,
pub attestation_hash: Option<BytesN<32>>,
pub donate_on_failure: bool,
pub ledger: u32,
pub timestamp: u64,
}
/// Issue #449: Multi-phase invoice state machine.
#[contracttype]
#[derive(Clone, Debug, PartialEq)]
pub enum InvoicePhase {
Draft,
Active,
Locked,
Released,
}
/// Issue #447: Per-invoice analytics accumulator.
#[contracttype]
#[derive(Clone, Debug)]
pub struct InvoiceAnalytics {
pub payment_count: u64,
pub total_funded: i128,
pub unique_payers: u32,
pub first_payment_ledger: u32,
pub last_payment_ledger: u32,
}
#[contracttype]
#[derive(Clone, Debug, PartialEq)]
pub enum AdminRole {
SuperAdmin,
Operator,
}
/// Issue RBAC: Fine-grained role assigned to an address.
/// - Admin : may perform any action (equivalent to SuperAdmin for RBAC gates).
/// - Creator : may call `create_invoice`.
/// - Operator : may call `release` / `release_invoice`.
/// - Auditor : read-only; may call `get_invoice` and other query entry points.
#[contracttype]
#[derive(Clone, Debug, PartialEq)]
pub enum Role {
Admin,
Creator,
Operator,
Auditor,
}
/// Category of a token transfer recorded in the audit log.
#[contracttype]
#[derive(Clone, Debug, PartialEq)]
pub enum TransferKind {
/// A payer contributing funds toward an invoice.
Contribution,
/// Funds released to a recipient.
Payout,
/// Funds refunded to a payer.
Refund,
/// A fee charged by the contract.
Fee,
/// Sweep of remaining funds to a designated address.
Sweep,
}
/// A single token transfer event recorded on-chain.
#[contracttype]
#[derive(Clone, Debug)]
pub struct TransferRecord {
/// Source of the transfer.
pub from: Address,
/// Destination of the transfer.
pub to: Address,
/// Amount transferred in stroops.
pub amount: i128,
/// Category of the transfer.
pub kind: TransferKind,
/// Ledger sequence at the time of the transfer.
pub ledger: u32,
}
// ---------------------------------------------------------------------------
// Invoice
// ---------------------------------------------------------------------------
/// An on-chain invoice splitting payment among multiple recipients.
#[contracttype]
#[derive(Clone, Debug)]
pub struct AuditEntry {
pub action: Symbol,
pub actor: Address,
pub timestamp: u64,
}
#[contracttype]
#[derive(Clone, Debug)]
pub struct SubscriptionParams {
pub creator: Address,
pub recipients: Vec<Address>,
pub amounts: Vec<i128>,
pub tokens: Vec<Address>,
/// Optional recurrence interval in days. Defaults to 30 if None.
pub interval_days: Option<u32>,
}
/// Issue #414: Per-recipient payout configuration.
#[contracttype]
#[derive(Clone, Debug)]
pub struct Recipient {
pub address: Address,
pub token: Address,
}
#[contracttype]
#[derive(Clone, Debug)]
pub struct CompletionProof {
pub id: u64,
pub status: InvoiceStatus,
pub funded: i128,
pub timestamp: u64,
pub hash: BytesN<32>,
}
#[contracttype]
#[derive(Clone, Debug)]
pub struct PaymentProof {
pub invoice_id: u64,
pub payer: Address,
pub total_paid: i128,
pub proof_hash: BytesN<32>,
}
#[contracttype]
#[derive(Clone, Debug)]
pub struct InvoiceTemplate {
pub recipients: Vec<Address>,
pub amounts: Vec<i128>,
pub token: Address,
/// Ledger sequence after which the invoice can be refunded.
pub deadline_ledger: u32,
/// Total amount collected so far.
pub funded: i128,
/// Current lifecycle status.
pub status: InvoiceStatus,
/// All payments made toward this invoice.
pub payments: Vec<Payment>,
/// Optional whitelist of addresses allowed to pay this invoice.
/// When None, any address may pay.
pub allowed_payers: Option<Vec<Address>>,
}
#[contracttype]
#[derive(Clone, Debug)]
pub struct CreateInvoiceParams {
pub recipients: Vec<Address>,
pub amounts: Vec<i128>,
pub token: Address,
pub deadline: u64,
}
#[contracttype]
#[derive(Clone, Debug)]
pub struct PaymentCommitment {
pub commitment_hash: BytesN<32>,
pub commit_ledger: u32,
}
/// A single graduated release tranche: `basis_points` out of 10 000 of the
/// invoice total becomes releasable once the ledger time reaches `timestamp`.
#[contracttype]
#[derive(Clone, Debug)]
pub struct Tranche {
pub timestamp: u64,
pub basis_points: u32,
}
/// On-chain reputation scoring metrics for an address (issue #349).
#[contracttype]
#[derive(Clone, Debug, PartialEq, Eq, Default)]
pub struct RepScore {
pub paid_on_time: u32,
pub late_pays: u32,
pub invoices_released: u32,
pub invoices_refunded: u32,
}
/// Issue #431: Payment fingerprint for duplicate detection.
#[contracttype]
#[derive(Clone, Debug)]
pub struct PaymentFingerprint {
/// Timestamp (ledger sequence) when the payment was recorded.
pub recorded_at_ledger: u32,
/// Hash of (invoice_id || payer || amount || ledger_sequence).
pub fingerprint_hash: BytesN<32>,
}
/// Optional parameters for `create_invoice`, grouped to keep the function
/// within Soroban's 10-parameter limit.
#[contracttype]
#[derive(Clone, Debug)]
pub struct InvoiceOptions {
/// Additional creators who share ownership/rights over the invoice.
pub co_creators: Vec<Address>,
/// When true, payers may withdraw their contribution before the deadline.
pub allow_early_withdrawal: bool,
/// Size of the bonus pool funded alongside the invoice, in token units.
pub bonus_pool: i128,
/// Maximum number of distinct payers that may contribute to the bonus pool.
pub bonus_max_payers: u32,
/// Optional creator cosigner address that must co-author creator actions.
pub creator_cosigner: Option<Address>,
/// Velocity limit in token units for a single payer over `velocity_window`.
pub velocity_limit: i128,
/// Window length in seconds for velocity limiting.
pub velocity_window: u64,
/// Issue #22: block release until this invoice is Released.
pub prerequisite_id: Option<u64>,
/// Issue #23: graduated release schedule; empty = release all at once.
pub tranches: Vec<Tranche>,
/// Co-signers whose approval is required before release.
pub co_signers: Vec<Address>,
/// How many co-signer approvals are needed (≤ `co_signers.len()`).
pub required_signatures: u32,
/// Penalty basis points for late payments (issue #42).
pub penalty_bps: Option<u32>,
/// Soft deadline timestamp; payments after this incur a penalty (issue #42).
pub penalty_deadline: Option<u64>,
/// Minimum funding threshold in basis points (issue #43).
pub min_funding_bps: Option<u32>,
/// Issue #86: creator-triggered staged release schedule; each entry is
/// basis points (must sum to 10 000 when non-empty).
pub release_stages: Vec<u32>,
/// Issue #142: optional price oracle contract for dynamic pricing.
pub price_oracle: Option<Address>,
/// Issue #41: optional preferred output token per recipient for DEX swap on release.
pub swap_tokens: Vec<Option<Address>>,
pub tax_bps: Option<u32>,
pub tax_authority: Option<Address>,
pub insurance_premium_bps: Option<u32>,
pub smart_route: Option<bool>,
pub notification_contract: Option<Address>,
pub overflow_behavior: OverflowBehavior,
/// Issue #1: when true, _release() registers funds with the stream contract instead of direct transfer.
pub convert_to_stream: bool,
/// Issue #2: tokens accepted in pay_with_token(); base token is always accepted implicitly.
pub accepted_tokens: Vec<Address>,
/// Optional automatic forwarding address target for leftover funds.
pub forward_to: Option<Address>,
/// Optional automatic forwarding to another invoice id.
pub forward_invoice_id: Option<u64>,
/// Issue: per-recipient split rules evaluated at release time; empty = use amounts[].
pub split_rules: Vec<SplitRule>,
/// Issue: pre-agreed auto-resolution rules evaluated in order when auto_resolve() is called.
pub auto_resolve_rules: Vec<ResolveRule>,
/// Optional oracle address that must confirm the condition before release.
pub condition_oracle: Option<Address>,
/// Optional cross-chain reference carried through invoice creation.
pub cross_chain_ref: Option<String>,
/// Issue #98: restrict payments to this allowlist; None = open.
pub allowed_payers: Option<Vec<Address>>,
/// Issue: per-recipient release priorities (parallel to recipients); empty = no ordering.
pub priorities: Vec<u32>,
/// Issue #199: grace period in seconds after deadline before refund is allowed.
pub refund_grace_secs: Option<u64>,
/// Scheduled release timestamp (issue #207).
pub scheduled_release_at: Option<u64>,
/// KYC verification requirement.
pub require_kyc: bool,
/// Per-recipient split ratios in basis points (must sum to [`BASIS_POINTS_TOTAL`] = 10 000
/// when non-empty). Empty vec means "no ratio constraint — use amounts directly."
pub ratios: Vec<u32>,
/// Co-signer addresses whose approval (via `approve_release`) is required
/// before this invoice can be released. Independent of the legacy
/// `co_signers` / `sign_release` gate above. `None` disables the gate.
pub cosigners: Option<Vec<Address>>,
/// Number of distinct `cosigners` approvals required before release is
/// permitted. Only meaningful when `cosigners` is `Some`; must be in
/// `1..=cosigners.len()`.
pub cosigner_threshold: Option<u32>,
/// Overflow fields that would otherwise push this struct past Soroban's
/// 40-field `#[contracttype]` limit — see [`InvoiceOptions2`].
pub ext: InvoiceOptions2,
}
/// Overflow options for `create_invoice`, split off from [`InvoiceOptions`] to stay within
/// Soroban's 40-field `#[contracttype]` limit.
#[contracttype]
#[derive(Clone, Debug)]
pub struct InvoiceOptions2 {
/// Issue #274: invoice target in USD cents; used with price_oracle for dynamic funding.
pub target_usd_cents: Option<u64>,
/// Issue #307: explicit payment token override; uses this token instead of the invoice base token.
pub payment_token: Option<Address>,
/// Issue #327: ledgers to lock funds after full funding (max 100_000 ≈ 5 days).
pub release_delay_ledgers: Option<u32>,
/// Issue #329: optional IPFS CID / SHA-256 hash of off-chain invoice metadata.
pub metadata_hash: Option<BytesN<32>>,
/// Per-payer cooldown window in seconds (issue #168).
pub payment_cooldown_secs: Option<u64>,
/// Maximum payments allowed per window (issue #168).
pub max_payments_per_window: Option<u32>,
/// Window duration in seconds for payment rate limiting (issue #168).
pub payment_window_secs: Option<u64>,
/// Oracle contract used for oracle-priced invoices: the funding target is
/// computed at payment time from a live exchange rate instead of being
/// fixed at creation. When set, `oracle_asset_pair` must also be set and
/// `amounts` is interpreted as the USD-cents funding target.
pub oracle: Option<Address>,
/// Base asset symbol passed to the oracle's `price` call (e.g. XLM).
pub oracle_asset_pair_base: Option<Symbol>,
/// Quote asset symbol passed to the oracle's `price` call (e.g. USD).
pub oracle_asset_pair_quote: Option<Symbol>,
/// Minimum required payer reputation score to pay this invoice (issue #349).
pub min_payer_rep: Option<u32>,
/// Issue #430: payments are rejected before this timestamp, if set.
pub payment_open_at: Option<u64>,
/// Issue #430: payments are rejected after this timestamp, if set.
/// Must be strictly before `deadline` when set.
pub payment_close_at: Option<u64>,
/// Optional milestone thresholds in basis points for auto-release gates.
pub milestones: Option<Vec<u32>>,
/// Optional per-recipient payout caps parallel to `recipients`.
pub recipient_max_payouts: Option<Vec<Option<i128>>>,
/// Issue #416: SHA-256 hash of the required off-chain release preimage.
pub release_condition_hash: Option<BytesN<32>>,
/// Issue #417: enable recipient whitelist enforcement for this invoice.
pub recipient_whitelist_enabled: bool,
/// Issue #188: escrow hold period in ledgers.
pub escrow_hold_period: Option<u32>,
/// Issue #420: how overfunding payments are handled. Use `Cap` for the
/// historical behaviour. (Not `Option`-wrapped: `#[contracttype]` cannot
/// derive the `ScVal` conversions for `Option<CustomEnum>`.)
pub overfunding_policy: OverfundingPolicy,
/// Issue #489: number of ledgers after creation during which contributions
/// qualify for the discounted `early_bird_fee_bps` platform fee. 0 disables
/// the early-bird discount entirely.
pub early_bird_window_ledgers: u32,
/// Issue #489: discounted platform fee (bps) applied to contributions made
/// within `early_bird_window_ledgers` of invoice creation. Must be ≤ the
/// standard platform fee in effect at creation time.
pub early_bird_fee_bps: u32,
/// Issue #559: creator-declared fee in basis points (0–10 000).
/// Deducted from gross collected funds before recipient payouts.
pub creator_fee_bps: u32,
/// Issue #489: total platform-fee discount accrued from early-bird
/// contributions so far; deducted from the platform fee at release.
pub early_bird_fee_credit: i128,
/// Issue #518: denominator for high-precision ratio splits.
pub ratio_denominator: u64,
}
impl Default for InvoiceOptions2 {
/// Returns an `InvoiceOptions2` with every optional field set to `None`,
/// every boolean to `false`, every numeric to `0`, and
/// `overfunding_policy` to [`OverfundingPolicy::Cap`] (the historical
/// behaviour). `ratio_denominator` is `10_000` to match
/// [`InvoiceExt2::default`].
///
/// Tests that only care about one or two fields can use this as a
/// starting point and override just those fields:
/// ```ignore
/// let opts = InvoiceOptions2 {
/// payment_cooldown_secs: Some(60),
/// ..Default::default()
/// };
/// ```
fn default() -> Self {
InvoiceOptions2 {
target_usd_cents: None,
payment_token: None,
release_delay_ledgers: None,
metadata_hash: None,
payment_cooldown_secs: None,
max_payments_per_window: None,
payment_window_secs: None,
oracle: None,
oracle_asset_pair_base: None,
oracle_asset_pair_quote: None,
min_payer_rep: None,
payment_open_at: None,
payment_close_at: None,
milestones: None,
recipient_max_payouts: None,
release_condition_hash: None,
recipient_whitelist_enabled: false,
escrow_hold_period: None,
overfunding_policy: OverfundingPolicy::Cap,
early_bird_window_ledgers: 0,
early_bird_fee_bps: 0,
creator_fee_bps: 0,
early_bird_fee_credit: 0,
ratio_denominator: 10_000,
}
}
}
/// Legacy invoice layout used by stored invoices created before the `version`
/// field was added. Kept for on-chain migration so old data can be
/// deserialised and re-saved in the current schema.
#[contracttype]
#[derive(Clone, Debug)]
pub struct LegacyInvoice {
pub creator: Address,
pub co_creators: Vec<Address>,
pub recipients: Vec<Address>,
pub amounts: Vec<i128>,
pub tokens: Vec<Address>,
pub deadline: u64,
pub funded: i128,
pub status: InvoiceStatus,
pub payments: Vec<Payment>,
pub drip_duration: Option<u64>,
pub release_timestamp: Option<u64>,
pub claimed: Vec<i128>,
pub frozen: bool,
pub completion_time: Option<u64>,
pub allow_early_withdrawal: bool,
pub bonus_pool: i128,
pub bonus_max_payers: u32,
pub prerequisite_id: Option<u64>,
pub tranches: Vec<Tranche>,
pub released_bps: u32,
pub stake_amount: i128,
pub referrer: Option<Address>,
pub tax_bps: u32,
pub tax_authority: Option<Address>,
pub insurance_premium_bps: u32,
pub insurance_fund: i128,
pub smart_route: bool,
}
#[contracttype]
#[derive(Clone, Debug)]
pub struct InvoiceCore {
pub version: u32,
pub creator: Address,
pub co_creators: Vec<Address>,
pub recipients: Vec<Address>,
pub amounts: Vec<i128>,
pub tokens: Vec<Address>,
pub funding_token: Address,
pub deadline: u64,
pub funded: i128,
pub status: InvoiceStatus,
pub payments: Vec<Payment>,
pub drip_duration: Option<u64>,
pub release_timestamp: Option<u64>,
pub claimed: Vec<i128>,
pub frozen: bool,
pub completion_time: Option<u64>,
pub allow_early_withdrawal: bool,
pub bonus_pool: i128,
pub bonus_max_payers: u32,
pub prerequisite_id: Option<u64>,
pub tranches: Vec<Tranche>,
pub released_bps: u32,
pub clone_depth: u32,
pub predecessor_id: Option<u64>,
/// Issue #329: optional IPFS CID / SHA-256 hash of off-chain invoice metadata.
pub metadata_hash: Option<BytesN<32>>,
}
#[contracttype]
#[derive(Clone, Debug)]
pub struct InvoiceExt {
/// Addresses whose approval is required before the invoice can be released.
pub co_signers: Vec<Address>,
/// How many of `co_signers` approvals are required (≤ `co_signers.len()`).
pub required_signatures: u32,
/// Addresses that have actually approved the release so far.
pub signatures: Vec<Address>,
/// Optional address permitted to call `approve_release` on behalf of the creator.
pub approver: Option<Address>,
/// Whether the release has been approved by the required approvers.
pub approved: bool,
/// Optional oracle that must confirm a release condition before funds move.
pub condition_oracle: Option<Address>,
/// Whether the `condition_oracle` has signalled the condition is met.
pub condition_met: bool,
/// Penalty applied (in basis points) to late payments.
pub penalty_bps: u32,
/// Timestamp after which the `penalty_bps` penalty begins to apply.
pub penalty_deadline: u64,
/// Minimum funding threshold (basis points) that must be reached before release.
pub min_funding_bps: u32,
/// Configured release stages as basis points; empty = release all at once.
pub release_stages: Vec<u32>,
/// Count of release stages already paid out.
pub released_stages: u32,
/// Optional allowlist of payer addresses; `None` = open to anyone.
pub allowed_payers: Option<Vec<Address>>,
/// Optional price oracle used for dynamic (oracle-priced) funding.
pub price_oracle: Option<Address>,
/// Cached per-recipient base amounts used during release math.
pub base_amounts: Vec<i128>,
/// Per-recipient optional output token used for a DEX swap on release.
pub swap_tokens: Vec<Option<Address>>,
/// Tax levied on the invoice, in basis points.
pub tax_bps: u32,
/// Address that receives the tax withheld from the invoice.
pub tax_authority: Option<Address>,
/// Insurance premium (basis points) charged on the invoice.
pub insurance_premium_bps: u32,
/// Funds held in the insurance pool for this invoice.
pub insurance_fund: i128,
/// Whether payouts are routed through a smart-order router for best execution.
pub smart_route: bool,
/// When true, release registers the funds with the stream contract instead of a direct transfer.
pub convert_to_stream: bool,
/// Additional tokens (beyond the base token) accepted by `pay_with_token`.
pub accepted_tokens: Vec<Address>,
/// Optional address that leftover funds are forwarded to on release.
pub forward_to: Option<Address>,
/// Optional invoice id that leftover funds are forwarded to on release.
pub forward_invoice_id: Option<u64>,
/// Per-recipient split rules evaluated at release time; empty = use `amounts`.
pub split_rules: Vec<SplitRule>,
/// Pre-agreed auto-resolution rules evaluated in order by `auto_resolve`.
pub auto_resolve_rules: Vec<ResolveRule>,
/// Optional creator cosigner that must co-author creator actions.
pub creator_cosigner: Option<Address>,
/// Velocity limit (token units) for a single payer over `velocity_window`.
pub velocity_limit: i128,
/// Window length (seconds) for velocity limiting of payer contributions.
pub velocity_window: u64,
/// Optional id of the parent invoice this one was cloned from.
pub parent_invoice_id: Option<u64>,
/// Optional human-readable reason the invoice is paused.
pub pause_reason: Option<String>,
/// Optional timestamp at which a paused invoice auto-resumes.
pub auto_resume_at: Option<u64>,
/// Optional per-payer cooldown (seconds) between payments.
pub payment_cooldown_secs: Option<u64>,
/// Optional maximum number of payments allowed per `payment_window_secs`.
pub max_payments_per_window: Option<u32>,
/// Optional window (seconds) for per-payer payment rate limiting.
pub payment_window_secs: Option<u64>,
/// Optional timestamp at which release is scheduled to become available.
pub scheduled_release_at: Option<u64>,
/// Configured penalty tiers applied at different late-payment thresholds.
pub penalty_tiers: Vec<PenaltyTier>,
/// Optional allowlist of callers permitted to invoke mutating entry points.
pub allowed_callers: Option<Vec<Address>>,
/// Grace period (seconds) after `deadline` before a refund is allowed.
pub refund_grace_secs: Option<u64>,
}
impl InvoiceExt {
/// Issue #629: Return an InvoiceExt with all fields set to their zero /
/// empty / None defaults. Use this as a starting point when constructing
/// a new InvoiceExt so that new fields are never accidentally omitted.
pub fn default(env: &Env) -> Self {
InvoiceExt {
co_signers: Vec::new(env),
required_signatures: 0,
signatures: Vec::new(env),
approver: None,
approved: false,
condition_oracle: None,
condition_met: false,
penalty_bps: 0,
penalty_deadline: 0,
min_funding_bps: 0,
release_stages: Vec::new(env),
released_stages: 0,
allowed_payers: None,
price_oracle: None,
base_amounts: Vec::new(env),
swap_tokens: Vec::new(env),
tax_bps: 0,
tax_authority: None,
insurance_premium_bps: 0,
insurance_fund: 0,
smart_route: false,
convert_to_stream: false,
accepted_tokens: Vec::new(env),
forward_to: None,
forward_invoice_id: None,
split_rules: Vec::new(env),
auto_resolve_rules: Vec::new(env),
creator_cosigner: None,
velocity_limit: 0,
velocity_window: 0,
parent_invoice_id: None,
pause_reason: None,
auto_resume_at: None,
payment_cooldown_secs: None,
max_payments_per_window: None,
payment_window_secs: None,
scheduled_release_at: None,
penalty_tiers: Vec::new(env),
allowed_callers: None,
refund_grace_secs: None,
}
}
}
#[contracttype]
#[derive(Clone, Debug)]
pub struct InvoiceExt2 {
pub notification_contract: Option<Address>,
pub overflow_behavior: OverflowBehavior,
pub cross_chain_ref: Option<String>,
pub require_kyc: bool,
/// Issue #188: arbiter address that can raise and resolve disputes.
pub arbiter: Option<Address>,
/// Issue #188: whether this invoice is under active dispute.
pub disputed: bool,
pub admin_frozen: bool,
pub auction_on_expiry: bool,
pub auction_end: u64,
pub bids: Vec<Bid>,
pub min_payment: i128,
pub min_funding_amount: i128,
pub priorities: Vec<u32>,
/// Issue #274: invoice target in USD cents for oracle-based dynamic funding.
pub target_usd_cents: Option<u64>,
/// Issue #308: addresses that have already claimed a per-payer refund on this invoice.
pub refunded_addresses: Vec<Address>,
/// Oracle-priced invoices: oracle contract queried at payment time.
pub oracle: Option<Address>,
/// Oracle-priced invoices: base asset symbol passed to the oracle.
pub oracle_asset_pair_base: Option<Symbol>,
/// Oracle-priced invoices: quote asset symbol passed to the oracle.
pub oracle_asset_pair_quote: Option<Symbol>,
/// Issue #349: minimum required payer reputation score.
pub min_payer_rep: Option<u32>,
pub escrow_hold_period: Option<u32>,
pub held_until: Option<u32>,
/// Funding milestone thresholds in basis points.
pub milestones: Vec<u32>,
/// Number of milestones already released.
pub milestones_released: u32,
/// Optional per-recipient payout caps parallel to `recipients`.
pub recipient_max_payouts: Vec<Option<i128>>,
/// Time-weighted average funding rate accumulator numerator.
pub twafr_numerator: i128,
/// Last ledger sequence used to update TWAFR.
pub twafr_last_ledger: u32,
/// Issue #416: SHA-256 hash required to release the invoice.
pub release_condition_hash: Option<BytesN<32>>,
/// Issue #417: recipient whitelist enforcement flag.
pub recipient_whitelist_enabled: bool,
/// Issue #420: creator-configurable overfunding behaviour.
pub overfunding_policy: OverfundingPolicy,
/// Issue #485: optional contributor allowlist; when Some only listed addresses may call pay/contribute.
pub contributor_allowlist: Option<Vec<Address>>,
/// Issue #489: ledgers after creation during which contributions qualify
/// for `early_bird_fee_bps`. 0 disables the discount.
pub early_bird_window_ledgers: u32,
/// Issue #489: discounted platform fee (bps) for contributions made within
/// the early-bird window.
pub early_bird_fee_bps: u32,
/// Issue #489: total platform-fee discount accrued from early-bird
/// contributions so far; deducted from the platform fee at release.
pub early_bird_fee_credit: i128,
/// Issue #559: creator fee in basis points (taken before platform fee).
pub creator_fee_bps: u32,
/// Issue #518: denominator for custom split ratios.
pub ratio_denominator: u64,
/// Issue #518: per-recipient split ratios (parallel to recipients vec).
pub ratios: Vec<u32>,
}
impl InvoiceExt2 {
/// Issue #629: Return an InvoiceExt2 with all fields set to their zero /
/// empty / None defaults. Use this as a starting point when constructing
/// a new InvoiceExt2 so that new fields are never accidentally omitted.
pub fn default(env: &Env) -> Self {
InvoiceExt2 {
notification_contract: None,
overflow_behavior: OverflowBehavior::Reject,
cross_chain_ref: None,
require_kyc: false,
arbiter: None,
disputed: false,
admin_frozen: false,
auction_on_expiry: false,
auction_end: 0,
bids: Vec::new(env),
min_payment: 0,
min_funding_amount: 0,
priorities: Vec::new(env),
target_usd_cents: None,
refunded_addresses: Vec::new(env),
oracle: None,
oracle_asset_pair_base: None,
oracle_asset_pair_quote: None,
min_payer_rep: None,
escrow_hold_period: None,
held_until: None,
milestones: Vec::new(env),
milestones_released: 0,
recipient_max_payouts: Vec::new(env),
twafr_numerator: 0,
twafr_last_ledger: 0,
release_condition_hash: None,
recipient_whitelist_enabled: false,
// Issue #420: Cap delegates to overflow_behavior and preserves
// historical semantics for invoices created before this field existed.
overfunding_policy: OverfundingPolicy::Cap,
contributor_allowlist: None,
early_bird_window_ledgers: 0,
early_bird_fee_bps: 0,
early_bird_fee_credit: 0,
creator_fee_bps: 0,
ratio_denominator: 10_000,
ratios: Vec::new(env),
}
}
}
/// Issue #211: A single escalating penalty tier (seconds_after_deadline, bps).
#[contracttype]
#[derive(Clone, Debug)]
pub struct PenaltyTier {
pub seconds_after_deadline: u64,
pub bps: u32,
}
/// Issue #475: Multi-signature admin set — replaces the single-admin model.
/// Sensitive operations require `threshold`-of-N signers to approve.
#[contracttype]
#[derive(Clone, Debug, PartialEq)]
pub struct AdminSet {
/// All recognised admin signers.
pub signers: Vec<Address>,
/// Minimum number of approvals required to finalise an action.
pub threshold: u32,
}
/// Issue #475: Discriminated union of admin actions that can be proposed.
#[contracttype]
#[derive(Clone, Debug, PartialEq)]
pub enum AdminAction {
/// Pause the entire contract.
PauseContract,
/// Unpause the contract.
UnpauseContract,
/// Update the platform fee in basis points.
SetPlatformFeeBps(u32),
/// Replace the treasury address.
SetTreasury(Address),
/// Replace the full AdminSet (rotate signers / change threshold).
ReplaceAdminSet(AdminSet),
}
/// Issue #475: On-chain record of a pending multi-sig admin proposal.
#[contracttype]
#[derive(Clone, Debug)]
pub struct PendingAdminAction {
/// Keccak-like identifier (SHA-256 hash of the serialised action payload).
pub action_hash: BytesN<32>,
/// The actual action to execute once approved.
pub action: AdminAction,
/// Ledger timestamp when the proposal was created.
pub proposed_at: u64,
/// Set of signers who have already approved this proposal.
pub approvals: Vec<Address>,
/// Whether the proposal has been executed.
pub executed: bool,
}
/// Timelocked admin action queued for future execution.
#[contracttype]
#[derive(Clone, Debug)]
pub enum TimelockAction {
SetTreasury(Address),
SetPlatformFee(u32),
}
/// A queued timelock action with metadata.
#[contracttype]
#[derive(Clone, Debug)]
pub struct QueuedAction {
pub action: TimelockAction,
pub queued_at: u64,
pub executed: bool,
}
/// Full invoice — assembled from InvoiceCore + InvoiceExt + InvoiceExt2.
/// Never stored directly; use save_invoice / load_invoice helpers in lib.rs.
#[derive(Clone, Debug)]
pub struct Invoice {
pub version: u32,
pub creator: Address,
pub co_creators: Vec<Address>,
pub recipients: Vec<Address>,
pub amounts: Vec<i128>,
pub tokens: Vec<Address>,
pub funding_token: Address,
pub deadline: u64,
pub funded: i128,
pub status: InvoiceStatus,
pub payments: Vec<Payment>,
pub drip_duration: Option<u64>,
pub release_timestamp: Option<u64>,
pub claimed: Vec<i128>,
pub frozen: bool,
pub completion_time: Option<u64>,
pub allow_early_withdrawal: bool,