@hyperscale0/hsx 2.4.1 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +4 -406
- package/README.md +3 -92
- package/dist/src/ast.d.ts +99 -310
- package/dist/src/ast.d.ts.map +1 -1
- package/dist/src/ast.js +16 -69
- package/dist/src/ast.js.map +1 -1
- package/dist/src/cli.d.ts +6 -19
- package/dist/src/cli.d.ts.map +1 -1
- package/dist/src/cli.js +77 -315
- package/dist/src/cli.js.map +1 -1
- package/dist/src/compile.d.ts +23 -73
- package/dist/src/compile.d.ts.map +1 -1
- package/dist/src/compile.js +1113 -148
- package/dist/src/compile.js.map +1 -1
- package/dist/src/cost.d.ts +8 -77
- package/dist/src/cost.d.ts.map +1 -1
- package/dist/src/cost.js +13 -422
- package/dist/src/cost.js.map +1 -1
- package/dist/src/format.d.ts +8 -7
- package/dist/src/format.d.ts.map +1 -1
- package/dist/src/format.js +9 -175
- package/dist/src/format.js.map +1 -1
- package/dist/src/headers.d.ts +35 -0
- package/dist/src/headers.d.ts.map +1 -0
- package/dist/src/headers.js +108 -0
- package/dist/src/headers.js.map +1 -0
- package/dist/src/index.d.ts +6 -12
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +4 -9
- package/dist/src/index.js.map +1 -1
- package/dist/src/lex.d.ts +8 -28
- package/dist/src/lex.d.ts.map +1 -1
- package/dist/src/lex.js +88 -146
- package/dist/src/lex.js.map +1 -1
- package/dist/src/parse.d.ts +4 -17
- package/dist/src/parse.d.ts.map +1 -1
- package/dist/src/parse.js +402 -1040
- package/dist/src/parse.js.map +1 -1
- package/dist/src/std-bundle.d.ts.map +1 -1
- package/dist/src/std-bundle.js +12 -20
- package/dist/src/std-bundle.js.map +1 -1
- package/dist/src/std-library.d.ts +1 -1
- package/dist/src/std-library.d.ts.map +1 -1
- package/dist/src/std-library.js +1 -5
- package/dist/src/std-library.js.map +1 -1
- package/dist/src/tunables.d.ts +7 -0
- package/dist/src/tunables.d.ts.map +1 -0
- package/dist/src/tunables.js +26 -0
- package/dist/src/tunables.js.map +1 -0
- package/dist/src/version.d.ts +2 -4
- package/dist/src/version.d.ts.map +1 -1
- package/dist/src/version.js +2 -4
- package/dist/src/version.js.map +1 -1
- package/docs/README.md +179 -41
- package/docs/headers.md +44 -0
- package/examples/cost-table.json +99 -751
- package/examples/library.hsx +59 -0
- package/package.json +9 -17
- package/src/ast.ts +82 -441
- package/src/cli.ts +78 -362
- package/src/compile.ts +1535 -247
- package/src/cost.ts +28 -671
- package/src/format.ts +12 -256
- package/src/headers.ts +126 -0
- package/src/index.ts +7 -39
- package/src/lex.ts +99 -195
- package/src/parse.ts +403 -1209
- package/src/std-bundle.ts +12 -20
- package/src/std-library.ts +2 -7
- package/src/tunables.ts +31 -0
- package/src/version.ts +2 -5
- package/std/approvals.hsx +17 -0
- package/std/cards.hsx +63 -0
- package/std/collections.hsx +31 -0
- package/std/escrow.hsx +43 -0
- package/std/financing.hsx +372 -0
- package/std/insurance.hsx +59 -0
- package/std/lending.hsx +115 -0
- package/std/marketplace.hsx +24 -0
- package/std/money.hsx +195 -0
- package/std/savings.hsx +44 -0
- package/std/travel.hsx +98 -0
- package/std/wallet.hsx +41 -0
- package/dist/src/diagnostics.d.ts +0 -13
- package/dist/src/diagnostics.d.ts.map +0 -1
- package/dist/src/diagnostics.js +0 -587
- package/dist/src/diagnostics.js.map +0 -1
- package/dist/src/emit.d.ts +0 -51
- package/dist/src/emit.d.ts.map +0 -1
- package/dist/src/emit.js +0 -192
- package/dist/src/emit.js.map +0 -1
- package/dist/src/entry-overrides.d.ts +0 -58
- package/dist/src/entry-overrides.d.ts.map +0 -1
- package/dist/src/entry-overrides.js +0 -284
- package/dist/src/entry-overrides.js.map +0 -1
- package/dist/src/ir.d.ts +0 -73
- package/dist/src/ir.d.ts.map +0 -1
- package/dist/src/ir.js +0 -15
- package/dist/src/ir.js.map +0 -1
- package/dist/src/limits.d.ts +0 -23
- package/dist/src/limits.d.ts.map +0 -1
- package/dist/src/limits.js +0 -23
- package/dist/src/limits.js.map +0 -1
- package/dist/src/lsp/server.d.ts +0 -32
- package/dist/src/lsp/server.d.ts.map +0 -1
- package/dist/src/lsp/server.js +0 -391
- package/dist/src/lsp/server.js.map +0 -1
- package/dist/src/modules.d.ts +0 -38
- package/dist/src/modules.d.ts.map +0 -1
- package/dist/src/modules.js +0 -372
- package/dist/src/modules.js.map +0 -1
- package/dist/src/typecheck.d.ts +0 -8
- package/dist/src/typecheck.d.ts.map +0 -1
- package/dist/src/typecheck.js +0 -4160
- package/dist/src/typecheck.js.map +0 -1
- package/docs/guide/01-first-program.md +0 -24
- package/docs/guide/02-money.md +0 -25
- package/docs/guide/03-instruments.md +0 -93
- package/docs/guide/04-lifecycles.md +0 -40
- package/docs/guide/05-fees-and-splits.md +0 -45
- package/docs/guide/06-schedules.md +0 -26
- package/docs/guide/07-composition.md +0 -52
- package/docs/guide/08-writing-a-module.md +0 -85
- package/docs/guide/09-cost.md +0 -24
- package/docs/guide/10-diagnostics.md +0 -27
- package/docs/llms-full.txt +0 -3684
- package/docs/llms.txt +0 -44
- package/docs/piece-plans.md +0 -120
- package/docs/playground.md +0 -55
- package/docs/reference/cli.md +0 -36
- package/docs/reference/diagnostics.md +0 -901
- package/docs/reference/grammar.md +0 -38
- package/docs/reference/std/advance.md +0 -112
- package/docs/reference/std/cancellable_booking.md +0 -159
- package/docs/reference/std/captured_payment.md +0 -115
- package/docs/reference/std/conditional_disbursement.md +0 -101
- package/docs/reference/std/credit_facility.md +0 -108
- package/docs/reference/std/held_payment.md +0 -173
- package/docs/reference/std/instant_transfer.md +0 -97
- package/docs/reference/std/metered.md +0 -89
- package/docs/reference/std/pooled_split.md +0 -95
- package/docs/reference/std/premium_forward.md +0 -119
- package/docs/reference/std/reconciled_payout.md +0 -103
- package/docs/reference/std/rotating_pool.md +0 -130
- package/docs/reference/std/scheduled.md +0 -136
- package/docs/reference/std/security_deposit.md +0 -138
- package/docs/reference/std/settlement_batch.md +0 -123
- package/docs/reference/std/swap.md +0 -133
- package/docs/reference/std/threshold_pool.md +0 -138
- package/docs/reference/std/weighted_distribution.md +0 -135
- package/docs/reference/types.md +0 -61
- package/docs/reference/udl-output.md +0 -13
- package/examples/01-first-program/README.md +0 -45
- package/examples/01-first-program/tip-jar.hsx +0 -17
- package/examples/02-imports-and-modules/README.md +0 -96
- package/examples/02-imports-and-modules/photo-booth.hsx +0 -37
- package/examples/03-diagnostics/README.md +0 -68
- package/examples/03-diagnostics/corner-shop-fixed.hsx +0 -22
- package/examples/03-diagnostics/corner-shop.hsx +0 -19
- package/examples/04-complete-product/README.md +0 -84
- package/examples/04-complete-product/study-hall.hsx +0 -67
- package/examples/05-authored-instrument/README.md +0 -5
- package/examples/05-authored-instrument/payment.hsx +0 -37
- package/examples/05-watch-club/README.md +0 -6
- package/examples/05-watch-club/watch-club.hsx +0 -123
- package/examples/README.md +0 -48
- package/examples/advance/README.md +0 -3
- package/examples/advance/advance.hsx +0 -13
- package/examples/advance/advance.udl +0 -367
- package/examples/cancellable_booking/README.md +0 -3
- package/examples/cancellable_booking/cancellable_booking.hsx +0 -14
- package/examples/cancellable_booking/cancellable_booking.udl +0 -413
- package/examples/captured_payment/README.md +0 -3
- package/examples/captured_payment/captured_payment.hsx +0 -17
- package/examples/captured_payment/captured_payment.udl +0 -686
- package/examples/conditional_disbursement/README.md +0 -3
- package/examples/conditional_disbursement/conditional_disbursement.hsx +0 -15
- package/examples/conditional_disbursement/conditional_disbursement.udl +0 -418
- package/examples/credit_facility/README.md +0 -3
- package/examples/credit_facility/credit_facility.hsx +0 -24
- package/examples/credit_facility/credit_facility.udl +0 -1396
- package/examples/held_payment/README.md +0 -3
- package/examples/held_payment/held_payment.hsx +0 -13
- package/examples/held_payment/held_payment.udl +0 -569
- package/examples/instant_transfer/README.md +0 -3
- package/examples/instant_transfer/instant_transfer.hsx +0 -16
- package/examples/instant_transfer/instant_transfer.udl +0 -376
- package/examples/metered/README.md +0 -3
- package/examples/metered/metered.hsx +0 -13
- package/examples/metered/metered.udl +0 -253
- package/examples/pooled_split/README.md +0 -3
- package/examples/pooled_split/pooled_split.hsx +0 -15
- package/examples/pooled_split/pooled_split.udl +0 -432
- package/examples/premium_forward/README.md +0 -3
- package/examples/premium_forward/premium_forward.hsx +0 -19
- package/examples/premium_forward/premium_forward.udl +0 -604
- package/examples/reconciled_payout/README.md +0 -3
- package/examples/reconciled_payout/reconciled_payout.hsx +0 -13
- package/examples/reconciled_payout/reconciled_payout.udl +0 -312
- package/examples/rotating_pool/README.md +0 -3
- package/examples/rotating_pool/rotating_pool.hsx +0 -18
- package/examples/rotating_pool/rotating_pool.udl +0 -4370
- package/examples/scheduled/README.md +0 -3
- package/examples/scheduled/scheduled.hsx +0 -12
- package/examples/scheduled/scheduled.udl +0 -355
- package/examples/security_deposit/README.md +0 -3
- package/examples/security_deposit/security_deposit.hsx +0 -21
- package/examples/security_deposit/security_deposit.udl +0 -320
- package/examples/settlement_batch/README.md +0 -3
- package/examples/settlement_batch/settlement_batch.hsx +0 -19
- package/examples/settlement_batch/settlement_batch.udl +0 -757
- package/examples/swap/README.md +0 -3
- package/examples/swap/swap.hsx +0 -19
- package/examples/swap/swap.udl +0 -892
- package/examples/threshold_pool/README.md +0 -3
- package/examples/threshold_pool/threshold_pool.hsx +0 -16
- package/examples/threshold_pool/threshold_pool.udl +0 -680
- package/examples/weighted_distribution/README.md +0 -3
- package/examples/weighted_distribution/weighted_distribution.hsx +0 -20
- package/examples/weighted_distribution/weighted_distribution.udl +0 -348
- package/skills/hsx/SKILL.md +0 -449
- package/src/diagnostics.ts +0 -620
- package/src/emit.ts +0 -282
- package/src/entry-overrides.ts +0 -428
- package/src/ir.ts +0 -113
- package/src/limits.ts +0 -22
- package/src/lsp/server.ts +0 -460
- package/src/modules.ts +0 -452
- package/src/typecheck.ts +0 -5515
- package/std/SEMANTICS.md +0 -44
- package/std/money_flows/advance.hsx +0 -235
- package/std/money_flows/cancellable_booking.hsx +0 -402
- package/std/money_flows/captured_payment.hsx +0 -220
- package/std/money_flows/conditional_disbursement.hsx +0 -226
- package/std/money_flows/credit_facility.hsx +0 -214
- package/std/money_flows/held_payment.hsx +0 -812
- package/std/money_flows/index.hsx +0 -4
- package/std/money_flows/instant_transfer.hsx +0 -231
- package/std/money_flows/metered.hsx +0 -104
- package/std/money_flows/pooled_split.hsx +0 -109
- package/std/money_flows/premium_forward.hsx +0 -307
- package/std/money_flows/reconciled_payout.hsx +0 -119
- package/std/money_flows/rotating_pool.hsx +0 -305
- package/std/money_flows/scheduled.hsx +0 -599
- package/std/money_flows/security_deposit.hsx +0 -248
- package/std/money_flows/settlement_batch.hsx +0 -173
- package/std/money_flows/swap.hsx +0 -1192
- package/std/money_flows/threshold_pool.hsx +0 -307
- package/std/money_flows/weighted_distribution.hsx +0 -237
|
@@ -1,307 +0,0 @@
|
|
|
1
|
-
module std.money_flows.threshold_pool
|
|
2
|
-
|
|
3
|
-
// All-or-nothing capital accumulation pool collecting commitments toward a target amount before a close deadline.
|
|
4
|
-
//
|
|
5
|
-
// ### Purpose
|
|
6
|
-
// `threshold_pool` powers all-or-nothing crowdfunding, capital calls, collective purchasing, and consortium rounds.
|
|
7
|
-
// Multiple contributors pledge commitments toward a monetary `target`. Commitments sit in escrow until the pool
|
|
8
|
-
// reaches the target before `close_by` (activating and settling funds to the beneficiary), or fails (triggering full refunds).
|
|
9
|
-
//
|
|
10
|
-
// ### Selection guidance
|
|
11
|
-
// - vs `rotating_pool`: `threshold_pool` is all-or-nothing fundraising where many contributors fund a single beneficiary.
|
|
12
|
-
// `rotating_pool` is a peer savings circle where members contribute identically in each cycle and rotate who receives the pot.
|
|
13
|
-
// - vs `weighted_distribution`: `threshold_pool` pools funds inward from many contributors to one beneficiary.
|
|
14
|
-
// `weighted_distribution` pays funds outward from one source pool to many weighted recipients.
|
|
15
|
-
//
|
|
16
|
-
// ### Parameters
|
|
17
|
-
// - `contributor`: The party representing individual contributors.
|
|
18
|
-
// - `beneficiary`: The beneficiary party receiving the settled pool if the target is met.
|
|
19
|
-
// - `target`: Total target funding threshold in minor units of currency `C`.
|
|
20
|
-
// - `commitment`: Binding name for individual contributor commitment amounts.
|
|
21
|
-
// - `max_contributors`: Optional maximum count of admitted contributors.
|
|
22
|
-
// - `close_by`: Stored date deadline by which the target must be met.
|
|
23
|
-
// - `close_policy`: Policy determining pool close behavior (`threshold`).
|
|
24
|
-
// - `overfund_policy`: Policy on commitments exceeding the target (`reject`).
|
|
25
|
-
// - `cancel_policy`: Contributor withdrawal policy before close (`before_close`).
|
|
26
|
-
// - `fail_policy`: Refund policy if the pool fails to hit target (`whole_commitment_refund`).
|
|
27
|
-
// - `beneficiary_account`: Optional direct account reference for the beneficiary.
|
|
28
|
-
// - `memo`: Optional memo text stored on the pool.
|
|
29
|
-
// - `contribution_instrument`: Optional custom contribution child instrument block.
|
|
30
|
-
// - `wording`: Optional custom UI wording block.
|
|
31
|
-
//
|
|
32
|
-
// ### Decision ports
|
|
33
|
-
// None. Lifecycle transitions (`activate`, `fail`, `close`) are driven by target threshold evaluation and the `close_by` date.
|
|
34
|
-
//
|
|
35
|
-
// ### Example
|
|
36
|
-
// ```hsx
|
|
37
|
-
// program capital_pool_example "Capital pool example"
|
|
38
|
-
// import { threshold_pool } from "std/money_flows"
|
|
39
|
-
// party contributor: person
|
|
40
|
-
// party company: business
|
|
41
|
-
// settlement round = threshold_pool {
|
|
42
|
-
// contributor: contributor
|
|
43
|
-
// beneficiary: company
|
|
44
|
-
// target: targetAmount: money(SAR)
|
|
45
|
-
// commitment: commitmentAmount: money(SAR)
|
|
46
|
-
// max_contributors: 100
|
|
47
|
-
// close_by: closeBy
|
|
48
|
-
// close_policy: threshold
|
|
49
|
-
// overfund_policy: reject
|
|
50
|
-
// cancel_policy: before_close
|
|
51
|
-
// fail_policy: whole_commitment_refund
|
|
52
|
-
// }
|
|
53
|
-
// ```
|
|
54
|
-
// ### Funding join
|
|
55
|
-
// funding_join configures the flat contribution-instrument mode for a priced
|
|
56
|
-
// obligation. Opening freezes minimum ticket, investor cap, profit and schedule
|
|
57
|
-
// terms. Closing binds the obligation, freezes committed ticket identities and
|
|
58
|
-
// moves the principal once into its funding account. A ticket's engine-owned
|
|
59
|
-
// collection action has no independent payment or clock trigger.
|
|
60
|
-
//
|
|
61
|
-
export instrument threshold_pool<C>(
|
|
62
|
-
contributor: optional<party>,
|
|
63
|
-
beneficiary: optional<party>,
|
|
64
|
-
target: money<C>,
|
|
65
|
-
commitment: money<C>,
|
|
66
|
-
max_contributors: optional<integer>,
|
|
67
|
-
close_by: date,
|
|
68
|
-
close_policy: optional<text>,
|
|
69
|
-
overfund_policy: optional<text>,
|
|
70
|
-
cancel_policy: optional<text>,
|
|
71
|
-
fail_policy: optional<text>,
|
|
72
|
-
beneficiary_account: optional<account<C>>,
|
|
73
|
-
memo: optional<text>,
|
|
74
|
-
contribution_instrument: optional<block>,
|
|
75
|
-
wording: optional<block>,
|
|
76
|
-
funding_join: optional<block>,
|
|
77
|
-
) {
|
|
78
|
-
when(wording) {
|
|
79
|
-
let(wording_unknown_keys): keys_except(wording, "pool_summary", "pool_description", "create_summary", "commitment_create_summary", "commitment_cancel_summary", "commitment_refund_summary");
|
|
80
|
-
when(wording_unknown_keys) {
|
|
81
|
-
unsupported {
|
|
82
|
-
code: HSX1407;
|
|
83
|
-
message: "threshold_pool wording contains an unknown key";
|
|
84
|
-
fix: "use only the six declared threshold_pool wording keys";
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
agent_description: "Reach for threshold pool when many contributors commit toward one target and nothing is collected unless the pool reaches it by the close date. Commitments sit in escrow until the pool activates or fails. Pick weighted distribution to pay a pool out and instant transfer for a single payment.";
|
|
89
|
-
when_not(contribution_instrument) {
|
|
90
|
-
fields {
|
|
91
|
-
[beneficiary]AccountId { type: account<C>; description: concat("The ", words(beneficiary), " account"); "x-hyperscale-reference-filter": { column: role; values: [customer_balance]; }; }
|
|
92
|
-
close_by { type: date; description: "Stored close anchor"; }
|
|
93
|
-
currency { type: text; description: "ISO 4217 currency code"; minLength: 3; maxLength: 3; pattern: "^[A-Z]{3}$"; }
|
|
94
|
-
maxContributors { type: integer; description: concat("Exactly ", max_contributors, " admitted contributors"); const: max_contributors; }
|
|
95
|
-
target { type: money<C>; description: "Funding target in {C} minor units"; }
|
|
96
|
-
}
|
|
97
|
-
lifecycle {
|
|
98
|
-
states open active failed settled;
|
|
99
|
-
initial open;
|
|
100
|
-
on activate: open -> active;
|
|
101
|
-
on fail: open -> failed;
|
|
102
|
-
on close: active -> settled;
|
|
103
|
-
}
|
|
104
|
-
parties { beneficiary: concat(camel(beneficiary), "AccountId"); }
|
|
105
|
-
required: [concat(camel(beneficiary), "AccountId"), currency, target, close_by, maxContributors];
|
|
106
|
-
title: sentence(instrument);
|
|
107
|
-
id_prefix: prefix(instrument);
|
|
108
|
-
let(pool_summary): get(wording, "pool_summary");
|
|
109
|
-
when_not(pool_summary) { summary: concat("Threshold contribution pool for ", words(beneficiary)); }
|
|
110
|
-
when(pool_summary) { summary: pool_summary; }
|
|
111
|
-
let(pool_description): get(wording, "pool_description");
|
|
112
|
-
when_not(pool_description) { description: "All-or-nothing aggregate contribution threshold"; }
|
|
113
|
-
when(pool_description) { description: pool_description; }
|
|
114
|
-
aggregate_invariants { childField: amount; childInstrumentId: concat(instrument, "_commitment"); childRefField: concat(camel(instrument), "Id"); childStatuses: [committed]; parentField: target; }
|
|
115
|
-
aggregate_invariants { childInstrumentId: concat(instrument, "_commitment"); childRefField: concat(camel(instrument), "Id"); childStatuses: [committed]; count: true; parentField: maxContributors; }
|
|
116
|
-
action create {
|
|
117
|
-
agent_description: "Open the pool before any commitment exists. The caller supplies the target, the currency, the beneficiary account, the close anchor, and the pinned contributor count. No money moves and the pool opens for commitments.";
|
|
118
|
-
let(create_summary): get(wording, "create_summary");
|
|
119
|
-
when_not(create_summary) { summary: "Open the contribution pool"; }
|
|
120
|
-
when(create_summary) { summary: create_summary; }
|
|
121
|
-
moves: [];
|
|
122
|
-
steps: [];
|
|
123
|
-
}
|
|
124
|
-
action activate {
|
|
125
|
-
summary: "Activate when commitments meet the target";
|
|
126
|
-
due { field: close_by; }
|
|
127
|
-
requires aggregate { instrumentId: concat(instrument, "_commitment"); over: children; refField: concat(camel(instrument), "Id"); statuses: [committed]; check: { kind: sum_at_least; amountField: amount; targetField: target; }; }
|
|
128
|
-
moves: [];
|
|
129
|
-
steps: [];
|
|
130
|
-
}
|
|
131
|
-
action fail {
|
|
132
|
-
summary: "Fail when commitments remain below target";
|
|
133
|
-
due { field: close_by; }
|
|
134
|
-
requires aggregate { instrumentId: concat(instrument, "_commitment"); over: children; refField: concat(camel(instrument), "Id"); statuses: [committed]; check: { kind: sum_below; amountField: amount; targetField: target; }; }
|
|
135
|
-
moves: [];
|
|
136
|
-
steps: [];
|
|
137
|
-
}
|
|
138
|
-
action close {
|
|
139
|
-
agent_description: "Settle the pool once every admitted commitment is collected or was cancelled before activation. The pool must be active. No money moves here, collect already paid the beneficiary.";
|
|
140
|
-
summary: "Settle after every admitted row is collected or was cancelled before activation";
|
|
141
|
-
requires aggregate { instrumentId: concat(instrument, "_commitment"); over: children; refField: concat(camel(instrument), "Id"); statuses: [cancelled, collected]; check: { kind: all_in; }; }
|
|
142
|
-
moves: [];
|
|
143
|
-
steps: [];
|
|
144
|
-
}
|
|
145
|
-
instruments {
|
|
146
|
-
instrument {
|
|
147
|
-
let(parent_name): instrument;
|
|
148
|
-
id: concat(parent_name, "_commitment");
|
|
149
|
-
generatedPrefix: true;
|
|
150
|
-
fields {
|
|
151
|
-
amount { type: money<C>; description: "One whole commitment amount"; }
|
|
152
|
-
[beneficiary]AccountId { type: account<C>; description: concat("The ", words(beneficiary), " account"); "x-hyperscale-reference-filter": { column: role; values: [customer_balance]; }; }
|
|
153
|
-
[parent_name]Id { type: ref<parent_name>; description: concat("The exact ", parent_name); pattern: concat("^", prefix(parent_name), "_(sandbox|live)_[a-z0-9]{8,64}$"); }
|
|
154
|
-
[contributor]AccountId { type: account<C>; description: concat("The ", words(contributor), " account"); "x-hyperscale-reference-filter": { column: role; values: [customer_balance]; }; }
|
|
155
|
-
currency { type: text; description: "ISO 4217 currency code"; minLength: 3; maxLength: 3; pattern: "^[A-Z]{3}$"; }
|
|
156
|
-
}
|
|
157
|
-
lifecycle {
|
|
158
|
-
states committed cancelled collected refunded;
|
|
159
|
-
initial committed;
|
|
160
|
-
on cancel: committed -> cancelled;
|
|
161
|
-
on collect: committed -> collected;
|
|
162
|
-
on refund: committed -> refunded;
|
|
163
|
-
}
|
|
164
|
-
parties { beneficiary: concat(camel(beneficiary), "AccountId"); payer: concat(camel(contributor), "AccountId"); }
|
|
165
|
-
required: [concat(camel(beneficiary), "AccountId"), concat(camel(contributor), "AccountId"), amount, currency, concat(camel(parent_name), "Id")];
|
|
166
|
-
title: concat(sentence(parent_name), " Commitment");
|
|
167
|
-
agent_description: concat("Reach for this child row to record one whole commitment into a ", words(parent_name), ". Each contributor commits once, the money sits in the product escrow, and whether it is collected or refunded depends on the pool reaching its target.");
|
|
168
|
-
summary: concat("Whole commitment to ", parent_name);
|
|
169
|
-
description: concat("One whole commitment linked to ", parent_name);
|
|
170
|
-
action create {
|
|
171
|
-
agent_description: "Commit one whole amount into the pool escrow. The pool must still be open, the currency and beneficiary account must match it, and the committed total plus this amount must stay within the target. The money leaves the contributor now.";
|
|
172
|
-
let(commitment_create_summary): get(wording, "commitment_create_summary");
|
|
173
|
-
when_not(commitment_create_summary) { summary: "Store one whole commitment without exceeding the pool target"; }
|
|
174
|
-
when(commitment_create_summary) { summary: commitment_create_summary; }
|
|
175
|
-
requires refs { field: concat(camel(parent_name), "Id"); statuses: [open]; bind: { [beneficiary]AccountId: concat("fields.", camel(beneficiary), "AccountId"); currency: "fields.currency"; }; }
|
|
176
|
-
requires exposure { amountField: amount; anchorField: concat(camel(parent_name), "Id"); capField: target; capOnAnchor: true; childInstrumentId: concat(parent_name, "_commitment"); statuses: [committed]; }
|
|
177
|
-
moves: [{ amount: amount; from: payer; to: refs.escrowAccountId; key: commit; }];
|
|
178
|
-
steps: [{ operation: "account.escrow.provision"; bind: { currency: { from: instance; path: "fields.currency"; }; "owner.id": { from: instance; path: productId; }; "owner.type": { from: const; value: product; }; productId: { from: instance; path: productId; }; role: { from: const; value: product_escrow; }; }; capture: { escrowAccountId: accountId; }; }];
|
|
179
|
-
}
|
|
180
|
-
action cancel {
|
|
181
|
-
agent_description: "Pull one commitment back out of escrow to the contributor. The pool must still be open, so this is unavailable once the pool activates or fails. The money returns and the commitment is closed.";
|
|
182
|
-
let(commitment_cancel_summary): get(wording, "commitment_cancel_summary");
|
|
183
|
-
when_not(commitment_cancel_summary) { summary: "Cancel one commitment while the pool is open"; }
|
|
184
|
-
when(commitment_cancel_summary) { summary: commitment_cancel_summary; }
|
|
185
|
-
requires refs { field: concat(camel(parent_name), "Id"); statuses: [open]; match: { "fields.[beneficiary]AccountId": concat("fields.", camel(beneficiary), "AccountId"); "fields.currency": "fields.currency"; }; }
|
|
186
|
-
moves: [{ amount: amount; from: refs.escrowAccountId; to: payer; key: cancel; }];
|
|
187
|
-
steps: [];
|
|
188
|
-
}
|
|
189
|
-
action collect {
|
|
190
|
-
agent_description: "Release one commitment out of escrow to the beneficiary. The pool must be active, which happens only once commitments meet the target. The money moves for good.";
|
|
191
|
-
summary: "Collect one successful commitment whole";
|
|
192
|
-
requires refs { field: concat(camel(parent_name), "Id"); statuses: [active]; match: { "fields.[beneficiary]AccountId": concat("fields.", camel(beneficiary), "AccountId"); "fields.currency": "fields.currency"; }; }
|
|
193
|
-
moves: [{ amount: amount; from: refs.escrowAccountId; to: beneficiary; key: collect; }];
|
|
194
|
-
steps: [];
|
|
195
|
-
}
|
|
196
|
-
action refund {
|
|
197
|
-
agent_description: "Return one commitment out of escrow to the contributor after the pool failed to reach its target. The pool must be in failed. The money returns and the commitment is closed.";
|
|
198
|
-
let(commitment_refund_summary): get(wording, "commitment_refund_summary");
|
|
199
|
-
when_not(commitment_refund_summary) { summary: "Refund one failed-pool commitment whole"; }
|
|
200
|
-
when(commitment_refund_summary) { summary: commitment_refund_summary; }
|
|
201
|
-
requires refs { field: concat(camel(parent_name), "Id"); statuses: [failed]; match: { "fields.[beneficiary]AccountId": concat("fields.", camel(beneficiary), "AccountId"); "fields.currency": "fields.currency"; }; }
|
|
202
|
-
moves: [{ amount: amount; from: refs.escrowAccountId; to: payer; key: refund; }];
|
|
203
|
-
steps: [];
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
when(contribution_instrument) {
|
|
210
|
-
let(contribution_id): get(contribution_instrument, "instrument");
|
|
211
|
-
let(contribution_ref_field): get(contribution_instrument, "ref_field");
|
|
212
|
-
let(contribution_amount_field): get(contribution_instrument, "amount_field");
|
|
213
|
-
let(committed_status): get(contribution_instrument, "committed_status");
|
|
214
|
-
let(collected_status): get(contribution_instrument, "collected_status");
|
|
215
|
-
let(instrument_id_prefix): get(contribution_instrument, "id_prefix");
|
|
216
|
-
let(active_parked): get(contribution_instrument, "active_parked");
|
|
217
|
-
let(custody_ref): get(contribution_instrument, "custody_ref");
|
|
218
|
-
let(beneficiary_account_description): get(contribution_instrument, "beneficiary_account_description");
|
|
219
|
-
let(target_description): get(contribution_instrument, "target_description");
|
|
220
|
-
let(currency_description): get(contribution_instrument, "currency_description");
|
|
221
|
-
let(create_examples): get(contribution_instrument, "create_examples");
|
|
222
|
-
let(activate_examples): get(contribution_instrument, "activate_examples");
|
|
223
|
-
let(fail_examples): get(contribution_instrument, "fail_examples");
|
|
224
|
-
let(close_examples): get(contribution_instrument, "close_examples");
|
|
225
|
-
let(beneficiary_account_field): camel(beneficiary_account);
|
|
226
|
-
let(target_field): camel(target);
|
|
227
|
-
let(close_field): camel(close_by);
|
|
228
|
-
let(obligation_type): get(funding_join, "obligation");
|
|
229
|
-
|
|
230
|
-
fields {
|
|
231
|
-
[beneficiary_account_field] { type: account<C>; description: beneficiary_account_description; "x-hyperscale-reference-filter": { column: role; values: [customer_balance]; }; }
|
|
232
|
-
[close_field] { type: date; }
|
|
233
|
-
currency { type: text; description: currency_description; maxLength: 3; minLength: 3; pattern: "^[A-Z]{3}$"; }
|
|
234
|
-
when(memo) { [memo] { type: text; optional: true; maxLength: 180; minLength: 1; } }
|
|
235
|
-
[target_field] { type: money<C>; description: target_description; }
|
|
236
|
-
when(funding_join) {
|
|
237
|
-
fundingAccountId { type: account<C>; "x-hyperscale-reference-filter": { column: role; values: [customer_balance]; }; }
|
|
238
|
-
obligationId { type: ref<obligation_type>; optional: true; }
|
|
239
|
-
minimumTicket: money<C>;
|
|
240
|
-
investorCapBps { type: integer; minimum: 1; maximum: 9999; }
|
|
241
|
-
maximumTicket { type: money<C>; optional: true; }
|
|
242
|
-
fixedProfit: money<C>;
|
|
243
|
-
installmentCount { type: integer; minimum: 1; maximum: 360; }
|
|
244
|
-
earningRule { type: text; enum: [per_slice_on_due, on_disbursement]; }
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
when(funding_join) { computes derived: { field: maximumTicket; source_field: target_field; rule: { kind: percentage_of; bps: { field: investorCapBps; }; }; rounding: floor; };
|
|
248
|
-
}
|
|
249
|
-
lifecycle {
|
|
250
|
-
states open active failed settled;
|
|
251
|
-
initial open;
|
|
252
|
-
on activate: open -> active;
|
|
253
|
-
on close: active -> settled;
|
|
254
|
-
on fail: open -> failed;
|
|
255
|
-
}
|
|
256
|
-
parties { beneficiary: beneficiary_account_field; }
|
|
257
|
-
when_not(funding_join) { required: [beneficiary_account_field, target_field, currency, close_field]; }
|
|
258
|
-
when(funding_join) { required: [beneficiary_account_field, target_field, currency, close_field, fundingAccountId, minimumTicket, investorCapBps, fixedProfit, installmentCount, earningRule]; }
|
|
259
|
-
aggregate_invariants { childField: contribution_amount_field; childInstrumentId: contribution_id; childRefField: contribution_ref_field; childStatuses: [committed_status, collected_status]; parentField: target_field; }
|
|
260
|
-
caller_parked_states { active: active_parked; }
|
|
261
|
-
id_prefix: instrument_id_prefix;
|
|
262
|
-
action create {
|
|
263
|
-
public: none;
|
|
264
|
-
agent_description: "Open the pool before any contribution exists. The caller supplies the target, the currency, the beneficiary account, and the close anchor, and the pool provisions its escrow account. No money moves.";
|
|
265
|
-
when_not(funding_join) { examples: create_examples; }
|
|
266
|
-
moves: [];
|
|
267
|
-
steps: [{ operation: "account.escrow.provision"; bind: { currency: { from: instance; path: "fields.currency"; }; "owner.id": { from: instance; path: productId; }; "owner.type": { from: const; value: product; }; productId: { from: instance; path: productId; }; role: { from: const; value: product_escrow; }; }; capture: { [custody_ref]: accountId; }; }];
|
|
268
|
-
}
|
|
269
|
-
action activate {
|
|
270
|
-
public: none;
|
|
271
|
-
due { field: close_field; }
|
|
272
|
-
examples: activate_examples;
|
|
273
|
-
requires aggregate { instrumentId: contribution_id; over: children; refField: contribution_ref_field; statuses: [committed_status]; check: { kind: sum_at_least; amountField: contribution_amount_field; targetField: target_field; }; }
|
|
274
|
-
moves: [];
|
|
275
|
-
steps: [];
|
|
276
|
-
}
|
|
277
|
-
action fail {
|
|
278
|
-
public: none;
|
|
279
|
-
due { field: close_field; }
|
|
280
|
-
examples: fail_examples;
|
|
281
|
-
requires aggregate { instrumentId: contribution_id; over: children; refField: contribution_ref_field; statuses: [committed_status]; check: { kind: sum_below; amountField: contribution_amount_field; targetField: target_field; }; }
|
|
282
|
-
moves: [];
|
|
283
|
-
steps: [];
|
|
284
|
-
}
|
|
285
|
-
action close {
|
|
286
|
-
public: none;
|
|
287
|
-
agent_description: "Settle the pool once every contribution has been collected into the beneficiary account. No money moves. Refused while any contribution is still uncollected, so wait for the per-contribution collections to run rather than retrying.";
|
|
288
|
-
when_not(funding_join) { examples: close_examples; }
|
|
289
|
-
when_not(funding_join) { requires aggregate { instrumentId: contribution_id; over: children; refField: contribution_ref_field; statuses: [collected_status]; check: { kind: all_in; }; } }
|
|
290
|
-
when(funding_join) {
|
|
291
|
-
input: { type: object; additionalProperties: false; properties: { obligationId: { type: string; pattern: get(funding_join, "obligation_pattern"); }; }; required: [obligationId]; };
|
|
292
|
-
updates: [obligationId];
|
|
293
|
-
requires refs: { field: obligationId; statuses: [created]; match: { "fields.fundingAccountId": fields.fundingAccountId; "fields.currency": fields.currency; }; };
|
|
294
|
-
funding: {
|
|
295
|
-
obligation_field: obligationId; ticket_instrument_id: contribution_id; ticket_ref_field: contribution_ref_field;
|
|
296
|
-
ticket_amount_field: contribution_amount_field; ticket_investor_field: get(funding_join, "investor_field"); ticket_account_field: get(funding_join, "wallet_account_field");
|
|
297
|
-
ticket_status: committed_status; collect_action: collect; principal_field: target_field;
|
|
298
|
-
source_account_path: concat("refs.", custody_ref); destination_account_field: fundingAccountId;
|
|
299
|
-
terms: { [target_field]: principal; fixedProfit: fixedProfit; installmentCount: installmentCount; earningRule: earningRule; [beneficiary_account_field]: borrowerAccountId; currency: currency; };
|
|
300
|
-
capture: fundingSnapshot;
|
|
301
|
-
};
|
|
302
|
-
}
|
|
303
|
-
moves: [];
|
|
304
|
-
steps: [];
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
}
|
|
@@ -1,237 +0,0 @@
|
|
|
1
|
-
module std.money_flows.weighted_distribution
|
|
2
|
-
|
|
3
|
-
// Frozen largest-remainder distribution splitting one pool across dynamic recipients by recorded weights.
|
|
4
|
-
//
|
|
5
|
-
// ### Purpose
|
|
6
|
-
// `weighted_distribution` distributes dividend pools, creator royalties, liquidation proceeds, and investment returns.
|
|
7
|
-
// Child entitlement rows are recorded for each recipient with their respective weight. Once all recipients are recorded,
|
|
8
|
-
// an evidence-backed snapshot freezes the entitlement set. Each recipient is then paid their exact largest-remainder
|
|
9
|
-
// share such that all payouts sum exactly to `amount` without rounding leaks.
|
|
10
|
-
//
|
|
11
|
-
// ### Selection guidance
|
|
12
|
-
// - vs `pooled_split`: `weighted_distribution` handles dynamic recipient rosters recorded as child rows and frozen
|
|
13
|
-
// via snapshot. `pooled_split` hardcodes a fixed set of recipients and static percentages at definition time.
|
|
14
|
-
// - vs `settlement_batch`: `weighted_distribution` splits one funding pool out to many recipients.
|
|
15
|
-
// `settlement_batch` aggregates many inbound charges and adjustments into a single net payout.
|
|
16
|
-
//
|
|
17
|
-
// ### Parameters
|
|
18
|
-
// - `source`: The funding party providing the distribution pool.
|
|
19
|
-
// - `recipient`: The party representing entitled recipients.
|
|
20
|
-
// - `amount`: Total distribution pool in minor units of currency `C`.
|
|
21
|
-
// - `record_at`: Date when entitlement eligibility is established.
|
|
22
|
-
// - `weight`: Binding name for individual recipient weight amounts.
|
|
23
|
-
// - `max_recipients`: Exact number of entitlement rows required before snapshotting.
|
|
24
|
-
// - `snapshot`: Port freezing the entitlement set against stored decision evidence.
|
|
25
|
-
// - `rounding_policy`: Mathematical rounding policy (`largest_remainder`).
|
|
26
|
-
// - `withholding_policy`: Tax or withholding policy (`refuse`).
|
|
27
|
-
// - `correction_policy`: Error correction policy (`new_distribution`).
|
|
28
|
-
// - `flat`: Optional flat distribution configuration block.
|
|
29
|
-
// - `id_prefix_override`: Optional custom prefix for generated instrument IDs.
|
|
30
|
-
//
|
|
31
|
-
// ### Decision ports
|
|
32
|
-
// - `snapshot`: Port freezing the entitlement set with an evidence reference, preventing further entries.
|
|
33
|
-
//
|
|
34
|
-
// ### Example
|
|
35
|
-
// ```hsx
|
|
36
|
-
// program weighted_distribution_example "Weighted distribution example"
|
|
37
|
-
// import { weighted_distribution } from "std/money_flows"
|
|
38
|
-
// party distribution_source: business
|
|
39
|
-
// party recipient: business
|
|
40
|
-
// settlement proceeds = weighted_distribution {
|
|
41
|
-
// source: distribution_source
|
|
42
|
-
// recipient: recipient
|
|
43
|
-
// amount: distributableAmount: money(SAR)
|
|
44
|
-
// weight: entitlementWeight: money(SAR)
|
|
45
|
-
// max_recipients: 12
|
|
46
|
-
// record_at: recordAt
|
|
47
|
-
// snapshot: port snapshot_entitlements
|
|
48
|
-
// rounding_policy: largest_remainder
|
|
49
|
-
// withholding_policy: refuse
|
|
50
|
-
// correction_policy: new_distribution
|
|
51
|
-
// }
|
|
52
|
-
// port snapshot_entitlements {
|
|
53
|
-
// allowed: [distribution_source]
|
|
54
|
-
// shape: { evidenceReference: text }
|
|
55
|
-
// }
|
|
56
|
-
// ```
|
|
57
|
-
// ### Receipt-backed distributions
|
|
58
|
-
// receipts selects repeated distributions from immutable allocation receipts.
|
|
59
|
-
// It names round, receipt, path, mode (cash or loss), fee_bps and vat_bps.
|
|
60
|
-
// The funding snapshot owns ticket weights and original wallet destinations.
|
|
61
|
-
// Cash floors each ratio after fee and fee VAT; a named account receives residual.
|
|
62
|
-
// Loss assigns principal by largest remainder without cash, fee or VAT.
|
|
63
|
-
// Each receipt is consumed once; a new receipt may fund a new distribution record.
|
|
64
|
-
// This mode does not use caller-entered weights or a once-only settled principal pool.
|
|
65
|
-
//
|
|
66
|
-
export instrument weighted_distribution<C>(
|
|
67
|
-
source: optional<party>,
|
|
68
|
-
recipient: optional<party>,
|
|
69
|
-
amount: optional<money<C>>,
|
|
70
|
-
record_at: optional<date>,
|
|
71
|
-
weight: optional<money<C>>,
|
|
72
|
-
max_recipients: optional<integer>,
|
|
73
|
-
snapshot: optional<condition>,
|
|
74
|
-
rounding_policy: optional<text>,
|
|
75
|
-
withholding_policy: optional<text>,
|
|
76
|
-
correction_policy: optional<text>,
|
|
77
|
-
flat: optional<block>,
|
|
78
|
-
receipts: optional<block>,
|
|
79
|
-
id_prefix_override: optional<text>,
|
|
80
|
-
) {
|
|
81
|
-
when_not(receipts) {
|
|
82
|
-
when_not(flat) {
|
|
83
|
-
fields {
|
|
84
|
-
currency { type: text; description: "ISO 4217 currency code"; minLength: 3; maxLength: 3; pattern: "^[A-Z]{3}$"; }
|
|
85
|
-
amount { type: money<C>; description: "Distribution pool in {C} minor units"; }
|
|
86
|
-
[source]AccountId { type: account<C>; description: concat("The ", words(source), " account"); "x-hyperscale-reference-filter": { column: role; values: [customer_balance]; }; }
|
|
87
|
-
maxRecipients { type: integer; description: concat("Exactly ", max_recipients, " frozen entitlement rows"); const: max_recipients; }
|
|
88
|
-
record_at { type: date; description: "Stored record date"; }
|
|
89
|
-
}
|
|
90
|
-
lifecycle { states open snapshotted; initial open; on [snapshot]: open -> snapshotted; }
|
|
91
|
-
parties { payer: concat(camel(source), "AccountId"); }
|
|
92
|
-
required: [concat(camel(source), "AccountId"), currency, amount, record_at, maxRecipients];
|
|
93
|
-
title: sentence(instrument);
|
|
94
|
-
id_prefix: prefix(instrument);
|
|
95
|
-
agent_description: "Reach for weighted distribution when one pool splits across many recipients by declared weights. Record one entitlement per recipient, freeze the set with the snapshot port, then each entitlement pays its own largest-remainder share, so the shares add back to the pool exactly. Settlement batch goes the other way, many charges into one payout.";
|
|
96
|
-
summary: "Frozen largest-remainder distribution";
|
|
97
|
-
description: "Evidence-frozen weighted distribution";
|
|
98
|
-
aggregate_invariants { childInstrumentId: concat(instrument, "_entitlement"); childRefField: concat(camel(instrument), "Id"); childStatuses: [recorded, paid]; count: true; parentField: maxRecipients; }
|
|
99
|
-
action create { agent_description: "Open a distribution and start recording entitlements. No money moves. The pool amount, the record date, and the exact recipient count are fixed here."; summary: "Open entitlement recording"; moves: []; steps: []; }
|
|
100
|
-
action [snapshot] {
|
|
101
|
-
agent_description: "Freeze the entitlement set against a stated evidence reference. The distribution must still be open and its recorded entitlements must match the declared recipient count. Nothing can be recorded afterwards and there is no unfreeze. No money moves.";
|
|
102
|
-
summary: "Freeze the entitlement set from stored evidence";
|
|
103
|
-
port { allowedParties: [payer]; }
|
|
104
|
-
input { type: object; properties: { evidenceReference: { type: string; description: "Required decision reference retained in the operation receipt"; minLength: 1; maxLength: 180; }; }; required: [evidenceReference]; additionalProperties: false; }
|
|
105
|
-
capture_input { snapshotEvidenceReference: evidenceReference; }
|
|
106
|
-
moves: [];
|
|
107
|
-
steps: [];
|
|
108
|
-
}
|
|
109
|
-
instruments {
|
|
110
|
-
instrument {
|
|
111
|
-
let(parent_name): instrument;
|
|
112
|
-
id: concat(parent_name, "_entitlement");
|
|
113
|
-
generatedPrefix: true;
|
|
114
|
-
fields {
|
|
115
|
-
currency { type: text; description: "ISO 4217 currency code"; minLength: 3; maxLength: 3; pattern: "^[A-Z]{3}$"; }
|
|
116
|
-
[source]AccountId { type: account<C>; description: concat("The ", words(source), " account"); "x-hyperscale-reference-filter": { column: role; values: [customer_balance]; }; }
|
|
117
|
-
[weight] { type: money<C>; description: "Stored non-negative entitlement weight"; }
|
|
118
|
-
[parent_name]Id { type: ref<parent_name>; description: concat("The exact ", parent_name); pattern: concat("^", prefix(parent_name), "_(sandbox|live)_[a-z0-9]{8,64}$"); }
|
|
119
|
-
[recipient]AccountId { type: account<C>; description: concat("The ", words(recipient), " account"); "x-hyperscale-reference-filter": { column: role; values: [customer_balance]; }; }
|
|
120
|
-
}
|
|
121
|
-
lifecycle { states recorded paid; initial recorded; on payout: recorded -> paid; }
|
|
122
|
-
parties { beneficiary: concat(camel(recipient), "AccountId"); payer: concat(camel(source), "AccountId"); }
|
|
123
|
-
required: [concat(camel(recipient), "AccountId"), concat(camel(source), "AccountId"), currency, concat(camel(parent_name), "Id"), weight];
|
|
124
|
-
title: concat(sentence(parent_name), " Entitlement");
|
|
125
|
-
agent_description: concat("One recipient weighted claim on ", parent_name, ". Record one per recipient before the snapshot. Its share is derived from its weight against the frozen set, never supplied by the caller.");
|
|
126
|
-
summary: concat("Frozen entitlement in ", parent_name);
|
|
127
|
-
description: concat("One frozen weighted entitlement in ", parent_name);
|
|
128
|
-
action create {
|
|
129
|
-
agent_description: "Record one recipient weight against an open distribution. The parent distribution must still be open, and the currency and source account are copied from it. No money moves. Do not supply a share. It is derived later from the frozen set.";
|
|
130
|
-
summary: "Record one entitlement before snapshot";
|
|
131
|
-
requires refs { field: concat(camel(parent_name), "Id"); statuses: [open]; bind: { currency: "fields.currency"; [source]AccountId: concat("fields.", camel(source), "AccountId"); }; }
|
|
132
|
-
moves: [];
|
|
133
|
-
steps: [];
|
|
134
|
-
}
|
|
135
|
-
action payout {
|
|
136
|
-
agent_description: "Pay this recipient their largest-remainder share of the pool. The parent distribution must already be snapshotted. The amount is computed from the recorded weights, not supplied by the caller, and a zero share moves nothing. This moves money and does not reverse.";
|
|
137
|
-
summary: "Pay the deterministic largest-remainder share once";
|
|
138
|
-
requires refs { field: concat(camel(parent_name), "Id"); statuses: [snapshotted]; match: { "fields.currency": "fields.currency"; "fields.[source]AccountId": concat("fields.", camel(source), "AccountId"); }; }
|
|
139
|
-
computes distribute { amountRef: payoutShare; onZero: skip_steps; pool: { from: parent; path: concat("fields.", amount); }; refField: concat(camel(parent_name), "Id"); statuses: [recorded, paid]; weightField: weight; }
|
|
140
|
-
moves: [{ amount: refs.payoutShare; from: payer; to: beneficiary; key: payout; }];
|
|
141
|
-
steps: [];
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
when(flat) {
|
|
147
|
-
let(pool): get(flat, "pool");
|
|
148
|
-
let(pool_ref): get(pool, "field");
|
|
149
|
-
let(pool_ref_field): camel(pool_ref);
|
|
150
|
-
let(pool_instrument): get(pool, "instrument");
|
|
151
|
-
let(pool_create_statuses): get(pool, "create_statuses");
|
|
152
|
-
let(pool_payout_statuses): get(pool, "payout_statuses");
|
|
153
|
-
let(pool_bind): get(pool, "bind");
|
|
154
|
-
let(pool_match): get(pool, "match");
|
|
155
|
-
let(pool_amount_path): get(pool, "amount_path");
|
|
156
|
-
let(weight_source): get(flat, "weight_source");
|
|
157
|
-
let(weight_ref): get(weight_source, "field");
|
|
158
|
-
let(weight_ref_field): camel(weight_ref);
|
|
159
|
-
let(weight_statuses): get(weight_source, "statuses");
|
|
160
|
-
let(weight_bind): get(weight_source, "bind");
|
|
161
|
-
let(weight_match): get(weight_source, "match");
|
|
162
|
-
let(unique_weight_source): get(weight_source, "unique");
|
|
163
|
-
let(group): get(flat, "group");
|
|
164
|
-
let(group_ref): get(group, "field");
|
|
165
|
-
let(source_account_field): camel(concat(source, "_account_id"));
|
|
166
|
-
let(recipient_account_field): camel(concat(recipient, "_account_id"));
|
|
167
|
-
fields {
|
|
168
|
-
[amount] { type: money<C>; description: get(flat, "amount_description"); }
|
|
169
|
-
[record_at] { type: date; }
|
|
170
|
-
[pool_ref] { type: pool_ref; description: get(pool, "description"); pattern: get(pool, "pattern"); }
|
|
171
|
-
currency { type: text; description: "ISO 4217 currency code"; minLength: 3; maxLength: 3; pattern: "^[A-Z]{3}$"; }
|
|
172
|
-
[weight_ref] { type: weight_ref; description: get(weight_source, "description"); pattern: get(weight_source, "pattern"); }
|
|
173
|
-
[group_ref] { type: group_ref; description: get(group, "description"); pattern: get(group, "pattern"); }
|
|
174
|
-
[recipient]AccountId { type: account<C>; description: get(flat, "recipient_account_description"); "x-hyperscale-reference-filter": { column: role; values: [customer_balance]; }; }
|
|
175
|
-
[source]AccountId { type: account<C>; description: get(flat, "source_account_description"); "x-hyperscale-reference-filter": { column: role; values: [customer_balance]; }; }
|
|
176
|
-
}
|
|
177
|
-
lifecycle { states claimed paid; initial claimed; on payout: claimed -> paid; }
|
|
178
|
-
parties { beneficiary: recipient_account_field; }
|
|
179
|
-
required: get(flat, "required");
|
|
180
|
-
title: sentence(instrument);
|
|
181
|
-
id_prefix: if_eq(id_prefix_override, false, prefix(instrument), id_prefix_override);
|
|
182
|
-
agent_description: concat("One weighted claim on a distribution pool that another instrument owns. Record one claim per recipient before the pool settles. Its share is derived from its weight against the settled pool, never supplied by the caller, and the shares add back to the pool exactly.");
|
|
183
|
-
summary: "Flat largest-remainder distribution claim";
|
|
184
|
-
description: "One weighted claim on a settled distribution pool";
|
|
185
|
-
action create {
|
|
186
|
-
public: none;
|
|
187
|
-
agent_description: concat("Record one recipient weight against a pool that is still open. Both the pool and the weight source must be in an accepting state, and the same weight source cannot be claimed twice. No money moves. Do not supply a share. It is derived at payout from the settled pool.");
|
|
188
|
-
summary: "Open one weighted distribution claim";
|
|
189
|
-
requires_refs: [
|
|
190
|
-
{ field: pool_ref_field; statuses: pool_create_statuses; bind: pool_bind; match: pool_match; },
|
|
191
|
-
{ field: weight_ref_field; statuses: weight_statuses; bind: weight_bind; match: weight_match; unique: unique_weight_source; }
|
|
192
|
-
];
|
|
193
|
-
moves: [];
|
|
194
|
-
steps: [];
|
|
195
|
-
}
|
|
196
|
-
action payout {
|
|
197
|
-
public: none;
|
|
198
|
-
summary: "Pay one largest-remainder share from the settled pool";
|
|
199
|
-
due { field: record_at; whenParentStatus: { instrumentId: pool_instrument; refField: pool_ref_field; statuses: pool_payout_statuses; }; }
|
|
200
|
-
requires refs { field: pool_ref_field; statuses: pool_payout_statuses; }
|
|
201
|
-
computes distribute { amountRef: payoutShare; onZero: skip_steps; pool: { from: parent; path: pool_amount_path; }; refField: pool_ref_field; statuses: [claimed, paid]; weightField: amount; }
|
|
202
|
-
moves: [{ key: transfer; operation: internal_transfer.create; bind: { amount: { from: instance; path: refs.payoutShare; }; currency: { from: instance; path: fields.currency; }; destinationAccountId: { from: instance; path: concat("fields.", recipient_account_field); }; productId: { from: instance; path: productId; }; sourceAccountId: { from: instance; path: concat("fields.", source_account_field); }; }; capture: { payoutTransferId: transferId; }; }];
|
|
203
|
-
steps: [];
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
when(receipts) {
|
|
208
|
-
let(round_type): get(receipts, "round");
|
|
209
|
-
let(receipt_type): get(receipts, "receipt");
|
|
210
|
-
fields {
|
|
211
|
-
roundId: ref<round_type>;
|
|
212
|
-
receiptId: ref<receipt_type>;
|
|
213
|
-
currency { type: text; pattern: "^[A-Z]{3}$"; minLength: 3; maxLength: 3; }
|
|
214
|
-
feeAccountId { type: account<C>; "x-hyperscale-reference-filter": { column: role; values: [customer_balance]; }; }
|
|
215
|
-
taxAccountId { type: account<C>; "x-hyperscale-reference-filter": { column: role; values: [customer_balance]; }; }
|
|
216
|
-
residualAccountId { type: account<C>; "x-hyperscale-reference-filter": { column: role; values: [customer_balance]; }; }
|
|
217
|
-
}
|
|
218
|
-
parties { fee: feeAccountId; tax: taxAccountId; beneficiary: residualAccountId; }
|
|
219
|
-
lifecycle { states distributed; initial distributed; }
|
|
220
|
-
title: sentence(instrument);
|
|
221
|
-
summary: "Receipt-backed frozen-weight distribution";
|
|
222
|
-
agent_description: "Consume one allocation receipt against the round's frozen tickets. Cash distributions carve fee and VAT, floor each ticket share and pay the residual to its named owner. Loss mode records principal loss without cash.";
|
|
223
|
-
action create {
|
|
224
|
-
public: none;
|
|
225
|
-
agent_description: "Distribute one receipt once. All amounts and sources come from the consumed receipt and frozen funding snapshot.";
|
|
226
|
-
requires refs: { field: roundId; statuses: [settled]; match: { "fields.currency": fields.currency; }; };
|
|
227
|
-
receipt distribution: {
|
|
228
|
-
round_field: roundId; snapshot_ref: fundingSnapshot;
|
|
229
|
-
receipt_field: receiptId; receipt_path: get(receipts, "path"); mode: get(receipts, "mode");
|
|
230
|
-
fee_bps: get(receipts, "fee_bps"); vat_bps: get(receipts, "vat_bps");
|
|
231
|
-
fee_account_field: feeAccountId; tax_account_field: taxAccountId; residual_account_field: residualAccountId;
|
|
232
|
-
capture: distributionReceipt;
|
|
233
|
-
};
|
|
234
|
-
steps: [];
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
}
|